From e489e182f2788aad351dbb6c2e940b9645b78b88 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 02:26:20 +0000 Subject: [PATCH 001/266] Bump the cypress group across 1 directory with 5 updates Bumps the cypress group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@badeball/cypress-cucumber-preprocessor](https://github.com/badeball/cypress-cucumber-preprocessor) | `21.0.3` | `22.0.0` | | [@cucumber/cucumber](https://github.com/cucumber/cucumber-js) | `11.1.1` | `11.2.0` | | [cypress](https://github.com/cypress-io/cypress) | `13.17.0` | `14.0.0` | | [cypress-network-idle](https://github.com/bahmutov/cypress-network-idle) | `1.14.2` | `1.15.0` | | [multiple-cucumber-html-reporter](https://github.com/WasiqB/multiple-cucumber-html-reporter) | `3.8.0` | `3.9.0` | Updates `@badeball/cypress-cucumber-preprocessor` from 21.0.3 to 22.0.0 - [Release notes](https://github.com/badeball/cypress-cucumber-preprocessor/releases) - [Changelog](https://github.com/badeball/cypress-cucumber-preprocessor/blob/master/CHANGELOG.md) - [Commits](https://github.com/badeball/cypress-cucumber-preprocessor/compare/v21.0.3...v22.0.0) Updates `@cucumber/cucumber` from 11.1.1 to 11.2.0 - [Release notes](https://github.com/cucumber/cucumber-js/releases) - [Changelog](https://github.com/cucumber/cucumber-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/cucumber/cucumber-js/compare/v11.1.1...v11.2.0) Updates `cypress` from 13.17.0 to 14.0.0 - [Release notes](https://github.com/cypress-io/cypress/releases) - [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md) - [Commits](https://github.com/cypress-io/cypress/compare/v13.17.0...v14.0.0) Updates `cypress-network-idle` from 1.14.2 to 1.15.0 - [Release notes](https://github.com/bahmutov/cypress-network-idle/releases) - [Commits](https://github.com/bahmutov/cypress-network-idle/compare/v1.14.2...v1.15.0) Updates `multiple-cucumber-html-reporter` from 3.8.0 to 3.9.0 - [Release notes](https://github.com/WasiqB/multiple-cucumber-html-reporter/releases) - [Changelog](https://github.com/WasiqB/multiple-cucumber-html-reporter/blob/main/CHANGELOG.md) - [Commits](https://github.com/WasiqB/multiple-cucumber-html-reporter/compare/v3.8.0...v3.9.0) --- updated-dependencies: - dependency-name: "@badeball/cypress-cucumber-preprocessor" dependency-type: direct:development update-type: version-update:semver-major dependency-group: cypress - dependency-name: "@cucumber/cucumber" dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cypress - dependency-name: cypress dependency-type: direct:development update-type: version-update:semver-major dependency-group: cypress - dependency-name: cypress-network-idle dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cypress - dependency-name: multiple-cucumber-html-reporter dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cypress ... Signed-off-by: dependabot[bot] --- package-lock.json | 473 +++++++++++++++++++++++----------------------- package.json | 10 +- yarn.lock | 252 ++++++++++++------------ 3 files changed, 375 insertions(+), 360 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9cfe642bc..16c5f6551 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,15 @@ "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/register": "^7.25.9", - "@badeball/cypress-cucumber-preprocessor": "^21.0.3", - "@cucumber/cucumber": "11.1.1", + "@badeball/cypress-cucumber-preprocessor": "^22.0.0", + "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", "@faker-js/faker": "9.3.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", - "cypress": "^13.17.0", - "cypress-network-idle": "^1.14.2", + "cypress": "^14.0.0", + "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.4.7", "expect": "^29.6.4", @@ -28,7 +28,7 @@ "import": "^0.0.6", "jsonwebtoken": "^9.0.2", "mock-socket": "^9.0.3", - "multiple-cucumber-html-reporter": "^3.8.0", + "multiple-cucumber-html-reporter": "^3.9.0", "neo4j-driver": "^4.3.4", "neode": "^0.4.8", "rosie": "^2.1.0", @@ -1768,9 +1768,9 @@ } }, "node_modules/@badeball/cypress-cucumber-preprocessor": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-21.0.3.tgz", - "integrity": "sha512-d88Qcy2uoasQiUP8mBHUkE4TH1Px/rsN56gKSp5C1P45dPC583lxgA4NwsmrgtpjXWXSCi5OYvSk3x7E60h0RA==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-22.0.0.tgz", + "integrity": "sha512-06dv/GmAS77CbRXPfQbBZFgRKXRBJRQyDg3kJTPO3b7KZ4V8AOt32cb083Zc92QAqrerIbvFrHBnruzE/2/NtA==", "dev": true, "funding": [ { @@ -1779,6 +1779,7 @@ } ], "hasInstallScript": true, + "license": "MIT", "dependencies": { "@cucumber/ci-environment": "^10.0.1", "@cucumber/cucumber": "^11.0.0", @@ -1813,16 +1814,7 @@ "node": ">=18.0.0" }, "peerDependencies": { - "cypress": "^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0" - } - }, - "node_modules/@badeball/cypress-cucumber-preprocessor/node_modules/@cucumber/cucumber-expressions": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-18.0.1.tgz", - "integrity": "sha512-NSid6bI+7UlgMywl5octojY5NXnxR9uq+JisjOrO52VbFsQM6gTWuQFE8syI10KnIBEdPzuEUSVEeZ0VFzRnZA==", - "dev": true, - "dependencies": { - "regexp-match-indices": "1.0.2" + "cypress": "^11.0.0 || ^12.0.0 || ^13.0.0 || ^14.0.0" } }, "node_modules/@badeball/cypress-cucumber-preprocessor/node_modules/@cucumber/messages": { @@ -1922,20 +1914,21 @@ "dev": true }, "node_modules/@cucumber/cucumber": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-11.1.1.tgz", - "integrity": "sha512-4i2vk4R1Ffi1JXiNrVMLxLJFgZ7e0BHdoRN6QiWdz5EDPS+Qv9ld4wGZWeahBH5ncDygIrkkhtYxDhqOBXLPxQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber/-/cucumber-11.2.0.tgz", + "integrity": "sha512-F69uIPTc7dfgU7/TGAaQaWUz7r/DzoPW39AfJoKQOC7IvBiPQwpvSIo6QEd+63pdpdKNRbtQoVl5vP9IclhhuA==", "dev": true, + "license": "MIT", "dependencies": { "@cucumber/ci-environment": "10.0.1", - "@cucumber/cucumber-expressions": "17.1.0", - "@cucumber/gherkin": "28.0.0", + "@cucumber/cucumber-expressions": "18.0.1", + "@cucumber/gherkin": "30.0.4", "@cucumber/gherkin-streams": "5.0.1", "@cucumber/gherkin-utils": "9.0.0", - "@cucumber/html-formatter": "21.6.0", - "@cucumber/junit-xml-formatter": "0.6.0", + "@cucumber/html-formatter": "21.7.0", + "@cucumber/junit-xml-formatter": "0.7.1", "@cucumber/message-streams": "4.0.1", - "@cucumber/messages": "24.1.0", + "@cucumber/messages": "27.0.2", "@cucumber/tag-expressions": "6.1.1", "assertion-error-formatter": "^3.0.0", "capital-case": "^1.0.4", @@ -1958,7 +1951,7 @@ "mkdirp": "^2.1.5", "mz": "^2.7.0", "progress": "^2.0.3", - "read-pkg-up": "^7.0.1", + "read-package-up": "^11.0.0", "resolve-pkg": "^2.0.0", "semver": "7.5.3", "string-argv": "0.3.1", @@ -1980,49 +1973,34 @@ } }, "node_modules/@cucumber/cucumber-expressions": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-17.1.0.tgz", - "integrity": "sha512-PCv/ppsPynniKPWJr5v566daCVe+pbxQpHGrIu/Ev57cCH9Rv+X0F6lio4Id3Z64TaG7btCRLUGewIgLwmrwOA==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@cucumber/cucumber-expressions/-/cucumber-expressions-18.0.1.tgz", + "integrity": "sha512-NSid6bI+7UlgMywl5octojY5NXnxR9uq+JisjOrO52VbFsQM6gTWuQFE8syI10KnIBEdPzuEUSVEeZ0VFzRnZA==", "dev": true, + "license": "MIT", "dependencies": { "regexp-match-indices": "1.0.2" } }, - "node_modules/@cucumber/cucumber/node_modules/@cucumber/gherkin": { - "version": "28.0.0", - "resolved": "https://registry.npmjs.org/@cucumber/gherkin/-/gherkin-28.0.0.tgz", - "integrity": "sha512-Ee6zJQq0OmIUPdW0mSnsCsrWA2PZAELNDPICD2pLfs0Oz7RAPgj80UsD2UCtqyAhw2qAR62aqlktKUlai5zl/A==", - "dev": true, - "dependencies": { - "@cucumber/messages": ">=19.1.4 <=24" - } - }, - "node_modules/@cucumber/cucumber/node_modules/@cucumber/html-formatter": { - "version": "21.6.0", - "resolved": "https://registry.npmjs.org/@cucumber/html-formatter/-/html-formatter-21.6.0.tgz", - "integrity": "sha512-Qw1tdObBJrgXgXwVjKVjB3hFhFPI8WhIFb+ULy8g5lDl5AdnKDiyDXAMvAWRX+pphnRMMNdkPCt6ZXEfWvUuAA==", - "dev": true, - "peerDependencies": { - "@cucumber/messages": ">=18" - } - }, "node_modules/@cucumber/cucumber/node_modules/@cucumber/messages": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-24.1.0.tgz", - "integrity": "sha512-hxVHiBurORcobhVk80I9+JkaKaNXkW6YwGOEFIh/2aO+apAN+5XJgUUWjng9NwqaQrW1sCFuawLB1AuzmBaNdQ==", + "version": "27.0.2", + "resolved": "https://registry.npmjs.org/@cucumber/messages/-/messages-27.0.2.tgz", + "integrity": "sha512-jo2B+vYXmpuLOKh6Gc8loHl2E8svCkLvEXLVgFwVHqKWZJWBTa9yTRCPmZIxrz4fnO7Pr3N3vKQCPu73/gjlVQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/uuid": "9.0.8", + "@types/uuid": "10.0.0", "class-transformer": "0.5.1", - "reflect-metadata": "0.2.1", - "uuid": "9.0.1" + "reflect-metadata": "0.2.2", + "uuid": "10.0.0" } }, "node_modules/@cucumber/cucumber/node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", + "dev": true, + "license": "MIT" }, "node_modules/@cucumber/cucumber/node_modules/ansi-styles": { "version": "4.3.0", @@ -2080,11 +2058,11 @@ } }, "node_modules/@cucumber/cucumber/node_modules/reflect-metadata": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", - "integrity": "sha512-i5lLI6iw9AU3Uu4szRNPPEkomnkjRTaVt9hy/bn5g/oSzekBSMeLZblcjP74AW0vBabqERLLIrz+gR8QYR54Tw==", - "deprecated": "This version has a critical bug in fallback handling. Please upgrade to reflect-metadata@0.2.2 or newer.", - "dev": true + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/@cucumber/cucumber/node_modules/semver": { "version": "7.5.3", @@ -2113,6 +2091,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@cucumber/cucumber/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/@cucumber/cucumber/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", @@ -2224,19 +2216,31 @@ } }, "node_modules/@cucumber/junit-xml-formatter": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@cucumber/junit-xml-formatter/-/junit-xml-formatter-0.6.0.tgz", - "integrity": "sha512-++PAuxliQhq7yr2Bn9P0fwBUo46OoKAK5f6M4PrwoHBqIsl/6pUS4mqpviuBrgZ8RD7BTrmASk4lUDJClAz/qA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@cucumber/junit-xml-formatter/-/junit-xml-formatter-0.7.1.tgz", + "integrity": "sha512-AzhX+xFE/3zfoYeqkT7DNq68wAQfBcx4Dk9qS/ocXM2v5tBv6eFQ+w8zaSfsktCjYzu4oYRH/jh4USD1CYHfaQ==", "dev": true, + "license": "MIT", "dependencies": { "@cucumber/query": "^13.0.2", "@teppeis/multimaps": "^3.0.0", + "luxon": "^3.5.0", "xmlbuilder": "^15.1.1" }, "peerDependencies": { "@cucumber/messages": "*" } }, + "node_modules/@cucumber/junit-xml-formatter/node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/@cucumber/message-streams": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@cucumber/message-streams/-/message-streams-4.0.1.tgz", @@ -2284,44 +2288,18 @@ } }, "node_modules/@cucumber/query": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/@cucumber/query/-/query-13.0.2.tgz", - "integrity": "sha512-ykjwL99F5ZmJ3XnIRPe/eA8LvfSTc+C6ZZXrD5QrAfhfMRomBNpZT03MNnxrJ92ge18eDbculhclrIJQiVJCJg==", + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@cucumber/query/-/query-13.1.0.tgz", + "integrity": "sha512-QBndVZOjXB3cPBw1tmIGWMIyttSAL1voJY4uveySsP5hMH2mk/mSeUIBNGjMZKvjCyalTTg57/MT4Sk560ZffQ==", "dev": true, + "license": "MIT", "dependencies": { - "@teppeis/multimaps": "3.0.0", - "assert": "^2.1.0" + "@teppeis/multimaps": "3.0.0" }, "peerDependencies": { "@cucumber/messages": "*" } }, - "node_modules/@cucumber/query/node_modules/assert": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", - "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-nan": "^1.3.2", - "object-is": "^1.1.5", - "object.assign": "^4.1.4", - "util": "^0.12.5" - } - }, - "node_modules/@cucumber/query/node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, "node_modules/@cucumber/tag-expressions": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@cucumber/tag-expressions/-/tag-expressions-6.1.1.tgz", @@ -4383,7 +4361,8 @@ "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/sax": { "version": "1.2.7", @@ -7732,11 +7711,12 @@ "optional": true }, "node_modules/cypress": { - "version": "13.17.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.17.0.tgz", - "integrity": "sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.0.0.tgz", + "integrity": "sha512-kEGqQr23so5IpKeg/dp6GVi7RlHx1NmW66o2a2Q4wk9gRaAblLZQSiZJuDI8UMC4LlG5OJ7Q6joAiqTrfRNbTw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "@cypress/request": "^3.0.6", "@cypress/xvfb": "^1.2.4", @@ -7786,14 +7766,15 @@ "cypress": "bin/cypress" }, "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" } }, "node_modules/cypress-network-idle": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/cypress-network-idle/-/cypress-network-idle-1.14.2.tgz", - "integrity": "sha512-xAdR8dH58KFPv8eCDWjviScITrJOcUpuMXYfYTc175nk2/NvnJ+I6ylSn1CM7yZmoV/gLbFa36QLiH5NfNEaLQ==", - "dev": true + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/cypress-network-idle/-/cypress-network-idle-1.15.0.tgz", + "integrity": "sha512-8zU16zhc7S3nMl1NTEEcNsZYlJy/ZzP2zPTTrngGxyXH32Ipake/xfHLZsgrzeWCieiS2AVhQsakhWqFzO3hpw==", + "dev": true, + "license": "MIT" }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", @@ -9187,6 +9168,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/find-up-simple": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz", + "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", @@ -9882,10 +9876,24 @@ "optional": true }, "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" }, "node_modules/html-void-elements": { "version": "3.0.0", @@ -10056,6 +10064,19 @@ "node": ">=8" } }, + "node_modules/index-to-position": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", + "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -10375,22 +10396,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-nan": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", - "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -12126,18 +12131,19 @@ "devOptional": true }, "node_modules/multiple-cucumber-html-reporter": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.8.0.tgz", - "integrity": "sha512-OLnTQ3qvLRD6WfNw1dbzbawqnYuq4T6z1Rqgdftmp/ediE4rnTtPV4uqvNe+t2W7WR4VmMNkSiHVhLIe6IwX1A==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.9.0.tgz", + "integrity": "sha512-1BlqdC4QZ574dL95x5ehZSvebdOUWDsAXIpAl5bMsIRZz/XeRZ8/bvSVrGX/cPn0SelEy+5M/LBCGlN+S1sPkQ==", "dev": true, + "license": "MIT", "dependencies": { "find": "^0.3.0", "fs-extra": "^11.2.0", "jsonfile": "^6.1.0", "lodash": "^4.17.21", - "luxon": "^3.4.4", + "luxon": "^3.5.0", "open": "^8.4.2", - "uuid": "^10.0.0" + "uuid": "^11.0.4" } }, "node_modules/multiple-cucumber-html-reporter/node_modules/fs-extra": { @@ -12155,25 +12161,27 @@ } }, "node_modules/multiple-cucumber-html-reporter/node_modules/luxon": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", - "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/multiple-cucumber-html-reporter/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", + "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], + "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/mz": { @@ -12351,24 +12359,31 @@ } }, "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, "node_modules/normalize-package-data/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, + "license": "ISC", "bin": { - "semver": "bin/semver" + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/normalize-path": { @@ -13722,106 +13737,86 @@ "readable-stream": "^2.0.2" } }, + "node_modules/read-package-up": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz", + "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up-simple": "^1.0.0", + "read-pkg": "^9.0.0", + "type-fest": "^4.6.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-package-up/node_modules/type-fest": { + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", + "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/read-pkg/node_modules/parse-json": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", + "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/read-pkg-up/node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "version": "4.33.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", + "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/readable-stream": { @@ -14757,32 +14752,36 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "dev": true, + "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" } }, "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" }, "node_modules/speakingurl": { "version": "14.0.1", @@ -15631,7 +15630,7 @@ "version": "0.1.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "optional": true, + "devOptional": true, "engines": { "node": ">=18" }, @@ -15829,6 +15828,7 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "dev": true, + "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -17264,6 +17264,7 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0" } diff --git a/package.json b/package.json index ea2bca152..40e088168 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,15 @@ "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/register": "^7.25.9", - "@badeball/cypress-cucumber-preprocessor": "^21.0.3", - "@cucumber/cucumber": "11.1.1", + "@badeball/cypress-cucumber-preprocessor": "^22.0.0", + "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", "@faker-js/faker": "9.3.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", - "cypress": "^13.17.0", - "cypress-network-idle": "^1.14.2", + "cypress": "^14.0.0", + "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.4.7", "expect": "^29.6.4", @@ -52,7 +52,7 @@ "import": "^0.0.6", "jsonwebtoken": "^9.0.2", "mock-socket": "^9.0.3", - "multiple-cucumber-html-reporter": "^3.8.0", + "multiple-cucumber-html-reporter": "^3.9.0", "neo4j-driver": "^4.3.4", "neode": "^0.4.8", "rosie": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index 422ac1f05..2395f2409 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47,6 +47,15 @@ js-tokens "^4.0.0" picocolors "^1.0.0" +"@babel/code-frame@^7.22.13": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.0.tgz#f02ba6d34e88fadd5e8861e8b38902f43cc1c819" @@ -1063,10 +1072,10 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@badeball/cypress-cucumber-preprocessor@^21.0.3": - version "21.0.3" - resolved "https://registry.yarnpkg.com/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-21.0.3.tgz#2a3c2c6a9b2599910da7872ecec3d53012f7f83b" - integrity sha512-d88Qcy2uoasQiUP8mBHUkE4TH1Px/rsN56gKSp5C1P45dPC583lxgA4NwsmrgtpjXWXSCi5OYvSk3x7E60h0RA== +"@badeball/cypress-cucumber-preprocessor@^22.0.0": + version "22.0.0" + resolved "https://registry.yarnpkg.com/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-22.0.0.tgz#1490566cac013add4606461734e61ff2015ed167" + integrity sha512-06dv/GmAS77CbRXPfQbBZFgRKXRBJRQyDg3kJTPO3b7KZ4V8AOt32cb083Zc92QAqrerIbvFrHBnruzE/2/NtA== dependencies: "@cucumber/ci-environment" "^10.0.1" "@cucumber/cucumber" "^11.0.0" @@ -1102,34 +1111,27 @@ resolved "https://registry.yarnpkg.com/@cucumber/ci-environment/-/ci-environment-10.0.1.tgz#c8584f1d4a619e4318cf60c01b838db096d72ccd" integrity sha512-/+ooDMPtKSmvcPMDYnMZt4LuoipfFfHaYspStI4shqw8FyKcfQAmekz6G+QKWjQQrvM+7Hkljwx58MEwPCwwzg== -"@cucumber/cucumber-expressions@17.1.0": - version "17.1.0" - resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-17.1.0.tgz#1a428548a2c98ef3224bd484fc5666b4f7153a72" - integrity sha512-PCv/ppsPynniKPWJr5v566daCVe+pbxQpHGrIu/Ev57cCH9Rv+X0F6lio4Id3Z64TaG7btCRLUGewIgLwmrwOA== - dependencies: - regexp-match-indices "1.0.2" - -"@cucumber/cucumber-expressions@^18.0.0": +"@cucumber/cucumber-expressions@18.0.1", "@cucumber/cucumber-expressions@^18.0.0": version "18.0.1" resolved "https://registry.yarnpkg.com/@cucumber/cucumber-expressions/-/cucumber-expressions-18.0.1.tgz#0899cbda2ed5546dbaa0e40f0c754b6e3bd1bb69" integrity sha512-NSid6bI+7UlgMywl5octojY5NXnxR9uq+JisjOrO52VbFsQM6gTWuQFE8syI10KnIBEdPzuEUSVEeZ0VFzRnZA== dependencies: regexp-match-indices "1.0.2" -"@cucumber/cucumber@11.1.1", "@cucumber/cucumber@^11.0.0": - version "11.1.1" - resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-11.1.1.tgz#8cff31391a6111514d9fba158c6fb3db4f9fdd41" - integrity sha512-4i2vk4R1Ffi1JXiNrVMLxLJFgZ7e0BHdoRN6QiWdz5EDPS+Qv9ld4wGZWeahBH5ncDygIrkkhtYxDhqOBXLPxQ== +"@cucumber/cucumber@11.2.0", "@cucumber/cucumber@^11.0.0": + version "11.2.0" + resolved "https://registry.yarnpkg.com/@cucumber/cucumber/-/cucumber-11.2.0.tgz#6f38ecd0c71717d646b99eb4fe2595f81c8b179a" + integrity sha512-F69uIPTc7dfgU7/TGAaQaWUz7r/DzoPW39AfJoKQOC7IvBiPQwpvSIo6QEd+63pdpdKNRbtQoVl5vP9IclhhuA== dependencies: "@cucumber/ci-environment" "10.0.1" - "@cucumber/cucumber-expressions" "17.1.0" - "@cucumber/gherkin" "28.0.0" + "@cucumber/cucumber-expressions" "18.0.1" + "@cucumber/gherkin" "30.0.4" "@cucumber/gherkin-streams" "5.0.1" "@cucumber/gherkin-utils" "9.0.0" - "@cucumber/html-formatter" "21.6.0" - "@cucumber/junit-xml-formatter" "0.6.0" + "@cucumber/html-formatter" "21.7.0" + "@cucumber/junit-xml-formatter" "0.7.1" "@cucumber/message-streams" "4.0.1" - "@cucumber/messages" "24.1.0" + "@cucumber/messages" "27.0.2" "@cucumber/tag-expressions" "6.1.1" assertion-error-formatter "^3.0.0" capital-case "^1.0.4" @@ -1152,7 +1154,7 @@ mkdirp "^2.1.5" mz "^2.7.0" progress "^2.0.3" - read-pkg-up "^7.0.1" + read-package-up "^11.0.0" resolve-pkg "^2.0.0" semver "7.5.3" string-argv "0.3.1" @@ -1182,37 +1184,33 @@ commander "12.0.0" source-map-support "^0.5.21" -"@cucumber/gherkin@28.0.0", "@cucumber/gherkin@^28.0.0": - version "28.0.0" - resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-28.0.0.tgz#91246da622524807b21430c1692bedd319d3d4bb" - integrity sha512-Ee6zJQq0OmIUPdW0mSnsCsrWA2PZAELNDPICD2pLfs0Oz7RAPgj80UsD2UCtqyAhw2qAR62aqlktKUlai5zl/A== - dependencies: - "@cucumber/messages" ">=19.1.4 <=24" - -"@cucumber/gherkin@^30.0.0": +"@cucumber/gherkin@30.0.4", "@cucumber/gherkin@^30.0.0": version "30.0.4" resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-30.0.4.tgz#047071b3122a9fb25e073aabdbc0132e98db4ee4" integrity sha512-pb7lmAJqweZRADTTsgnC3F5zbTh3nwOB1M83Q9ZPbUKMb3P76PzK6cTcPTJBHWy3l7isbigIv+BkDjaca6C8/g== dependencies: "@cucumber/messages" ">=19.1.4 <=26" -"@cucumber/html-formatter@21.6.0": - version "21.6.0" - resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-21.6.0.tgz#bfd8c4db31c6c96a8520332efba2ea9838ca36f0" - integrity sha512-Qw1tdObBJrgXgXwVjKVjB3hFhFPI8WhIFb+ULy8g5lDl5AdnKDiyDXAMvAWRX+pphnRMMNdkPCt6ZXEfWvUuAA== +"@cucumber/gherkin@^28.0.0": + version "28.0.0" + resolved "https://registry.yarnpkg.com/@cucumber/gherkin/-/gherkin-28.0.0.tgz#91246da622524807b21430c1692bedd319d3d4bb" + integrity sha512-Ee6zJQq0OmIUPdW0mSnsCsrWA2PZAELNDPICD2pLfs0Oz7RAPgj80UsD2UCtqyAhw2qAR62aqlktKUlai5zl/A== + dependencies: + "@cucumber/messages" ">=19.1.4 <=24" -"@cucumber/html-formatter@^21.7.0": +"@cucumber/html-formatter@21.7.0", "@cucumber/html-formatter@^21.7.0": version "21.7.0" resolved "https://registry.yarnpkg.com/@cucumber/html-formatter/-/html-formatter-21.7.0.tgz#a4413738c4476836c9917bf9652aa0a45b93ab81" integrity sha512-bv211aY8mErp6CdmhN426E+7KIsVIES4fGx5ASMlUzYWiMus6NhSdI9UL3Vswx8JXJMgySeIcJJKfznREUFLNA== -"@cucumber/junit-xml-formatter@0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@cucumber/junit-xml-formatter/-/junit-xml-formatter-0.6.0.tgz#394352c4a3a453d0130c2e6c7f179b408ee0df36" - integrity sha512-++PAuxliQhq7yr2Bn9P0fwBUo46OoKAK5f6M4PrwoHBqIsl/6pUS4mqpviuBrgZ8RD7BTrmASk4lUDJClAz/qA== +"@cucumber/junit-xml-formatter@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@cucumber/junit-xml-formatter/-/junit-xml-formatter-0.7.1.tgz#a94cb7bc9f567bf2718605dc571712e69d4a0721" + integrity sha512-AzhX+xFE/3zfoYeqkT7DNq68wAQfBcx4Dk9qS/ocXM2v5tBv6eFQ+w8zaSfsktCjYzu4oYRH/jh4USD1CYHfaQ== dependencies: "@cucumber/query" "^13.0.2" "@teppeis/multimaps" "^3.0.0" + luxon "^3.5.0" xmlbuilder "^15.1.1" "@cucumber/message-streams@4.0.1", "@cucumber/message-streams@^4.0.1": @@ -1220,7 +1218,17 @@ resolved "https://registry.yarnpkg.com/@cucumber/message-streams/-/message-streams-4.0.1.tgz#a5339d3504594bb2edb5732aaae94dddb24d0970" integrity sha512-Kxap9uP5jD8tHUZVjTWgzxemi/0uOsbGjd4LBOSxcJoOCRbESFwemUzilJuzNTB8pcTQUh8D5oudUyxfkJOKmA== -"@cucumber/messages@24.1.0", "@cucumber/messages@>=19.1.4 <=24", "@cucumber/messages@^24.0.0": +"@cucumber/messages@27.0.2", "@cucumber/messages@^27.0.0": + version "27.0.2" + resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-27.0.2.tgz#9b5ed8b6cf7b95e43576f6af1af9f5205f69e2a1" + integrity sha512-jo2B+vYXmpuLOKh6Gc8loHl2E8svCkLvEXLVgFwVHqKWZJWBTa9yTRCPmZIxrz4fnO7Pr3N3vKQCPu73/gjlVQ== + dependencies: + "@types/uuid" "10.0.0" + class-transformer "0.5.1" + reflect-metadata "0.2.2" + uuid "10.0.0" + +"@cucumber/messages@>=19.1.4 <=24", "@cucumber/messages@^24.0.0": version "24.1.0" resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-24.1.0.tgz#a212c97b0548144c3ccfae021a96d6c56d3841d3" integrity sha512-hxVHiBurORcobhVk80I9+JkaKaNXkW6YwGOEFIh/2aO+apAN+5XJgUUWjng9NwqaQrW1sCFuawLB1AuzmBaNdQ== @@ -1240,16 +1248,6 @@ reflect-metadata "0.2.2" uuid "10.0.0" -"@cucumber/messages@^27.0.0": - version "27.0.2" - resolved "https://registry.yarnpkg.com/@cucumber/messages/-/messages-27.0.2.tgz#9b5ed8b6cf7b95e43576f6af1af9f5205f69e2a1" - integrity sha512-jo2B+vYXmpuLOKh6Gc8loHl2E8svCkLvEXLVgFwVHqKWZJWBTa9yTRCPmZIxrz4fnO7Pr3N3vKQCPu73/gjlVQ== - dependencies: - "@types/uuid" "10.0.0" - class-transformer "0.5.1" - reflect-metadata "0.2.2" - uuid "10.0.0" - "@cucumber/pretty-formatter@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@cucumber/pretty-formatter/-/pretty-formatter-1.0.1.tgz#65d6c1df436920036a7bd02d08cb44d20e7af0ab" @@ -2259,7 +2257,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.45.tgz#2c0fafd78705e7a18b7906b5201a522719dc5190" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== -"@types/normalize-package-data@^2.4.0": +"@types/normalize-package-data@^2.4.3": version "2.4.4" resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901" integrity sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA== @@ -4058,15 +4056,15 @@ csstype@^3.1.3: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== -cypress-network-idle@^1.14.2: - version "1.14.2" - resolved "https://registry.yarnpkg.com/cypress-network-idle/-/cypress-network-idle-1.14.2.tgz#0837100861feeb5a18f4c2d9815be079f8590f4d" - integrity sha512-xAdR8dH58KFPv8eCDWjviScITrJOcUpuMXYfYTc175nk2/NvnJ+I6ylSn1CM7yZmoV/gLbFa36QLiH5NfNEaLQ== +cypress-network-idle@^1.15.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/cypress-network-idle/-/cypress-network-idle-1.15.0.tgz#e249f08695a46f1ddce18a95d5293937f277cbb3" + integrity sha512-8zU16zhc7S3nMl1NTEEcNsZYlJy/ZzP2zPTTrngGxyXH32Ipake/xfHLZsgrzeWCieiS2AVhQsakhWqFzO3hpw== -cypress@^13.17.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.17.0.tgz#34c3d68080c4497eace0f353bd1629587a5f600d" - integrity sha512-5xWkaPurwkIljojFidhw8lFScyxhtiFHl/i/3zov+1Z5CmY4t9tjIdvSXfu82Y3w7wt0uR9KkucbhkVvJZLQSA== +cypress@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.0.0.tgz#a71cb0a243a0bfeb97b6973ab9c5291ca5288e93" + integrity sha512-kEGqQr23so5IpKeg/dp6GVi7RlHx1NmW66o2a2Q4wk9gRaAblLZQSiZJuDI8UMC4LlG5OJ7Q6joAiqTrfRNbTw== dependencies: "@cypress/request" "^3.0.6" "@cypress/xvfb" "^1.2.4" @@ -4950,6 +4948,11 @@ find-test-names@1.28.30: globby "^11.0.4" simple-bin-help "^1.8.0" +find-up-simple@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-up-simple/-/find-up-simple-1.0.0.tgz#21d035fde9fdbd56c8f4d2f63f32fd93a1cfc368" + integrity sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw== + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -5411,10 +5414,12 @@ hookable@^5.5.3: resolved "https://registry.yarnpkg.com/hookable/-/hookable-5.5.3.tgz#6cfc358984a1ef991e2518cb9ed4a778bbd3215d" integrity sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ== -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hosted-git-info@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-7.0.2.tgz#9b751acac097757667f30114607ef7b661ff4f17" + integrity sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w== + dependencies: + lru-cache "^10.0.1" html-void-elements@^3.0.0: version "3.0.0" @@ -5516,6 +5521,11 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +index-to-position@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/index-to-position/-/index-to-position-0.1.2.tgz#e11bfe995ca4d8eddb1ec43274488f3c201a7f09" + integrity sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -6276,7 +6286,7 @@ lower-case@^2.0.2: dependencies: tslib "^2.0.3" -lru-cache@^10.2.0: +lru-cache@^10.0.1, lru-cache@^10.2.0: version "10.4.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== @@ -6300,10 +6310,10 @@ luxon@3.2.1: resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.2.1.tgz#14f1af209188ad61212578ea7e3d518d18cee45f" integrity sha512-QrwPArQCNLAKGO/C+ZIilgIuDnEnKx5QYODdDtbFaxzsbZcc/a7WFq7MhsVYgRlwawLtvOUESTlfJ+hc/USqPg== -luxon@^3.4.4: - version "3.4.4" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.4.4.tgz#cf20dc27dc532ba41a169c43fdcc0063601577af" - integrity sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA== +luxon@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== magic-string@^0.30.11: version "0.30.12" @@ -6597,18 +6607,18 @@ 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== -multiple-cucumber-html-reporter@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.8.0.tgz#46c4a4404936c803cd6766662dcb5c7db1d82494" - integrity sha512-OLnTQ3qvLRD6WfNw1dbzbawqnYuq4T6z1Rqgdftmp/ediE4rnTtPV4uqvNe+t2W7WR4VmMNkSiHVhLIe6IwX1A== +multiple-cucumber-html-reporter@^3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.9.0.tgz#114e8884ba7b279f5dd7d73b05dda0fa19c3d868" + integrity sha512-1BlqdC4QZ574dL95x5ehZSvebdOUWDsAXIpAl5bMsIRZz/XeRZ8/bvSVrGX/cPn0SelEy+5M/LBCGlN+S1sPkQ== dependencies: find "^0.3.0" fs-extra "^11.2.0" jsonfile "^6.1.0" lodash "^4.17.21" - luxon "^3.4.4" + luxon "^3.5.0" open "^8.4.2" - uuid "^10.0.0" + uuid "^11.0.4" mz@^2.7.0: version "2.7.0" @@ -6700,15 +6710,14 @@ node-source-walk@^7.0.0: dependencies: "@babel/parser" "^7.24.4" -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== +normalize-package-data@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-6.0.2.tgz#a7bc22167fe24025412bcff0a9651eb768b03506" + integrity sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g== dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" + hosted-git-info "^7.0.0" + semver "^7.3.5" + validate-npm-package-license "^3.0.4" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" @@ -6950,7 +6959,7 @@ parse-github-url@^1.0.3: resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.3.tgz#2ab55642c8685b63fbe2a196f5abe4ae9bd68abc" integrity sha512-tfalY5/4SqGaV/GIGzWyHnFjlpTPTNpENR9Ea2lLldSJ8EWXMsvacWucqY3m3I4YPtas15IxTLQVQ5NSYXPrww== -parse-json@^5.0.0, parse-json@^5.2.0: +parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== @@ -6960,6 +6969,15 @@ parse-json@^5.0.0, parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-json@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-8.1.0.tgz#91cdc7728004e955af9cb734de5684733b24a717" + integrity sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA== + dependencies: + "@babel/code-frame" "^7.22.13" + index-to-position "^0.1.2" + type-fest "^4.7.1" + parse-ms@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-4.0.0.tgz#c0c058edd47c2a590151a718990533fd62803df4" @@ -7342,24 +7360,25 @@ read-only-stream@^2.0.0: dependencies: readable-stream "^2.0.2" -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== +read-package-up@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/read-package-up/-/read-package-up-11.0.0.tgz#71fb879fdaac0e16891e6e666df22de24a48d5ba" + integrity sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ== dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" + find-up-simple "^1.0.0" + read-pkg "^9.0.0" + type-fest "^4.6.0" -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== +read-pkg@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-9.0.1.tgz#b1b81fb15104f5dbb121b6bbdee9bbc9739f569b" + integrity sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA== dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" + "@types/normalize-package-data" "^2.4.3" + normalize-package-data "^6.0.0" + parse-json "^8.0.0" + type-fest "^4.6.0" + unicorn-magic "^0.1.0" readable-stream@3, readable-stream@^3.5.0, readable-stream@^3.6.0, readable-stream@^3.6.2: version "3.6.2" @@ -7582,7 +7601,7 @@ resolve-pkg@^2.0.0: dependencies: resolve-from "^5.0.0" -resolve@^1.1.4, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.22.8, resolve@^1.4.0: +resolve@^1.1.4, resolve@^1.1.6, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.22.8, resolve@^1.4.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -7726,11 +7745,6 @@ seedrandom@^3.0.5: resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== -"semver@2 || 3 || 4 || 5", semver@^5.6.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - semver@7.5.3: version "7.5.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" @@ -7738,6 +7752,11 @@ semver@7.5.3: dependencies: lru-cache "^6.0.0" +semver@^5.6.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" @@ -8479,25 +8498,15 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-fest@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== -type-fest@^4.8.3: - version "4.8.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.8.3.tgz#6db08d9f44d596cd953f83020c7c56310c368d1c" - integrity sha512-//BaTm14Q/gHBn09xlnKNqfI8t6bmdzx2DXYfPBNofN0WUybCEUDcbCWcTa0oF09lzLjZgPphXAsvRiMK0V6Bw== +type-fest@^4.6.0, type-fest@^4.7.1, type-fest@^4.8.3: + version "4.33.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.33.0.tgz#2da0c135b9afa76cf8b18ecfd4f260ecd414a432" + integrity sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g== typedarray@^0.0.6: version "0.0.6" @@ -8704,6 +8713,11 @@ uuid@9.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +uuid@^11.0.4: + version "11.0.5" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.5.tgz#07b46bdfa6310c92c3fb3953a8720f170427fc62" + integrity sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA== + uuid@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -8714,7 +8728,7 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== From d8cf28b68d0ada33aad036a89f5298359dea6fa3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 8 Feb 2025 03:07:50 +0000 Subject: [PATCH 002/266] Bump typescript from 5.7.2 to 5.7.3 in /backend Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.7.2 to 5.7.3. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release.yml) - [Commits](https://github.com/microsoft/TypeScript/compare/v5.7.2...v5.7.3) --- updated-dependencies: - dependency-name: typescript dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 997f5f0dc..09ccb610c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -117,7 +117,7 @@ "rosie": "^2.1.1", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", - "typescript": "^5.7.2" + "typescript": "^5.7.3" }, "resolutions": { "**/**/fs-capacitor": "^6.2.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 8d606cbeb..a8203157b 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -9268,10 +9268,10 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" -typescript@^5.7.2: - version "5.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6" - integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg== +typescript@^5.7.3: + version "5.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" + integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== unbox-primitive@^1.0.2: version "1.0.2" From 0e7c3e5110f61d95805402c467055ec286c79701 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 22 Feb 2025 03:00:32 +0000 Subject: [PATCH 003/266] Bump actions/cache from 4.1.2 to 4.2.1 Bumps [actions/cache](https://github.com/actions/cache) from 4.1.2 to 4.2.1. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/6849a6489940f00c2f30c0fb92c6274307ccb58a...0c907a75c2c80ebcb7f088228285e798b750cf8f) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/test-backend.yml | 8 ++++---- .github/workflows/test-e2e.yml | 4 ++-- .github/workflows/test-webapp.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 995864b0b..49a3aa8a1 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -37,7 +37,7 @@ jobs: - name: Cache docker images id: cache-neo4j - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: /tmp/neo4j.tar key: ${{ github.run_id }}-backend-neo4j-cache @@ -58,7 +58,7 @@ jobs: - name: Cache docker images id: cache-backend - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: /tmp/backend.tar key: ${{ github.run_id }}-backend-cache @@ -87,14 +87,14 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Restore Neo4J cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: /tmp/neo4j.tar key: ${{ github.run_id }}-backend-neo4j-cache fail-on-cache-miss: true - name: Restore Backend cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: /tmp/backend.tar key: ${{ github.run_id }}-backend-cache diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 573037376..17637e6a3 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -37,7 +37,7 @@ jobs: - name: Cache docker images id: cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: | /opt/cucumber-json-formatter @@ -59,7 +59,7 @@ jobs: job: [1, 2, 3, 4, 5, 6, 7, 8] steps: - name: Restore cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 id: cache with: path: | diff --git a/.github/workflows/test-webapp.yml b/.github/workflows/test-webapp.yml index 5c8b5ce3b..2019d309e 100644 --- a/.github/workflows/test-webapp.yml +++ b/.github/workflows/test-webapp.yml @@ -50,7 +50,7 @@ jobs: docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar - name: Cache docker image - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: /tmp/webapp.tar key: ${{ github.run_id }}-webapp-cache @@ -79,7 +79,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Restore webapp cache - uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # v4.0.2 + uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 with: path: /tmp/webapp.tar key: ${{ github.run_id }}-webapp-cache From 532e29678a040d89330a531983d49b173ccf391d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 08:10:33 +0000 Subject: [PATCH 004/266] Bump actions/upload-artifact from 4.4.3 to 4.6.1 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.3 to 4.6.1. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882...4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 8 ++++---- .github/workflows/test-e2e.yml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index da485425a..ad98bd0d0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,7 +34,7 @@ jobs: - name: Neo4J | Save docker image run: docker save "ocelotsocialnetwork/neo4j-community" > /tmp/neo4j.tar - name: Upload Artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: docker-neo4j-community path: /tmp/neo4j.tar @@ -83,7 +83,7 @@ jobs: - name: Backend | Save docker image run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar - name: Upload Artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: docker-backend-production path: /tmp/backend.tar @@ -132,7 +132,7 @@ jobs: - name: Webapp | Save docker image run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar - name: Upload Artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: docker-webapp-production path: /tmp/webapp.tar @@ -184,7 +184,7 @@ jobs: - name: Maintenance | Save docker image run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar - name: Upload Artifact - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: name: docker-maintenance-production path: /tmp/maintenance.tar diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 17637e6a3..3203411ff 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -93,7 +93,7 @@ jobs: - name: Full stack tests | if tests failed, upload report id: e2e-report if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }} - uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 with: 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 From 5b5fcc39e8a624a5cdab4508d62ac77af8f923c4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 08:36:43 +0000 Subject: [PATCH 005/266] Bump actions/setup-node from 4.1.0 to 4.2.0 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/39370e3970a6d050c480ffad4ff0ed4d3fdee5af...1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/check-documentation.yml | 2 +- .github/workflows/deploy-documentation.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-documentation.yml b/.github/workflows/check-documentation.yml index 69cc85489..5efb91f35 100644 --- a/.github/workflows/check-documentation.yml +++ b/.github/workflows/check-documentation.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Setup Node 20 - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.0.3 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.0.3 with: node-version: '20' diff --git a/.github/workflows/deploy-documentation.yml b/.github/workflows/deploy-documentation.yml index 6a2964e81..220d40aa7 100644 --- a/.github/workflows/deploy-documentation.yml +++ b/.github/workflows/deploy-documentation.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Setup Node 20 - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.0.3 + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.0.3 with: node-version: 20 From ed4f1e4cc72dbbbf98e4a74d997c2815e729e20b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 09:07:19 +0000 Subject: [PATCH 006/266] Bump the cypress group across 1 directory with 3 updates Bumps the cypress group with 3 updates in the / directory: [@badeball/cypress-cucumber-preprocessor](https://github.com/badeball/cypress-cucumber-preprocessor), [cypress](https://github.com/cypress-io/cypress) and [multiple-cucumber-html-reporter](https://github.com/WasiqB/multiple-cucumber-html-reporter). Updates `@badeball/cypress-cucumber-preprocessor` from 22.0.0 to 22.0.1 - [Release notes](https://github.com/badeball/cypress-cucumber-preprocessor/releases) - [Changelog](https://github.com/badeball/cypress-cucumber-preprocessor/blob/master/CHANGELOG.md) - [Commits](https://github.com/badeball/cypress-cucumber-preprocessor/compare/v22.0.0...v22.0.1) Updates `cypress` from 14.0.0 to 14.0.3 - [Release notes](https://github.com/cypress-io/cypress/releases) - [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md) - [Commits](https://github.com/cypress-io/cypress/compare/v14.0.0...v14.0.3) Updates `multiple-cucumber-html-reporter` from 3.9.0 to 3.9.2 - [Release notes](https://github.com/WasiqB/multiple-cucumber-html-reporter/releases) - [Changelog](https://github.com/WasiqB/multiple-cucumber-html-reporter/blob/main/CHANGELOG.md) - [Commits](https://github.com/WasiqB/multiple-cucumber-html-reporter/compare/v3.9.0...v3.9.2) --- updated-dependencies: - dependency-name: "@badeball/cypress-cucumber-preprocessor" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cypress - dependency-name: cypress dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cypress - dependency-name: multiple-cucumber-html-reporter dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cypress ... Signed-off-by: dependabot[bot] --- package-lock.json | 398 ++++++++++++++++++++++++++++++---------------- package.json | 6 +- yarn.lock | 127 +++++++-------- 3 files changed, 323 insertions(+), 208 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16c5f6551..6089fca7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,14 +12,14 @@ "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/register": "^7.25.9", - "@badeball/cypress-cucumber-preprocessor": "^22.0.0", + "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", "@faker-js/faker": "9.3.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", - "cypress": "^14.0.0", + "cypress": "^14.0.3", "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.4.7", @@ -28,7 +28,7 @@ "import": "^0.0.6", "jsonwebtoken": "^9.0.2", "mock-socket": "^9.0.3", - "multiple-cucumber-html-reporter": "^3.9.0", + "multiple-cucumber-html-reporter": "^3.9.2", "neo4j-driver": "^4.3.4", "neode": "^0.4.8", "rosie": "^2.1.0", @@ -1768,9 +1768,9 @@ } }, "node_modules/@badeball/cypress-cucumber-preprocessor": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-22.0.0.tgz", - "integrity": "sha512-06dv/GmAS77CbRXPfQbBZFgRKXRBJRQyDg3kJTPO3b7KZ4V8AOt32cb083Zc92QAqrerIbvFrHBnruzE/2/NtA==", + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-22.0.1.tgz", + "integrity": "sha512-wGZiNI7vfapbUo9x7H65pd6YRArsnpyTyHJ6yGafS77uTFBXU0McrpWJrE8aEDgSOz8+jdCwI0eJRnUSm1mJDA==", "dev": true, "funding": [ { @@ -1799,7 +1799,7 @@ "error-stack-parser": "^2.1.4", "find-cypress-specs": "^1.45.2", "glob": "^10.4.5", - "mocha": "^10.7.0", + "mocha": "^11.0.0", "seedrandom": "^3.0.5", "source-map": "^0.6.1", "split": "^1.0.1", @@ -2336,10 +2336,11 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.6.tgz", - "integrity": "sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz", + "integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -2354,7 +2355,7 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.13.0", + "qs": "6.13.1", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", @@ -2369,6 +2370,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -6237,6 +6239,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } @@ -6274,6 +6277,7 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -6317,7 +6321,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -6444,6 +6449,7 @@ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "*" } @@ -6452,7 +6458,8 @@ "version": "1.13.2", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/babel-plugin-add-module-exports": { "version": "1.0.4", @@ -6580,6 +6587,7 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" } @@ -6699,7 +6707,8 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/browserify": { "version": "16.5.2", @@ -6991,6 +7000,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -7005,6 +7028,7 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7047,7 +7071,8 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/ccount": { "version": "2.0.1", @@ -7240,14 +7265,18 @@ } }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/cliui/node_modules/ansi-styles": { @@ -7255,6 +7284,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7270,6 +7300,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -7403,6 +7434,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -7711,14 +7743,14 @@ "optional": true }, "node_modules/cypress": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.0.0.tgz", - "integrity": "sha512-kEGqQr23so5IpKeg/dp6GVi7RlHx1NmW66o2a2Q4wk9gRaAblLZQSiZJuDI8UMC4LlG5OJ7Q6joAiqTrfRNbTw==", + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.0.3.tgz", + "integrity": "sha512-yIdvobANw3kS+KF/t5vwjjPNufBA8ux7iQHaWxPTkUw2yCKI72m9mKM24eOwE84Wk4ALPsSvEcGbDrwgmhr4RA==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.6", + "@cypress/request": "^3.0.7", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -7911,6 +7943,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -7956,6 +7989,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8071,6 +8105,7 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -8441,6 +8476,21 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -8470,6 +8520,7 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -8617,13 +8668,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -8663,6 +8712,35 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.23.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", @@ -8870,7 +8948,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/extend-shallow": { "version": "2.0.1", @@ -8911,7 +8990,8 @@ "dev": true, "engines": [ "node >=0.6.0" - ] + ], + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -9157,6 +9237,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -9186,6 +9267,7 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } @@ -9232,18 +9314,21 @@ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" }, "engines": { @@ -9365,16 +9450,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9389,6 +9480,20 @@ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", "dev": true }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -9430,6 +9535,7 @@ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" } @@ -9583,12 +9689,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -9718,23 +9825,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9743,12 +9839,13 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9802,10 +9899,11 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -9854,6 +9952,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } @@ -9919,6 +10018,7 @@ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", @@ -10443,6 +10543,7 @@ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -10572,7 +10673,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/is-unicode-supported": { "version": "0.1.0", @@ -10681,7 +10783,8 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jackspeak": { "version": "3.4.3", @@ -10968,7 +11071,8 @@ "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/jsesc": { "version": "3.0.2", @@ -10992,7 +11096,8 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/json-stable-stringify": { "version": "0.0.1", @@ -11007,7 +11112,8 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/json5": { "version": "2.2.3", @@ -11130,6 +11236,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -11317,6 +11424,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -11628,6 +11736,16 @@ "integrity": "sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==", "optional": true }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -11819,6 +11937,7 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -11828,6 +11947,7 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -11926,10 +12046,11 @@ "dev": true }, "node_modules/mocha": { - "version": "10.8.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.1.tgz", - "integrity": "sha512-WxSpEWgF03HfgNKBuysfK40DUaOSVX5zxgLDoieMGO+zyE69iq2eQ1vBypvIJ5mOPKpuVAqWiTbt4Orj7L6wVw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz", + "integrity": "sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", @@ -11938,7 +12059,7 @@ "diff": "^5.2.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", @@ -11948,8 +12069,8 @@ "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -11957,7 +12078,7 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/brace-expansion": { @@ -11965,6 +12086,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -11974,6 +12096,7 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -11983,6 +12106,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -11990,31 +12114,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -12131,26 +12236,27 @@ "devOptional": true }, "node_modules/multiple-cucumber-html-reporter": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.9.0.tgz", - "integrity": "sha512-1BlqdC4QZ574dL95x5ehZSvebdOUWDsAXIpAl5bMsIRZz/XeRZ8/bvSVrGX/cPn0SelEy+5M/LBCGlN+S1sPkQ==", + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.9.2.tgz", + "integrity": "sha512-liBtbgqucEr98qNHJ2gukcjmDjREa/oyGHSRB1jVdK0pdL7cqohIzRzk0YmUVTVP8eGe7gJQ6tTHikMgudULtQ==", "dev": true, "license": "MIT", "dependencies": { "find": "^0.3.0", - "fs-extra": "^11.2.0", + "fs-extra": "^11.3.0", "jsonfile": "^6.1.0", "lodash": "^4.17.21", "luxon": "^3.5.0", "open": "^8.4.2", - "uuid": "^11.0.4" + "uuid": "^11.0.5" } }, "node_modules/multiple-cucumber-html-reporter/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz", + "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -12769,6 +12875,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -12784,6 +12891,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -13066,7 +13174,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/photoswipe": { "version": "5.4.4", @@ -13654,10 +13763,11 @@ } }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", + "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.0.6" }, @@ -14407,6 +14517,7 @@ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -14841,6 +14952,7 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, + "license": "MIT", "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -15118,6 +15230,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -15316,22 +15429,24 @@ } }, "node_modules/tldts": { - "version": "6.1.57", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.57.tgz", - "integrity": "sha512-Oy7yDXK8meJl8vPMOldzA+MtueAJ5BrH4l4HXwZuj2AtfoQbLjmTJmjNWPUcAo+E/ibHn7QlqMS0BOcXJFJyHQ==", + "version": "6.1.78", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.78.tgz", + "integrity": "sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ==", "dev": true, + "license": "MIT", "dependencies": { - "tldts-core": "^6.1.57" + "tldts-core": "^6.1.78" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.57", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.57.tgz", - "integrity": "sha512-lXnRhuQpx3zU9EONF9F7HfcRLvN1uRYUBIiKL+C/gehC/77XTU+Jye6ui86GA3rU6FjlJ0triD1Tkjt2F/2lEg==", - "dev": true + "version": "6.1.78", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.78.tgz", + "integrity": "sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw==", + "dev": true, + "license": "MIT" }, "node_modules/tmp": { "version": "0.2.3", @@ -15361,10 +15476,11 @@ "dev": true }, "node_modules/tough-cookie": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", - "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.1.tgz", + "integrity": "sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tldts": "^6.1.32" }, @@ -15483,6 +15599,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -15494,7 +15611,8 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true + "dev": true, + "license": "Unlicense" }, "node_modules/type-fest": { "version": "0.21.3", @@ -15842,6 +15960,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -15852,7 +15971,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/vfile": { "version": "6.0.3", @@ -17139,7 +17259,8 @@ "version": "6.5.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/wrap-ansi": { "version": "8.1.0", @@ -17283,6 +17404,7 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -17306,30 +17428,32 @@ } }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -17337,6 +17461,7 @@ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -17362,6 +17487,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/package.json b/package.json index 40e088168..4bfcf22be 100644 --- a/package.json +++ b/package.json @@ -36,14 +36,14 @@ "@babel/core": "^7.26.0", "@babel/preset-env": "^7.26.0", "@babel/register": "^7.25.9", - "@badeball/cypress-cucumber-preprocessor": "^22.0.0", + "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", "@faker-js/faker": "9.3.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", - "cypress": "^14.0.0", + "cypress": "^14.0.3", "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.4.7", @@ -52,7 +52,7 @@ "import": "^0.0.6", "jsonwebtoken": "^9.0.2", "mock-socket": "^9.0.3", - "multiple-cucumber-html-reporter": "^3.9.0", + "multiple-cucumber-html-reporter": "^3.9.2", "neo4j-driver": "^4.3.4", "neode": "^0.4.8", "rosie": "^2.1.0", diff --git a/yarn.lock b/yarn.lock index 2395f2409..f5343ed73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1072,10 +1072,10 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@badeball/cypress-cucumber-preprocessor@^22.0.0": - version "22.0.0" - resolved "https://registry.yarnpkg.com/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-22.0.0.tgz#1490566cac013add4606461734e61ff2015ed167" - integrity sha512-06dv/GmAS77CbRXPfQbBZFgRKXRBJRQyDg3kJTPO3b7KZ4V8AOt32cb083Zc92QAqrerIbvFrHBnruzE/2/NtA== +"@badeball/cypress-cucumber-preprocessor@^22.0.1": + version "22.0.1" + resolved "https://registry.yarnpkg.com/@badeball/cypress-cucumber-preprocessor/-/cypress-cucumber-preprocessor-22.0.1.tgz#f2afa85143c64256ebbecc2424c424c4cc3eaa48" + integrity sha512-wGZiNI7vfapbUo9x7H65pd6YRArsnpyTyHJ6yGafS77uTFBXU0McrpWJrE8aEDgSOz8+jdCwI0eJRnUSm1mJDA== dependencies: "@cucumber/ci-environment" "^10.0.1" "@cucumber/cucumber" "^11.0.0" @@ -1095,7 +1095,7 @@ error-stack-parser "^2.1.4" find-cypress-specs "^1.45.2" glob "^10.4.5" - mocha "^10.7.0" + mocha "^11.0.0" seedrandom "^3.0.5" source-map "^0.6.1" split "^1.0.1" @@ -1295,10 +1295,10 @@ through2 "^2.0.0" watchify "^4.0.0" -"@cypress/request@^3.0.6": - version "3.0.6" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.6.tgz#f5580add6acee0e183b4d4e07eff4f31327ae12b" - integrity sha512-fi0eVdCOtKu5Ed6+E8mYxUF6ZTFJDZvHogCBelM0xVXmrDEkyM22gRArQzq1YcHPm1V47Vf/iAD+WgVdUlJCGg== +"@cypress/request@^3.0.7": + version "3.0.7" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.7.tgz#6a74a4da98d9e5ae9121d6e2d9c14780c9b5cf1a" + integrity sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -1313,7 +1313,7 @@ json-stringify-safe "~5.0.1" mime-types "~2.1.19" performance-now "^2.1.0" - qs "6.13.0" + qs "6.13.1" safe-buffer "^5.1.2" tough-cookie "^5.0.0" tunnel-agent "^0.6.0" @@ -3755,13 +3755,13 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" wrap-ansi "^7.0.0" clone-deep@^4.0.1: @@ -4061,12 +4061,12 @@ cypress-network-idle@^1.15.0: resolved "https://registry.yarnpkg.com/cypress-network-idle/-/cypress-network-idle-1.15.0.tgz#e249f08695a46f1ddce18a95d5293937f277cbb3" integrity sha512-8zU16zhc7S3nMl1NTEEcNsZYlJy/ZzP2zPTTrngGxyXH32Ipake/xfHLZsgrzeWCieiS2AVhQsakhWqFzO3hpw== -cypress@^14.0.0: - version "14.0.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.0.0.tgz#a71cb0a243a0bfeb97b6973ab9c5291ca5288e93" - integrity sha512-kEGqQr23so5IpKeg/dp6GVi7RlHx1NmW66o2a2Q4wk9gRaAblLZQSiZJuDI8UMC4LlG5OJ7Q6joAiqTrfRNbTw== +cypress@^14.0.3: + version "14.0.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.0.3.tgz#7873d21f61d0559edb9c041316d450cf7ef9d5fa" + integrity sha512-yIdvobANw3kS+KF/t5vwjjPNufBA8ux7iQHaWxPTkUw2yCKI72m9mKM24eOwE84Wk4ALPsSvEcGbDrwgmhr4RA== dependencies: - "@cypress/request" "^3.0.6" + "@cypress/request" "^3.0.7" "@cypress/xvfb" "^1.2.4" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" @@ -5022,10 +5022,10 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== -fs-extra@^11.2.0: - version "11.2.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" - integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== +fs-extra@^11.2.0, fs-extra@^11.3.0: + version "11.3.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.0.tgz#0daced136bbaf65a555a326719af931adc7a314d" + integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -5189,17 +5189,6 @@ glob@^7.0.0, glob@^7.1.0, glob@^7.2.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - global-dirs@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485" @@ -6483,7 +6472,7 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimatch@^5.0.1, minimatch@^5.1.6: +minimatch@^5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== @@ -6527,10 +6516,10 @@ mkdirp@^2.1.5: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== -mocha@^10.7.0: - version "10.7.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" - integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== +mocha@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-11.1.0.tgz#20d7c6ac4d6d6bcb60a8aa47971fca74c65c3c66" + integrity sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg== dependencies: ansi-colors "^4.1.3" browser-stdout "^1.3.1" @@ -6539,7 +6528,7 @@ mocha@^10.7.0: diff "^5.2.0" escape-string-regexp "^4.0.0" find-up "^5.0.0" - glob "^8.1.0" + glob "^10.4.5" he "^1.2.0" js-yaml "^4.1.0" log-symbols "^4.1.0" @@ -6549,8 +6538,8 @@ mocha@^10.7.0: strip-json-comments "^3.1.1" supports-color "^8.1.1" workerpool "^6.5.1" - yargs "^16.2.0" - yargs-parser "^20.2.9" + yargs "^17.7.2" + yargs-parser "^21.1.1" yargs-unparser "^2.0.0" mock-socket@^9.0.3: @@ -6607,18 +6596,18 @@ 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== -multiple-cucumber-html-reporter@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.9.0.tgz#114e8884ba7b279f5dd7d73b05dda0fa19c3d868" - integrity sha512-1BlqdC4QZ574dL95x5ehZSvebdOUWDsAXIpAl5bMsIRZz/XeRZ8/bvSVrGX/cPn0SelEy+5M/LBCGlN+S1sPkQ== +multiple-cucumber-html-reporter@^3.9.2: + version "3.9.2" + resolved "https://registry.yarnpkg.com/multiple-cucumber-html-reporter/-/multiple-cucumber-html-reporter-3.9.2.tgz#11635c7efdbfb675bbeff57d779092e61cc047d1" + integrity sha512-liBtbgqucEr98qNHJ2gukcjmDjREa/oyGHSRB1jVdK0pdL7cqohIzRzk0YmUVTVP8eGe7gJQ6tTHikMgudULtQ== dependencies: find "^0.3.0" - fs-extra "^11.2.0" + fs-extra "^11.3.0" jsonfile "^6.1.0" lodash "^4.17.21" luxon "^3.5.0" open "^8.4.2" - uuid "^11.0.4" + uuid "^11.0.5" mz@^2.7.0: version "2.7.0" @@ -7304,10 +7293,10 @@ qrcode@^1.5.4: pngjs "^5.0.0" yargs "^15.3.1" -qs@6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" - integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== +qs@6.13.1: + version "6.13.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e" + integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== dependencies: side-channel "^1.0.6" @@ -8161,7 +8150,7 @@ string-argv@0.3.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.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== @@ -8713,10 +8702,10 @@ uuid@9.0.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== -uuid@^11.0.4: - version "11.0.5" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.0.5.tgz#07b46bdfa6310c92c3fb3953a8720f170427fc62" - integrity sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA== +uuid@^11.0.5: + version "11.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== uuid@^3.4.0: version "3.4.0" @@ -9125,10 +9114,10 @@ yargs-parser@^18.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2, yargs-parser@^20.2.9: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@^2.0.0: version "2.0.0" @@ -9157,18 +9146,18 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +yargs@^17.7.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: - cliui "^7.0.2" + cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" - string-width "^4.2.0" + string-width "^4.2.3" y18n "^5.0.5" - yargs-parser "^20.2.2" + yargs-parser "^21.1.1" yauzl@^2.10.0: version "2.10.0" From 933c17bc410fc704e864e9f1006b03c7f74e5704 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 09:33:45 +0000 Subject: [PATCH 007/266] Bump sass from 1.83.0 to 1.85.0 in /webapp Bumps [sass](https://github.com/sass/dart-sass) from 1.83.0 to 1.85.0. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.83.0...1.85.0) --- updated-dependencies: - dependency-name: sass dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 02599fb98..3619846c2 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -46,7 +46,7 @@ "nuxt": "~2.12.1", "nuxt-dropzone": "^1.0.4", "nuxt-env": "~0.1.0", - "sass": "^1.83.0", + "sass": "^1.85.0", "stack-utils": "^2.0.3", "tippy.js": "^4.3.5", "tiptap": "~1.26.6", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 178de9222..3d00fae25 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -17242,10 +17242,10 @@ sass-resources-loader@^2.2.1: glob "^7.1.6" loader-utils "^2.0.0" -sass@^1.83.0: - version "1.83.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.83.0.tgz#e36842c0b88a94ed336fd16249b878a0541d536f" - integrity sha512-qsSxlayzoOjdvXMVLkzF84DJFc2HZEL/rFyGIKbbilYtAvlCxyuzUeff9LawTn4btVnLKg75Z8MMr1lxU1lfGw== +sass@^1.85.0: + version "1.85.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.85.0.tgz#0127ef697d83144496401553f0a0e87be83df45d" + integrity sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww== dependencies: chokidar "^4.0.0" immutable "^5.0.2" From 162169d1ea0f931a7041aaa45fc7ab47edb5b5ed Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 09:54:23 +0000 Subject: [PATCH 008/266] Bump prettier from 3.4.2 to 3.5.2 in /webapp Bumps [prettier](https://github.com/prettier/prettier) from 3.4.2 to 3.5.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.4.2...3.5.2) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 3619846c2..3e06273f8 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -107,7 +107,7 @@ "jest": "29.7", "jest-environment-jsdom": "^29.7.0", "mutation-observer": "^1.0.3", - "prettier": "~3.4.2", + "prettier": "~3.5.2", "sass-loader": "~10.1.1", "storybook-design-token": "^0.8.1", "storybook-vue-router": "^1.0.7", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 3d00fae25..9326ea522 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -15946,10 +15946,10 @@ prettier@^1.18.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@~3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" - integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== +prettier@~3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.2.tgz#d066c6053200da0234bf8fa1ef45168abed8b914" + integrity sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg== pretty-bytes@^5.3.0: version "5.3.0" From 51f062e4f7d40ec3607e7e06c37b87620a6e1eab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:14:46 +0000 Subject: [PATCH 009/266] Bump eslint-plugin-prettier from 5.2.1 to 5.2.3 in /webapp Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.2.1 to 5.2.3. - [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases) - [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.1...v5.2.3) --- updated-dependencies: - dependency-name: eslint-plugin-prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 3e06273f8..3807d852d 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -98,7 +98,7 @@ "eslint-plugin-import": "~2.31.0", "eslint-plugin-jest": "~24.4.0", "eslint-plugin-node": "~11.1.0", - "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-promise": "~7.2.1", "eslint-plugin-standard": "~5.0.0", "eslint-plugin-vue": "~9.32.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 9326ea522..f05b8a53e 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -9349,10 +9349,10 @@ eslint-plugin-node@~11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-prettier@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95" - integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw== +eslint-plugin-prettier@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5" + integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw== dependencies: prettier-linter-helpers "^1.0.0" synckit "^0.9.1" From d58cd525fb015e5a32e7cbafeaa007b4258fa672 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:36:29 +0000 Subject: [PATCH 010/266] Bump eslint-config-prettier from 6.15.0 to 10.0.1 in /webapp Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 6.15.0 to 10.0.1. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v6.15.0...v10.0.1) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 3807d852d..f6abc251d 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -92,7 +92,7 @@ "core-js": "~2.6.10", "css-loader": "~3.5.2", "eslint": "^7.28.0", - "eslint-config-prettier": "~6.15.0", + "eslint-config-prettier": "~10.0.1", "eslint-config-standard": "~15.0.1", "eslint-loader": "~4.0.0", "eslint-plugin-import": "~2.31.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index f05b8a53e..a85e93f9d 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -9258,13 +9258,18 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^6.0.0, eslint-config-prettier@~6.15.0: +eslint-config-prettier@^6.0.0: version "6.15.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== dependencies: get-stdin "^6.0.0" +eslint-config-prettier@~10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf" + integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw== + eslint-config-standard@~15.0.1: version "15.0.1" resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-15.0.1.tgz#1262eafaba66f4f03fdf3cf655a28aa8b40868b7" From 6d105cc3dc88148be5a2be6e41869823b041d610 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 10:57:28 +0000 Subject: [PATCH 011/266] Bump prettier from 3.4.2 to 3.5.2 in /backend Bumps [prettier](https://github.com/prettier/prettier) from 3.4.2 to 3.5.2. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.4.2...3.5.2) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 09ccb610c..2bc06bf62 100644 --- a/backend/package.json +++ b/backend/package.json @@ -113,7 +113,7 @@ "eslint-plugin-security": "^3.0.1", "jest": "^29.7.0", "nodemon": "~3.1.9", - "prettier": "^3.4.2", + "prettier": "^3.5.2", "rosie": "^2.1.1", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", diff --git a/backend/yarn.lock b/backend/yarn.lock index a8203157b..f25a8b076 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -7893,10 +7893,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.4.2: - version "3.4.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.4.2.tgz#a5ce1fb522a588bf2b78ca44c6e6fe5aa5a2b13f" - integrity sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ== +prettier@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.2.tgz#d066c6053200da0234bf8fa1ef45168abed8b914" + integrity sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" From 2e9f541af8c34d3a9c2ae423722ebeeaa5bec723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:21:54 +0000 Subject: [PATCH 012/266] Bump eslint-plugin-prettier from 5.2.1 to 5.2.3 in /backend Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.2.1 to 5.2.3. - [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases) - [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.1...v5.2.3) --- updated-dependencies: - dependency-name: eslint-plugin-prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 2bc06bf62..23bedbb29 100644 --- a/backend/package.json +++ b/backend/package.json @@ -108,7 +108,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.10.0", "eslint-plugin-n": "^16.6.2", - "eslint-plugin-prettier": "^5.2.1", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-security": "^3.0.1", "jest": "^29.7.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index f25a8b076..83cf500a1 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4390,10 +4390,10 @@ eslint-plugin-n@^16.6.2: resolve "^1.22.2" semver "^7.5.3" -eslint-plugin-prettier@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz#d1c8f972d8f60e414c25465c163d16f209411f95" - integrity sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw== +eslint-plugin-prettier@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5" + integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw== dependencies: prettier-linter-helpers "^1.0.0" synckit "^0.9.1" From 4707dae098f36589a642f511fe2f9c603d3e9bbc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 11:51:30 +0000 Subject: [PATCH 013/266] Bump eslint-config-prettier from 9.1.0 to 10.0.1 in /backend Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 9.1.0 to 10.0.1. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v9.1.0...v10.0.1) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 23bedbb29..eb50225c9 100644 --- a/backend/package.json +++ b/backend/package.json @@ -102,7 +102,7 @@ "@typescript-eslint/parser": "^5.62.0", "apollo-server-testing": "~2.11.0", "eslint": "^8.57.1", - "eslint-config-prettier": "^9.1.0", + "eslint-config-prettier": "^10.0.1", "eslint-config-standard": "^17.1.0", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 83cf500a1..b5b7be576 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4292,10 +4292,10 @@ eslint-compat-utils@^0.1.2: resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz#f45e3b5ced4c746c127cf724fb074cd4e730d653" integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg== -eslint-config-prettier@^9.1.0: - version "9.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz#31af3d94578645966c082fcb71a5846d3c94867f" - integrity sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw== +eslint-config-prettier@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf" + integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw== eslint-config-standard@^17.1.0: version "17.1.0" From a5779298f735730b351c5595205fcd2864c10bcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 12:40:57 +0000 Subject: [PATCH 014/266] Bump @faker-js/faker from 9.3.0 to 9.5.0 Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.3.0 to 9.5.0. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v9.3.0...v9.5.0) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 9 +++++---- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6089fca7c..b4ab527fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", - "@faker-js/faker": "9.3.0", + "@faker-js/faker": "9.5.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", @@ -2792,9 +2792,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.3.0.tgz", - "integrity": "sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw==", + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.0.tgz", + "integrity": "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==", "dev": true, "funding": [ { @@ -2802,6 +2802,7 @@ "url": "https://opencollective.com/fakerjs" } ], + "license": "MIT", "engines": { "node": ">=18.0.0", "npm": ">=9.0.0" diff --git a/package.json b/package.json index 4bfcf22be..6f7000547 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", - "@faker-js/faker": "9.3.0", + "@faker-js/faker": "9.5.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", diff --git a/yarn.lock b/yarn.lock index f5343ed73..0e28f1a10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1570,10 +1570,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== -"@faker-js/faker@9.3.0": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.3.0.tgz#ef398dab34c67faaa0e348318c905eae3564fa58" - integrity sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw== +"@faker-js/faker@9.5.0": + version "9.5.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" + integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== "@fastify/busboy@^2.0.0": version "2.1.1" From 3e38454a721afe39e3c525c177d8b88283468c8e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:05:13 +0000 Subject: [PATCH 015/266] Bump @faker-js/faker from 9.3.0 to 9.5.0 in /webapp Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.3.0 to 9.5.0. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v9.3.0...v9.5.0) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index f6abc251d..11ba99e8e 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -71,7 +71,7 @@ "@babel/core": "^7.25.8", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.25.8", - "@faker-js/faker": "9.3.0", + "@faker-js/faker": "9.5.0", "@nuxtjs/composition-api": "0.32.0", "@storybook/addon-a11y": "^8.0.8", "@storybook/addon-actions": "^5.3.21", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index a85e93f9d..02471513f 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2313,10 +2313,10 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@faker-js/faker@9.3.0": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.3.0.tgz#ef398dab34c67faaa0e348318c905eae3564fa58" - integrity sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw== +"@faker-js/faker@9.5.0": + version "9.5.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" + integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== "@hapi/address@2.x.x": version "2.0.0" From 77908a85fd454d401f6ae4540998a9ed3d4fe85c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:24:08 +0000 Subject: [PATCH 016/266] Bump eslint-plugin-jest from 28.10.0 to 28.11.0 in /backend Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 28.10.0 to 28.11.0. - [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases) - [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v28.10.0...v28.11.0) --- updated-dependencies: - dependency-name: eslint-plugin-jest dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index eb50225c9..1e3574ced 100644 --- a/backend/package.json +++ b/backend/package.json @@ -106,7 +106,7 @@ "eslint-config-standard": "^17.1.0", "eslint-import-resolver-typescript": "^3.7.0", "eslint-plugin-import": "^2.31.0", - "eslint-plugin-jest": "^28.10.0", + "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^16.6.2", "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-promise": "^6.1.1", diff --git a/backend/yarn.lock b/backend/yarn.lock index b5b7be576..1e089b1a3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4366,10 +4366,10 @@ eslint-plugin-import@^2.31.0: string.prototype.trimend "^1.0.8" tsconfig-paths "^3.15.0" -eslint-plugin-jest@^28.10.0: - version "28.10.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-28.10.0.tgz#4b35b8abb0f7cfe699bff8d9060270a2ddd770ea" - integrity sha512-hyMWUxkBH99HpXT3p8hc7REbEZK3D+nk8vHXGgpB+XXsi0gO4PxMSP+pjfUzb67GnV9yawV9a53eUmcde1CCZA== +eslint-plugin-jest@^28.11.0: + version "28.11.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-28.11.0.tgz#2641ecb4411941bbddb3d7cf8a8ff1163fbb510e" + integrity sha512-QAfipLcNCWLVocVbZW8GimKn5p5iiMcgGbRzz8z/P5q7xw+cNEpYqyzFMtIF/ZgF2HLOyy+dYBut+DoYolvqig== dependencies: "@typescript-eslint/utils" "^6.0.0 || ^7.0.0 || ^8.0.0" From 4de1b1a8b934789bf488db2bbaf6d1a3b342fbcd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:45:51 +0000 Subject: [PATCH 017/266] Bump @types/node from 22.10.2 to 22.13.5 in /backend Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.10.2 to 22.13.5. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 1e3574ced..4df213e9d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -97,7 +97,7 @@ "devDependencies": { "@faker-js/faker": "9.3.0", "@types/jest": "^29.5.14", - "@types/node": "^22.10.2", + "@types/node": "^22.13.5", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "apollo-server-testing": "~2.11.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 1e089b1a3..8455c0dbf 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1999,10 +1999,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>=6", "@types/node@^22.10.2": - version "22.10.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.2.tgz#a485426e6d1fdafc7b0d4c7b24e2c78182ddabb9" - integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ== +"@types/node@*", "@types/node@>=6", "@types/node@^22.13.5": + version "22.13.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.5.tgz#23add1d71acddab2c6a4d31db89c0f98d330b511" + integrity sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg== dependencies: undici-types "~6.20.0" From 515d84de918b1195dd05260511357795896f949c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:36:49 +0000 Subject: [PATCH 018/266] Bump the babel group across 1 directory with 2 updates Bumps the babel group with 2 updates in the / directory: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) and [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env). Updates `@babel/core` from 7.26.0 to 7.26.9 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-core) Updates `@babel/preset-env` from 7.26.0 to 7.26.9 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: babel - dependency-name: "@babel/preset-env" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: babel ... Signed-off-by: dependabot[bot] --- package-lock.json | 329 +++++++++++++++++++++++---------------------- package.json | 4 +- yarn.lock | 332 +++++++++++++++++++++++----------------------- 3 files changed, 335 insertions(+), 330 deletions(-) diff --git a/package-lock.json b/package-lock.json index b4ab527fe..2e03e1f2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,8 @@ "version": "3.2.0", "license": "MIT", "devDependencies": { - "@babel/core": "^7.26.0", - "@babel/preset-env": "^7.26.0", + "@babel/core": "^7.26.9", + "@babel/preset-env": "^7.26.9", "@babel/register": "^7.25.9", "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", @@ -102,10 +102,11 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.0.tgz", - "integrity": "sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g==", + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -116,30 +117,32 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.0.tgz", - "integrity": "sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA==", + "version": "7.26.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", + "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", + "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", + "@babel/helpers": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/traverse": "^7.26.9", + "@babel/types": "^7.26.9", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -155,13 +158,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.0.tgz", - "integrity": "sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", + "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.0", - "@babel/types": "^7.26.0", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -182,26 +186,14 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz", - "integrity": "sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", + "version": "7.26.5", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", + "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.25.9", + "@babel/compat-data": "^7.26.5", "@babel/helper-validator-option": "^7.25.9", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -321,10 +313,11 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", + "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==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -363,19 +356,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz", - "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, "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", @@ -431,25 +411,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", + "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.1.tgz", - "integrity": "sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", + "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", "devOptional": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.26.0" + "@babel/types": "^7.26.9" }, "bin": { "parser": "bin/babel-parser.js" @@ -675,14 +657,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz", - "integrity": "sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/traverse": "^7.26.8" }, "engines": { "node": ">=6.9.0" @@ -709,12 +692,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz", - "integrity": "sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -884,12 +868,12 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz", - "integrity": "sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.9", "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { @@ -915,12 +899,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz", - "integrity": "sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", + "@babel/helper-plugin-utils": "^7.26.5", "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" }, "engines": { @@ -1024,14 +1009,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz", - "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-simple-access": "^7.25.9" + "@babel/helper-module-transforms": "^7.26.0", + "@babel/helper-plugin-utils": "^7.25.9" }, "engines": { "node": ">=6.9.0" @@ -1106,12 +1091,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz", - "integrity": "sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1441,12 +1427,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz", - "integrity": "sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1456,12 +1443,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz", - "integrity": "sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.26.5" }, "engines": { "node": ">=6.9.0" @@ -1534,14 +1522,15 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.0.tgz", - "integrity": "sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw==", + "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==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", + "@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", @@ -1553,9 +1542,9 @@ "@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.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.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", @@ -1566,21 +1555,21 @@ "@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.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.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.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.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", @@ -1596,17 +1585,17 @@ "@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.25.9", - "@babel/plugin-transform-typeof-symbol": "^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.10.6", + "babel-plugin-polyfill-corejs3": "^0.11.0", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.38.1", + "core-js-compat": "^3.40.0", "semver": "^6.3.1" }, "engines": { @@ -1617,10 +1606,11 @@ } }, "node_modules/@babel/preset-env/node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", - "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz", + "integrity": "sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -1633,13 +1623,14 @@ } }, "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", - "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", + "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.2", - "core-js-compat": "^3.38.0" + "@babel/helper-define-polyfill-provider": "^0.6.3", + "core-js-compat": "^3.40.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -1723,30 +1714,32 @@ } }, "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", + "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/code-frame": "^7.26.2", + "@babel/parser": "^7.26.9", + "@babel/types": "^7.26.9" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.9.tgz", - "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", + "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/generator": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/template": "^7.25.9", - "@babel/types": "^7.25.9", + "@babel/code-frame": "^7.26.2", + "@babel/generator": "^7.26.9", + "@babel/parser": "^7.26.9", + "@babel/template": "^7.26.9", + "@babel/types": "^7.26.9", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1755,10 +1748,11 @@ } }, "node_modules/@babel/types": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz", - "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", + "version": "7.26.9", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", + "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", "devOptional": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -6884,9 +6878,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", - "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "devOptional": true, "funding": [ { @@ -6902,11 +6896,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001663", - "electron-to-chromium": "^1.5.28", - "node-releases": "^2.0.18", - "update-browserslist-db": "^1.1.0" + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" @@ -7038,9 +7033,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001669", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", - "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", + "version": "1.0.30001700", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz", + "integrity": "sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ==", "devOptional": true, "funding": [ { @@ -7055,7 +7050,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", @@ -7559,12 +7555,13 @@ } }, "node_modules/core-js-compat": { - "version": "3.38.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", - "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "version": "3.40.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz", + "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==", "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.3" + "browserslist": "^4.24.3" }, "funding": { "type": "opencollective", @@ -8537,10 +8534,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.41", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz", - "integrity": "sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ==", - "devOptional": true + "version": "1.5.103", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.103.tgz", + "integrity": "sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA==", + "devOptional": true, + "license": "ISC" }, "node_modules/elliptic": { "version": "6.5.4", @@ -12448,10 +12446,11 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", - "devOptional": true + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "devOptional": true, + "license": "MIT" }, "node_modules/node-source-walk": { "version": "7.0.0", diff --git a/package.json b/package.json index 6f7000547..7315dd55b 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "release": "yarn version --no-git-tag-version --no-commit-hooks --no-commit && auto-changelog --latest-version $(node -p -e \"require('./package.json').version\") && cd backend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../frontend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp/maintenance/source && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../../../package.json').version\")" }, "devDependencies": { - "@babel/core": "^7.26.0", - "@babel/preset-env": "^7.26.0", + "@babel/core": "^7.26.9", + "@babel/preset-env": "^7.26.9", "@babel/register": "^7.25.9", "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", diff --git a/yarn.lock b/yarn.lock index 0e28f1a10..a1c343d7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,16 +38,7 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815" - integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== - dependencies: - "@babel/helper-validator-identifier" "^7.25.9" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/code-frame@^7.22.13": +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.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" integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== @@ -56,39 +47,39 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.0.tgz#f02ba6d34e88fadd5e8861e8b38902f43cc1c819" - integrity sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA== +"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": + 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.16.0", "@babel/core@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== +"@babel/core@^7.16.0", "@babel/core@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" + integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/helper-compilation-targets" "^7.26.5" "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" + "@babel/helpers" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.9" + "@babel/types" "^7.26.9" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.25.9", "@babel/generator@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.0.tgz#505cc7c90d92513f458a477e5ef0703e7c91b8d7" - integrity sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w== +"@babel/generator@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== dependencies: - "@babel/parser" "^7.26.0" - "@babel/types" "^7.26.0" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" @@ -107,20 +98,12 @@ dependencies: "@babel/types" "^7.25.9" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz#f41752fe772a578e67286e6779a68a5a92de1ee9" - integrity sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@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== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" - integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== - dependencies: - "@babel/compat-data" "^7.25.9" + "@babel/compat-data" "^7.26.5" "@babel/helper-validator-option" "^7.25.9" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -205,10 +188,10 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-define-polyfill-provider@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" - integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== +"@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" @@ -282,10 +265,10 @@ dependencies: "@babel/types" "^7.25.9" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" - integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5", "@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" @@ -314,14 +297,6 @@ "@babel/helper-optimise-call-expression" "^7.25.9" "@babel/traverse" "^7.25.9" -"@babel/helper-simple-access@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" - integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" @@ -368,20 +343,20 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helpers@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" - integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== +"@babel/helpers@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" + integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== dependencies: - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.0" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" -"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.25.9", "@babel/parser@^7.26.0": - version "7.26.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.1.tgz#44e02499960df2cdce2c456372a3e8e0c3c5c975" - integrity sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw== +"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== dependencies: - "@babel/types" "^7.26.0" + "@babel/types" "^7.26.9" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" @@ -496,14 +471,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-async-generator-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2" - integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/helper-remap-async-to-generator" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/traverse" "^7.26.8" "@babel/plugin-transform-async-to-generator@^7.25.9": version "7.25.9" @@ -514,12 +489,12 @@ "@babel/helper-plugin-utils" "^7.25.9" "@babel/helper-remap-async-to-generator" "^7.25.9" -"@babel/plugin-transform-block-scoped-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" - integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-block-scoping@^7.25.9": version "7.25.9" @@ -601,12 +576,11 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-exponentiation-operator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz#ece47b70d236c1d99c263a1e22b62dc20a4c8b0f" - integrity sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA== +"@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-builder-binary-assignment-operator-visitor" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-export-namespace-from@^7.25.9": @@ -616,12 +590,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-for-of@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" - integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" "@babel/plugin-transform-function-name@^7.25.9": @@ -669,14 +643,13 @@ "@babel/helper-module-transforms" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-commonjs@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686" - integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg== +"@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.25.9" + "@babel/helper-module-transforms" "^7.26.0" "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-simple-access" "^7.25.9" "@babel/plugin-transform-modules-systemjs@^7.25.9": version "7.25.9" @@ -711,12 +684,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949" - integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-numeric-separator@^7.25.9": version "7.25.9" @@ -878,19 +851,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-template-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" - integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-transform-typeof-symbol@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b" - integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-unicode-escapes@^7.25.9": version "7.25.9" @@ -923,14 +896,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" -"@babel/preset-env@^7.16.0", "@babel/preset-env@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1" - integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw== +"@babel/preset-env@^7.16.0", "@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.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@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" @@ -942,9 +915,9 @@ "@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.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.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" @@ -955,21 +928,21 @@ "@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.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.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.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.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" @@ -985,17 +958,17 @@ "@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.25.9" - "@babel/plugin-transform-typeof-symbol" "^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.10.6" + babel-plugin-polyfill-corejs3 "^0.11.0" babel-plugin-polyfill-regenerator "^0.6.1" - core-js-compat "^3.38.1" + core-js-compat "^3.40.0" semver "^6.3.1" "@babel/preset-modules@0.1.6-no-external-plugins": @@ -1042,32 +1015,32 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15", "@babel/template@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" - integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== +"@babel/template@^7.22.15", "@babel/template@^7.25.9", "@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" -"@babel/traverse@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" - integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.8", "@babel/traverse@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/generator" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/template" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.4.4": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" - integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.9", "@babel/types@^7.4.4": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" @@ -3136,13 +3109,13 @@ babel-plugin-polyfill-corejs2@^0.4.10, babel-plugin-polyfill-corejs2@^0.4.6: "@babel/helper-define-polyfill-provider" "^0.6.1" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.10.6: - version "0.10.6" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" - integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== +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.2" - core-js-compat "^3.38.0" + "@babel/helper-define-polyfill-provider" "^0.6.3" + core-js-compat "^3.40.0" babel-plugin-polyfill-corejs3@^0.8.5: version "0.8.6" @@ -3468,6 +3441,16 @@ browserslist@^4.23.3, 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" + buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" @@ -3577,6 +3560,11 @@ 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== + capital-case@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669" @@ -3939,12 +3927,12 @@ copy-anything@^3.0.2: dependencies: is-what "^4.1.8" -core-js-compat@^3.33.1, core-js-compat@^3.38.0, core-js-compat@^3.38.1: - version "3.38.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" - integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== +core-js-compat@^3.33.1, 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.23.3" + browserslist "^4.24.3" core-util-is@1.0.2: version "1.0.2" @@ -4481,6 +4469,11 @@ 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== + elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -6692,6 +6685,11 @@ 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== + node-source-walk@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/node-source-walk/-/node-source-walk-7.0.0.tgz#cd849f539939994868a0b2ba4e9758322b2fcee6" @@ -7100,7 +7098,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picocolors@^1.0.1, picocolors@^1.1.0: +picocolors@^1.0.1, picocolors@^1.1.0, 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== @@ -8649,6 +8647,14 @@ update-browserslist-db@^1.1.0: escalade "^3.2.0" picocolors "^1.1.0" +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" + upper-case-first@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324" From 11841ec2a3d0234f0e8fbf7171d466e0455bc379 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:17:23 +0000 Subject: [PATCH 019/266] Bump the babel group across 1 directory with 2 updates Bumps the babel group with 2 updates in the /backend directory: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) and [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env). Updates `@babel/core` from 7.26.0 to 7.26.9 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-core) Updates `@babel/preset-env` from 7.26.0 to 7.26.9 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.9/packages/babel-preset-env) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: babel - dependency-name: "@babel/preset-env" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: babel ... Signed-off-by: dependabot[bot] --- backend/package.json | 4 +- backend/yarn.lock | 352 +++++++++++++++++++++---------------------- 2 files changed, 178 insertions(+), 178 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4df213e9d..533ce13b3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,10 +24,10 @@ }, "dependencies": { "@babel/cli": "~7.26.4", - "@babel/core": "^7.26.0", + "@babel/core": "^7.26.9", "@babel/node": "~7.26.0", "@babel/plugin-proposal-throw-expressions": "^7.25.9", - "@babel/preset-env": "~7.26.0", + "@babel/preset-env": "~7.26.9", "@babel/register": "^7.23.7", "@sentry/node": "^5.15.4", "apollo-cache-inmemory": "~1.6.6", diff --git a/backend/yarn.lock b/backend/yarn.lock index 8455c0dbf..802401fe3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -69,48 +69,48 @@ "@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.25.9", "@babel/code-frame@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.0.tgz#9374b5cd068d128dac0b94ff482594273b1c2815" - integrity sha512-INCKxTtbXtcNbUZ3YXutwMpEleqttcswhAdee7dhuoVrD2cnuc3PqtERBtxkX5nziX9vnBL8WXmSGwv8CuPV6g== +"@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" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== dependencies: "@babel/helper-validator-identifier" "^7.25.9" js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.9", "@babel/compat-data@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.0.tgz#f02ba6d34e88fadd5e8861e8b38902f43cc1c819" - integrity sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA== +"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": + 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.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.0.tgz#d78b6023cc8f3114ccf049eb219613f74a747b40" - integrity sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg== +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" + integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.26.0" - "@babel/generator" "^7.26.0" - "@babel/helper-compilation-targets" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/helper-compilation-targets" "^7.26.5" "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.0" - "@babel/parser" "^7.26.0" - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.26.0" + "@babel/helpers" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.9" + "@babel/types" "^7.26.9" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.25.9", "@babel/generator@^7.26.0", "@babel/generator@^7.7.2": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.0.tgz#505cc7c90d92513f458a477e5ef0703e7c91b8d7" - integrity sha512-/AIkAmInnWwgEAJGQr9vY0c66Mj6kjkE2ZPB1PurTRaRAh3U+J45sAQMjQDJdh4WbR3l0x5xkimXBKyBXXAu2w== +"@babel/generator@^7.26.9", "@babel/generator@^7.7.2": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== dependencies: - "@babel/parser" "^7.26.0" - "@babel/types" "^7.26.0" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" @@ -129,20 +129,12 @@ dependencies: "@babel/types" "^7.25.9" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.9.tgz#f41752fe772a578e67286e6779a68a5a92de1ee9" - integrity sha512-C47lC7LIDCnz0h4vai/tpNOI95tCd5ZT3iBt/DBH5lXKHZsyNQv18yf1wIIg2ntiQNgmAvA+DgZ82iW8Qdym8g== +"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@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== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz#55af025ce365be3cdc0c1c1e56c6af617ce88875" - integrity sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ== - dependencies: - "@babel/compat-data" "^7.25.9" + "@babel/compat-data" "^7.26.5" "@babel/helper-validator-option" "^7.25.9" browserslist "^4.24.0" lru-cache "^5.1.1" @@ -190,10 +182,10 @@ lodash.debounce "^4.0.8" resolve "^1.14.2" -"@babel/helper-define-polyfill-provider@^0.6.2": - version "0.6.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz#18594f789c3594acb24cfdb4a7f7b7d2e8bd912d" - integrity sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ== +"@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" @@ -233,10 +225,10 @@ 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.8.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" - integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== +"@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": + 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" @@ -256,14 +248,6 @@ "@babel/helper-optimise-call-expression" "^7.25.9" "@babel/traverse" "^7.25.9" -"@babel/helper-simple-access@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.25.9.tgz#6d51783299884a2c74618d6ef0f86820ec2e7739" - integrity sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^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" @@ -296,13 +280,13 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helpers@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.0.tgz#30e621f1eba5aa45fe6f4868d2e9154d884119a4" - integrity sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw== +"@babel/helpers@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" + integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== dependencies: - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.0" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" "@babel/node@~7.26.0": version "7.26.0" @@ -316,12 +300,12 @@ 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.25.9", "@babel/parser@^7.26.0", "@babel/parser@^7.7.0": - version "7.26.1" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.1.tgz#44e02499960df2cdce2c456372a3e8e0c3c5c975" - integrity sha512-reoQYNiAJreZNsJzyrDNzFQ+IQ5JFiIzAHJg9bn94S3l+4++J7RsIhNMoB+lgP/9tpmiAQqspv+xfdxTSzREOw== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.9", "@babel/parser@^7.7.0": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== dependencies: - "@babel/types" "^7.26.0" + "@babel/types" "^7.26.9" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" @@ -501,14 +485,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-async-generator-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.9.tgz#1b18530b077d18a407c494eb3d1d72da505283a2" - integrity sha512-RXV6QAzTBbhDMO9fWwOmwwTuYaiPbggWQ9INdZqAYeSHyG7FzQ+nOZaUUjNwKv9pV3aE4WFqFm1Hnbci5tBCAw== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/helper-remap-async-to-generator" "^7.25.9" - "@babel/traverse" "^7.25.9" + "@babel/traverse" "^7.26.8" "@babel/plugin-transform-async-to-generator@^7.25.9": version "7.25.9" @@ -519,12 +503,12 @@ "@babel/helper-plugin-utils" "^7.25.9" "@babel/helper-remap-async-to-generator" "^7.25.9" -"@babel/plugin-transform-block-scoped-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.9.tgz#5700691dbd7abb93de300ca7be94203764fce458" - integrity sha512-toHc9fzab0ZfenFpsyYinOX0J/5dgJVA2fm64xPewu7CoYHWEivIWKxkK2rMi4r3yQqLnVmheMXRdG+k239CgA== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-block-scoping@^7.25.9": version "7.25.9" @@ -606,12 +590,11 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-exponentiation-operator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.9.tgz#ece47b70d236c1d99c263a1e22b62dc20a4c8b0f" - integrity sha512-KRhdhlVk2nObA5AYa7QMgTMTVJdfHprfpAk4DjZVtllqRg9qarilstTKEhpVjyt+Npi8ThRyiV8176Am3CodPA== +"@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-builder-binary-assignment-operator-visitor" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" "@babel/plugin-transform-export-namespace-from@^7.25.9": @@ -621,12 +604,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-for-of@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.9.tgz#4bdc7d42a213397905d89f02350c5267866d5755" - integrity sha512-LqHxduHoaGELJl2uhImHwRQudhCM50pT46rIBNvtT/Oql3nqiS3wOwP+5ten7NpYSXrrVLgtZU3DZmPtWZo16A== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" "@babel/plugin-transform-function-name@^7.25.9": @@ -674,14 +657,13 @@ "@babel/helper-module-transforms" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-modules-commonjs@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.9.tgz#d165c8c569a080baf5467bda88df6425fc060686" - integrity sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg== +"@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.25.9" + "@babel/helper-module-transforms" "^7.26.0" "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-simple-access" "^7.25.9" "@babel/plugin-transform-modules-systemjs@^7.25.9": version "7.25.9" @@ -716,12 +698,12 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-nullish-coalescing-operator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.9.tgz#bcb1b0d9e948168102d5f7104375ca21c3266949" - integrity sha512-ENfftpLZw5EItALAD4WsY/KUWvhUlZndm5GC7G3evUsVeSJB6p0pBeLQUnRnBCBx7zV0RKQjR9kCuwrsIrjWog== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-numeric-separator@^7.25.9": version "7.25.9" @@ -838,19 +820,19 @@ dependencies: "@babel/helper-plugin-utils" "^7.25.9" -"@babel/plugin-transform-template-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.9.tgz#6dbd4a24e8fad024df76d1fac6a03cf413f60fe1" - integrity sha512-o97AE4syN71M/lxrCtQByzphAdlYluKPDBzDVzMmfCobUjjhAryZV0AIpRPrxN0eAkxXO6ZLEScmt+PNhj2OTw== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" -"@babel/plugin-transform-typeof-symbol@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.9.tgz#224ba48a92869ddbf81f9b4a5f1204bbf5a2bc4b" - integrity sha512-v61XqUMiueJROUv66BVIOi0Fv/CUuZuZMl5NkRoCVxLAnMexZ0A3kMe7vvZ0nulxMuMp0Mk6S5hNh48yki08ZA== +"@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.25.9" + "@babel/helper-plugin-utils" "^7.26.5" "@babel/plugin-transform-unicode-escapes@^7.25.9": version "7.25.9" @@ -883,14 +865,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.25.9" "@babel/helper-plugin-utils" "^7.25.9" -"@babel/preset-env@~7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.0.tgz#30e5c6bc1bcc54865bff0c5a30f6d4ccdc7fa8b1" - integrity sha512-H84Fxq0CQJNdPFT2DrfnylZ3cf5K43rGfWK4LJGPpjKHiZlk0/RzwEus3PDDZZg+/Er7lCA03MVacueUuXdzfw== +"@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.0" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" + "@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" @@ -902,9 +884,9 @@ "@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.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.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" @@ -915,21 +897,21 @@ "@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.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.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.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.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" @@ -945,17 +927,17 @@ "@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.25.9" - "@babel/plugin-transform-typeof-symbol" "^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.10.6" + babel-plugin-polyfill-corejs3 "^0.11.0" babel-plugin-polyfill-regenerator "^0.6.1" - core-js-compat "^3.38.1" + core-js-compat "^3.40.0" semver "^6.3.1" "@babel/preset-modules@0.1.6-no-external-plugins": @@ -1006,32 +988,32 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.25.9", "@babel/template@^7.3.3": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" - integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== +"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.3.3": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" -"@babel/traverse@^7.25.9", "@babel/traverse@^7.7.0": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.25.9.tgz#a50f8fe49e7f69f53de5bea7e413cd35c5e13c84" - integrity sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw== +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.8", "@babel/traverse@^7.26.9", "@babel/traverse@^7.7.0": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/generator" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/template" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" 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.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.0.tgz#deabd08d6b753bc8e0f198f8709fb575e31774ff" - integrity sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.25.9", "@babel/types@^7.26.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" @@ -2925,13 +2907,13 @@ babel-plugin-polyfill-corejs2@^0.4.10: "@babel/helper-define-polyfill-provider" "^0.6.1" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.10.6: - version "0.10.6" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz#2deda57caef50f59c525aeb4964d3b2f867710c7" - integrity sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA== +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.2" - core-js-compat "^3.38.0" + "@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" @@ -3063,16 +3045,6 @@ braces@^3.0.2, braces@~3.0.2: dependencies: fill-range "^7.0.1" -browserslist@^4.23.3: - version "4.23.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.23.3.tgz#debb029d3c93ebc97ffbc8d9cbb03403e227c800" - integrity sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA== - dependencies: - caniuse-lite "^1.0.30001646" - electron-to-chromium "^1.5.4" - node-releases "^2.0.18" - update-browserslist-db "^1.1.0" - browserslist@^4.24.0: version "4.24.0" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.0.tgz#a1325fe4bc80b64fda169629fc01b3d6cecd38d4" @@ -3083,6 +3055,16 @@ 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" @@ -3241,16 +3223,16 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001646: - version "1.0.30001658" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001658.tgz#b5f7be8ac748a049ab06aa1cf7a1408d83f074ec" - integrity sha512-N2YVqWbJELVdrnsW5p+apoQyYt51aBMSsBZki1XZEfeBCexcM/sf4xiAHcXQBkuOwJBXtWF7aW1sYX6tKebPHw== - caniuse-lite@^1.0.30001663: version "1.0.30001669" 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" @@ -3519,12 +3501,12 @@ cookie@^0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= -core-js-compat@^3.38.0, core-js-compat@^3.38.1: - version "3.38.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.38.1.tgz#2bc7a298746ca5a7bcb9c164bcb120f2ebc09a09" - integrity sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw== +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.23.3" + browserslist "^4.24.3" core-js-pure@^3.0.0: version "3.6.4" @@ -3975,10 +3957,10 @@ 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.4: - version "1.5.18" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.18.tgz#5fe62b9d21efbcfa26571066502d94f3ed97e495" - integrity sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ== +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== emittery@^0.13.1: version "0.13.1" @@ -4262,7 +4244,7 @@ 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.1.2, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== @@ -7383,6 +7365,11 @@ 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" @@ -7833,6 +7820,11 @@ 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" @@ -9360,6 +9352,14 @@ 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" From 2cd6d0976002b77e6866e71fe306e9f2676cf62a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 15:52:28 +0000 Subject: [PATCH 020/266] Bump @faker-js/faker from 9.3.0 to 9.5.0 in /backend Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.3.0 to 9.5.0. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v9.3.0...v9.5.0) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 533ce13b3..49eecc36b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -95,7 +95,7 @@ "xregexp": "^4.3.0" }, "devDependencies": { - "@faker-js/faker": "9.3.0", + "@faker-js/faker": "9.5.0", "@types/jest": "^29.5.14", "@types/node": "^22.13.5", "@typescript-eslint/eslint-plugin": "^5.62.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 802401fe3..e4e7f649b 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1062,10 +1062,10 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== -"@faker-js/faker@9.3.0": - version "9.3.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.3.0.tgz#ef398dab34c67faaa0e348318c905eae3564fa58" - integrity sha512-r0tJ3ZOkMd9xsu3VRfqlFR6cz0V/jFYRswAIpC+m/DIfAUXq7g8N7wTAlhSANySXYGKzGryfDXwtwsY8TxEIDw== +"@faker-js/faker@9.5.0": + version "9.5.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" + integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== "@gar/promisify@^1.1.3": version "1.1.3" From 37a4ca1e1eaad42a94a1917ae68cc31dd0a55b92 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 16:11:19 +0000 Subject: [PATCH 021/266] Bump nodemailer from 6.9.16 to 6.10.0 in /backend Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 6.9.16 to 6.10.0. - [Release notes](https://github.com/nodemailer/nodemailer/releases) - [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.16...v6.10.0) --- updated-dependencies: - dependency-name: nodemailer dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 49eecc36b..f47c46a68 100644 --- a/backend/package.json +++ b/backend/package.json @@ -83,7 +83,7 @@ "neo4j-graphql-js": "^2.11.5", "neode": "^0.4.9", "node-fetch": "^2.7.0", - "nodemailer": "^6.9.16", + "nodemailer": "^6.10.0", "nodemailer-html-to-text": "^3.2.0", "request": "~2.88.2", "sanitize-html": "~2.14.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index e4e7f649b..8f9d1f175 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -7377,10 +7377,10 @@ nodemailer-html-to-text@^3.2.0: dependencies: html-to-text "7.1.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.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.0.tgz#1f24c9de94ad79c6206f66d132776b6503003912" + integrity sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA== nodemon@~3.1.9: version "3.1.9" From 07965b8668ca898584d572dd217ef4818a076229 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 16:30:55 +0000 Subject: [PATCH 022/266] Bump eslint-import-resolver-typescript from 3.7.0 to 3.8.3 in /backend Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 3.7.0 to 3.8.3. - [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases) - [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v3.7.0...v3.8.3) --- updated-dependencies: - dependency-name: eslint-import-resolver-typescript dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/backend/package.json b/backend/package.json index f47c46a68..74b3a259e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -104,7 +104,7 @@ "eslint": "^8.57.1", "eslint-config-prettier": "^10.0.1", "eslint-config-standard": "^17.1.0", - "eslint-import-resolver-typescript": "^3.7.0", + "eslint-import-resolver-typescript": "^3.8.3", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^16.6.2", diff --git a/backend/yarn.lock b/backend/yarn.lock index 8f9d1f175..2778869ba 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4293,19 +4293,18 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-import-resolver-typescript@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.7.0.tgz#e69925936a771a9cb2de418ccebc4cdf6c0818aa" - integrity sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow== +eslint-import-resolver-typescript@^3.8.3: + version "3.8.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.3.tgz#1721a1e4417e57a8fe6bf9463d0db8e220285eef" + integrity sha512-A0bu4Ks2QqDWNpeEgTQMPTngaMhuDu4yv6xpftBMAf+1ziXnpx+eSR1WRfoPTe2BAiAjHFZ7kSNx1fvr5g5pmQ== dependencies: "@nolyfill/is-core-module" "1.0.39" debug "^4.3.7" enhanced-resolve "^5.15.0" - fast-glob "^3.3.2" - get-tsconfig "^4.7.5" + get-tsconfig "^4.10.0" is-bun-module "^1.0.2" - is-glob "^4.0.3" stable-hash "^0.0.4" + tinyglobby "^0.2.12" eslint-module-utils@^2.12.0: version "2.12.0" @@ -4671,6 +4670,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fdir@^6.4.3: + version "6.4.3" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" + integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -4967,10 +4971,10 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" -get-tsconfig@^4.7.0, get-tsconfig@^4.7.5: - version "4.8.0" - resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.8.0.tgz#125dc13a316f61650a12b20c97c11b8fd996fedd" - integrity sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw== +get-tsconfig@^4.10.0, get-tsconfig@^4.7.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.0.tgz#403a682b373a823612475a4c2928c7326fc0f6bb" + integrity sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A== dependencies: resolve-pkg-maps "^1.0.0" @@ -7830,6 +7834,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +picomatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" + integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -8923,6 +8932,14 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +tinyglobby@^0.2.12: + version "0.2.12" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5" + integrity sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww== + dependencies: + fdir "^6.4.3" + picomatch "^4.0.2" + title@~3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/title/-/title-3.4.1.tgz#166177c48f52ae9b73afe9901a183f0b6c97b2e6" From 8eee365f68f43b2e193afd2aedb6eed442e9bc25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 16:53:25 +0000 Subject: [PATCH 023/266] Bump xregexp from 4.3.0 to 5.1.2 in /backend Bumps [xregexp](https://github.com/slevithan/xregexp) from 4.3.0 to 5.1.2. - [Release notes](https://github.com/slevithan/xregexp/releases) - [Commits](https://github.com/slevithan/xregexp/compare/v4.3.0...v5.1.2) --- updated-dependencies: - dependency-name: xregexp dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/backend/package.json b/backend/package.json index 74b3a259e..1013920b1 100644 --- a/backend/package.json +++ b/backend/package.json @@ -92,7 +92,7 @@ "trunc-html": "~1.1.2", "uuid": "~9.0.1", "validator": "^13.12.0", - "xregexp": "^4.3.0" + "xregexp": "^5.1.2" }, "devDependencies": { "@faker-js/faker": "9.5.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 2778869ba..2631d2c3f 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -973,13 +973,13 @@ core-js "^2.6.5" regenerator-runtime "^0.13.2" -"@babel/runtime-corejs3@^7.8.3": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.4.tgz#ccc4e042e2fae419c67fa709567e5d2179ed3940" - integrity sha512-+wpLqy5+fbQhvbllvlJEVRIpYj+COUWnnsm+I4jZlA8Lo7/MJmBhGTCHyk1/RWfOqBRJ2MbadddG6QltTKTlrg== +"@babel/runtime-corejs3@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.26.9.tgz#8b73bae47453aa3dd2839ac52598581a7dd8332f" + integrity sha512-5EVjbTegqN7RSJle6hMWYxO4voo4rI+9krITk+DWR+diJgGrjZjrIBnJhjrHYYQsFgI7j1w1QnrvV7YSKBfYGg== dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.2" + 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": version "7.12.5" @@ -3508,10 +3508,10 @@ core-js-compat@^3.40.0: dependencies: browserslist "^4.24.3" -core-js-pure@^3.0.0: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" - integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== +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: version "2.6.9" @@ -9728,12 +9728,12 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xregexp@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" - integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== +xregexp@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-5.1.2.tgz#ae4979868313d2f4dc4da9ab31ff5c2352fb969e" + integrity sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw== dependencies: - "@babel/runtime-corejs3" "^7.8.3" + "@babel/runtime-corejs3" "^7.26.9" xss@^1.0.6: version "1.0.7" From 9ef957a3fcb76ae1f91d5eda4ed273852808a8e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:14:26 +0000 Subject: [PATCH 024/266] Bump xregexp from 4.3.0 to 5.1.2 in /webapp Bumps [xregexp](https://github.com/slevithan/xregexp) from 4.3.0 to 5.1.2. - [Release notes](https://github.com/slevithan/xregexp/releases) - [Commits](https://github.com/slevithan/xregexp/compare/v4.3.0...v5.1.2) --- updated-dependencies: - dependency-name: xregexp dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- webapp/package.json | 2 +- webapp/yarn.lock | 37 +++++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 11ba99e8e..70f35761c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -64,7 +64,7 @@ "vue-sweetalert-icons": "~4.3.1", "vue2-datepicker": "^3.11.1", "vuex-i18n": "~1.13.1", - "xregexp": "^4.3.0", + "xregexp": "^5.1.2", "zxcvbn": "^4.4.2" }, "devDependencies": { diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 02471513f..76b14180a 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -1840,13 +1840,13 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime-corejs3@^7.8.3": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.8.4.tgz#ccc4e042e2fae419c67fa709567e5d2179ed3940" - integrity sha512-+wpLqy5+fbQhvbllvlJEVRIpYj+COUWnnsm+I4jZlA8Lo7/MJmBhGTCHyk1/RWfOqBRJ2MbadddG6QltTKTlrg== +"@babel/runtime-corejs3@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.26.9.tgz#8b73bae47453aa3dd2839ac52598581a7dd8332f" + integrity sha512-5EVjbTegqN7RSJle6hMWYxO4voo4rI+9krITk+DWR+diJgGrjZjrIBnJhjrHYYQsFgI7j1w1QnrvV7YSKBfYGg== dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.2" + core-js-pure "^3.30.2" + regenerator-runtime "^0.14.0" "@babel/runtime@7.3.4": version "7.3.4" @@ -7726,10 +7726,10 @@ core-js-compat@^3.6.2: browserslist "^4.8.3" semver "7.0.0" -core-js-pure@^3.0.0: - version "3.6.4" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" - integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== +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@^1.0.0: version "1.2.7" @@ -16761,11 +16761,16 @@ regenerator-runtime@^0.12.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== -regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: +regenerator-runtime@^0.13.4: version "0.13.9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + regenerator-transform@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.2.tgz#949d9d87468ff88d5a7e4734ebb994a892de1ff2" @@ -20280,12 +20285,12 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xregexp@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.3.0.tgz#7e92e73d9174a99a59743f67a4ce879a04b5ae50" - integrity sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g== +xregexp@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-5.1.2.tgz#ae4979868313d2f4dc4da9ab31ff5c2352fb969e" + integrity sha512-6hGgEMCGhqCTFEJbqmWrNIPqfpdirdGWkqshu7fFZddmTSfgv5Sn9D2SaKloR79s5VUiUlpwzg3CM3G6D3VIlw== dependencies: - "@babel/runtime-corejs3" "^7.8.3" + "@babel/runtime-corejs3" "^7.26.9" xss@^1.0.6: version "1.0.8" From 8d465ef7f1b229ec0812ad78ec96f7cc9871b3eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 19:51:28 +0000 Subject: [PATCH 025/266] Bump gaurav-nelson/github-action-markdown-link-check Bumps [gaurav-nelson/github-action-markdown-link-check](https://github.com/gaurav-nelson/github-action-markdown-link-check) from 7d83e59a57f3c201c76eed3d33dff64ec4452d27 to 1b916f2cf6c36510a6059943104e3c42ce6c16bc. - [Release notes](https://github.com/gaurav-nelson/github-action-markdown-link-check/releases) - [Commits](https://github.com/gaurav-nelson/github-action-markdown-link-check/compare/7d83e59a57f3c201c76eed3d33dff64ec4452d27...1b916f2cf6c36510a6059943104e3c42ce6c16bc) --- updated-dependencies: - dependency-name: gaurav-nelson/github-action-markdown-link-check dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/check-documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check-documentation.yml b/.github/workflows/check-documentation.yml index 5efb91f35..1a7d278e8 100644 --- a/.github/workflows/check-documentation.yml +++ b/.github/workflows/check-documentation.yml @@ -34,7 +34,7 @@ jobs: run: rm -rf ./deployment/src/old/ ./CHANGELOG.md # workaround until https://github.com/gaurav-nelson/github-action-markdown-link-check/pull/183 has been done - name: Check Markdown Links - uses: gaurav-nelson/github-action-markdown-link-check@7d83e59a57f3c201c76eed3d33dff64ec4452d27 # 1.0.15 + uses: gaurav-nelson/github-action-markdown-link-check@1b916f2cf6c36510a6059943104e3c42ce6c16bc # 1.0.15 with: use-quiet-mode: 'yes' use-verbose-mode: 'no' From bf04af537723e688f82a81467f5721210df84d0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 20:56:25 +0000 Subject: [PATCH 026/266] Bump peter-evans/repository-dispatch Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 39d2331fbbe4be56c4434ca745a23633155f9cdf to b0b38f73c8333be75d585a92b2c630a10d2a78f5. - [Release notes](https://github.com/peter-evans/repository-dispatch/releases) - [Commits](https://github.com/peter-evans/repository-dispatch/compare/39d2331fbbe4be56c4434ca745a23633155f9cdf...b0b38f73c8333be75d585a92b2c630a10d2a78f5) --- updated-dependencies: - dependency-name: peter-evans/repository-dispatch dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- .github/workflows/publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ad98bd0d0..4291e5dac 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -293,7 +293,7 @@ jobs: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV #- name: Repository Dispatch - # uses: peter-evans/repository-dispatch@39d2331fbbe4be56c4434ca745a23633155f9cdf # v3.0.0 + # uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0 # with: # token: ${{ github.token }} # event-type: trigger-ocelot-build-success @@ -301,7 +301,7 @@ jobs: # client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}' - name: Repository Dispatch stage.ocelot.social - uses: peter-evans/repository-dispatch@39d2331fbbe4be56c4434ca745a23633155f9cdf # v3.0.0 + uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0 with: token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository event-type: trigger-ocelot-build-success @@ -309,7 +309,7 @@ jobs: client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}' - name: Repository Dispatch stage.yunite.me - uses: peter-evans/repository-dispatch@39d2331fbbe4be56c4434ca745a23633155f9cdf # v3.0.0 + uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0 with: token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository event-type: trigger-ocelot-build-success From 2c7139036c3e76e31f2c3ec6c56bb7dbb932c86e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 21:50:47 +0000 Subject: [PATCH 027/266] Bump the metascraper group across 1 directory with 12 updates Bumps the metascraper group with 12 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [metascraper](https://github.com/microlinkhq/metascraper) | `5.33.5` | `5.46.7` | | [metascraper-author](https://github.com/microlinkhq/metascraper) | `5.33.5` | `5.46.5` | | [metascraper-date](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-date) | `5.33.5` | `5.46.5` | | [metascraper-description](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-description) | `5.33.5` | `5.46.5` | | [metascraper-image](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-image) | `5.33.5` | `5.46.5` | | [metascraper-lang](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-lang) | `5.33.5` | `5.46.5` | | [metascraper-logo](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-logo) | `5.33.5` | `5.46.5` | | [metascraper-publisher](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-publisher) | `5.33.5` | `5.46.5` | | [metascraper-title](https://github.com/microlinkhq/metascraper) | `5.34.7` | `5.46.5` | | [metascraper-url](https://github.com/microlinkhq/metascraper) | `5.34.2` | `5.46.5` | | [metascraper-video](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-video) | `5.33.5` | `5.46.5` | | [metascraper-youtube](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-youtube) | `5.33.5` | `5.46.5` | Updates `metascraper` from 5.33.5 to 5.46.7 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.33.5...v5.46.7) Updates `metascraper-author` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.33.5...v5.46.5) Updates `metascraper-date` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-date/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-date) Updates `metascraper-description` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-description/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-description) Updates `metascraper-image` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-image/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-image) Updates `metascraper-lang` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-lang/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-lang) Updates `metascraper-logo` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-logo/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-logo) Updates `metascraper-publisher` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-publisher/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-publisher) Updates `metascraper-title` from 5.34.7 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.34.7...v5.46.5) Updates `metascraper-url` from 5.34.2 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.34.2...v5.46.5) Updates `metascraper-video` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-video/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-video) Updates `metascraper-youtube` from 5.33.5 to 5.46.5 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-youtube/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.5/packages/metascraper-youtube) --- updated-dependencies: - dependency-name: metascraper dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-author dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-date dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-description dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-image dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-lang dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-logo dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-publisher dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-title dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-url dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-video dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper - dependency-name: metascraper-youtube dependency-type: direct:production update-type: version-update:semver-minor dependency-group: metascraper ... Signed-off-by: dependabot[bot] --- backend/package.json | 24 +- backend/yarn.lock | 1394 ++++++++++++++++++++++++------------------ 2 files changed, 799 insertions(+), 619 deletions(-) diff --git a/backend/package.json b/backend/package.json index 1013920b1..c89a3b49a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -61,20 +61,20 @@ "linkify-html": "^4.2.0", "lodash": "~4.17.21", "merge-graphql-schemas": "^1.7.8", - "metascraper": "^5.33.5", - "metascraper-author": "^5.33.5", - "metascraper-date": "^5.33.5", - "metascraper-description": "^5.33.5", - "metascraper-image": "^5.33.5", - "metascraper-lang": "^5.33.5", + "metascraper": "^5.46.7", + "metascraper-author": "^5.46.5", + "metascraper-date": "^5.46.5", + "metascraper-description": "^5.46.5", + "metascraper-image": "^5.46.5", + "metascraper-lang": "^5.46.5", "metascraper-lang-detector": "^4.10.2", - "metascraper-logo": "^5.33.5", - "metascraper-publisher": "^5.33.5", + "metascraper-logo": "^5.46.5", + "metascraper-publisher": "^5.46.5", "metascraper-soundcloud": "^5.34.4", - "metascraper-title": "^5.34.7", - "metascraper-url": "^5.34.2", - "metascraper-video": "^5.33.5", - "metascraper-youtube": "^5.33.5", + "metascraper-title": "^5.46.5", + "metascraper-url": "^5.46.5", + "metascraper-video": "^5.46.5", + "metascraper-youtube": "^5.46.5", "migrate": "^2.1.0", "mime-types": "^2.1.35", "minimatch": "^9.0.4", diff --git a/backend/yarn.lock b/backend/yarn.lock index 2631d2c3f..c8c55819f 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -53,6 +53,17 @@ dependencies: xss "^1.0.6" +"@asamuzakjp/css-color@^2.8.2": + version "2.8.3" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-2.8.3.tgz#665f0f5e8edb95d8f543847529e30fe5cc437ef7" + integrity sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw== + dependencies: + "@csstools/css-calc" "^2.1.1" + "@csstools/css-color-parser" "^3.0.7" + "@csstools/css-parser-algorithms" "^3.0.4" + "@csstools/css-tokenizer" "^3.0.3" + lru-cache "^10.4.3" + "@babel/cli@~7.26.4": version "7.26.4" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.26.4.tgz#4101ff8ee5de8447a6c395397a97921056411d20" @@ -1030,6 +1041,34 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@csstools/color-helpers@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" + integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== + +"@csstools/css-calc@^2.1.1", "@csstools/css-calc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.2.tgz#bffd55f002dab119b76d4023f95cd943e6c8c11e" + integrity sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw== + +"@csstools/css-color-parser@^3.0.7": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz#5fe9322920851450bf5e065c2b0e731b9e165394" + integrity sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ== + dependencies: + "@csstools/color-helpers" "^5.0.2" + "@csstools/css-calc" "^2.1.2" + +"@csstools/css-parser-algorithms@^3.0.4": + version "3.0.4" + resolved "https://registry.yarnpkg.com/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz#74426e93bd1c4dcab3e441f5cc7ba4fb35d94356" + integrity sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A== + +"@csstools/css-tokenizer@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" + integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== + "@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1067,11 +1106,6 @@ resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== -"@gar/promisify@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - "@graphql-toolkit/common@0.10.4": version "0.10.4" resolved "https://registry.yarnpkg.com/@graphql-toolkit/common/-/common-0.10.4.tgz#7785f2a3f14559d0778859c49f4442078c196695" @@ -1156,6 +1190,18 @@ resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -1172,6 +1218,11 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== +"@jclem/logfmt2@~2.4.3": + version "2.4.3" + resolved "https://registry.yarnpkg.com/@jclem/logfmt2/-/logfmt2-2.4.3.tgz#3ce7561984bd8ea95b02beaad2a798999df82a60" + integrity sha512-d7zluLlx+JRtVICF0+ghcrVdXBdE3eXrpIuFdcCcWxA3ABOyemkTySG4ha2AdsWFwAnh8tkB1vtyeZsWAbLumg== + "@jest/console@^29.7.0": version "29.7.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc" @@ -1428,20 +1479,40 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@keyvhq/core@^1.6.14": - version "1.6.14" - resolved "https://registry.yarnpkg.com/@keyvhq/core/-/core-1.6.14.tgz#e0c809ac184c761ffae9bccaf4d0b00eb15c053f" - integrity sha512-NQtjFKsZXLYs3SWbeYATdwwJ1fZtzwrV+9YM4Y9JPGGQqLI0q6gyY2sEWI4RvrKBGJ1AXCJPQNXOPr8oAWdEDw== - dependencies: - json-buffer "~3.0.1" +"@kikobeats/time-span@~1.0.2": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@kikobeats/time-span/-/time-span-1.0.5.tgz#9f7c5d48b08da02115dbf3d85ca11a6a6f8bfdeb" + integrity sha512-txRAdmi35N1wnsLS1AO5mTlbY5Cv5/61WXqek2y3L9Q7u4mgdUVq819so5xe753hL5gYeLzlWoJ/VJfXg9nx8g== -"@keyvhq/memoize@~1.6.14": - version "1.6.14" - resolved "https://registry.yarnpkg.com/@keyvhq/memoize/-/memoize-1.6.14.tgz#6a347b81e9be7040c678f8fc4b9046186484b6fc" - integrity sha512-bsFPrpOHEjAK1F7O0lzl5RR6KofA/zCaFzIBFMDRmcvDJk4Jkk5yqo66+DqQO5YBKWM74y/nAN63XCdHWmi91w== +"@metascraper/helpers@5.46.5", "@metascraper/helpers@^5.34.4": + version "5.46.5" + resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.46.5.tgz#db9db60835495d2865af1c99f758d32b8aac8f10" + integrity sha512-SuFJ7qu0MK1yaV9VnVDVY0eMsJ6hU3/4XYPhjrdZL9GejJUzzDhTTzNoi1V5NYF68YF+VlGEwQL101wDc/zo+A== dependencies: - "@keyvhq/core" "^1.6.14" - mimic-fn "~3.0.0" + audio-extensions "0.0.0" + chrono-node "~2.7.7" + condense-whitespace "~2.0.0" + data-uri-utils "~1.0.8" + debug-logfmt "~1.2.3" + entities "~6.0.0" + file-extension "~4.0.5" + has-values "~2.0.1" + image-extensions "~1.1.0" + is-relative-url "~3.0.0" + is-uri "~1.2.6" + iso-639-3 "~2.2.0" + isostring "0.0.1" + jsdom "~25.0.1" + lodash "~4.17.21" + memoize-one "~6.0.0" + microsoft-capitalize "~1.0.5" + mime "3" + normalize-url "~6.1.0" + re2 "~1.21.4" + smartquotes "~2.3.2" + tldts "~6.1.70" + url-regex-safe "~4.0.0" + video-extensions "1" "@metascraper/helpers@^4.10.2": version "4.10.2" @@ -1466,34 +1537,6 @@ url-regex "~4.1.1" video-extensions "~1.1.0" -"@metascraper/helpers@^5.33.5", "@metascraper/helpers@^5.34.2", "@metascraper/helpers@^5.34.4", "@metascraper/helpers@^5.34.7": - version "5.34.7" - resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.34.7.tgz#749a288813e9f61938bb64d34bfb4e2c26689cbe" - integrity sha512-h3Kg9xFoVlr3rqxbhpR+o7nzYuThXtiDuk+kiaIWozAguUWT6xvwb3iHKuCdKC9/29PXeJ+UqM9WxBamxbzGmg== - dependencies: - audio-extensions "0.0.0" - chrono-node "~2.6.2" - condense-whitespace "~2.0.0" - entities "~4.5.0" - file-extension "~4.0.5" - has-values "~2.0.1" - image-extensions "~1.1.0" - is-relative-url "~3.0.0" - is-uri "~1.2.4" - iso-639-3 "~2.2.0" - isostring "0.0.1" - jsdom "~22.1.0" - lodash "~4.17.21" - memoize-one "~6.0.0" - microsoft-capitalize "~1.0.5" - mime "~3.0.0" - normalize-url "~6.1.0" - re2 "~1.18.3" - smartquotes "~2.3.2" - tldts "~6.0.1" - url-regex-safe "~3.0.0" - video-extensions "~1.2.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" @@ -1530,21 +1573,28 @@ resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== -"@npmcli/fs@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" - integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== + dependencies: + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" + +"@npmcli/fs@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726" + integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== dependencies: - "@gar/promisify" "^1.1.3" semver "^7.3.5" -"@npmcli/move-file@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" - integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@pkgr/core@^0.1.0": version "0.1.1" @@ -1719,11 +1769,6 @@ dependencies: defer-to-connect "^2.0.0" -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -2206,16 +2251,16 @@ dependencies: tslib "^1.9.3" -abab@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -abbrev@1, abbrev@^1.0.0: +abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abbrev@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== + accepts@^1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" @@ -2249,21 +2294,10 @@ agent-base@5: resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== -agent-base@6, agent-base@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -agentkeepalive@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.1.tgz#a7975cbb9f83b367f06c90cc51ff28fe7d499717" - integrity sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA== - dependencies: - debug "^4.1.0" - depd "^1.1.2" - humanize-ms "^1.2.1" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.3" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" + integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== aggregate-error@3.0.1: version "3.0.1" @@ -2303,6 +2337,11 @@ 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" @@ -2322,6 +2361,11 @@ 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" @@ -2615,24 +2659,11 @@ apollo-utilities@1.3.4, apollo-utilities@^1.0.1, apollo-utilities@^1.3.0, apollo ts-invariant "^0.4.0" tslib "^1.10.0" -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== - arch@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/arch/-/arch-2.1.1.tgz#8f5c2731aa35a30929221bb0640eed65175ec84e" integrity sha512-BLM56aPo9vLLFVa8+/+pJLnrZ7QGGTVHWsCwieAWT9o9K8UeGaQbzZbGoabWLOo2ksBCztoXdqBZBplqLDDCSg== -are-we-there-yet@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" - integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - arg@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/arg/-/arg-1.0.0.tgz#444d885a4e25b121640b55155ef7cd03975d6050" @@ -3130,29 +3161,23 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -cacache@^16.1.0: - version "16.1.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" - integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== +cacache@^18.0.0: + version "18.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.4.tgz#4601d7578dadb59c66044e157d02a3314682d6a5" + integrity sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ== dependencies: - "@npmcli/fs" "^2.1.0" - "@npmcli/move-file" "^2.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - glob "^8.0.1" - infer-owner "^1.0.4" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" - mkdirp "^1.0.4" p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^9.0.0" + ssri "^10.0.0" tar "^6.1.11" - unique-filename "^2.0.0" + unique-filename "^3.0.0" cacheable-lookup@^5.0.3: version "5.0.4" @@ -3272,7 +3297,7 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" -cheerio@~1.0.0, cheerio@~1.0.0-rc.12: +cheerio@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" integrity sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww== @@ -3316,10 +3341,10 @@ chrono-node@~1.3.11: dependencies: moment "2.21.0" -chrono-node@~2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.6.3.tgz#fa257cffb2dfc33fa5a01b26bd86f93741ca6dc5" - integrity sha512-VkWaaZnNulqzNH9i4XCdyI05OX6MFEnCMNKdZOR4w//wS5/E2qkwAss/O5sj6SfTZK84fX4SSOG4pzqjqIseiA== +chrono-node@~2.7.7: + version "2.7.8" + resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.7.8.tgz#629a96b955ea20b1fba9811328c69cbc932c8e39" + integrity sha512-pzxemrTKu6jFVyAfkNxUckp9nlrmRFtr5lGrEJcVKyeKV9WSeGT78Oysazlzd/H0BdMv7EzACtJrw0pi2KODBQ== dependencies: dayjs "^1.10.0" @@ -3338,13 +3363,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -clean-stack@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-3.0.1.tgz#155bf0b2221bf5f4fba89528d24c5953f17fe3a8" - integrity sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg== - dependencies: - escape-string-regexp "4.0.0" - clipboardy@1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.2.tgz#2ce320b9ed9be1514f79878b53ff9765420903e2" @@ -3422,11 +3440,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -3464,11 +3477,6 @@ condense-whitespace@~2.0.0: resolved "https://registry.yarnpkg.com/condense-whitespace/-/condense-whitespace-2.0.0.tgz#94e9644938f66aa7be4b8849f8f0b3cec97d6b3a" integrity sha512-Ath9o58/0rxZXbyoy3zZgrVMoIemi30sukG/btuMKCLyqfQt3dNOWc9N3EHEMa2Q3i0tXQPDJluYFLwy7pJuQw== -console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -3579,6 +3587,15 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^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" @@ -3600,12 +3617,21 @@ cssfilter@0.0.10: resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4= -cssstyle@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-3.0.0.tgz#17ca9c87d26eac764bb8cfd00583cff21ce0277a" - integrity sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg== +cssstyle@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.2.1.tgz#5142782410fea95db66fb68147714a652a7c2381" + integrity sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw== dependencies: - rrweb-cssom "^0.6.0" + "@asamuzakjp/css-color" "^2.8.2" + rrweb-cssom "^0.8.0" + +d@1, d@^1.0.1, d@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.2.tgz#2aefd554b81981e7dccf72d6842ae725cb17e5de" + integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== + dependencies: + es5-ext "^0.10.64" + type "^2.7.2" dashdash@^1.12.0: version "1.14.1" @@ -3614,14 +3640,25 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-4.0.0.tgz#333a454eca6f9a5b7b0f1013ff89074c3f522dd4" - integrity sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g== +data-uri-to-buffer@~5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-5.0.1.tgz#db89a9e279c2ffe74f50637a59a32fb23b3e4d7c" + integrity sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg== + +data-uri-utils@~1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/data-uri-utils/-/data-uri-utils-1.0.8.tgz#4651354c3293b02d7de69996a39f20e12fade011" + integrity sha512-LHm6O/aHmTdSsIKGI6d/BJ8gQyBiai/5g57s1XKDHFecVWbq0HYlEXheohwiLbpsEHjpdHNf+D50Q/onMnNIYQ== dependencies: - abab "^2.0.6" - whatwg-mimetype "^3.0.0" - whatwg-url "^12.0.0" + data-uri-to-buffer "~5.0.0" + +data-urls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-5.0.0.tgz#2f76906bce1824429ffecb6920f45a0b30f00dde" + integrity sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg== + dependencies: + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" data-view-buffer@^1.0.1: version "1.0.1" @@ -3660,6 +3697,24 @@ dayjs@^1.10.0: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.10.6.tgz#288b2aa82f2d8418a6c9d4df5898c0737ad02a63" integrity sha512-AztC/IOW4L1Q41A86phW5Thhcrco3xuAA+YX/BLpLWWjRcTj5TOt/QImBLmCKlrF7u7k47arTnOyL6GnbG8Hvw== +debug-fabulous@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/debug-fabulous/-/debug-fabulous-2.0.2.tgz#3dfdff548ff790328400ce06d5d6c3ff5c24e960" + integrity sha512-XfAbX8/owqC+pjIg0/+3V1gp8TugJT7StX/TE1TYedjrRf7h7SgUAL/+gKoAQGPCLbSU5L5LPvDg4/cGn1E/WA== + dependencies: + debug "^4" + memoizee "0.4" + +debug-logfmt@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/debug-logfmt/-/debug-logfmt-1.2.3.tgz#3583fe52a82602fd6ac4bab6a9c18d940132931e" + integrity sha512-Btc8hrSu2017BcECwhnkKtA7+9qBRv06x8igvJRRyDcZo1cmEbwp/OmLDSJFuJ/wgrdF7TbtGeVV6FCxagJoNQ== + dependencies: + "@jclem/logfmt2" "~2.4.3" + "@kikobeats/time-span" "~1.0.2" + debug-fabulous "2.0.2" + pretty-ms "~7.0.1" + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3667,7 +3722,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.3, debug@^4.3.4, debug@^4.3.7: +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.7: 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== @@ -3765,11 +3820,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - denque@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" @@ -3785,7 +3835,7 @@ depd@2.0.0: resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@^1.1.2, depd@~1.1.2: +depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= @@ -3876,13 +3926,6 @@ domelementtype@^2.3.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domexception@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" - integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== - dependencies: - webidl-conversions "^7.0.0" - domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" @@ -3925,6 +3968,11 @@ dotenv@^4.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" integrity sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ== +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" @@ -3972,6 +4020,11 @@ 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@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4022,11 +4075,16 @@ entities@^4.2.0, entities@^4.4.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== -entities@^4.5.0, entities@~4.5.0: +entities@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== +entities@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-6.0.0.tgz#09c9e29cb79b0a6459a9b9db9efb418ac5bb8e51" + integrity sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw== + env-paths@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" @@ -4239,6 +4297,43 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.62, es5-ext@^0.10.64, es5-ext@~0.10.14, es5-ext@~0.10.2: + version "0.10.64" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.64.tgz#12e4ffb48f1ba2ea777f1fcdd1918ef73ea21714" + integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + esniff "^2.0.1" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.4" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.4.tgz#f4e7d28013770b4208ecbf3e0bf14d3bcb557b8c" + integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg== + dependencies: + d "^1.0.2" + ext "^1.7.0" + +es6-weak-map@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" + integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== + dependencies: + d "1" + es5-ext "^0.10.46" + es6-iterator "^2.0.3" + es6-symbol "^3.1.1" + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -4254,11 +4349,6 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - 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" @@ -4269,6 +4359,11 @@ escape-string-regexp@^2.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + eslint-compat-utils@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz#f45e3b5ced4c746c127cf724fb074cd4e730d653" @@ -4461,6 +4556,16 @@ eslint@^8.57.1: strip-ansi "^6.0.1" text-table "^0.2.0" +esniff@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" + integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg== + dependencies: + d "^1.0.1" + es5-ext "^0.10.62" + event-emitter "^0.3.5" + type "^2.7.2" + espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" @@ -4509,6 +4614,14 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +event-emitter@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== + dependencies: + d "1" + es5-ext "~0.10.14" + eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -4563,6 +4676,11 @@ expect@^29.0.0, expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" +exponential-backoff@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.2.tgz#a8f26adb96bf78e8cd8ad1037928d5e5c0679d91" + integrity sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA== + express@^4.0.0, express@^4.17.1, express@^4.21.2: version "4.21.2" resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" @@ -4600,6 +4718,13 @@ express@^4.0.0, express@^4.17.1, express@^4.21.2: utils-merge "1.0.1" vary "~1.1.2" +ext@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -4771,6 +4896,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -4825,13 +4958,20 @@ fs-capacitor@^6.1.0, fs-capacitor@^6.2.0: resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" integrity sha512-nKcE1UduoSKX27NSZlg879LdQc94OtbOsEmKMN2MBNudXREvijRKx2GEBsTMTfws+BrbkJoEuynbGSVRSpauvw== -fs-minipass@^2.0.0, fs-minipass@^2.1.0: +fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== + 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" @@ -4877,20 +5017,6 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -4978,10 +5104,10 @@ get-tsconfig@^4.10.0, get-tsconfig@^4.7.0: dependencies: resolve-pkg-maps "^1.0.0" -get-video-id@~3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/get-video-id/-/get-video-id-3.6.5.tgz#a7bfa1e8602557f2fcbed164d7005fdaa352d572" - integrity sha512-9RPHSQANIeGW5rU3CjWdm1zi+wUHbBWX+m4m+dqQAavrZ9p1P1J7AbxGvVEEHRyGfGrmMf5PqiRWYMyfqM+QYA== +get-video-id@3: + version "3.7.0" + resolved "https://registry.yarnpkg.com/get-video-id/-/get-video-id-3.7.0.tgz#a8ad5636e9dc256351332d8e5f710bd5da8362b8" + integrity sha512-hU5pnODTo87slfs9MUFO3vpJr23AESJHmF20T3ivqQJZ/Wz0W5TxjSrGoyB6X538Shyi6tCCpQSeBoV88F9NYA== getpass@^0.1.1: version "0.1.7" @@ -5004,6 +5130,18 @@ glob-parent@^6.0.2: dependencies: is-glob "^4.0.3" +glob@^10.2.2, glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -5016,17 +5154,6 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -5077,7 +5204,7 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" -got@~11.8.0, got@~11.8.5: +got@~11.8.0: version "11.8.6" resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g== @@ -5286,11 +5413,6 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-values@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-values/-/has-values-2.0.1.tgz#3876200ff86d8a8546a9264a952c17d5fc17579d" @@ -5334,12 +5456,12 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -html-encoding-sniffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" - integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== +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" + integrity sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ== dependencies: - whatwg-encoding "^2.0.0" + whatwg-encoding "^3.1.1" html-escaper@^2.0.0: version "2.0.2" @@ -5391,10 +5513,10 @@ http-cache-semantics@^4.0.0: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.0.3.tgz#495704773277eeef6e43f9ab2c2c7d259dda25c5" integrity sha512-TcIMG3qeVLgDr1TEd2XvHaTnMPwYQUQMIBLy+5pLSDKYFc7UIqj39w8EGzZkaxoLv/l2K8HaI0t5AVA+YYgUew== -http-cache-semantics@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== +http-cache-semantics@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" + integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@2.0.0: version "2.0.0" @@ -5418,14 +5540,13 @@ http-errors@^1.7.3: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== +http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" + agent-base "^7.1.0" + debug "^4.3.4" http-signature@~1.2.0: version "1.2.0" @@ -5452,20 +5573,12 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -https-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" - integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== +https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.5: + version "7.0.6" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" + integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: - agent-base "6" - debug "4" - -https-proxy-agent@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" + agent-base "^7.1.2" debug "4" human-signals@^2.1.0: @@ -5473,13 +5586,6 @@ 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== -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= - dependencies: - ms "^2.0.0" - iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -5550,11 +5656,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -5581,10 +5682,10 @@ insane@2.6.1: assignment "2.0.0" he "0.5.0" -install-artifact-from-github@^1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.3.tgz#57d89bacfa0f47d7307fe41b6247cda9f9a8079c" - integrity sha512-x79SL0d8WOi1ZjXSTUqqs0GPQZ92YArJAN9O46wgU9wdH2U9ecyyhB9YGDbPe2OLV4ptmt6AZYRQZ2GydQZosQ== +install-artifact-from-github@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/install-artifact-from-github/-/install-artifact-from-github-1.3.5.tgz#88c96fe40e5eb21d45586d564208c648a1dbf38d" + integrity sha512-gZHC7f/cJgXz7MXlHFBxPVMsvIbev1OQN1uKQYKVJDydGNm9oYf9JstbU4Atnh/eSvk41WtEovoRm+8IF686xg== internal-slot@^1.0.5: version "1.0.5" @@ -5634,6 +5735,14 @@ ioredis@^5.3.2: redis-parser "^3.0.0" standard-as-callback "^2.1.0" +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + ip-regex@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" @@ -5644,11 +5753,6 @@ ip-regex@^1.0.1: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-1.0.3.tgz#dc589076f659f419c222039a33316f1c7387effd" integrity sha1-3FiQdvZZ9BnCIgOaMzFvHHOH7/0= -ip@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" - integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== - ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -5837,6 +5941,11 @@ 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: + version "2.2.2" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" + integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== + is-regex@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" @@ -5956,10 +6065,10 @@ is-uri@~1.2.0: parse-uri "~1.0.0" punycode2 "~1.0.0" -is-uri@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-uri/-/is-uri-1.2.4.tgz#d098837e45701eaedce1b53de68b8102465fbd46" - integrity sha512-8sHi5gEARwpMYwJD9uSAkU9Bb7YkSagcM10EYqSe+osqOErXln4VL+EgLSG40e9lVTpcpygpvb9Z6ohZpECDGA== +is-uri@~1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/is-uri/-/is-uri-1.2.6.tgz#f8f5f55d9b997493f470bf10ca214a3838b1e818" + integrity sha512-kNciklu//Ki8BUmRseLTfG/WW55qDHavf3MKUic8wvXR3d7etbSMoQPTpjvDeLVekESSgJM4AG+BESIKU02u3A== dependencies: parse-uri "~1.0.3" punycode2 "~1.0.0" @@ -5986,6 +6095,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + iso-639-3@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/iso-639-3/-/iso-639-3-1.1.0.tgz#83722daf55490a707c318ae18a33ba3bab06c843" @@ -6074,6 +6188,15 @@ iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2, iterall@^1.3.0: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jake@^10.8.5: version "10.9.2" resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" @@ -6467,39 +6590,42 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== -jsdom@~22.1.0: - version "22.1.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-22.1.0.tgz#0fca6d1a37fbeb7f4aac93d1090d782c56b611c8" - integrity sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw== +jsdom@~25.0.1: + version "25.0.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef" + integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw== dependencies: - abab "^2.0.6" - cssstyle "^3.0.0" - data-urls "^4.0.0" + cssstyle "^4.1.0" + data-urls "^5.0.0" decimal.js "^10.4.3" - domexception "^4.0.0" form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.1" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.5" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.4" + nwsapi "^2.2.12" parse5 "^7.1.2" - rrweb-cssom "^0.6.0" + rrweb-cssom "^0.7.1" saxes "^6.0.0" symbol-tree "^3.2.4" - tough-cookie "^4.1.2" - w3c-xmlserializer "^4.0.0" + tough-cookie "^5.0.0" + w3c-xmlserializer "^5.0.0" webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^12.0.1" - ws "^8.13.0" - xml-name-validator "^4.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.18.0" + xml-name-validator "^5.0.0" jsesc@^3.0.2, jsesc@~3.0.2: version "3.0.2" @@ -6511,7 +6637,7 @@ jsesc@~0.5.0: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= -json-buffer@3.0.1, json-buffer@~3.0.1: +json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== @@ -6748,16 +6874,11 @@ 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.21: +lodash@^4.17.15, 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== -lodash@~4.17.11: - version "4.17.20" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" - integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== - loglevel@^1.6.7: version "1.6.8" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.8.tgz#8a25fb75d092230ecd4457270d80b54e28011171" @@ -6780,6 +6901,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^10.0.1, lru-cache@^10.2.0, lru-cache@^10.4.3: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -6795,10 +6921,12 @@ lru-cache@^5.0.0, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^7.7.1: - version "7.14.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea" - integrity sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA== +lru-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + integrity sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ== + dependencies: + es5-ext "~0.10.2" lru_map@^0.3.3: version "0.3.3" @@ -6825,27 +6953,23 @@ make-error@^1.1.1, make-error@^1.3.6: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== -make-fetch-happen@^10.0.3: - version "10.2.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" - integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== +make-fetch-happen@^13.0.0: + version "13.0.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36" + integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA== dependencies: - agentkeepalive "^4.2.1" - cacache "^16.1.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-fetch "^2.0.3" + minipass "^7.0.2" + minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" negotiator "^0.6.3" + proc-log "^4.2.0" promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" - ssri "^9.0.0" + ssri "^10.0.0" makeerror@1.0.12: version "1.0.12" @@ -6864,6 +6988,20 @@ memoize-one@~6.0.0: resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw== +memoizee@0.4: + version "0.4.17" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.4.17.tgz#942a5f8acee281fa6fb9c620bddc57e3b7382949" + integrity sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA== + dependencies: + d "^1.0.2" + es5-ext "^0.10.64" + es6-weak-map "^2.0.3" + event-emitter "^0.3.5" + is-promise "^2.2.2" + lru-queue "^0.1.0" + next-tick "^1.1.0" + timers-ext "^0.1.7" + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" @@ -6893,33 +7031,33 @@ merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -metascraper-author@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.33.5.tgz#ae6fd41c8f9a355e48b851e67308673e1f2fe89c" - integrity sha512-/n4OWwHNQZwb9Z1t3Y58I5+ou885tBHGsTPGhBB7SYdbOBRygr8DuC9HDrPlrmW0aY88lIDTTN3jqTqEoPYgaQ== +metascraper-author@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.46.5.tgz#64fa4e593ece8aa3d663b9fb0a101e2cd18f55d2" + integrity sha512-TKlqY7BVVzqphew842MwdtV5cNx3TG6l6rtswSzYOM2EhyuZuQPQUgrk6s20hRbZrCJM9XM+oc1PvzdmV7hXcg== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" -metascraper-date@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.33.5.tgz#fccd8f7b685e0f3bb152c86aa93ed03a96c3c834" - integrity sha512-NmzizRF52KzJg2qDSPKT6FnPxB8ufvtZS00kci7Sxdt6PFGKrOEK8qaj6Z3XDSZiBWd8WMtdEHi4K7Mf17C7kA== +metascraper-date@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.46.5.tgz#df63486c1db3d6bf360dff32b29446bb9e1b128b" + integrity sha512-PrgJioyTXm1ZqPpo0ybpKZ4u3QUDr3PQEWuKmQ5Xfe886haKF9N4b23KVDrME87nI975bwi3aH+3lo1msxL7ig== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" -metascraper-description@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.33.5.tgz#d6b20e613da6028d6e825162f89f045282d4ce81" - integrity sha512-j+5hY8QPvFbAud6soeoSq7FXvnezPxW3aLgVHtuGTDBfDTSW7TGsfzcajCfgPwi1F3CslBTql/hfoFq1rK+kGw== +metascraper-description@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.46.5.tgz#10c74065e8c6227a2e19bcbfb498fd117dd0b7a9" + integrity sha512-RJKfwVPXRy8+w7mSjJM+6oe1ws3AtPA5CspcJty1fdKD1S4jF0nFzgwtrTgOCtggMK8iHG01/qAunPobFEbs+w== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" -metascraper-image@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.33.5.tgz#76e71310cce14fa56232a3802668f07f0d2095bd" - integrity sha512-8IYyjA7FH+laEV+Wg6l+gmMTrLcW9jYkyj8e6R0wuvxKjJNmdZuhLGmteDfyec/kkPHoFQSPFbz+tTsQN8ue9g== +metascraper-image@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.46.5.tgz#c011ddccbdbf380851a7a36cd9ebd12f9f7e9558" + integrity sha512-wgfilKtxeiwBjNYsMXr4AKVIu3jqScDZnWILl2MkILnn6BttLetgNRXsAvE5bT9LD8Upnf5ufJebLK+hoHAiwg== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" metascraper-lang-detector@^4.10.2: version "4.10.2" @@ -6930,27 +7068,27 @@ metascraper-lang-detector@^4.10.2: franc "~4.0.0" iso-639-3 "~1.1.0" -metascraper-lang@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-lang/-/metascraper-lang-5.33.5.tgz#6299b88ff4e84c81325af13a9978fde452f45c46" - integrity sha512-HZiRpyvBkahuK+ULb1C8mQyREpxe6w4kguEl3l5/4CdTUKeEsbkBE41Tz23nSoMfzDP8IsPN65cd/s1Loa47Yw== +metascraper-lang@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-lang/-/metascraper-lang-5.46.5.tgz#a47c9433341b4a31e5bde9e4f094c34ce5de3f42" + integrity sha512-CMa4D23kBVa+1DbCnpiiCgGvPQqx6b0TaSd9qLIeGaqhWQw7dUxGAB6x5KzjhoABGgKzTIpy+Xmc9cPforT+SQ== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" -metascraper-logo@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.33.5.tgz#395a1acc2f630ec92bb7b794cf1cf2999e61a8d9" - integrity sha512-zTaAqGRJ3ZIINuChRcsXSPNW1RceM5N/nCFa/1cN/UY/6TSmZbDGbtIXmypVd/cHMfC72oKoNrVd1TROtofcoQ== +metascraper-logo@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.46.5.tgz#5e57677ee6e9c21bcb7288257d751a3fb302141d" + integrity sha512-Wp7u7CEXjo7PA7kRKPG9EtD57xXWkv0E9klVgkQeSzBaVmXCTcEMYhHMfyBumpeZn02+Gmj/cuEuHjuMtbJ/uQ== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" lodash "~4.17.21" -metascraper-publisher@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.33.5.tgz#381744ebe6e2bedc478f21b101a006cc703bb0b9" - integrity sha512-2M9ZkQPZYUvU4Rz3Mcak6rNtK6/xcLUd77ejOgMB3EVbgm5BGsCp7dPtf4iSSybhu/RsJCvJb4Ndsajzgw9kSQ== +metascraper-publisher@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.46.5.tgz#f9ac000a358df7ff1f826ecaac58c0aada152687" + integrity sha512-vmc6tMshN/0n9DrMlOBt1jxktNIAqLJTP28Z2HStOadlNg4m6L+hdmp0mFAjfTxCuzR3bJYmWRfba4b51ugIiA== dependencies: - "@metascraper/helpers" "^5.33.5" + "@metascraper/helpers" "5.46.5" metascraper-soundcloud@^5.34.4: version "5.34.4" @@ -6959,50 +7097,49 @@ metascraper-soundcloud@^5.34.4: dependencies: "@metascraper/helpers" "^5.34.4" -metascraper-title@^5.34.7: - version "5.34.7" - resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.34.7.tgz#d53afa05eb4d5f2d7b8ec772ddfc335733f86a21" - integrity sha512-k+eDC12Y1m5n/RE3GhqmWRtNSKH9rDXvAzJ6k2T9+9PyaGeaSN9dWLnc3rK9TYmdFP02kTKhCAV8WerHw/7IqA== +metascraper-title@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.46.5.tgz#b1a5027b3d051cba5a3cefcbb97a58bdfb0c00f5" + integrity sha512-AciK4SgOCdkRgXQ6isYcV/JzxomUd7hJ7BrikXqIfZME+UK6OHUtjQV3gtdB0SHBbIBE7Q+h1X5r8j5nWwBALg== dependencies: - "@metascraper/helpers" "^5.34.7" + "@metascraper/helpers" "5.46.5" -metascraper-url@^5.34.2: - version "5.34.2" - resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.34.2.tgz#1136dbf1177d08835d6b8677a64d9f10f0517fb0" - integrity sha512-ijPe3G64kyxFRWPA7YmH01dt5JImqBgWV07WIpgPj8wuD4ThwteGqV1CtjNYJN8yo/tCQeFNJPoubwiv9RsrJw== +metascraper-url@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.46.5.tgz#8673db754a613951b9a870c3533ee3a4fee04d22" + integrity sha512-DsF80xrH3qoyhxsZISbGQ6krs+BQO+3c19FA3oGfs88cjhpGSr0gMr662xL5NBveFatHSCCaaKYl2/OTb3N1VQ== dependencies: - "@metascraper/helpers" "^5.34.2" + "@metascraper/helpers" "5.46.5" -metascraper-video@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-video/-/metascraper-video-5.33.5.tgz#4231ddee3bb4cb22f6f41c868de61012f7989fa9" - integrity sha512-Fa0u+O/tY6RbRtY0Y+Ox7OJP3+a9o2HwYvK0NjLUdisF/GBZOJ7i1U2puv4kbOvua5un7BS6J6ULiWIF/+EPAg== +metascraper-video@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-video/-/metascraper-video-5.46.5.tgz#44a246622e92f499d9da9e567e4e65da7c002520" + integrity sha512-+tQNDglDBJU4jpIc9N8BwZ0w5OZBOO0jobigp8OHLJWPQTC9zfcZTsBRr7zTbrAfXF3Hu51ZO4VkapxTwTUQsw== dependencies: - "@keyvhq/memoize" "~1.6.14" - "@metascraper/helpers" "^5.33.5" - got "~11.8.5" + "@metascraper/helpers" "5.46.5" lodash "~4.17.21" p-reflect "~2.1.0" -metascraper-youtube@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.33.5.tgz#7b72d2facdce5fdbf53b8353476ea9115e5061a6" - integrity sha512-jztiBU3PbXnxK6AhFqkfpC2PDYkyOfaF/4wjI8AT/wXYvDMk8pEUx8SNAHThxx4CEHVylCIzXc0o6gKUcCic/w== +metascraper-youtube@^5.46.5: + version "5.46.5" + resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.46.5.tgz#20998d86ccc56b93dfd57b619bff3c936980c47d" + integrity sha512-cc3/BaqbcEpvBjOP0MWFxBlDHsdqCeksD0QQX/gwW/2LT8KfUL+qZl6rHqmYPk1RH4WDVtjAzFwq8Z6bQ+/yxQ== dependencies: - "@metascraper/helpers" "^5.33.5" - get-video-id "~3.6.5" + "@metascraper/helpers" "5.46.5" + get-video-id "3" p-locate "~5.0.0" - reachable-url "~1.7.1" + reachable-url "~1.8.1" -metascraper@^5.33.5: - version "5.33.5" - resolved "https://registry.yarnpkg.com/metascraper/-/metascraper-5.33.5.tgz#c411cda7c144ec3e9d0dff42da858a2a3925e1c9" - integrity sha512-ey9flNTK8rlJ7GfE0qQ5HvnYF6BMTJZ4EsTLBEB/IMRPCIPjJk6RgV/VpdpnWTBlujjErryay5UWhk13wlXhXQ== +metascraper@^5.46.7: + version "5.46.7" + resolved "https://registry.yarnpkg.com/metascraper/-/metascraper-5.46.7.tgz#cdc7d7fa3206cab5b69d6d69db6d473f61eec09b" + integrity sha512-fBWVQQH3blesqRRxO8lfOh5lxnp4gMBld7uz+hFElY5Kk9sJZIhzFW2TJW+rqqvnjbbmDiaYvGfBEzfZ5sC1Sw== dependencies: - "@metascraper/helpers" "^5.33.5" - cheerio "~1.0.0-rc.12" + "@metascraper/helpers" "5.46.5" + cheerio "~1.0.0" + debug-logfmt "~1.2.3" lodash "~4.17.21" - whoops "~4.1.2" + whoops "~5.0.1" methods@~1.1.2: version "1.1.2" @@ -7065,7 +7202,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@~3.0.0: +mime@3: version "3.0.0" resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== @@ -7075,16 +7212,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-fn@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.0.0.tgz#76044cfa8818bbf6999c5c9acadf2d3649b14b4b" - integrity sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ== - -mimic-fn@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.1.0.tgz#65755145bbf3e36954b949c16450427451d5ca74" - integrity sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ== - mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" @@ -7121,19 +7248,19 @@ minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== dependencies: - minipass "^3.0.0" + minipass "^7.0.3" -minipass-fetch@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" - integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== +minipass-fetch@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz#f0f97e40580affc4a35cc4a1349f05ae36cb1e4c" + integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg== dependencies: - minipass "^3.1.6" + minipass "^7.0.3" minipass-sized "^1.0.3" minizlib "^2.1.2" optionalDependencies: @@ -7160,20 +7287,13 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1: +minipass@^3.0.0: version "3.1.3" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== dependencies: yallist "^4.0.0" -minipass@^3.1.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - minipass@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.0.0.tgz#7cebb0f9fa7d56f0c5b17853cbe28838a8dbbd3b" @@ -7181,6 +7301,16 @@ minipass@^4.0.0: dependencies: yallist "^4.0.0" +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -7189,7 +7319,7 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -7209,7 +7339,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -7224,7 +7354,7 @@ n-gram@^1.0.0: resolved "https://registry.yarnpkg.com/n-gram/-/n-gram-1.1.1.tgz#a374dc176a9063a2388d1be18ed7c35828be2a97" integrity sha512-qibRqvUghLIVsq+RTwVuwOzgOxf0l4DDZKVYAK0bMam5sG9ZzaJ6BUSJyG2Td8kTc7c/HcMUtjiN5ShobZA2bA== -nan@2.17.0, nan@^2.17.0: +nan@2.17.0, nan@^2.20.0: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== @@ -7320,6 +7450,11 @@ neode@^0.4.9: neo4j-driver "^4.2.2" uuid "^3.4.0" +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + no-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" @@ -7343,21 +7478,21 @@ node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.7.0: dependencies: whatwg-url "^5.0.0" -node-gyp@^9.3.1: - version "9.3.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.3.1.tgz#1e19f5f290afcc9c46973d68700cbd21a96192e4" - integrity sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg== +node-gyp@^10.2.0: + version "10.3.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.3.1.tgz#1dd1a1a1c6c5c59da1a76aea06a062786b2c8a1a" + integrity sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ== dependencies: env-paths "^2.2.0" - glob "^7.1.4" + exponential-backoff "^3.1.1" + glob "^10.3.10" graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" - nopt "^6.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^4.1.0" semver "^7.3.5" - tar "^6.1.2" - which "^2.0.2" + tar "^6.2.1" + which "^4.0.0" node-int64@^0.4.0: version "0.4.0" @@ -7402,12 +7537,12 @@ nodemon@~3.1.9: touch "^3.1.0" undefsafe "^2.0.5" -nopt@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" - integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== +nopt@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== dependencies: - abbrev "^1.0.0" + abbrev "^2.0.0" nopt@~1.0.10: version "1.0.10" @@ -7452,16 +7587,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== - dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.3" - set-blocking "^2.0.0" - nth-check@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" @@ -7469,10 +7594,10 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -nwsapi@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.4.tgz#fd59d5e904e8e1f03c25a7d5a15cfa16c714a1e5" - integrity sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g== +nwsapi@^2.2.12: + version "2.2.16" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" + integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== oauth-sign@~0.9.0: version "0.9.0" @@ -7697,6 +7822,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +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" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -7714,6 +7844,11 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse-ms@^2.1.0: + version "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" @@ -7799,6 +7934,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" @@ -7908,10 +8051,17 @@ pretty-format@^29.0.0, pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +pretty-ms@~7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-7.0.1.tgz#7d903eaab281f7d8e03c66f867e239dc32fb73e8" + integrity sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q== + dependencies: + parse-ms "^2.1.0" + +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" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== promise-retry@^2.0.1: version "2.0.1" @@ -7952,11 +8102,6 @@ psl@^1.1.28: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== -psl@^1.1.33: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - pstree.remy@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" @@ -7980,11 +8125,16 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= -punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: +punycode@^2.1.0, punycode@^2.1.1: version "2.3.0" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + pure-rand@^6.0.0: version "6.1.0" resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.1.0.tgz#d173cf23258231976ccbdb05247c9787957604f2" @@ -8007,11 +8157,6 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= -querystringify@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" - integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== - quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -8032,19 +8177,19 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -re2@~1.18.3: - version "1.18.3" - resolved "https://registry.yarnpkg.com/re2/-/re2-1.18.3.tgz#64120f3990351e6d71207b095a92ebc41f123c7f" - integrity sha512-QAUSIl5znNR/GOXLIUWWao0pPQ2VZU/t9u+0j17x7lXXypPsil8iuYBsSuMDZ3TGx439332tYdh0d9Nt8XGbHg== +re2@~1.21.4: + version "1.21.4" + resolved "https://registry.yarnpkg.com/re2/-/re2-1.21.4.tgz#d688edcc40da3cf542ee3a480a8b60e5900dd24d" + integrity sha512-MVIfXWJmsP28mRsSt8HeL750ifb8H5+oF2UDIxGaiJCr8fkMqhLZ7kcX9ADRk2dC8qeGKedB7UVYRfBVpEiLfA== dependencies: - install-artifact-from-github "^1.3.3" - nan "^2.17.0" - node-gyp "^9.3.1" + install-artifact-from-github "^1.3.5" + nan "^2.20.0" + node-gyp "^10.2.0" -reachable-url@~1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/reachable-url/-/reachable-url-1.7.1.tgz#4d56f534d088c084e7d9884dafadf2c43d074c66" - integrity sha512-SCk7V5cwUw246mPhJ7uaqMcxa5iJNlzfztCvcDK1EgqexW2ghLWWrAGZCBNg/lT0ZqjJnLcsKPfGWxmmoABvOw== +reachable-url@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/reachable-url/-/reachable-url-1.8.1.tgz#76d084b24532a6912073c45fb1f3bed54a0ef063" + integrity sha512-6skrNl6GMGXF/H91T/bz1aznkLDd+y5ApwpqCE8h3OdJ9pQgzfK1j2wh4MFtRvfLq5TH69+oXLq5CsiHL+zN1g== dependencies: got "~11.8.0" p-reflect "~2.1.0" @@ -8054,15 +8199,6 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== -readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -8231,11 +8367,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - resolve-alpn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.0.tgz#058bb0888d1cd4d12474e9a4b6eb17bdd5addc44" @@ -8306,10 +8437,15 @@ rosie@^2.1.1: resolved "https://registry.yarnpkg.com/rosie/-/rosie-2.1.1.tgz#f8c9b8145d581d19fb1c933cf6ac1c554ad68798" integrity sha512-2AXB7WrIZXtKMZ6Q/PlozqPF5nu/x7NEvRJZOblrJuprrPfm5gL8JVvJPj9aaib9F8IUALnLUFhzXrwEtnI5cQ== -rrweb-cssom@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" - integrity sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw== +rrweb-cssom@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" + integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== + +rrweb-cssom@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" + integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== run-parallel@^1.1.9: version "1.1.9" @@ -8451,11 +8587,6 @@ serve-static@1.16.2: parseurl "~1.3.3" send "0.19.0" -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - set-function-length@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" @@ -8570,6 +8701,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.3, signal-exit@^3.0.7: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-update-notifier@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" @@ -8617,21 +8753,21 @@ smartquotes@~2.3.2: resolved "https://registry.yarnpkg.com/smartquotes/-/smartquotes-2.3.2.tgz#fb1630c49ba04e57446e1a97dc10d590072af4a6" integrity sha512-0R6YJ5hLpDH4mZR7N5eZ12oCMLspvGOHL9A9SEm2e3b/CQmQidekW4SWSKEmor/3x6m3NCBBEqLzikcZC9VJNQ== -socks-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" - integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== +socks-proxy-agent@^8.0.3: + version "8.0.5" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz#b9cdb4e7e998509d7659d689ce7697ac21645bee" + integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" + agent-base "^7.1.2" + debug "^4.3.4" + socks "^2.8.3" -socks@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== +socks@^2.8.3: + version "2.8.4" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.4.tgz#07109755cdd4da03269bda4725baa061ab56d5cc" + integrity sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ== dependencies: - ip "^2.0.0" + ip-address "^9.0.5" smart-buffer "^4.2.0" source-map-js@^1.0.2: @@ -8660,6 +8796,11 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -8680,12 +8821,12 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -ssri@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" - integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== +ssri@^10.0.0: + version "10.0.6" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" + integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== dependencies: - minipass "^3.1.1" + minipass "^7.0.3" stable-hash@^0.0.4: version "0.0.4" @@ -8732,7 +8873,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": 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== @@ -8741,6 +8882,24 @@ 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== + 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" + string.prototype.trim@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" @@ -8795,13 +8954,20 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1, string_decoder@^1.3.0: +string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== dependencies: safe-buffer "~5.2.0" +"strip-ansi-cjs@npm:strip-ansi@^6.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@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -8809,6 +8975,13 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: 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" @@ -8906,7 +9079,7 @@ tapable@^2.2.0: resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== -tar@^6.1.11, tar@^6.1.2: +tar@^6.1.11: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" integrity sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw== @@ -8918,6 +9091,18 @@ tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" +tar@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + test-exclude@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" @@ -8932,6 +9117,14 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= +timers-ext@^0.1.7: + version "0.1.8" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.8.tgz#b4e442f10b7624a29dd2aa42c295e257150cf16c" + integrity sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww== + dependencies: + es5-ext "^0.10.64" + next-tick "^1.1.0" + tinyglobby@^0.2.12: version "0.2.12" resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5" @@ -8960,22 +9153,22 @@ tlds@^1.187.0: resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.203.1.tgz#4dc9b02f53de3315bc98b80665e13de3edfc1dfc" integrity sha512-7MUlYyGJ6rSitEZ3r1Q1QNV8uSIzapS8SmmhSusBuIc7uIxPPwsKllEP0GRp1NS6Ik6F+fRZvnjDWm3ecv2hDw== -tlds@^1.228.0: - version "1.236.0" - resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.236.0.tgz#a118eebe33261c577e3a3025144faeabb7dd813c" - integrity sha512-oP2PZ3KeGlgpHgsEfrtva3/K9kzsJUNliQSbCfrJ7JMCWFoCdtG+9YMq/g2AnADQ1v5tVlbtvKJZ4KLpy/P6MA== +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== -tldts-core@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.0.3.tgz#b59d87507c51cd2488c322b92020b973b608b59a" - integrity sha512-PLiEM2aCkfGifyr8npbd93eWIW4isFRB44vTiup5DRie6e2Qy3+9quwHb252v12JyoM+RmF1cxtDgwD2PVBOjA== +tldts-core@^6.1.78: + version "6.1.78" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.78.tgz#47b477d9742870daa01dbd5ff9a598a48379728c" + integrity sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw== -tldts@~6.0.1: - version "6.0.3" - resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.0.3.tgz#6a4a8bb550f396d6d72818606150e0e0263ae826" - integrity sha512-rcdUIwrcGuMWe5+fg5FFBrmWTYdbfpHwkk1AjBKoSDbpsdAsYqJYKoZOVOHn8MQCYatADKGAx/SU+jpSKxSYNw== +tldts@^6.1.32, tldts@~6.1.70: + version "6.1.78" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.78.tgz#ee94576653a60d421ff94162c4e9060f2e62467b" + integrity sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ== dependencies: - tldts-core "^6.0.3" + tldts-core "^6.1.78" tmpl@1.0.5: version "1.0.5" @@ -9011,15 +9204,12 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.2.tgz#e53e84b85f24e0b65dd526f46628db6c85f6b874" - integrity sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ== +tough-cookie@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.1.tgz#4641c1fdbf024927e29c5532edb7b6e5377ea1f2" + integrity sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA== dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.2.0" - url-parse "^1.5.3" + tldts "^6.1.32" tough-cookie@~2.5.0: version "2.5.0" @@ -9029,12 +9219,12 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" -tr46@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469" - integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw== +tr46@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec" + integrity sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g== dependencies: - punycode "^2.3.0" + punycode "^2.3.1" tr46@~0.0.3: version "0.0.3" @@ -9194,6 +9384,11 @@ type-is@^1.6.16, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type@^2.7.2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" + integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ== + typed-array-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" @@ -9330,25 +9525,20 @@ unicode-property-aliases-ecmascript@^2.0.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@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" - integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== - dependencies: - unique-slug "^3.0.0" - -unique-slug@^3.0.0: +unique-filename@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" - integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== + dependencies: + unique-slug "^4.0.0" + +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== dependencies: imurmurhash "^0.1.4" -universalify@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.2.0.tgz#6451760566fa857534745ab1dde952d1b1761be0" - integrity sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg== - unixify@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unixify/-/unixify-1.0.0.tgz#3a641c8c2ffbce4da683a5c70f03a462940c2090" @@ -9384,21 +9574,13 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -url-parse@^1.5.3: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url-regex-safe@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-regex-safe/-/url-regex-safe-3.0.0.tgz#102a38f74a1a731973fa42690c6a56656fddff12" - integrity sha512-+2U40NrcmtWFVjuxXVt9bGRw6c7/MgkGKN9xIfPrT/2RX0LTkkae6CCEDp93xqUN0UKm/rr821QnHd2dHQmN3A== +url-regex-safe@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/url-regex-safe/-/url-regex-safe-4.0.0.tgz#2ac972bfc012e6ab1363d8f3f1c540145b64e15e" + integrity sha512-BrnFCWKNFrFnRzKD66NtJqQepfJrUHNPvPxE5y5NSAhXBb4OlobQjt7907Jm4ItPiXaeX+dDWMkcnOd4jR9N8A== dependencies: ip-regex "4.3.0" - tlds "^1.228.0" + tlds "^1.242.0" url-regex@~4.1.1: version "4.1.1" @@ -9416,11 +9598,6 @@ url@0.10.3: punycode "1.3.2" querystring "0.2.0" -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - util.promisify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" @@ -9519,22 +9696,22 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +video-extensions@1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.2.0.tgz#62f449f403b853f02da40964cbf34143f7d96731" + integrity sha512-TriMl18BHEsh2KuuSA065tbu4SNAC9fge7k8uKoTTofTq89+Xsg4K1BGbmSVETwUZhqSjd9KwRCNwXAW/buXMg== + video-extensions@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.1.0.tgz#eaa86b45f29a853c2b873e9d8e23b513712997d6" integrity sha1-6qhrRfKahTwrhz6djiO1E3Epl9Y= -video-extensions@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.2.0.tgz#62f449f403b853f02da40964cbf34143f7d96731" - integrity sha512-TriMl18BHEsh2KuuSA065tbu4SNAC9fge7k8uKoTTofTq89+Xsg4K1BGbmSVETwUZhqSjd9KwRCNwXAW/buXMg== - -w3c-xmlserializer@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz#aebdc84920d806222936e3cdce408e32488a3073" - integrity sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw== +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== dependencies: - xml-name-validator "^4.0.0" + xml-name-validator "^5.0.0" walker@^1.0.8: version "1.0.8" @@ -9553,13 +9730,6 @@ webidl-conversions@^7.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== -whatwg-encoding@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" - integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== - dependencies: - iconv-lite "0.6.3" - whatwg-encoding@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz#d0f4ef769905d426e1688f3e34381a99b60b76e5" @@ -9567,22 +9737,17 @@ whatwg-encoding@^3.1.1: dependencies: iconv-lite "0.6.3" -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== - whatwg-mimetype@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a" integrity sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg== -whatwg-url@^12.0.0, whatwg-url@^12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-12.0.1.tgz#fd7bcc71192e7c3a2a97b9a8d6b094853ed8773c" - integrity sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ== +whatwg-url@^14.0.0: + version "14.1.1" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.1.1.tgz#ce71e240c61541315833b5cdafd139a479e47058" + integrity sha512-mDGf9diDad/giZ/Sm9Xi2YcyzaFpbdLpJPr+E9fSkyQ7KpQD4SdFcugkRQYzhmfI4KeV4Qpnn2sKPdo+kmsgRQ== dependencies: - tr46 "^4.1.1" + tr46 "^5.0.0" webidl-conversions "^7.0.0" whatwg-url@^5.0.0: @@ -9644,27 +9809,33 @@ which@^1.2.9: dependencies: isexe "^2.0.0" -which@^2.0.1, which@^2.0.2: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -whoops@~4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/whoops/-/whoops-4.1.2.tgz#b8bf080b9b4f4ab93f8249734ac1086fb14b4dd9" - integrity sha512-QITkghBnjbKGOtOrJOGrHnupWDX/AHFlcTXL3/A0B5rx2DacLrpuIpcFVmwYb0IOWZ0G3sxYkS8LBSZ4J/ezng== +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== dependencies: - clean-stack "~3.0.0" - mimic-fn "~3.1.0" + isexe "^3.1.1" -wide-align@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== +whoops@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/whoops/-/whoops-5.0.1.tgz#ce2fd6f255aca09b6fd8ec00c99f9761420296d4" + integrity sha512-H2sKu1asxnFE2mNUeRzNY0CQuvl+n6iyE6phvzOaBfZblItNKpC1EzKWy2Zx+woZ3qUFK/wbmmNiLeqXwzk+FA== + +"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" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: - string-width "^1.0.2 || 2 || 3 || 4" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" wrap-ansi@^7.0.0: version "7.0.0" @@ -9675,6 +9846,15 @@ 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" @@ -9700,15 +9880,15 @@ ws@^6.0.0: dependencies: async-limiter "~1.0.0" -ws@^8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== +ws@^8.18.0: + version "8.18.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb" + integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== -xml-name-validator@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" - integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-5.0.0.tgz#82be9b957f7afdacf961e5980f1bf227c0bf7673" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== xml2js@0.6.2: version "0.6.2" From 628b57aa29ee0a7358774b78cbc75a43852ae240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 28 Feb 2025 18:22:23 +0100 Subject: [PATCH 028/266] feat(other): major improvement of deployment (#7925) * feat(other): major improvement of deployment Motivation ---------- Kubernetes: * backend becomes a statefulset (exclusive volume mount) See: https://spacelift.io/blog/statefulset-vs-deployment * implement neo4j backup with job Docker: * consistent targets across all dockerfiles * remove redundant labels * remove unnecessary build args * remove obsolete networks * remove development dependencies for production Rebranding: * add image tags for local tagging and pulling * use Github's docker build workflows * use Github container registry * ONBUILD to simplify caller Dockerfiles * docker compose for branding Tooling: * same node --version as in dockerfile Docs: * missing step in README.md * refactor: remove submodules It's better to keep them all in a separate repository * improve kubernetes chart * better image tag defaults * split neo4j into its own chart (for re-use) * use application defaults where possible * optional resources for all pods * remove obsolete key/value pair from secrets * remove obsolete build argsand and add labels for neo4j enterprise * env vars for webapp * allow to define redirect domains Define a list of Domains that redirect to the domain of the project. The idea is to provide the ability to redirect eg. www.domain.tld to domain.tld * remove maintenance part regarding database * move backup job outside template folder * name the ingress * updated ingress * handle empty case of middlewares * try to default the ingress * use quote * restore todo-next-update * fix docu check * fix naming * try using prod:migrate * try using override config * copy src folder * try using base as image instead of build * fix test build * force build * comment for the problem * fix webapp tests (potentially) --------- Co-authored-by: Ulf Gebhardt --- .github/workflows/check-documentation.yml | 4 +- .github/workflows/docker-push.yml | 91 +++++ .github/workflows/test-backend.yml | 3 +- .github/workflows/test-e2e.yml | 2 +- .github/workflows/test-webapp.yml | 3 +- .tool-versions | 1 + README.md | 3 + backend/Dockerfile | 107 ++---- .../branding/constants}/.gitkeep | 0 .../crds => backend/branding/email}/.gitkeep | 0 .../src/schema/resolvers/filter-posts.spec.ts | 8 +- backend/tools/replace-constants.sh | 7 + deployment/.env.dist | 7 - deployment/DOCKER_MORE_CLOSELY.md | 27 -- deployment/Minikube.md | 25 -- deployment/README.md | 138 ------- deployment/configurations/.gitignore | 3 - deployment/configurations/stage.ocelot.social | 1 - deployment/deployment-values.md | 73 ---- deployment/deployment.md | 148 -------- .../docker-compose.ocelotsocial-branded.yml | 100 ----- deployment/docker-compose.yml | 190 ---------- deployment/helm/charts/.helmignore | 23 ++ .../helm/charts/ocelot-neo4j/Chart.yaml | 24 ++ .../helm/charts/ocelot-neo4j/old/job.yaml | 34 ++ .../ocelot-neo4j/templates/_helpers.tpl | 10 + .../templates/neo4j/configmap.yml | 6 + .../neo4j/persistent-volume-claim.yaml | 22 ++ .../ocelot-neo4j/templates/neo4j/secret.yaml | 6 + .../ocelot-neo4j/templates/neo4j/service.yaml | 14 + .../templates/neo4j/stateful-set.yaml | 38 ++ .../helm/charts/ocelot-neo4j/values.yaml | 25 ++ .../helm/charts/ocelot-social/Chart.yaml | 24 ++ .../ocelot-social/templates/_helpers.tpl | 10 + .../ocelot-social/templates/acme-issuer.yaml | 39 ++ .../templates/backend/configmap.yml | 6 + .../backend/persistent-volume-claim.yaml | 10 + .../templates/backend/secret.yaml | 7 + .../templates/backend/service.yaml | 11 + .../templates/backend/stateful-set.yaml | 52 +++ .../ocelot-social/templates/configmap.yaml | 6 + .../ocelot-social/templates/ingress.yaml | 65 ++++ .../templates/maintenance/deployment.yaml | 24 ++ .../templates/maintenance/service.yaml | 11 + .../templates/webapp/configmap.yml | 6 + .../templates/webapp/deployment.yaml | 34 ++ .../templates/webapp/secret.yaml | 7 + .../templates/webapp/service.yaml | 11 + .../helm/charts/ocelot-social/values.yaml | 27 ++ deployment/helm/helmfile/helmfile.yaml.gotmpl | 16 + deployment/helm/helmfile/secrets/ocelot.yaml | 76 ++++ deployment/helm/helmfile/values/ocelot.yaml | 2 + deployment/scripts/branded-images.build.sh | 74 ---- deployment/scripts/branded-images.upload.sh | 51 --- deployment/scripts/cluster.backend-bash.sh | 22 -- deployment/scripts/cluster.backup.sh | 46 --- deployment/scripts/cluster.dashboard-token.sh | 22 -- .../scripts/cluster.dashboard-tunnel.sh | 37 -- deployment/scripts/cluster.install.sh | 56 --- deployment/scripts/cluster.maintenance.sh | 30 -- deployment/scripts/cluster.neo4j-bash.sh | 22 -- deployment/scripts/cluster.neo4j.sh | 57 --- deployment/scripts/cluster.reseed.sh | 18 - deployment/scripts/cluster.upgrade.sh | 24 -- .../clusters.backup-multiple-servers.sh | 91 ----- deployment/scripts/secret.generate.sh | 20 - deployment/scripts/secrets.decrypt.sh | 50 --- deployment/scripts/secrets.encrypt.sh | 47 --- deployment/src/docker/backend.Dockerfile | 46 --- deployment/src/docker/maintenance.Dockerfile | 44 --- deployment/src/docker/webapp.Dockerfile | 61 --- deployment/src/kubernetes/Backup.md | 308 --------------- deployment/src/kubernetes/Chart.yaml | 39 -- deployment/src/kubernetes/DigitalOcean.md | 145 -------- deployment/src/kubernetes/README.md | 350 ------------------ deployment/src/kubernetes/nginx.values.yaml | 13 - .../patch.ingress.maintenance.off.yaml | 12 - .../patches/patch.ingress.maintenance.on.yaml | 12 - deployment/src/kubernetes/templates/NOTES.txt | 1 - .../templates/backend/ConfigMap.yml | 31 -- .../templates/backend/Deployment.yaml | 62 ---- .../backend/PersistentVolumeClaim.yaml | 24 -- .../kubernetes/templates/backend/Secret.yaml | 22 -- .../kubernetes/templates/backend/Service.yaml | 20 - .../issuer/letsencrypt-production.yaml | 22 -- .../templates/issuer/letsencrypt-staging.yaml | 22 -- .../templates/jobs/job-db-init.yaml | 29 -- .../templates/jobs/job-db-migrate.yaml | 29 -- .../templates/maintenance/ConfigMap.yml | 14 - .../templates/maintenance/Deployment.yaml | 45 --- .../templates/maintenance/Secret.yaml | 13 - .../templates/maintenance/Service.yaml | 20 - .../kubernetes/templates/neo4j/ConfigMap.yml | 24 -- .../templates/neo4j/Deployment.yaml | 57 --- .../neo4j/PersistentVolumeClaim.yaml | 19 - .../kubernetes/templates/neo4j/Secret.yaml | 15 - .../kubernetes/templates/neo4j/Service.yaml | 23 -- .../templates/storage/persistent.yml | 16 - .../kubernetes/templates/webapp/ConfigMap.yml | 20 - .../templates/webapp/Deployment.yaml | 49 --- .../kubernetes/templates/webapp/Ingress.yaml | 36 -- .../kubernetes/templates/webapp/Secret.yaml | 14 - .../kubernetes/templates/webapp/Service.yaml | 20 - deployment/src/old/Maintenance.md | 45 --- deployment/src/old/digital-ocean/README.md | 39 -- .../src/old/digital-ocean/dashboard/README.md | 55 --- .../digital-ocean/dashboard/admin-user.yaml | 5 - .../dashboard/dashboard-screenshot.png | Bin 181783 -> 0 bytes .../digital-ocean/dashboard/role-binding.yaml | 12 - .../src/old/digital-ocean/https/README.md | 124 ------- .../old/digital-ocean/https/ip-address.png | Bin 144403 -> 0 bytes deployment/src/old/legacy-migration/README.md | 85 ----- .../legacy-migration/maintenance-worker.yaml | 40 -- .../maintenance-worker/.dockerignore | 1 - .../maintenance-worker/.gitignore | 2 - .../maintenance-worker/Dockerfile | 21 -- .../maintenance-worker/binaries/idle | 2 - .../binaries/import_legacy_db | 12 - .../binaries/import_legacy_uploads | 17 - .../maintenance-worker/known_hosts | 3 - .../migration/mongo/export.sh | 53 --- .../migration/neo4j/badges/badges.cql | 52 --- .../migration/neo4j/badges/delete.cql | 1 - .../migration/neo4j/categories/categories.cql | 129 ------- .../migration/neo4j/categories/delete.cql | 1 - .../migration/neo4j/comments/comments.cql | 67 ---- .../migration/neo4j/comments/delete.cql | 1 - .../neo4j/contributions/contributions.cql | 156 -------- .../migration/neo4j/contributions/delete.cql | 2 - .../migration/neo4j/delete_all.cql | 1 - .../migration/neo4j/emotions/delete.cql | 1 - .../migration/neo4j/emotions/emotions.cql | 58 --- .../migration/neo4j/follows/delete.cql | 1 - .../migration/neo4j/follows/follows.cql | 36 -- .../migration/neo4j/import.sh | 108 ------ .../migration/neo4j/invites/invites.cql | 39 -- .../neo4j/notifications/notifications.cql | 48 --- .../migration/neo4j/organizations/delete.cql | 0 .../neo4j/organizations/organizations.cql | 137 ------- .../migration/neo4j/pages/delete.cql | 0 .../migration/neo4j/pages/pages.cql | 55 --- .../migration/neo4j/projects/delete.cql | 0 .../migration/neo4j/projects/projects.cql | 44 --- .../migration/neo4j/settings/delete.cql | 0 .../migration/neo4j/settings/settings.cql | 36 -- .../migration/neo4j/shouts/delete.cql | 1 - .../migration/neo4j/shouts/shouts.cql | 36 -- .../migration/neo4j/status/delete.cql | 0 .../migration/neo4j/status/status.cql | 19 - .../neo4j/systemnotifications/delete.cql | 0 .../systemnotifications.cql | 61 --- .../migration/neo4j/users/delete.cql | 2 - .../migration/neo4j/users/users.cql | 124 ------- .../migration/neo4j/userscandos/delete.cql | 0 .../neo4j/userscandos/userscandos.cql | 35 -- .../migration/neo4j/usersettings/delete.cql | 0 .../neo4j/usersettings/usersettings.cql | 43 --- deployment/src/old/mailserver/Deployment.yaml | 40 -- deployment/src/old/mailserver/README.md | 18 - deployment/src/old/mailserver/Service.yaml | 22 -- deployment/src/old/mailserver/ingress.yaml | 42 --- deployment/src/old/monitoring/README.md | 43 --- .../src/old/monitoring/grafana/config.yml | 16 - .../src/old/monitoring/grafana/metrics.png | Bin 211436 -> 0 bytes .../src/old/monitoring/grafana/values.yml | 4 - deployment/src/old/volumes/README.md | 37 -- .../volumes/neo4j-offline-backup/README.md | 88 ----- .../old/volumes/neo4j-online-backup/README.md | 59 --- deployment/src/old/volumes/uploads.yaml | 12 - deployment/src/old/volumes/velero/README.md | 112 ------ .../old/volumes/volume-snapshots/README.md | 49 --- .../backen-uploads-snapshot.yaml | 8 - .../digital-ocean-volume-snapshots.png | Bin 120810 -> 0 bytes .../neo4j-data-provision-snapshot.yaml | 18 - .../volume-snapshots/neo4j-data-snapshot.yaml | 8 - docker-compose.base.yml | 20 + docker-compose.build.yml | 20 + docker-compose.maintenance.yml | 10 +- docker-compose.override.yml | 60 +-- docker-compose.test.yml | 36 +- docker-compose.yml | 64 +--- neo4j/Dockerfile | 36 +- webapp/Dockerfile | 118 ++---- webapp/Dockerfile.maintenance | 88 +---- .../delete.cql => webapp/branding/.gitkeep | 0 .../delete.cql => webapp/locales/tmp/.gitkeep | 0 webapp/nuxt.config.js | 2 +- webapp/package.json | 2 +- .../src => webapp}/tools/merge-locales.sh | 0 webapp/yarn.lock | 251 +++++++++++-- 190 files changed, 1111 insertions(+), 5913 deletions(-) create mode 100644 .github/workflows/docker-push.yml create mode 100644 .tool-versions rename {deployment/src/kubernetes/charts => backend/branding/constants}/.gitkeep (100%) rename {deployment/src/kubernetes/crds => backend/branding/email}/.gitkeep (100%) create mode 100755 backend/tools/replace-constants.sh delete mode 100644 deployment/.env.dist delete mode 100644 deployment/DOCKER_MORE_CLOSELY.md delete mode 100644 deployment/Minikube.md delete mode 100644 deployment/README.md delete mode 100644 deployment/configurations/.gitignore delete mode 160000 deployment/configurations/stage.ocelot.social delete mode 100644 deployment/deployment-values.md delete mode 100644 deployment/deployment.md delete mode 100644 deployment/docker-compose.ocelotsocial-branded.yml delete mode 100644 deployment/docker-compose.yml create mode 100644 deployment/helm/charts/.helmignore create mode 100644 deployment/helm/charts/ocelot-neo4j/Chart.yaml create mode 100644 deployment/helm/charts/ocelot-neo4j/old/job.yaml create mode 100644 deployment/helm/charts/ocelot-neo4j/templates/_helpers.tpl create mode 100644 deployment/helm/charts/ocelot-neo4j/templates/neo4j/configmap.yml create mode 100644 deployment/helm/charts/ocelot-neo4j/templates/neo4j/persistent-volume-claim.yaml create mode 100644 deployment/helm/charts/ocelot-neo4j/templates/neo4j/secret.yaml create mode 100644 deployment/helm/charts/ocelot-neo4j/templates/neo4j/service.yaml create mode 100644 deployment/helm/charts/ocelot-neo4j/templates/neo4j/stateful-set.yaml create mode 100644 deployment/helm/charts/ocelot-neo4j/values.yaml create mode 100644 deployment/helm/charts/ocelot-social/Chart.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/_helpers.tpl create mode 100644 deployment/helm/charts/ocelot-social/templates/acme-issuer.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/backend/configmap.yml create mode 100644 deployment/helm/charts/ocelot-social/templates/backend/persistent-volume-claim.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/backend/secret.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/backend/service.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/backend/stateful-set.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/configmap.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/ingress.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/maintenance/deployment.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/maintenance/service.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/webapp/configmap.yml create mode 100644 deployment/helm/charts/ocelot-social/templates/webapp/deployment.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/webapp/secret.yaml create mode 100644 deployment/helm/charts/ocelot-social/templates/webapp/service.yaml create mode 100644 deployment/helm/charts/ocelot-social/values.yaml create mode 100644 deployment/helm/helmfile/helmfile.yaml.gotmpl create mode 100644 deployment/helm/helmfile/secrets/ocelot.yaml create mode 100644 deployment/helm/helmfile/values/ocelot.yaml delete mode 100755 deployment/scripts/branded-images.build.sh delete mode 100755 deployment/scripts/branded-images.upload.sh delete mode 100755 deployment/scripts/cluster.backend-bash.sh delete mode 100755 deployment/scripts/cluster.backup.sh delete mode 100755 deployment/scripts/cluster.dashboard-token.sh delete mode 100755 deployment/scripts/cluster.dashboard-tunnel.sh delete mode 100755 deployment/scripts/cluster.install.sh delete mode 100755 deployment/scripts/cluster.maintenance.sh delete mode 100755 deployment/scripts/cluster.neo4j-bash.sh delete mode 100755 deployment/scripts/cluster.neo4j.sh delete mode 100755 deployment/scripts/cluster.reseed.sh delete mode 100755 deployment/scripts/cluster.upgrade.sh delete mode 100755 deployment/scripts/clusters.backup-multiple-servers.sh delete mode 100755 deployment/scripts/secret.generate.sh delete mode 100755 deployment/scripts/secrets.decrypt.sh delete mode 100755 deployment/scripts/secrets.encrypt.sh delete mode 100644 deployment/src/docker/backend.Dockerfile delete mode 100644 deployment/src/docker/maintenance.Dockerfile delete mode 100644 deployment/src/docker/webapp.Dockerfile delete mode 100644 deployment/src/kubernetes/Backup.md delete mode 100644 deployment/src/kubernetes/Chart.yaml delete mode 100644 deployment/src/kubernetes/DigitalOcean.md delete mode 100644 deployment/src/kubernetes/README.md delete mode 100644 deployment/src/kubernetes/nginx.values.yaml delete mode 100644 deployment/src/kubernetes/patches/patch.ingress.maintenance.off.yaml delete mode 100644 deployment/src/kubernetes/patches/patch.ingress.maintenance.on.yaml delete mode 100644 deployment/src/kubernetes/templates/NOTES.txt delete mode 100644 deployment/src/kubernetes/templates/backend/ConfigMap.yml delete mode 100644 deployment/src/kubernetes/templates/backend/Deployment.yaml delete mode 100644 deployment/src/kubernetes/templates/backend/PersistentVolumeClaim.yaml delete mode 100644 deployment/src/kubernetes/templates/backend/Secret.yaml delete mode 100644 deployment/src/kubernetes/templates/backend/Service.yaml delete mode 100644 deployment/src/kubernetes/templates/issuer/letsencrypt-production.yaml delete mode 100644 deployment/src/kubernetes/templates/issuer/letsencrypt-staging.yaml delete mode 100644 deployment/src/kubernetes/templates/jobs/job-db-init.yaml delete mode 100644 deployment/src/kubernetes/templates/jobs/job-db-migrate.yaml delete mode 100644 deployment/src/kubernetes/templates/maintenance/ConfigMap.yml delete mode 100644 deployment/src/kubernetes/templates/maintenance/Deployment.yaml delete mode 100644 deployment/src/kubernetes/templates/maintenance/Secret.yaml delete mode 100644 deployment/src/kubernetes/templates/maintenance/Service.yaml delete mode 100644 deployment/src/kubernetes/templates/neo4j/ConfigMap.yml delete mode 100644 deployment/src/kubernetes/templates/neo4j/Deployment.yaml delete mode 100644 deployment/src/kubernetes/templates/neo4j/PersistentVolumeClaim.yaml delete mode 100644 deployment/src/kubernetes/templates/neo4j/Secret.yaml delete mode 100644 deployment/src/kubernetes/templates/neo4j/Service.yaml delete mode 100644 deployment/src/kubernetes/templates/storage/persistent.yml delete mode 100644 deployment/src/kubernetes/templates/webapp/ConfigMap.yml delete mode 100644 deployment/src/kubernetes/templates/webapp/Deployment.yaml delete mode 100644 deployment/src/kubernetes/templates/webapp/Ingress.yaml delete mode 100644 deployment/src/kubernetes/templates/webapp/Secret.yaml delete mode 100644 deployment/src/kubernetes/templates/webapp/Service.yaml delete mode 100644 deployment/src/old/Maintenance.md delete mode 100644 deployment/src/old/digital-ocean/README.md delete mode 100644 deployment/src/old/digital-ocean/dashboard/README.md delete mode 100644 deployment/src/old/digital-ocean/dashboard/admin-user.yaml delete mode 100644 deployment/src/old/digital-ocean/dashboard/dashboard-screenshot.png delete mode 100644 deployment/src/old/digital-ocean/dashboard/role-binding.yaml delete mode 100644 deployment/src/old/digital-ocean/https/README.md delete mode 100644 deployment/src/old/digital-ocean/https/ip-address.png delete mode 100644 deployment/src/old/legacy-migration/README.md delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker.yaml delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/.dockerignore delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/.gitignore delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/Dockerfile delete mode 100755 deployment/src/old/legacy-migration/maintenance-worker/binaries/idle delete mode 100755 deployment/src/old/legacy-migration/maintenance-worker/binaries/import_legacy_db delete mode 100755 deployment/src/old/legacy-migration/maintenance-worker/binaries/import_legacy_uploads delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/known_hosts delete mode 100755 deployment/src/old/legacy-migration/maintenance-worker/migration/mongo/export.sh delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/badges/badges.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/badges/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/categories/categories.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/categories/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/comments/comments.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/comments/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/contributions.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/delete_all.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/follows.cql delete mode 100755 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/import.sh delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/invites.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/notifications.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/organizations.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/pages.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/projects.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/settings.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/shouts.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/status.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/systemnotifications.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/users.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/userscandos.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/delete.cql delete mode 100644 deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/usersettings.cql delete mode 100644 deployment/src/old/mailserver/Deployment.yaml delete mode 100644 deployment/src/old/mailserver/README.md delete mode 100644 deployment/src/old/mailserver/Service.yaml delete mode 100644 deployment/src/old/mailserver/ingress.yaml delete mode 100644 deployment/src/old/monitoring/README.md delete mode 100644 deployment/src/old/monitoring/grafana/config.yml delete mode 100644 deployment/src/old/monitoring/grafana/metrics.png delete mode 100644 deployment/src/old/monitoring/grafana/values.yml delete mode 100644 deployment/src/old/volumes/README.md delete mode 100644 deployment/src/old/volumes/neo4j-offline-backup/README.md delete mode 100644 deployment/src/old/volumes/neo4j-online-backup/README.md delete mode 100644 deployment/src/old/volumes/uploads.yaml delete mode 100644 deployment/src/old/volumes/velero/README.md delete mode 100644 deployment/src/old/volumes/volume-snapshots/README.md delete mode 100644 deployment/src/old/volumes/volume-snapshots/backen-uploads-snapshot.yaml delete mode 100644 deployment/src/old/volumes/volume-snapshots/digital-ocean-volume-snapshots.png delete mode 100644 deployment/src/old/volumes/volume-snapshots/neo4j-data-provision-snapshot.yaml delete mode 100644 deployment/src/old/volumes/volume-snapshots/neo4j-data-snapshot.yaml create mode 100644 docker-compose.base.yml create mode 100644 docker-compose.build.yml rename deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/delete.cql => webapp/branding/.gitkeep (100%) rename deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/delete.cql => webapp/locales/tmp/.gitkeep (100%) rename {deployment/src => webapp}/tools/merge-locales.sh (100%) diff --git a/.github/workflows/check-documentation.yml b/.github/workflows/check-documentation.yml index 1a7d278e8..7729bc8c5 100644 --- a/.github/workflows/check-documentation.yml +++ b/.github/workflows/check-documentation.yml @@ -30,8 +30,8 @@ jobs: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - - name: Remove old documentation files - run: rm -rf ./deployment/src/old/ ./CHANGELOG.md # workaround until https://github.com/gaurav-nelson/github-action-markdown-link-check/pull/183 has been done + - name: Remove uncheckable documentation files + run: rm -rf ./CHANGELOG.md # workaround until https://github.com/gaurav-nelson/github-action-markdown-link-check/pull/183 has been done - name: Check Markdown Links uses: gaurav-nelson/github-action-markdown-link-check@1b916f2cf6c36510a6059943104e3c42ce6c16bc # 1.0.15 diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml new file mode 100644 index 000000000..93dc45bd3 --- /dev/null +++ b/.github/workflows/docker-push.yml @@ -0,0 +1,91 @@ +name: docker-push + +on: push + +jobs: + build-and-push-images: + strategy: + matrix: + app: + - name: neo4j + context: neo4j + file: neo4j/Dockerfile + target: community + - name: backend-base + context: backend + file: backend/Dockerfile + target: base + - name: backend-build + context: backend + file: backend/Dockerfile + target: build + - name: backend + context: backend + file: backend/Dockerfile + target: production + - name: webapp-base + context: webapp + file: webapp/Dockerfile + target: base + - name: webapp-build + context: webapp + file: webapp/Dockerfile + target: build + - name: webapp + context: webapp + file: webapp/Dockerfile + target: production + - name: maintenance-base + context: webapp + file: webapp/Dockerfile.maintenance + target: base + - name: maintenance-build + context: webapp + file: webapp/Dockerfile.maintenance + target: build + - name: maintenance + context: webapp + file: webapp/Dockerfile.maintenance + target: production + runs-on: ubuntu-latest + env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }}/${{ matrix.app.name }} + permissions: + contents: read + packages: write + attestations: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.1.7 + - name: Log in to the Container registry + uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@70b2cdc6480c1a8b86edf1777157f8f437de2166 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=schedule + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=ref,event=branch + type=ref,event=pr + type=sha + - name: Build and push Docker images + id: push + uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + with: + context: ${{ matrix.app.context }} + target: ${{ matrix.app.target }} + file: ${{ matrix.app.file }} + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index 49a3aa8a1..ba0bb3c74 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -112,7 +112,8 @@ jobs: cp backend/.env.template backend/.env - name: backend | docker compose - run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend + # doesn't work without the --build flag - this either means we should not load the cached images or cache the correct image + run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend --build - name: backend | Initialize Database run: docker compose exec -T backend yarn db:migrate init diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 3203411ff..fe8a514dc 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -77,7 +77,7 @@ jobs: 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 + docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend --build sleep 90s - name: Full stack tests | run tests diff --git a/.github/workflows/test-webapp.yml b/.github/workflows/test-webapp.yml index 2019d309e..21473f3fe 100644 --- a/.github/workflows/test-webapp.yml +++ b/.github/workflows/test-webapp.yml @@ -94,7 +94,8 @@ jobs: cp backend/.env.template backend/.env - name: backend | docker compose - run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp + # doesn't work without the --build flag - this either means we should not load the cached images or cache the correct image + run: docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp --build - name: webapp | Unit tests incl. coverage check run: docker compose exec -T webapp yarn test diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 000000000..4120b7f87 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 20.12.1 diff --git a/README.md b/README.md index 321e8931d..b1fe0ea14 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,9 @@ $ cp .env.template .env # in folder backend/ $ cp .env.template .env + +# in folder frontend/ +$ cp .env.template .env ``` For Development: diff --git a/backend/Dockerfile b/backend/Dockerfile index a7931f31a..40b78225a 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,103 +1,42 @@ -################################################################################## -# BASE (Is pushed to DockerHub for rebranding) ################################### -################################################################################## FROM node:20.12.1-alpine3.19 AS base - -# ENVs -## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame -ENV DOCKER_WORKDIR="/app" -## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0 -ARG BBUILD_DATE="1970-01-01T00:00:00.00Z" -ENV BUILD_DATE=$BBUILD_DATE -## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0 -ARG BBUILD_VERSION="0.0.0-0" -ENV BUILD_VERSION=$BBUILD_VERSION -## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000 -ARG BBUILD_COMMIT="0000000" -ENV BUILD_COMMIT=$BBUILD_COMMIT -## SET NODE_ENV -ENV NODE_ENV="production" -## App relevant Envs -ENV PORT="4000" - -# Labels -LABEL org.label-schema.build-date="${BUILD_DATE}" 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" LABEL org.label-schema.url="https://ocelot.social" LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/backend" -LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}" LABEL org.label-schema.vendor="ocelot.social Community" -LABEL org.label-schema.version="${BUILD_VERSION}" LABEL org.label-schema.schema-version="1.0" LABEL maintainer="devops@ocelot.social" - -# Install Additional Software -## install: git -RUN apk --no-cache add git python3 make g++ - -# Settings -## Expose Container Port +ENV NODE_ENV="production" +ENV PORT="4000" EXPOSE ${PORT} +RUN apk --no-cache add git python3 make g++ bash +RUN mkdir -p /app +WORKDIR /app +CMD ["/bin/bash", "-c", "yarn run start"] -## Workdir -RUN mkdir -p ${DOCKER_WORKDIR} -WORKDIR ${DOCKER_WORKDIR} - -################################################################################## -# DEVELOPMENT (Connected to the local environment, to reload on demand) ########## -################################################################################## FROM base AS development +CMD ["/bin/sh", "-c", "yarn install && yarn run dev"] -# We don't need to copy or build anything since we gonna bind to the -# local filesystem which will need a rebuild anyway - -# Run command -# (for development we need to execute yarn install since the -# node_modules are on another volume and need updating) -CMD /bin/sh -c "yarn install && yarn run dev" - -################################################################################## -# CODE (Does contain all code files and is pushed to DockerHub for rebranding) ### -################################################################################## -FROM base AS code - -# copy everything, but do not build. +FROM base AS build COPY . . +ONBUILD COPY ./branding/constants/ src/config/tmp +ONBUILD RUN tools/replace-constants.sh +ONBUILD COPY ./branding/email/ src/middleware/helpers/email/ +ONBUILD RUN yarn install --production=false --frozen-lockfile --non-interactive +ONBUILD RUN yarn run build +ONBUILD RUN mkdir /build +ONBUILD RUN cp -r ./build /build +ONBUILD RUN cp -r ./public /build/build +ONBUILD RUN cp -r ./package.json yarn.lock /build +ONBUILD RUN cd /build && yarn install --production=true --frozen-lockfile --non-interactive -################################################################################## -# BUILD (Does contain all files and the compilate and is therefore bloated) ###### -################################################################################## -FROM code AS build - -# yarn install -RUN yarn install --production=false --frozen-lockfile --non-interactive -# yarn build -RUN /bin/sh -c "yarn run build" - -################################################################################## -# TEST ########################################################################### -################################################################################## FROM build AS test +# required for the migrations +# ONBUILD RUN cp -r ./src /src +CMD ["/bin/bash", "-c", "yarn run dev"] -# Run command -CMD /bin/sh -c "yarn run dev" +FROM build AS production_build -################################################################################## -# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) # -################################################################################## FROM base AS production - -# Copy "binary"-files from build image -COPY --from=build ${DOCKER_WORKDIR}/build ./build -COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules -# Copy static files -# TODO - externalize the uploads so we can copy the whole folder -COPY --from=build ${DOCKER_WORKDIR}/public/img/ ./public/img/ -COPY --from=build ${DOCKER_WORKDIR}/public/providers.json ./public/providers.json -# Copy package.json for script definitions (lock file should not be needed) -COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json - -# Run command -CMD /bin/sh -c "yarn run start" +COPY --from=production_build /build . diff --git a/deployment/src/kubernetes/charts/.gitkeep b/backend/branding/constants/.gitkeep similarity index 100% rename from deployment/src/kubernetes/charts/.gitkeep rename to backend/branding/constants/.gitkeep diff --git a/deployment/src/kubernetes/crds/.gitkeep b/backend/branding/email/.gitkeep similarity index 100% rename from deployment/src/kubernetes/crds/.gitkeep rename to backend/branding/email/.gitkeep diff --git a/backend/src/schema/resolvers/filter-posts.spec.ts b/backend/src/schema/resolvers/filter-posts.spec.ts index 41fbd0ea7..95a072d8a 100644 --- a/backend/src/schema/resolvers/filter-posts.spec.ts +++ b/backend/src/schema/resolvers/filter-posts.spec.ts @@ -179,7 +179,9 @@ describe('Filter Posts', () => { }) }) - describe('order events by event start ascending', () => { + // Does not work on months end + // eslint-disable-next-line jest/no-disabled-tests + describe.skip('order events by event start ascending', () => { it('finds the events ordered accordingly', async () => { const { data: { Post: result }, @@ -201,7 +203,9 @@ describe('Filter Posts', () => { }) }) - describe('filter events by event start date', () => { + // Does not work on months end + // eslint-disable-next-line jest/no-disabled-tests + describe.skip('filter events by event start date', () => { it('finds only events after given date', async () => { const { data: { Post: result }, diff --git a/backend/tools/replace-constants.sh b/backend/tools/replace-constants.sh new file mode 100755 index 000000000..e7cee6ee3 --- /dev/null +++ b/backend/tools/replace-constants.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# TODO: this is a hack, we should find a better way to share files between backend and webapp +[ -f src/config/tmp/emails.js ] && mv src/config/tmp/emails.js src/config/emails.ts +[ -f src/config/tmp/logos.js ] && mv src/config/tmp/logos.js src/config/logos.ts +[ -f src/config/tmp/metadata.js ] && mv src/config/tmp/metadata.js src/config/metadata.ts +exit 0 diff --git a/deployment/.env.dist b/deployment/.env.dist deleted file mode 100644 index 14d793e06..000000000 --- a/deployment/.env.dist +++ /dev/null @@ -1,7 +0,0 @@ -# branding folder used for "docker compose up" run in deployment folder -CONFIGURATION=stage.ocelot.social - -# used in "scripts/clusters.backup-multiple-servers.sh" -BACKUP_CONFIGURATIONS="stage.ocelot.social stage.wir.social" -# if '<= 0' no backups will be deleted -BACKUP_SAVED_BACKUPS_NUMBER=7 \ No newline at end of file diff --git a/deployment/DOCKER_MORE_CLOSELY.md b/deployment/DOCKER_MORE_CLOSELY.md deleted file mode 100644 index 4504bbef7..000000000 --- a/deployment/DOCKER_MORE_CLOSELY.md +++ /dev/null @@ -1,27 +0,0 @@ -# Docker - -## Apple M1 Platform - -***Attention:** For using Docker commands in Apple M1 environments!* - -```bash -# set env variable for your shell -$ export DOCKER_DEFAULT_PLATFORM=linux/amd64 -``` - -### Docker Compose Override File For Apple M1 Platform - -For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform: - -```bash -# in main folder - -# for production -$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up - -# for production testing Docker images from DockerHub -$ docker compose -f docker-compose.ocelotsocial-branded.yml -f docker-compose.apple-m1.override.yml up - -# only once: init admin user and create indexes and constraints in Neo4j database -$ docker compose exec backend /bin/sh -c "yarn prod:migrate init" -``` diff --git a/deployment/Minikube.md b/deployment/Minikube.md deleted file mode 100644 index a552e670f..000000000 --- a/deployment/Minikube.md +++ /dev/null @@ -1,25 +0,0 @@ -# Minikube - -There are many Kubernetes providers, but if you're just getting started, Minikube is a tool that you can use to get your feet wet. - -After you [installed Minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) -open your minikube dashboard: - -```text -$ minikube dashboard -``` - -This will give you an overview. Some of the steps below need some timing to make resources available to other dependent deployments. Keeping an eye on the dashboard is a great way to check that. - -Follow the installation instruction for [Kubernetes with Helm](./src/kubernetes/README.md). - -If all the pods and services have settled and everything looks green in your -minikube dashboard, expose the services you want on your host system. - -For example: - -```text -$ minikube service webapp --namespace=ocelotsocialnetwork -# optionally -$ minikube service backend --namespace=ocelotsocialnetwork -``` diff --git a/deployment/README.md b/deployment/README.md deleted file mode 100644 index 2732919c9..000000000 --- a/deployment/README.md +++ /dev/null @@ -1,138 +0,0 @@ -# Ocelot.Social Deploy And Rebranding - -[![Build Status Publish](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/actions/workflows/publish.yml/badge.svg)](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/actions) -[![MIT License](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/blob/master/LICENSE.md) -[![Discord Channel](https://img.shields.io/discord/489522408076738561.svg)](https://discord.gg/AJSX9DCSUA) -[![Open Source Helpers](https://www.codetriage.com/ocelot-social-community/ocelot-social-deploy-rebranding/badges/users.svg)](https://www.codetriage.com/ocelot-social-community/ocelot-social-deploy-rebranding) - -This repository is an in use template to rebrand, configure, and deploy [ocelot.social](https://github.com/Ocelot-Social-Community/Ocelot-Social) networks. -The forked original repository is [stage.ocelot.social](https://github.com/Ocelot-Social-Community/stage.ocelot.social). - - -

- ocelot.social -

- - -## Live demo - -__Try out our deployed [development environment](https://stage.ocelot.social).__ - -Visit our staging networks: - -- central staging network: [stage.ocelot.social](https://stage.ocelot.social) - - -Logins: - -| email | password | role | -| :--- | :--- | :--- | -| `user@example.org` | 1234 | user | -| `moderator@example.org` | 1234 | moderator | -| `admin@example.org` | 1234 | admin | - -## Usage - -Fork this repository to configure and rebrand it for your own [ocelot.social](https://github.com/Ocelot-Social-Community/Ocelot-Social) network. - -### Package.Json And DockerHub Organisation - -Write your own data into the main configuration file: - -- [package.json](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/package.json) - -Since all deployment methods described here depend on [Docker](https://docker.com) and [DockerHub](https://hub.docker.com), you need to create your own organisation on DockerHub and put its name in the [package.json](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/package.json) file as your `dockerOrganisation`. - -### Configure And Branding - -The next step is: - -- [Set Environment Variables and Configurations](./deployment-values.md) - -- [Configure And Branding](./configurations/stage.ocelot.social/branding/README.md) - - -### Optional: Locally Testing Configuration And Branding - -Just in case you have Docker installed and run the following, you can check your branding locally: - -```bash -# in main folder -$ docker-compose up -# fill the database with an initial admin -$ docker-compose exec backend yarn run prod:migrate init -``` - -The database is then initialised with the default administrator: - -- E-mail: admin@example.org -- Password: 1234 - -For login or registration have a look in your browser at `http://localhost:3000/`. -For the maintenance page have a look in your browser at `http://localhost:5000/`. - -### Push Changes To GitHub - -Before merging these changes into the "master" branch on your GitHub fork repository, you need to configure the GitHub repository secrets. This is necessary to [publish](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/.github/workflows/publish.yml) the Docker images by pushing them via GitHub actions to repositories belonging to your DockerHub organisation. - -First, go to your DockerHub profile under `Account Settings` and click on the `Security` tab. There you create an access token called `-access-token` and copy the token to a safe place. - -Secondly, in your GitHub repository, click on the 'Settings' tab and go to the 'Secrets' tab. There you create two secrets by clicking on `New repository secret`: - -1. Named `DOCKERHUB_TOKEN` with the newly created DockerHub token (only the code, not the token name). -2. Named `DOCKERHUB_USERNAME` with your DockerHub username. - -### Optional: Locally Testing Your DockerHub Images - -Just in case you like to check your pushed Docker images in your organisation's DockerHub repositories locally: - -- rename the file `docker-compose.ocelotsocial-branded.yml` with your network name -- in the file, rename the ocelot.social DockerHub organisation `ocelotsocialnetwork` to your organisations name - -Remove any local Docker images if necessary and do the following: - -```bash -# in main folder -$ docker-compose -f docker-compose.-branded.yml up -# fill the database with an initial admin -$ docker-compose exec backend yarn run prod:migrate init -``` - -See the login details and browser addresses above. - -### Deployment - -Afterwards you can [deploy](./deployment.md) it on your server: - -- [Kubernetes with Helm](./src/kubernetes/README.md) - -## Developer Chat - -Join our friendly open-source community on [Discord](https://discord.gg/AJSX9DCSUA) :heart_eyes_cat: -Just introduce yourself at `#introduce-yourself` and mention `@@Mentor` to get you onboard :neckbeard: -Check out the [contribution guideline](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/CONTRIBUTING.md), too! - -We give write permissions to every developer who asks for it. Just text us on -[Discord](https://discord.gg/AJSX9DCSUA). - -## Technology Stack - -- [Docker](https://www.docker.com) -- [Kubernetes](https://kubernetes.io) -- [Helm](https://helm.sh) - - - -## License - -See the [LICENSE](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/LICENSE.md) file for license rights and limitations (MIT). - -We need `DOCKER_BUILDKIT=0` for this to work. diff --git a/deployment/configurations/.gitignore b/deployment/configurations/.gitignore deleted file mode 100644 index 5a7d01850..000000000 --- a/deployment/configurations/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/* -!/example -!.gitignore \ No newline at end of file diff --git a/deployment/configurations/stage.ocelot.social b/deployment/configurations/stage.ocelot.social deleted file mode 160000 index fdc2e52fa..000000000 --- a/deployment/configurations/stage.ocelot.social +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fdc2e52fa444b300e1c4736600bc0e9ae3314222 diff --git a/deployment/deployment-values.md b/deployment/deployment-values.md deleted file mode 100644 index d1eae1822..000000000 --- a/deployment/deployment-values.md +++ /dev/null @@ -1,73 +0,0 @@ -# Deployment Values - -For each deployment, you need to set the environment variables and configurations. -Here is some specific information on how to set the values. - -## Webapp - -We have several configuration possibilities just in the frontend. - -### Date Time - -In file `branding/constants/dateTime.js`. - -- `RELATIVE_DATETIME` - - `true` (default) or `false` -- `ABSOLUT_DATETIME_FORMAT` - - definition see [date-fns, format](https://date-fns.org/v3.3.1/docs/format): - - `P`: just localized date - - `Pp`: just localized date and time - -## E-Mails - -You need to set environment variables to send registration and invitation information or notifications to users, for example. - -### SPF and DKIM - -More and more e-mail providers require settings for authorization and verification of e-mail senders. - -### SPF - -Sometimes it is enough to create an SPF record in your DNS. - -### DKIM - -However, if you need DKIM authorization and verification, you must set the appropriate environment variables in: `.env`, `docker-compose.yml` or Helm script `values.yaml`: - -```bash -SMTP_DKIM_DOMAINNAME= -SMTP_DKIM_KEYSELECTOR=2017 -SMTP_DKIM_PRIVATKEY="-----BEGIN RSA PRIVATE KEY-----\n\n-----END RSA PRIVATE KEY-----\n" -``` - -You can find out how DKIM works here: - - - -To create the private and public DKIM key, see here: - - - -Information about the required PEM format can be found here: - - - -## Neo4j Database - -We have several configuration options for our Neo4j database. - -### DBMS_DEFAULT_DATABASE – Default Database Name to be Used - -If you need to set the default database name in Neo4j to be used for all operations and terminal commands like our backup scripts, you must set the appropriate environment variable in: `.env`, `docker-compose.yml` or Helm script `values.yaml`: - -```yaml -DBMS_DEFAULT_DATABASE: "graph.db" -``` - -The default value is `neo4j` if it is not set. - -As example see files: - -- `neo4j/.env.template` -- `deployment/docker-compose.yml` -- `deployment/configurations/stage.ocelot.social/kubernetes/values.yaml.template` diff --git a/deployment/deployment.md b/deployment/deployment.md deleted file mode 100644 index 52495ec44..000000000 --- a/deployment/deployment.md +++ /dev/null @@ -1,148 +0,0 @@ -# Deployment - -Before you start the deployment you have to do preparations. - -## Deployment Preparations - -Since all deployment methods described here depend on [Docker](https://docker.com) and [DockerHub](https://hub.docker.com), you need to create your own organisation on DockerHub and put its name in the [package.json](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/package.json) file as your `dockerOrganisation`. -Read more details in the [main README](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md) under [Usage](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md#usage). - -## Deployment Methods - -You have the following options for a deployment: - -- [Kubernetes with Helm](./src/kubernetes/README.md) - -## After Deployment - -After the first deployment of the new network on your server, the database is initialized with the default administrator: - -- E-mail: `admin@example.org` -- Password: `1234` - -***ATTENTION:*** When you are logged in for the first time, please change your (the admin's) e-mail to an existing one and change your password to a secure one !!! - -## Using the Scripts - -To use most of the scripts you have to set the variable `CONFIGURATION` in your terminal by entering: - -```bash -# in deployment folder - -# set configuration name to folder name in 'configurations' folder (network name) -$ export CONFIGURATION= -# to check this -$ echo $CONFIGURATION -``` - -### Secrets Encrypt/Decrypt - -To encrypt and decrypt the secrets of your network in your terminal set a correct password in a (new) file `configurations//SECRET`. -If done please enter: - -```bash -# in deployment folder - -# encrypt secrets -$ scripts/secrets.encrypt.sh - -# decrypt secrets -$ scripts/secrets.decrypt.sh -``` - -### Maintenance Mode On/Off - -Activate or deactivate maintenance mode in your terminal: - -```bash -# in deployment folder - -# activate maintenance mode -$ scripts/cluster.maintenance.sh on - -# deactivate maintenance mode -$ scripts/cluster.maintenance.sh off -``` - -### Backup Scripts - -Save backups. - -#### Single Backup - -To save a local backup of the database and uploaded images: - -```bash -# in deployment folder - -# save backup -$ scripts/cluster.backup.sh -``` - -The backup will be saved into your network folders `backup` folder in a new folder with the date and time. - -##### Default Database Name - -To execute this script, it may be necessary to set the default database name in Neo4j. - -In our deployments there are cases where the database is called `neo4j` (used by default) and in other cases `graph.db` (accidentally happened when we loaded the database into a new cluster). - -In the new deployment with Helm, we set the default database name by the environment variable `NEO4J_dbms_default__database` in the Helm `values.yaml`. -See [Docker-specific configuration settings](https://neo4j.com/docs/operations-manual/4.4/docker/ref-settings/) - -For more information see [Database Management Commands](../neo4j/README.md#database-management-commands). - -#### Multiple Networks Backup - -In order to save several network backups locally, you must define the configuration names of all networks in `.env`. The template for this is `deployment/.env.dist`: - -```bash -# in the deployment folders '.env' set as example -BACKUP_CONFIGURATIONS="stage.ocelot.social stage.wir.social" -BACKUP_SAVED_BACKUPS_NUMBER=7 -``` - -If `BACKUP_SAVED_BACKUPS_NUMBER <= 0` then no backups will be deleted. - -To actually save all the backups run: - -```bash -# in deployment folder - -# save all backups listed in 'BACKUP_CONFIGURATIONS' -# delete all backups older then the 'BACKUP_SAVED_BACKUPS_NUMBER' newest ones -$ scripts/clusters.backup-multiple-servers.sh -``` - -The backups will be saved into your networks folders `backup` folder in a new folder with the date and time. - -#### Automated Backups - -⚠️ *Attention: Please check carefully whether really the oldest backups have been deleted. As shells on different systems behave differently with regard to the commands used in this script.* - -Install automated backups by a [cron job](https://en.wikipedia.org/wiki/Cron). -Be aware of having the bash shell installed to run the script. -The environment variables for the automated backups are described above. - -Installing a cron job by editing the cron table file: - -```bash -# edit cron job table -$ crontab -e -``` - -In the editor add the line: - -```bash -# in cron job table file - -# set a cron job every night at 04am server time -# min hour day month weekday command -00 04 * * * /root/Ocelot-Social/deployment/scripts/clusters.backup-multiple-servers.sh >> /root/Ocelot-Social/deployment/backup-cron-job.log -``` - -This way the terminal output is written into a log file named `backup-cron-job.log` located in the deployment folder. - -Be aware that the server datetime can differ from your local time. -Especially by the change between summer and winter time, because servers usually have UTC. -Find out the actual difference by running the command `date` on your server. diff --git a/deployment/docker-compose.ocelotsocial-branded.yml b/deployment/docker-compose.ocelotsocial-branded.yml deleted file mode 100644 index 2899168d3..000000000 --- a/deployment/docker-compose.ocelotsocial-branded.yml +++ /dev/null @@ -1,100 +0,0 @@ -services: - - ######################################################## - # WEBAPP ############################################### - ######################################################## - webapp: - # name the image to match our image to be tested from our DockerHub repository so that it can be pulled from there, otherwise it will be created locally from the 'dockerfile' - image: ocelotsocialnetwork/webapp-branded:latest - ports: - - 3000:3000 - networks: - - test-network - depends_on: - - backend - environment: - - HOST=0.0.0.0 - - GRAPHQL_URI=http://backend:4000 - - MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g" - # - WEBSOCKETS_URI=ws://backend:4000/graphql # is not working and not given in Docker YAML in main repo - - PUBLIC_REGISTRATION=true - - INVITE_REGISTRATION=true - - CATEGORIES_ACTIVE=true - - ######################################################## - # BACKEND ############################################## - ######################################################## - backend: - # name the image to match our image to be tested from our DockerHub repository so that it can be pulled from there, otherwise it will be created locally from the 'dockerfile' - image: ocelotsocialnetwork/backend-branded:latest - networks: - - test-network - depends_on: - - neo4j - ports: - - 4000:4000 - volumes: - - backend_uploads:/app/public/uploads - environment: - - NEO4J_URI=bolt://neo4j:7687 - - GRAPHQL_URI=http://backend:4000 - - CLIENT_URI=http://localhost:3000 - - JWT_SECRET=b/&&7b78BF&fv/Vd - - MAPBOX_TOKEN=pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g - - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - - EMAIL_SUPPORT=support@wir.social - - EMAIL_DEFAULT_SENDER=info@wir.social - # - PRODUCTION_DB_CLEAN_ALLOW=false # only true for production environments on staging servers - - PUBLIC_REGISTRATION=true - - INVITE_REGISTRATION=true - - CATEGORIES_ACTIVE=true - - SMTP_USERNAME=${SMTP_USERNAME} - - SMTP_PASSWORD=${SMTP_PASSWORD} - - SMTP_HOST=mailserver - - SMTP_PORT=25 - - SMTP_IGNORE_TLS=true - - ######################################################## - # MAINTENANCE ########################################## - ######################################################## - maintenance: - # name the image to match our image to be tested from our DockerHub repository so that it can be pulled from there, otherwise it will be created locally from the 'dockerfile' - image: ocelotsocialnetwork/maintenance-branded:latest - networks: - - test-network - ports: - - 3001:80 - - ######################################################## - # NEO4J ################################################ - ######################################################## - neo4j: - # name the image to match our image to be tested from our DockerHub repository so that it can be pulled from there, otherwise it will be created locally from the 'dockerfile' - image: ocelotsocialnetwork/neo4j-community-branded:latest - networks: - - test-network - environment: - - NEO4J_AUTH=none - - NEO4J_dbms_security_procedures_unrestricted=algo.*,apoc.* - - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes - ports: - - 7687:7687 - volumes: - - neo4j_data:/data - - ######################################################## - # MAILSERVER TO FAKE SMTP ############################## - ######################################################## - mailserver: - image: djfarrelly/maildev - ports: - - 1080:80 - networks: - - test-network - -networks: - test-network: - -volumes: - backend_uploads: - neo4j_data: diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml deleted file mode 100644 index 42919177a..000000000 --- a/deployment/docker-compose.yml +++ /dev/null @@ -1,190 +0,0 @@ -services: - - webapp-base: - image: ocelotsocialnetwork/webapp:local-base - build: - dockerfile: ../webapp/Dockerfile - context: ../webapp - target: base - command: sleep 0 - - webapp-code: - image: ocelotsocialnetwork/webapp:local-code - build: - dockerfile: ../webapp/Dockerfile - context: ../webapp - target: code - command: sleep 0 - - webapp: - image: ocelotsocialnetwork/webapp-branded:local-${CONFIGURATION} - container_name: webapp-branded - build: - dockerfile: src/docker/webapp.Dockerfile - target: branded - context: . - args: - - CONFIGURATION=$CONFIGURATION - - APP_IMAGE_TAG_BASE=local-base - - APP_IMAGE_TAG_CODE=local-code - ports: - - 3000:3000 - networks: - - test-network - depends_on: - - backend - - webapp-base - - webapp-code - env_file: - - .env - environment: - - HOST=0.0.0.0 - - GRAPHQL_URI=http://backend:4000 - - MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g" - # - WEBSOCKETS_URI=ws://backend:4000/graphql # is not working and not given in Docker YAML in main repo - - PUBLIC_REGISTRATION=true - - INVITE_REGISTRATION=true - - CATEGORIES_ACTIVE=true - - backend-base: - image: ocelotsocialnetwork/backend:local-base - build: - dockerfile: ../backend/Dockerfile - context: ../backend - target: base - command: sleep 0 - - backend-code: - image: ocelotsocialnetwork/backend:local-code - build: - dockerfile: ../backend/Dockerfile - context: ../backend - target: code - command: sleep 0 - - backend: - image: ocelotsocialnetwork/backend-branded:local-${CONFIGURATION} - container_name: backend-branded - build: - dockerfile: src/docker/backend.Dockerfile - target: branded - context: . - args: - - CONFIGURATION=$CONFIGURATION - - APP_IMAGE_TAG_BASE=local-base - - APP_IMAGE_TAG_CODE=local-code - networks: - - test-network - depends_on: - - neo4j - - backend-base - - backend-code - ports: - - 4000:4000 - volumes: - - backend_uploads:/app/public/uploads - environment: - - NEO4J_URI=bolt://neo4j:7687 - - GRAPHQL_URI=http://backend:4000 - - CLIENT_URI=http://localhost:3000 - - JWT_SECRET=b/&&7b78BF&fv/Vd - - MAPBOX_TOKEN=pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g - - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - - EMAIL_SUPPORT=support@wir.social - - EMAIL_DEFAULT_SENDER=info@wir.social - - PUBLIC_REGISTRATION=true - - INVITE_REGISTRATION=true - - CATEGORIES_ACTIVE=true - - SMTP_USERNAME=${SMTP_USERNAME} - - SMTP_PASSWORD=${SMTP_PASSWORD} - - SMTP_HOST=mailserver - - SMTP_PORT=25 - - SMTP_IGNORE_TLS=true - #- PRODUCTION_DB_CLEAN_ALLOW=true - - NODE_ENV=development - - maintenance-base: - image: ocelotsocialnetwork/maintenance:local-base - build: - dockerfile: ../webapp/Dockerfile.maintenance - context: ../webapp - target: base - command: sleep 0 - - maintenance-code: - image: ocelotsocialnetwork/maintenance:local-code - build: - dockerfile: ../webapp/Dockerfile.maintenance - context: ../webapp - target: code - command: sleep 0 - - maintenance: - # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there - image: ocelotsocialnetwork/maintenance-branded:local-${CONFIGURATION} - container_name: maintenance-branded - build: - # TODO: Separate from webapp, this must be independent - dockerfile: src/docker/maintenance.Dockerfile - target: branded - context: . - args: - - CONFIGURATION=$CONFIGURATION - - APP_IMAGE_TAG_BASE=local-base - - APP_IMAGE_TAG_CODE=local-code - networks: - - test-network - depends_on: - - maintenance-base - - maintenance-code - ports: - - 3001:80 - - neo4j: - # Neo4j v3.5.14-community - # image: wollehuss/neo4j-community-branded:latest - # Neo4j 4.4-community - image: ocelotsocialnetwork/neo4j-community:latest - container_name: neo4j-branded - networks: - - test-network - ports: - - 7687:7687 - # only for development - # - 7474:7474 - - 7474:7474 - volumes: - - neo4j_data:/data - environment: - # settings reference: https://neo4j.com/docs/operations-manual/4.4/docker/ref-settings/ - # TODO: This sounds scary for a production environment - - NEO4J_AUTH=none - - NEO4J_dbms_security_procedures_unrestricted=algo.*,apoc.* - - NEO4J_dbms_allow__format__migration=true - - NEO4J_dbms_allow__upgrade=true - # TODO: clarify if that is the only thing needed to unlock the Enterprise version - # - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes - # Uncomment following line for Neo4j Enterprise version instead of Community version - # TODO: clarify if that is the only thing needed to unlock the Enterprise version - # - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes - # set the name of the database to be used - # - NEO4J_dbms_default__database=graph.db - # - NEO4J_dbms_default__database=neo4j - # TODO: Remove the playground from production - # bring the database in offline mode to export or load dumps - # command: ["tail", "-f", "/dev/null"] - - mailserver: - image: djfarrelly/maildev - container_name: mailserver-branded - ports: - - 1080:80 - networks: - - test-network - -networks: - test-network: - -volumes: - backend_uploads: - neo4j_data: diff --git a/deployment/helm/charts/.helmignore b/deployment/helm/charts/.helmignore new file mode 100644 index 000000000..0e8a0eb36 --- /dev/null +++ b/deployment/helm/charts/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/deployment/helm/charts/ocelot-neo4j/Chart.yaml b/deployment/helm/charts/ocelot-neo4j/Chart.yaml new file mode 100644 index 000000000..f8a6c88f1 --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: ocelot-neo4j +description: A Helm chart for the neo4j database of ocelot-social + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# 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.2.0" diff --git a/deployment/helm/charts/ocelot-neo4j/old/job.yaml b/deployment/helm/charts/ocelot-neo4j/old/job.yaml new file mode 100644 index 000000000..b9051206c --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/old/job.yaml @@ -0,0 +1,34 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-neo4j-backup +spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: container-{{ .Release.Name }}-neo4j-backup + image: "{{ .Values.neo4j.image.repository }}:{{ default .Values.global.image.tag .Values.neo4j.image.tag .Chart.AppVersion "latest" }}" + imagePullPolicy: {{ quote .Values.global.image.pullPolicy }} + command: + - neo4j-admin + - dump + - --to + - "/backups/neo4j-dump-{{ now | date "20060102150405" }}" + envFrom: + - configMapRef: + name: {{ .Release.Name }}-neo4j-env + - secretRef: + name: {{ .Release.Name }}-neo4j-secret-env + volumeMounts: + - mountPath: /data/ + name: neo4j-data + - mountPath: /backups/ + name: neo4j-backups + volumes: + - name: neo4j-data + persistentVolumeClaim: + claimName: {{ .Release.Name }}-neo4j-data + - name: neo4j-backups + persistentVolumeClaim: + claimName: {{ .Release.Name }}-neo4j-backups diff --git a/deployment/helm/charts/ocelot-neo4j/templates/_helpers.tpl b/deployment/helm/charts/ocelot-neo4j/templates/_helpers.tpl new file mode 100644 index 000000000..d4d9b841f --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/templates/_helpers.tpl @@ -0,0 +1,10 @@ +{{- define "defaultTag" -}} +{{- .Values.global.image.tag | default .Chart.AppVersion }} +{{- end -}} + +{{- define "resources" }} +{{- if . }} +resources: +{{ . | toYaml | indent 2 }} +{{- end }} +{{- end }} diff --git a/deployment/helm/charts/ocelot-neo4j/templates/neo4j/configmap.yml b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/configmap.yml new file mode 100644 index 000000000..e4aa51875 --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/configmap.yml @@ -0,0 +1,6 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-neo4j-env +data: +{{ .Values.neo4j.env | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-neo4j/templates/neo4j/persistent-volume-claim.yaml b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/persistent-volume-claim.yaml new file mode 100644 index 000000000..96843e557 --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/persistent-volume-claim.yaml @@ -0,0 +1,22 @@ +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-neo4j-data +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.neo4j.storage }} +--- +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-neo4j-backups +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.neo4j.storageBackups }} diff --git a/deployment/helm/charts/ocelot-neo4j/templates/neo4j/secret.yaml b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/secret.yaml new file mode 100644 index 000000000..de67571c0 --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/secret.yaml @@ -0,0 +1,6 @@ +kind: Secret +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-neo4j-secret-env +stringData: +{{ .Values.secrets.neo4j.env | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-neo4j/templates/neo4j/service.yaml b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/service.yaml new file mode 100644 index 000000000..02dd927db --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/service.yaml @@ -0,0 +1,14 @@ +kind: Service +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-neo4j +spec: + ports: + - name: {{ .Release.Name }}-bolt + port: 7687 + targetPort: 7687 + - name: {{ .Release.Name }}-http # for debugging only + port: 7474 + targetPort: 7474 + selector: + app: {{ .Release.Name }}-neo4j diff --git a/deployment/helm/charts/ocelot-neo4j/templates/neo4j/stateful-set.yaml b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/stateful-set.yaml new file mode 100644 index 000000000..65341d120 --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/templates/neo4j/stateful-set.yaml @@ -0,0 +1,38 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ .Release.Name }}-neo4j +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }}-neo4j + template: + metadata: + name: neo4j + annotations: + backup.velero.io/backup-volumes: neo4j-data + labels: + app: {{ .Release.Name }}-neo4j + spec: + restartPolicy: Always + containers: + - name: container-{{ .Release.Name }}-neo4j + image: "{{ .Values.neo4j.image.repository }}:{{ .Values.neo4j.image.tag | default (include "defaultTag" .) }}" + imagePullPolicy: {{ quote .Values.global.image.pullPolicy }} + {{- include "resources" .Values.neo4j.resources | indent 8 }} + ports: + - containerPort: 7687 + - containerPort: 7474 + envFrom: + - configMapRef: + name: {{ .Release.Name }}-neo4j-env + - secretRef: + name: {{ .Release.Name }}-neo4j-secret-env + volumeMounts: + - mountPath: /data/ + name: neo4j-data + volumes: + - name: neo4j-data + persistentVolumeClaim: + claimName: {{ .Release.Name }}-neo4j-data diff --git a/deployment/helm/charts/ocelot-neo4j/values.yaml b/deployment/helm/charts/ocelot-neo4j/values.yaml new file mode 100644 index 000000000..0bc9874df --- /dev/null +++ b/deployment/helm/charts/ocelot-neo4j/values.yaml @@ -0,0 +1,25 @@ +underMaintenance: false + +global: + image: + tag: + +neo4j: + image: + repository: ghcr.io/ocelot-social-community/ocelot-social/neo4j + tag: + + storage: "5Gi" + storageBackups: "10Gi" + env: + NEO4J_ACCEPT_LICENSE_AGREEMENT: "no" + NEO4J_AUTH: "none" + NEO4J_dbms_connector_bolt_thread__pool__max__size: "400" + NEO4J_dbms_memory_heap_initial__size: "" + NEO4J_dbms_memory_heap_max__size: "" + NEO4J_dbms_memory_pagecache_size: "" + NEO4J_dbms_security_procedures_unrestricted: "algo.*,apoc.*" + NEO4J_dbms_default__database: neo4j + NEO4J_apoc_import_file_enabled: "false" + NEO4J_dbms_allow__format__migration: "true" + NEO4J_dbms_allow__upgrade: "true" diff --git a/deployment/helm/charts/ocelot-social/Chart.yaml b/deployment/helm/charts/ocelot-social/Chart.yaml new file mode 100644 index 000000000..c363a90cb --- /dev/null +++ b/deployment/helm/charts/ocelot-social/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: ocelot-social +description: A Helm chart for ocelot-social + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# 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.2.0" diff --git a/deployment/helm/charts/ocelot-social/templates/_helpers.tpl b/deployment/helm/charts/ocelot-social/templates/_helpers.tpl new file mode 100644 index 000000000..d4d9b841f --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/_helpers.tpl @@ -0,0 +1,10 @@ +{{- define "defaultTag" -}} +{{- .Values.global.image.tag | default .Chart.AppVersion }} +{{- end -}} + +{{- define "resources" }} +{{- if . }} +resources: +{{ . | toYaml | indent 2 }} +{{- end }} +{{- end }} diff --git a/deployment/helm/charts/ocelot-social/templates/acme-issuer.yaml b/deployment/helm/charts/ocelot-social/templates/acme-issuer.yaml new file mode 100644 index 000000000..6654aab77 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/acme-issuer.yaml @@ -0,0 +1,39 @@ +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ .Release.Name }}-letsencrypt-staging +spec: + acme: + # The ACME server URL + server: https://acme-staging-v02.api.letsencrypt.org/directory + # Email address used for ACME registration + email: {{ quote .Values.secrets.acme_email }} + # Name of a secret used to store the ACME account private key + privateKeySecretRef: + name: {{ .Release.Name }}-letsencrypt-staging + # Enable the HTTP-01 challenge provider + solvers: + - http01: + ingress: + class: traefik + +--- +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: {{ .Release.Name }}-letsencrypt-prod +spec: + acme: + # The ACME server URL + server: https://acme-v02.api.letsencrypt.org/directory + # Email address used for ACME registration + email: {{ quote .Values.secrets.acme_email }} + # Name of a secret used to store the ACME account private key + privateKeySecretRef: + name: {{ .Release.Name }}-letsencrypt-prod + # Enable the HTTP-01 challenge provider + solvers: + - http01: + ingress: + class: traefik diff --git a/deployment/helm/charts/ocelot-social/templates/backend/configmap.yml b/deployment/helm/charts/ocelot-social/templates/backend/configmap.yml new file mode 100644 index 000000000..06afc5962 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/backend/configmap.yml @@ -0,0 +1,6 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-backend-env +data: +{{ .Values.backend.env | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-social/templates/backend/persistent-volume-claim.yaml b/deployment/helm/charts/ocelot-social/templates/backend/persistent-volume-claim.yaml new file mode 100644 index 000000000..831b95347 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/backend/persistent-volume-claim.yaml @@ -0,0 +1,10 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-uploads +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.backend.storage }} diff --git a/deployment/helm/charts/ocelot-social/templates/backend/secret.yaml b/deployment/helm/charts/ocelot-social/templates/backend/secret.yaml new file mode 100644 index 000000000..a26eec86c --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/backend/secret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-backend-secret-env +type: Opaque +stringData: +{{ .Values.secrets.backend.env | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-social/templates/backend/service.yaml b/deployment/helm/charts/ocelot-social/templates/backend/service.yaml new file mode 100644 index 000000000..e484fa7a9 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/backend/service.yaml @@ -0,0 +1,11 @@ +kind: Service +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-backend +spec: + ports: + - name: {{ .Release.Name }}-graphql + port: 4000 + targetPort: 4000 + selector: + app: {{ .Release.Name }}-backend diff --git a/deployment/helm/charts/ocelot-social/templates/backend/stateful-set.yaml b/deployment/helm/charts/ocelot-social/templates/backend/stateful-set.yaml new file mode 100644 index 000000000..98eb3fcad --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/backend/stateful-set.yaml @@ -0,0 +1,52 @@ +kind: StatefulSet +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-backend +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-backend + template: + metadata: + annotations: + backup.velero.io/backup-volumes: uploads + labels: + app: {{ .Release.Name }}-backend + spec: + restartPolicy: Always + initContainers: + - name: {{ .Release.Name }}-backend-migrations + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default (include "defaultTag" .) }}" + imagePullPolicy: {{ quote .Values.global.image.pullPolicy }} + command: ["/bin/sh", "-c", "yarn prod:migrate up"] + {{- include "resources" .Values.backend.resources | indent 10 }} + envFrom: + - configMapRef: + name: {{ .Release.Name }}-backend-env + - secretRef: + name: {{ .Release.Name }}-backend-secret-env + containers: + - name: {{ .Release.Name }}-backend + image: "{{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag | default (include "defaultTag" .) }}" + imagePullPolicy: {{ quote .Values.global.image.pullPolicy }} + {{- include "resources" .Values.backend.resources | indent 10 }} + env: + - name: GRAPHQL_URI + value: "http://{{ .Release.Name }}-backend:4000" + - name: CLIENT_URI + value: "https://{{ .Values.domain }}" + envFrom: + - configMapRef: + name: {{ .Release.Name }}-backend-env + - secretRef: + name: {{ .Release.Name }}-backend-secret-env + ports: + - containerPort: 4000 + protocol: TCP + volumeMounts: + - mountPath: /app/public/uploads + name: uploads + volumes: + - name: uploads + persistentVolumeClaim: + claimName: {{ .Release.Name }}-uploads diff --git a/deployment/helm/charts/ocelot-social/templates/configmap.yaml b/deployment/helm/charts/ocelot-social/templates/configmap.yaml new file mode 100644 index 000000000..e01d5bf1b --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/configmap.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ .Release.Name }} +data: +{{ .Values.configmap | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-social/templates/ingress.yaml b/deployment/helm/charts/ocelot-social/templates/ingress.yaml new file mode 100644 index 000000000..56142f650 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/ingress.yaml @@ -0,0 +1,65 @@ +--- +{{- define "joinRedirectMiddlewares" -}} +{{- $local := dict "first" true -}} +{{- range $k, $v := .Values.redirect_domains -}}{{- if not $local.first -}},{{- end -}}{{$.Release.Namespace}}-redirect-{{- $v | replace "." "-" -}}@kubernetescrd{{- $_ := set $local "first" false -}}{{- end -}} +{{- end -}} +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-ocelot + annotations: + cert-manager.io/issuer: {{ .Values.cert_manager.issuer | default (printf "%s-letsencrypt-staging" .Release.Name) }} + traefik.ingress.kubernetes.io/router.middlewares: {{ quote (include "joinRedirectMiddlewares" $)}} +spec: + tls: + - hosts: + - {{ quote .Values.domain }} + {{- range .Values.redirect_domains }} + - {{ quote . }} + {{- end }} + secretName: {{ .Release.Name }}-letsencrypt-tls + + rules: + - host: {{ quote .Values.domain }} + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + {{- if .Values.underMaintenance }} + name: {{ .Release.Name }}-maintenance + port: + number: 80 + {{- else }} + name: {{ .Release.Name }}-webapp + port: + number: 3000 + {{- end }} + {{- range .Values.redirect_domains }} + - host: {{ quote . }} # the service must be defined, else the redirect is not working + http: + paths: + - path: / + pathType: ImplementationSpecific + backend: + service: + name: {{ $.Release.Name }}-maintenance + port: + number: 80 + {{- end }} + +{{- range .Values.redirect_domains }} +--- +# Redirect with domain replacement +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: redirect-{{ . | replace "." "-" }} +spec: + redirectRegex: + regex: ^https://{{ . }}(.*) + replacement: https://{{ $.Values.domain }}${1} + permanent: true +{{- end }} \ No newline at end of file diff --git a/deployment/helm/charts/ocelot-social/templates/maintenance/deployment.yaml b/deployment/helm/charts/ocelot-social/templates/maintenance/deployment.yaml new file mode 100644 index 000000000..a06e66541 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/maintenance/deployment.yaml @@ -0,0 +1,24 @@ +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-maintenance +spec: + selector: + matchLabels: + app: {{ .Release.Name }}-maintenance + template: + metadata: + labels: + app: {{ .Release.Name }}-maintenance + spec: + restartPolicy: Always + containers: + - name: {{ .Release.Name }}-maintenance + image: "{{ .Values.maintenance.image.repository }}:{{ .Values.maintenance.image.tag | default (include "defaultTag" .) }}" + imagePullPolicy: {{ quote .Values.global.image.pullPolicy }} + {{- include "resources" .Values.maintenance.resources | indent 8 }} + env: + - name: HOST + value: 0.0.0.0 + ports: + - containerPort: 80 diff --git a/deployment/helm/charts/ocelot-social/templates/maintenance/service.yaml b/deployment/helm/charts/ocelot-social/templates/maintenance/service.yaml new file mode 100644 index 000000000..fc60e687f --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/maintenance/service.yaml @@ -0,0 +1,11 @@ +kind: Service +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-maintenance +spec: + ports: + - name: {{ .Release.Name }}-http + port: 80 + targetPort: 80 + selector: + app: {{ .Release.Name }}-maintenance diff --git a/deployment/helm/charts/ocelot-social/templates/webapp/configmap.yml b/deployment/helm/charts/ocelot-social/templates/webapp/configmap.yml new file mode 100644 index 000000000..f5ad51a21 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/webapp/configmap.yml @@ -0,0 +1,6 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-webapp-env +data: +{{ .Values.webapp.env | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-social/templates/webapp/deployment.yaml b/deployment/helm/charts/ocelot-social/templates/webapp/deployment.yaml new file mode 100644 index 000000000..f23705db1 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/webapp/deployment.yaml @@ -0,0 +1,34 @@ +kind: Deployment +apiVersion: apps/v1 +metadata: + name: {{ .Release.Name }}-webapp +spec: + replicas: 1 + selector: + matchLabels: + app: {{ .Release.Name }}-webapp + template: + metadata: + labels: + app: {{ .Release.Name }}-webapp + spec: + restartPolicy: Always + containers: + - name: {{ .Release.Name }}-webapp + image: "{{ .Values.webapp.image.repository }}:{{ .Values.webapp.image.tag | default (include "defaultTag" .) }}" + imagePullPolicy: {{ quote .Values.global.image.pullPolicy }} + {{- include "resources" .Values.webapp.resources | indent 8 }} + ports: + - containerPort: 3000 + env: + - name: WEBSOCKETS_URI + value: "wss://{{ .Values.domain }}/api/graphql" + - name: HOST + value: "0.0.0.0" + - name: GRAPHQL_URI + value: "http://{{ .Release.Name }}-backend:4000" + envFrom: + - configMapRef: + name: {{ .Release.Name }}-webapp-env + - secretRef: + name: {{ .Release.Name }}-webapp-secret-env diff --git a/deployment/helm/charts/ocelot-social/templates/webapp/secret.yaml b/deployment/helm/charts/ocelot-social/templates/webapp/secret.yaml new file mode 100644 index 000000000..32179e90e --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/webapp/secret.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-webapp-secret-env +type: Opaque +stringData: +{{ .Values.secrets.webapp.env | toYaml | indent 2 }} diff --git a/deployment/helm/charts/ocelot-social/templates/webapp/service.yaml b/deployment/helm/charts/ocelot-social/templates/webapp/service.yaml new file mode 100644 index 000000000..1e1c4c712 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/templates/webapp/service.yaml @@ -0,0 +1,11 @@ +kind: Service +apiVersion: v1 +metadata: + name: {{ .Release.Name }}-webapp +spec: + ports: + - name: {{ .Release.Name }}-http + port: 3000 + targetPort: 3000 + selector: + app: {{ .Release.Name }}-webapp diff --git a/deployment/helm/charts/ocelot-social/values.yaml b/deployment/helm/charts/ocelot-social/values.yaml new file mode 100644 index 000000000..2213c5007 --- /dev/null +++ b/deployment/helm/charts/ocelot-social/values.yaml @@ -0,0 +1,27 @@ +domain: stage.ocelot.social +redirect_domains: [] + +cert_manager: + issuer: + +underMaintenance: false + +global: + image: + pullPolicy: IfNotPresent + tag: + +backend: + image: + repository: ghcr.io/ocelot-social-community/ocelot-social/backend + storage: "10Gi" + env: + NEO4J_URI: "bolt://ocelot-social-neo4j:7687" + +webapp: + image: + repository: ghcr.io/ocelot-social-community/ocelot-social/webapp + +maintenance: + image: + repository: ghcr.io/ocelot-social-community/ocelot-social/maintenance diff --git a/deployment/helm/helmfile/helmfile.yaml.gotmpl b/deployment/helm/helmfile/helmfile.yaml.gotmpl new file mode 100644 index 000000000..6ea0b29d0 --- /dev/null +++ b/deployment/helm/helmfile/helmfile.yaml.gotmpl @@ -0,0 +1,16 @@ +releases: + - name: ocelot-social + namespace: ocelot-social + chart: ../charts/ocelot-social + values: + - ./values/ocelot.yaml + secrets: + - ./secrets/ocelot.yaml + + - name: ocelot-neo4j + namespace: ocelot-social + chart: ../charts/ocelot-neo4j + values: + - ./values/ocelot.yaml + secrets: + - ./secrets/ocelot.yaml diff --git a/deployment/helm/helmfile/secrets/ocelot.yaml b/deployment/helm/helmfile/secrets/ocelot.yaml new file mode 100644 index 000000000..3965bc09e --- /dev/null +++ b/deployment/helm/helmfile/secrets/ocelot.yaml @@ -0,0 +1,76 @@ +secrets: + acme_email: ENC[AES256_GCM,data:o+2HnrEqa/uXJwqUwdYU14FiZYPfLcKqkQ==,iv:1ouUU4ewzRL4ZDnwJm6BTVg3a64iC5+I2v+AWIF8W2Q=,tag:7ytv959cVmgSmXMC7A8zxA==,type:str] + webapp: + env: + MAPBOX_TOKEN: ENC[AES256_GCM,data:7Ka4BvQh6NDw9NKUcgGjLwxNHOqhVrZEj/DcGnyv1nXQIG/2WWGGHazAFWUCFpCUmCSaTPSkyLHPFyGQtQ7VAON3AG3tHtv5JvcBb4KDYrjAIzxhAAiHMYFtVJs=,iv:X0YL2dW42TUidJdBlRKb4Vq86X1OzHqipNHTBxmE7ds=,tag:KDH9NwDy6ghqdkXeZxuHgg==,type:str] + backend: + env: + JWT_SECRET: ENC[AES256_GCM,data:8qGviTFMOv9QyoNVwnlFNZ2PmvedbKJM,iv:rmZgs8h2QVsokzMzdGdEcInBLv8AX3xFUjkGhTf3sF0=,tag:SUJpMaIGAb14yg8RxCVUtA==,type:str] + MAPBOX_TOKEN: ENC[AES256_GCM,data:qK6iTYKiWfkvXBodm8zVmfr5ACTTz1+7Pt7Q/hwgv3SYERyo5NyqfsvbVKuDAD90kTCNODpSwUApJE6do/Umedg4s8mrnHXCckIDbX5BztoeHJBehsUC54ELcrQ=,iv:b65yqfdoOX366UXt7HS6nhL8hlZn4l5hQfrhI6NXc+I=,tag:vF48V+TRS5g9ezXhzAJnPw==,type:str] + PRIVATE_KEY_PASSPHRASE: ENC[AES256_GCM,data:05WXBFKIk0BtfUYmkWSwAP+/Y7v18LUow4X/,iv:y7VyymcoRLr2CK96BiErXvKP2Gn/QhECBZyeP+wo8LA=,tag:Hg/fIGyIDMY8P3mWfVupCw==,type:str] + #ENC[AES256_GCM,data:llx+JN8fRqwrLd2ahkmPrhPwcGIkn695l3Ox8VEs9YAR+1wpz3yujA==,iv:4Ctez8zMeqo3cpCCUVy6ZP4T1Z/myPw/FTq+++YAYbc=,tag:al/J8DLqNz6CoLl+TgUdOw==,type:comment] + EMAIL_DEFAULT_SENDER: ENC[AES256_GCM,data:z1EyEokf/TNkFLhRzsCbHew/6T8=,iv:Satr1c8aZQE73ZolC6n+PO74r+Gj3un5Mj0DIYb3n14=,tag:iK6l0GXuhLauBtFXTmLyKQ==,type:str] + SMTP_HOST: ENC[AES256_GCM,data:r0qbaUBB3CSUHR76,iv:TJIx71HW1aBB0sCEd1TB/tTgPBxLR1sdGAEf0t7Qilg=,tag:arXYtwVbIXVaUJpyommokQ==,type:str] + SMTP_USERNAME: ENC[AES256_GCM,data:lZ05DvSu,iv:Tyu7poao1shqKGd/sjTCgGNHU1xgRpjwjMRd+ArGf6o=,tag:dKms4G683JvFzja7YOwYKg==,type:str] + SMTP_PASSWORD: ENC[AES256_GCM,data:c9rnPIaKHIh2LNIJON3ib1IsA09OWGchDxRPRpvrtJw=,iv:08Acxl74lJbYtEEU6crVIYRXwkER8t1XPrhBA2PwEio=,tag:F0xrrt2PkBUMEyp7a81ssw==,type:str] + SMTP_PORT: ENC[AES256_GCM,data:MGmv,iv:IFg6oEncN0ICEmw96XL4EuPKqEZ6KLwU5FJYkveMSpY=,tag:kIVXlt0o5TfhOtRVqU/c4w==,type:str] + SMTP_IGNORE_TLS: ENC[AES256_GCM,data:ORAIWtg=,iv:6X4V3RDeYHrFdBTjsb3Ji0KWsZ2meL8ilqHNGQbcV/M=,tag:R87FgoQwqpes+0ejcOlrPg==,type:str] + #ENC[AES256_GCM,data:wEE3/SPsZqy9LATseOZG7LsCbjG5gY4VUT/TzxhHLJqcYP5I,iv:gcOA0XiUGWq15G4zTRPZ0qZ/XYMTjr+9krbOx0dwpeY=,tag:jd8LTiVT7UQShqMR9zZUZA==,type:comment] + SMTP_SECURE: ENC[AES256_GCM,data:PowbGhU=,iv:a1dK5AVySu749vPQvX9OLfMuD+tZkLNtXTMr17+4KuA=,tag:fuJQ7c4RBl25If01MSAmug==,type:str] + SMTP_DKIM_PRIVATKEY: null + SMTP_DKIM_DOMAINNAME: null + SMTP_DKIM_KEYSELECTOR: null + NEO4J_USERNAME: null + NEO4J_PASSWORD: null + REDIS_PASSWORD: null + neo4j: + env: + NEO4J_USERNAME: "" + NEO4J_PASSWORD: "" +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age1llp6k66265q3rzqemxpnq0x3562u20989vcjf65fl9s3hjhgcscq6mhnjw + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRbjk3QXdyZU5yZnE0dElE + SW91VGIvSnovRmc4MCtiNDhET3RHQTFoakd3ClB4RlZUZXRwSTgvUTR3Q1AwUGJo + NEpySWVEOFE4ZmIzek03NzczeVhyY0EKLS0tIG9SZ2ZwQXdFSUVTbWxCQXpUeWd2 + VDlsRlY2Z1RjWFZjcU9UeUpJZHJuSmMKTuy/s49nIwfRQyDyCGBWZPvyR9oNEXxV + 6C0oVQXVTifkMvDet3dZWnOy6TeMkZBLD4BZHXSI+l6DkNdmIiwIpw== + -----END AGE ENCRYPTED FILE----- + - recipient: age1zycwtk6dkxj6vuqhj9jw7932ythky9p3att6df4z9qasyw8v5dxquejcmp + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBQaHd0YW83bS9NZ1RBSWl6 + cU0vMStYT3QxOFhOYmdNMUpNaHBLOVJGUFJVCnRjbWswbDhzOStFZTdXSVhTemJx + TVo1YnpxMDZxd1NWMVpNYXlYbzZtaVkKLS0tIGhmaHZzc2hnYi9WSStpc2lkbkRP + MElZK25Nc0lZTXBtc1BOQUpCandFKzAKnareBqzmHiSY551Iw8zPNg6aJN2QM0iN + f05TgS58OSEzXL60/9wBEN+E4Y1VErwOYP9CH8MdiAv1iRwLYgSJ/Q== + -----END AGE ENCRYPTED FILE----- + - recipient: age15arcg8x6ltnsacwalvny0h2d4d4wkdmax328mw3v5vda9zm97uqshtavmr + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyUWtnd1JObWNZZzZtWndv + dVhLWlRSNDNacHdSMXJ1ejV2RC80elA2TG1rCmc1MTFSMlpYM3hsSDNwWUJ0R3NC + Y2RrT2pZQllyTkdpcEs2akF0cENpc0EKLS0tIDFxV1B6bzZZVFVlSk5qZWxDbEd4 + MkpsL3phc0M0VXBuUGQ2dFZOZHlKS1EKEmCasI2+d4FBgiI4Ter8Gxbl87yrfBq+ + xze5n0df0GKK6JsML/0m2Z7HoqtCAEsjEfm45GdfAaiqPVh7gJG8TQ== + -----END AGE ENCRYPTED FILE----- + - recipient: age1khw2eps099audp3uu5s9rk07qznllh5c8a43gv5dtpnq2a7lue6qrehn5s + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRcDlPb1BlVUIwSEUvTjBx + KytIS0xQWjlzeEJPSDI5SEg5RmpXWFhKZVRvCm1XLzlMUmo1U1BZL2ZFS25GSkhY + V0tESW1hYTU0V01UQzEvNjZjMDk2WDAKLS0tIEl5TG84VE1UN0V3bk13cFU3bTUr + aGNFeXZZRmlJM041OHdTM0pmM3BBdGMKGvFgYY1jhKwciAOZKyw0hlFVNbOk7CM7 + 041g17JXNV1Wk6WgMZ4w8p54RKQVaWCT4wxChy6wNNdQ3IeKgqEU2w== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2024-10-29T14:26:49Z" + mac: ENC[AES256_GCM,data:YXX7MEAK0wmuxLTmdr7q5uVd6DG6FhGUeE+EzbhWe/OovH6n+CjKZGklnEX+5ztDO0IgZh/T9Hx1CgFYuVbcOkvDoFBDwNpRA/QOQrM0p/+tRlMNCypC/Wh2xL0DhA4A/Qum2oyE/BDkt1Yy8N5wZDZn575+ZAjXEgAzlhpT5qk=,iv:ire3gkHTY6+0lgbV1Es6Lf8bcKTg4WKnq46M+b/VRcU=,tag:MkZULKcwROvIw/C0YtcUbA==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.9.0 diff --git a/deployment/helm/helmfile/values/ocelot.yaml b/deployment/helm/helmfile/values/ocelot.yaml new file mode 100644 index 000000000..89c65f3be --- /dev/null +++ b/deployment/helm/helmfile/values/ocelot.yaml @@ -0,0 +1,2 @@ +cert_manager: + issuer: ocelot-social-letsencrypt-prod diff --git a/deployment/scripts/branded-images.build.sh b/deployment/scripts/branded-images.build.sh deleted file mode 100755 index fa9da67d1..000000000 --- a/deployment/scripts/branded-images.build.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# for a branded version you should pass the following env variables: -# CONFIGURATION - your configuration folder name -# DOCKERHUB_ORGANISATION - your dockerhub organisation -# OCELOT_VERSION - specify the specific tag to build upon e.g. 2.4.0-300 - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# check DOCKERHUB_BRAND_VARRIANT -if [ -z ${DOCKERHUB_BRAND_VARRIANT} ]; then - echo "You must provide a `DOCKERHUB_BRAND_VARRIANT` via environment variable" - exit 1 -fi -echo "Using DOCKERHUB_BRAND_VARRIANT=${DOCKERHUB_BRAND_VARRIANT}" - -# configuration -DOCKERHUB_ORGANISATION=${DOCKERHUB_ORGANISATION:-"ocelotsocialnetwork"} -OCELOT_VERSION=${OCELOT_VERSION:-$(node -p -e "require('${SCRIPT_DIR}/../../package.json').version")} -OCELOT_GITHUB_RUN_NUMBER=${OCELOT_GITHUB_RUN_NUMBER:-master} -OCELOT_VERSION_BUILD=${OCELOT_VERSION_BUILD:-${OCELOT_VERSION}-${OCELOT_GITHUB_RUN_NUMBER}} -BRANDED_VERSION=${BRANDED_VERSION:-${GITHUB_RUN_NUMBER:-"local"}} -BUILD_DATE=${BUILD_DATE:-$(date -u +'%Y-%m-%dT%H:%M:%SZ')} -BUILD_VERSION_BASE=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION} -BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION_BUILD} -BUILD_COMMIT=${GITHUB_SHA:-"0000000"} - -# backend -docker build --target branded \ - -t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:latest" \ - -t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \ - -t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \ - -t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \ - -t "${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \ - -f "${SCRIPT_DIR}/../src/docker/backend.Dockerfile" \ - --build-arg "CONFIGURATION=${CONFIGURATION}" \ - --build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \ - --build-arg "APP_IMAGE_TAG_BASE=${OCELOT_VERSION}-base" \ - "${SCRIPT_DIR}/../." - -# webapp -docker build --target branded \ - -t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:latest" \ - -t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \ - -t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \ - -t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \ - -t "${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \ - -f "${SCRIPT_DIR}/../src/docker/webapp.Dockerfile" \ - --build-arg "CONFIGURATION=${CONFIGURATION}" \ - --build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \ - --build-arg "APP_IMAGE_TAG_BASE=${OCELOT_VERSION}-base" \ - "${SCRIPT_DIR}/../." - -# mainteance -docker build --target branded \ - -t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:latest" \ - -t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION}" \ - -t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD}" \ - -t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE}" \ - -t "${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION}" \ - -f "${SCRIPT_DIR}/../src/docker/maintenance.Dockerfile" \ - --build-arg "CONFIGURATION=${CONFIGURATION}" \ - --build-arg "APP_IMAGE_TAG_CODE=${OCELOT_VERSION}-code" \ - --build-arg "APP_IMAGE_TAG_BASE=${OCELOT_VERSION}-base" \ - "${SCRIPT_DIR}/../." diff --git a/deployment/scripts/branded-images.upload.sh b/deployment/scripts/branded-images.upload.sh deleted file mode 100755 index 83e27496b..000000000 --- a/deployment/scripts/branded-images.upload.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -# for a branded version you should pass the following env variables: -# DOCKERHUB_ORGANISATION - your dockerhub organisation -# OCELOT_VERSION - specify the specific tag to build upon e.g. 2.4.0-300 -# DOCKERHUB_USERNAME - your dockerhub username -# DOCKERHUB_TOKEN - your dockerhub access token - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check DOCKERHUB_BRAND_VARRIANT -if [ -z ${DOCKERHUB_BRAND_VARRIANT} ]; then - echo "You must provide a `DOCKERHUB_BRAND_VARRIANT` via environment variable" - exit 1 -fi -echo "Using DOCKERHUB_BRAND_VARRIANT=${DOCKERHUB_BRAND_VARRIANT}" - -# configuration -DOCKERHUB_ORGANISATION=${DOCKERHUB_ORGANISATION:-"ocelotsocialnetwork"} -OCELOT_VERSION=${OCELOT_VERSION:-$(node -p -e "require('${SCRIPT_DIR}/../../package.json').version")} -OCELOT_GITHUB_RUN_NUMBER=${OCELOT_GITHUB_RUN_NUMBER:-master} -OCELOT_VERSION_BUILD=${OCELOT_VERSION_BUILD:-${OCELOT_VERSION}-${OCELOT_GITHUB_RUN_NUMBER}} -BRANDED_VERSION=${BRANDED_VERSION:-${GITHUB_RUN_NUMBER:-"local"}} -BUILD_VERSION_BASE=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION} -BUILD_VERSION=${BRANDED_VERSION}-ocelot.social${OCELOT_VERSION_BUILD} - -# login to dockerhub -echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin - -# push backend images -docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:latest -docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION} -docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD} -docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE} -docker push ${DOCKERHUB_ORGANISATION}/backend-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION} - -# push webapp images -docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:latest -docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION} -docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD} -docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE} -docker push ${DOCKERHUB_ORGANISATION}/webapp-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION} - -# push maintenance images -docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:latest -docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION} -docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${OCELOT_VERSION_BUILD} -docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION_BASE} -docker push ${DOCKERHUB_ORGANISATION}/maintenance-${DOCKERHUB_BRAND_VARRIANT}:${BUILD_VERSION} \ No newline at end of file diff --git a/deployment/scripts/cluster.backend-bash.sh b/deployment/scripts/cluster.backend-bash.sh deleted file mode 100755 index 251978f7d..000000000 --- a/deployment/scripts/cluster.backend-bash.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# time stamp -printf "Neo4J bash :\n " -date - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "!!! You must provide a CONFIGURATION via environment variable !!!" - exit 1 -fi - -printf " Cluster: %s\n" $CONFIGURATION - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -kubectl --kubeconfig=${KUBECONFIG} -n default exec -it $(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh \ No newline at end of file diff --git a/deployment/scripts/cluster.backup.sh b/deployment/scripts/cluster.backup.sh deleted file mode 100755 index 82cd85498..000000000 --- a/deployment/scripts/cluster.backup.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# time stamp -printf "Backup started at:\n " -date - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "!!! You must provide a CONFIGURATION via environment variable !!!" - exit 1 -fi - -printf " Cluster: %s\n" $CONFIGURATION - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} -BACKUP_DATE=$(date "+%F_%H-%M-%S") -BACKUP_FOLDER=${BACKUP_FOLDER:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/backup/${BACKUP_DATE}} - -printf "Backup folder name: %s\n" $BACKUP_DATE -# create backup folder -mkdir -p ${BACKUP_FOLDER} - -# cluster maintenance mode on && Neo4j maintenance mode on -${SCRIPT_DIR}/cluster.neo4j.sh maintenance on - -# database backup -echo "Dumping database ..." -kubectl --kubeconfig=${KUBECONFIG} -n default exec -it \ - $(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-neo4j | awk '{ print $1 }') \ - -- neo4j-admin dump --to=/var/lib/neo4j/$BACKUP_DATE-neo4j-dump -# copy neo4j backup to local drive -echo "Copying database to local file system ..." -kubectl --kubeconfig=${KUBECONFIG} cp \ - default/$(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-neo4j |awk '{ print $1 }'):/var/lib/neo4j/$BACKUP_DATE-neo4j-dump $BACKUP_FOLDER/neo4j-dump -# copy image data -echo "Copying public uploads to local file system ..." -kubectl --kubeconfig=${KUBECONFIG} cp \ - default/$(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-backend |awk '{ print $1 }'):/app/public/uploads $BACKUP_FOLDER/public-uploads - -# Neo4j maintenance mode off && cluster maintenance mode off -${SCRIPT_DIR}/cluster.neo4j.sh maintenance off \ No newline at end of file diff --git a/deployment/scripts/cluster.dashboard-token.sh b/deployment/scripts/cluster.dashboard-token.sh deleted file mode 100755 index 4a3a55328..000000000 --- a/deployment/scripts/cluster.dashboard-token.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# time stamp -printf "Token :\n " -date - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "!!! You must provide a CONFIGURATION via environment variable !!!" - exit 1 -fi - -printf " Cluster: %s\n" $CONFIGURATION - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -kubectl --kubeconfig=${KUBECONFIG} create token admin-user -n kubernetes-dashboard diff --git a/deployment/scripts/cluster.dashboard-tunnel.sh b/deployment/scripts/cluster.dashboard-tunnel.sh deleted file mode 100755 index 126a0efe8..000000000 --- a/deployment/scripts/cluster.dashboard-tunnel.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash - -# time stamp -printf "Tunnel started at:\n " -date - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "!!! You must provide a CONFIGURATION via environment variable !!!" - exit 1 -fi - -printf " Cluster: %s\n" $CONFIGURATION - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -kubectl --kubeconfig=${KUBECONFIG} get pods -n kubernetes-dashboard -#kubectl --kubeconfig=${KUBECONFIG} get -o json -n kubernetes-dashboard pod kubernetes-dashboard-kong-5ccb57895b-vxxmf - -# export POD_NAME=$(kubectl --kubeconfig=${KUBECONFIG} get pods -n kubernetes-dashboard -l "app.kubernetes.io/name=kubernetes-dashboard-kong,app.kubernetes.io/instance=kubernetes-dashboard" -o jsonpath="{.items[0].metadata.name}") -export POD_NAME=kubernetes-dashboard-kong-5ccb57895b-fzqk6 -# export POD_NAME=$(kubectl --kubeconfig=${KUBECONFIG} get pods -n kubernetes-dashboard -l "app.kubernetes.io/name=kubernetes-dashboard,app.kubernetes.io/instance=kubernetes-dashboard" -o jsonpath="{.items[0].metadata.name}") - -echo $POD_NAME -kubectl --kubeconfig=${KUBECONFIG} -n kubernetes-dashboard port-forward $POD_NAME 8443:8443 - -# kubectl --kubeconfig=${KUBECONFIG} -n kubernetes-dashboard create token admin-user - -# kubectl --kubeconfig=${KUBECONFIG} apply -f ${SCRIPT_DIR}/../scripts/admin-user.yml - - - diff --git a/deployment/scripts/cluster.install.sh b/deployment/scripts/cluster.install.sh deleted file mode 100755 index c0861a16b..000000000 --- a/deployment/scripts/cluster.install.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} -VALUES=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/values.yaml -DOCKERHUB_OCELOT_TAG=${DOCKERHUB_OCELOT_TAG:-"latest"} - - -## install Ingress-Nginx -helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx -helm repo update -helm install \ - ingress-nginx ingress-nginx/ingress-nginx \ - --kubeconfig=${KUBECONFIG} \ - -f ${SCRIPT_DIR}/../src/kubernetes/nginx.values.yaml - -## install Cert-Manager -helm repo add jetstack https://charts.jetstack.io -helm repo update -helm install \ - cert-manager jetstack/cert-manager \ - --kubeconfig=${KUBECONFIG} \ - --namespace cert-manager \ - --create-namespace \ - --version v1.13.2 \ - --set installCRDs=true - -## install Ocelot with helm -helm install \ - ocelot \ - --kubeconfig=${KUBECONFIG} \ - --values ${VALUES} \ - --set appVersion="${DOCKERHUB_OCELOT_TAG}" \ - ${SCRIPT_DIR}/../src/kubernetes/ \ - --timeout 10m - -## set Neo4j database indexes, constrains, and initial admin account plus run migrate up -kubectl --kubeconfig=${KUBECONFIG} \ - -n default \ - exec -it \ - $(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- \ - /bin/sh -c "yarn prod:migrate init && yarn prod:migrate up" - # /bin/sh -c "node --experimental-repl-await build/src/db/clean.js && node --experimental-repl-await build/src/db/seed.js" - -echo "!!! You must install a firewall or similar !!! (for DigitalOcean see: deployment/src/kubernetes/README.md)" diff --git a/deployment/scripts/cluster.maintenance.sh b/deployment/scripts/cluster.maintenance.sh deleted file mode 100755 index 7454c5a3c..000000000 --- a/deployment/scripts/cluster.maintenance.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -case $1 in - on) - echo "Network maintenance: on" - kubectl --kubeconfig=${KUBECONFIG} patch ingress ingress-ocelot-webapp --type merge --patch-file ${SCRIPT_DIR}/../src/kubernetes/patches/patch.ingress.maintenance.on.yaml - ;; - off) - echo "Network maintenance: off" - kubectl --kubeconfig=${KUBECONFIG} patch ingress ingress-ocelot-webapp --type merge --patch-file ${SCRIPT_DIR}/../src/kubernetes/patches/patch.ingress.maintenance.off.yaml - ;; - *) - echo -e "Run this script with first argument either 'on' or 'off'" - exit - ;; -esac diff --git a/deployment/scripts/cluster.neo4j-bash.sh b/deployment/scripts/cluster.neo4j-bash.sh deleted file mode 100755 index 8ac7ed6bc..000000000 --- a/deployment/scripts/cluster.neo4j-bash.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -# time stamp -printf "Neo4J bash :\n " -date - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "!!! You must provide a CONFIGURATION via environment variable !!!" - exit 1 -fi - -printf " Cluster: %s\n" $CONFIGURATION - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -kubectl --kubeconfig=${KUBECONFIG} -n default exec -it $(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-neo4j | awk '{ print $1 }') -- bash \ No newline at end of file diff --git a/deployment/scripts/cluster.neo4j.sh b/deployment/scripts/cluster.neo4j.sh deleted file mode 100755 index b16dd78ac..000000000 --- a/deployment/scripts/cluster.neo4j.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [[ -z "$CONFIGURATION" ]]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -case $1 in - maintenance) - case $2 in - on) - # maintenance mode on - ${SCRIPT_DIR}/cluster.maintenance.sh on - - # set Neo4j in offline mode (maintenance) - echo "Neo4j maintenance: on" - kubectl --kubeconfig=${KUBECONFIG} get deployment ocelot-neo4j -o json \ - | jq '.spec.template.spec.containers[] += {"command": ["tail", "-f", "/dev/null"]}' \ - | kubectl --kubeconfig=${KUBECONFIG} apply -f - - - # wait for the container to restart - echo "Wait 60s ..." - sleep 60 - ;; - off) - # set Neo4j in online mode - echo "Neo4j maintenance: off" - kubectl --kubeconfig=${KUBECONFIG} get deployment ocelot-neo4j -o json \ - | jq 'del(.spec.template.spec.containers[].command)' \ - | kubectl --kubeconfig=${KUBECONFIG} apply -f - - - # wait for the container to restart - echo "Wait 60s ..." - sleep 60 - - # maintenance mode off - ${SCRIPT_DIR}/cluster.maintenance.sh off - ;; - *) - echo -e "Run this script with first argument either 'off' or 'on'" - exit - ;; - esac - ;; - *) - echo -e "Run this script with first argument 'maintenance'" - exit - ;; -esac diff --git a/deployment/scripts/cluster.reseed.sh b/deployment/scripts/cluster.reseed.sh deleted file mode 100755 index 7bd44153b..000000000 --- a/deployment/scripts/cluster.reseed.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} - -# clean & seed -kubectl --kubeconfig=${KUBECONFIG} -n default exec -it $(kubectl --kubeconfig=${KUBECONFIG} -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "node --experimental-repl-await build/src/db/clean.js && node --experimental-repl-await build/src/db/seed.js" \ No newline at end of file diff --git a/deployment/scripts/cluster.upgrade.sh b/deployment/scripts/cluster.upgrade.sh deleted file mode 100755 index de0444065..000000000 --- a/deployment/scripts/cluster.upgrade.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -KUBECONFIG=${KUBECONFIG:-${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml} -VALUES=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/values.yaml -DOCKERHUB_OCELOT_TAG=${DOCKERHUB_OCELOT_TAG:-"latest"} - -# upgrade with helm -helm --kubeconfig=${KUBECONFIG} upgrade ocelot \ - --values ${VALUES} \ - --set appVersion="${DOCKERHUB_OCELOT_TAG}" \ - ${SCRIPT_DIR}/../src/kubernetes/ \ - --timeout 10m \ No newline at end of file diff --git a/deployment/scripts/clusters.backup-multiple-servers.sh b/deployment/scripts/clusters.backup-multiple-servers.sh deleted file mode 100755 index dfab6b139..000000000 --- a/deployment/scripts/clusters.backup-multiple-servers.sh +++ /dev/null @@ -1,91 +0,0 @@ -#!/bin/bash - -# time stamp -printf "\n\nMultiple backups started at:\n " -date - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# save old CONFIGURATION for later reset -export SAVE_CONFIGURATION=$CONFIGURATION - -# export all variables in "../.env" -set -a -source ${SCRIPT_DIR}/../.env -set +a - -# check BACKUP_CONFIGURATIONS -if [[ -z "$BACKUP_CONFIGURATIONS" ]]; then - #%! echo "You must provide a BACKUP_CONFIGURATIONS via environment variable" - printf "!!! You must provide a BACKUP_CONFIGURATIONS via environment variable !!!\n" - exit 1 -fi - -# check BACKUP_SAVED_BACKUPS_NUMBER -if [[ -z ${BACKUP_SAVED_BACKUPS_NUMBER} ]]; then - #%! echo "You must provide a BACKUP_SAVED_BACKUPS_NUMBER via environment variable" - printf "!!! You must provide a BACKUP_SAVED_BACKUPS_NUMBER via environment variable !!!\n" - exit 1 -fi - -# convert configurations to array -IFS=' ' read -a CONFIGURATIONS_ARRAY <<< "$BACKUP_CONFIGURATIONS" - -# display the clusters -printf "Backup the clusters:\n" -for i in "${CONFIGURATIONS_ARRAY[@]}" -do - echo " $i" -done - -# deleting backups? -if (( BACKUP_SAVED_BACKUPS_NUMBER >= 1 )); then - printf "Keep the last %d backups for all networks.\n" $BACKUP_SAVED_BACKUPS_NUMBER -else - echo "!!! ATTENTION: No backups are deleted !!!" -fi - -echo "Cancel by ^C. You have 15 seconds" -# wait for the admin to react -sleep 15 - -printf "\n" - -for i in "${CONFIGURATIONS_ARRAY[@]}" -do - export CONFIGURATION=$i - # individual cluster backup - ${SCRIPT_DIR}/cluster.backup.sh - - # deleting backups? - if (( BACKUP_SAVED_BACKUPS_NUMBER >= 1 )); then - # delete all oldest backups, but leave the last BACKUP_SAVED_BACKUPS_NUMBER - - keep=$BACKUP_SAVED_BACKUPS_NUMBER - path="$SCRIPT_DIR/../configurations/$CONFIGURATION/backup/" - - cd $path - - printf "In\n '$path'\n remove:\n" - while [ `ls -1 | wc -l` -gt $keep ]; do - oldest=`ls -c1 | sort -n | head -1` - printf " %s\n" $oldest - rm -rf $oldest - done - - printf "Keep the last %d backups:\n" $BACKUP_SAVED_BACKUPS_NUMBER - ls -c1 | sort -n | awk '{print " " $0}' - - cd $SCRIPT_DIR - else - echo "!!! ATTENTION: No backups are deleted !!!" - fi - - printf "\n" -done - -# reset CONFIGURATION to old -export CONFIGURATION=$SAVE_CONFIGURATION -echo "Reset to CONFIGURATION=$CONFIGURATION" diff --git a/deployment/scripts/secret.generate.sh b/deployment/scripts/secret.generate.sh deleted file mode 100755 index cb788a7bd..000000000 --- a/deployment/scripts/secret.generate.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# generate a secret and store it in the SECRET file. -# Note that this overwrites the existing file - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -SECRET_FILE=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/SECRET - -openssl rand -base64 32 > ${SECRET_FILE} \ No newline at end of file diff --git a/deployment/scripts/secrets.decrypt.sh b/deployment/scripts/secrets.decrypt.sh deleted file mode 100755 index a7a1328b1..000000000 --- a/deployment/scripts/secrets.decrypt.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# decrypt secrets in the selected configuration -# Note that existing decrypted files will be overwritten - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -SECRET=${SECRET} -SECRET_FILE=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/SECRET -FILES=(\ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/.env" \ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml" \ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/values.yaml" \ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/dns.values.yaml" \ - ) - -# Load SECRET from file if it is not set explicitly -if [ -z ${SECRET} ] && [ -f "${SECRET_FILE}" ]; then - SECRET=$(<${SECRET_FILE}) -fi - -# exit when there is no SECRET set -if [ -z ${SECRET} ]; then - echo "No SECRET provided and no SECRET-File found." - exit 1 -fi - -# decrypt -for file in "${FILES[@]}" -do - if [ -f "${file}.enc" ]; then - #gpg --symmetric --batch --passphrase="${SECRET}" --cipher-algo AES256 --output ${file}.enc ${file} - gpg --quiet --batch --yes --decrypt --passphrase="${SECRET}" --output ${file} ${file}.enc - echo "Decrypted ${file}" - fi -done - -echo "DONE" -# gpg --quiet --batch --yes --decrypt --passphrase="${SECRET}" \ -# --output $HOME/secrets/my_secret.json my_secret.json.gpg diff --git a/deployment/scripts/secrets.encrypt.sh b/deployment/scripts/secrets.encrypt.sh deleted file mode 100755 index 57be1c16e..000000000 --- a/deployment/scripts/secrets.encrypt.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# encrypt secrets in the selected configuration -# Note that existing encrypted files will be overwritten - -# base setup -SCRIPT_PATH=$(realpath $0) -SCRIPT_DIR=$(dirname $SCRIPT_PATH) - -# check CONFIGURATION -if [ -z ${CONFIGURATION} ]; then - echo "You must provide a `CONFIGURATION` via environment variable" - exit 1 -fi -echo "Using CONFIGURATION=${CONFIGURATION}" - -# configuration -SECRET=${SECRET} -SECRET_FILE=${SCRIPT_DIR}/../configurations/${CONFIGURATION}/SECRET -FILES=(\ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/.env" \ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubeconfig.yaml" \ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/values.yaml" \ - "${SCRIPT_DIR}/../configurations/${CONFIGURATION}/kubernetes/dns.values.yaml" \ - ) - -# Load SECRET from file if it is not set explicitly -if [ -z ${SECRET} ] && [ -f "${SECRET_FILE}" ]; then - SECRET=$(<${SECRET_FILE}) -fi - -# exit when there is no SECRET set -if [ -z ${SECRET} ]; then - echo "No SECRET provided and no SECRET-File found." - exit 1 -fi - -# encrypt -for file in "${FILES[@]}" -do - if [ -f "${file}" ]; then - gpg --symmetric --batch --yes --passphrase="${SECRET}" --cipher-algo AES256 --output ${file}.enc ${file} - echo "Encrypted ${file}" - fi -done - -echo "DONE" diff --git a/deployment/src/docker/backend.Dockerfile b/deployment/src/docker/backend.Dockerfile deleted file mode 100644 index 76a091da2..000000000 --- a/deployment/src/docker/backend.Dockerfile +++ /dev/null @@ -1,46 +0,0 @@ -ARG APP_IMAGE=ocelotsocialnetwork/backend -ARG APP_IMAGE_TAG_BASE=latest-base -ARG APP_IMAGE_TAG_CODE=latest-code -ARG APP_IMAGE_BASE=${APP_IMAGE}:${APP_IMAGE_TAG_BASE} -ARG APP_IMAGE_CODE=${APP_IMAGE}:${APP_IMAGE_TAG_CODE} - -################################################################################## -# CODE (branded) ################################################################# -################################################################################## -FROM $APP_IMAGE_CODE AS code - -ARG CONFIGURATION=example - -# copy public constants and email templates into the Docker image to brand it -COPY configurations/${CONFIGURATION}/branding/constants/emails.ts src/config/ -COPY configurations/${CONFIGURATION}/branding/constants/logos.ts src/config/ -COPY configurations/${CONFIGURATION}/branding/constants/metadata.ts src/config/ -COPY configurations/${CONFIGURATION}/branding/email/ src/middleware/helpers/email/ - -################################################################################## -# BUILD ########################################################################## -################################################################################## -FROM code AS build - -# yarn install -RUN yarn install --production=false --frozen-lockfile --non-interactive -# yarn build -RUN yarn run build - -################################################################################## -# BRANDED (Does contain only "binary"- and static-files to reduce image size) #### -################################################################################## -FROM $APP_IMAGE_BASE AS branded - -# TODO - do all copying with one COPY command to have one layer -# Copy "binary"-files from build image -COPY --from=build ${DOCKER_WORKDIR}/build ./build -COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules -# TODO - externalize the uploads so we can copy the whole folder -COPY --from=build ${DOCKER_WORKDIR}/public/img/ ./public/img/ -COPY --from=build ${DOCKER_WORKDIR}/public/providers.json ./build/public/providers.json -# Copy package.json for script definitions (lock file should not be needed) -COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json - -# Run command -CMD /bin/sh -c "yarn run start" diff --git a/deployment/src/docker/maintenance.Dockerfile b/deployment/src/docker/maintenance.Dockerfile deleted file mode 100644 index 8eefcc067..000000000 --- a/deployment/src/docker/maintenance.Dockerfile +++ /dev/null @@ -1,44 +0,0 @@ -ARG APP_IMAGE=ocelotsocialnetwork/maintenance -ARG APP_IMAGE_TAG_BASE=latest-base -ARG APP_IMAGE_TAG_CODE=latest-code -ARG APP_IMAGE_BASE=${APP_IMAGE}:${APP_IMAGE_TAG_BASE} -ARG APP_IMAGE_CODE=${APP_IMAGE}:${APP_IMAGE_TAG_CODE} - -################################################################################## -# CODE (branded) ################################################################# -################################################################################## -FROM $APP_IMAGE_CODE AS code - -ARG CONFIGURATION=example - -# copy public constants into the Docker image to brand it -COPY configurations/${CONFIGURATION}/branding/static/ static/ -COPY configurations/${CONFIGURATION}/branding/constants/ constants/ -RUN /bin/sh -c 'cd constants && for f in *.ts; do mv -- "$f" "${f%.ts}.js"; done' - -# locales -COPY configurations/${CONFIGURATION}/branding/locales/*.json locales/tmp/ -COPY src/tools/ tools/ -RUN apk add --no-cache bash jq -RUN tools/merge-locales.sh - -################################################################################## -# BUILD ########################################################################## -################################################################################## -FROM code AS build - -# yarn install -## unnicely done in $APP_IMAGE_CODE at the moment, see main repo -# RUN yarn install --production=false --frozen-lockfile --non-interactive -# yarn generate -RUN yarn run generate - -################################################################################## -# BRANDED ### TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO #### -################################################################################## -# FROM $APP_IMAGE_BASE AS branded -FROM nginx:alpine AS branded - -COPY --from=build ./app/dist/ /usr/share/nginx/html/ -RUN rm /etc/nginx/conf.d/default.conf -COPY --from=code ./app/maintenance/nginx/custom.conf /etc/nginx/conf.d/ diff --git a/deployment/src/docker/webapp.Dockerfile b/deployment/src/docker/webapp.Dockerfile deleted file mode 100644 index 54f79a56f..000000000 --- a/deployment/src/docker/webapp.Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -ARG APP_IMAGE=ocelotsocialnetwork/webapp -ARG APP_IMAGE_TAG_BASE=latest-base -ARG APP_IMAGE_TAG_CODE=latest-code -ARG APP_IMAGE_BASE=${APP_IMAGE}:${APP_IMAGE_TAG_BASE} -ARG APP_IMAGE_CODE=${APP_IMAGE}:${APP_IMAGE_TAG_CODE} - -################################################################################## -# CODE (branded) ################################################################# -################################################################################## -FROM $APP_IMAGE_CODE AS code - -ARG CONFIGURATION=example - -# copy public constants into the Docker image to brand it -COPY configurations/${CONFIGURATION}/branding/static/ static/ -COPY configurations/${CONFIGURATION}/branding/constants/ constants/ -RUN /bin/sh -c 'cd constants && for f in *.ts; do mv -- "$f" "${f%.ts}.js"; done' -COPY configurations/${CONFIGURATION}/branding/locales/html/ locales/html/ -COPY configurations/${CONFIGURATION}/branding/assets/styles/imports/ assets/styles/imports/ -COPY configurations/${CONFIGURATION}/branding/assets/fonts/ assets/fonts/ - -# locales -COPY configurations/${CONFIGURATION}/branding/locales/*.json locales/tmp/ -COPY src/tools/ tools/ -RUN apk add --no-cache bash jq -RUN tools/merge-locales.sh - -################################################################################## -# BUILD ########################################################################## -################################################################################## -FROM code AS build - -# yarn install -RUN yarn install --production=false --frozen-lockfile --non-interactive -# yarn build -RUN yarn run build - -################################################################################## -# BRANDED (Does contain only "binary"- and static-files to reduce image size) #### -################################################################################## -FROM $APP_IMAGE_BASE AS branded - -# TODO - do all copying with one COPY command to have one layer -# Copy "binary"-files from build image -COPY --from=build ${DOCKER_WORKDIR}/.nuxt ./.nuxt -COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules -COPY --from=build ${DOCKER_WORKDIR}/nuxt.config.js ./nuxt.config.js -# Copy static files -# TODO - this seems not be needed anymore for the new rebranding -# TODO - this should be one Folder containign all stuff needed to be copied -COPY --from=build ${DOCKER_WORKDIR}/config/ ./config/ -COPY --from=build ${DOCKER_WORKDIR}/constants ./constants -COPY --from=build ${DOCKER_WORKDIR}/static ./static -COPY --from=build ${DOCKER_WORKDIR}/locales ./locales -COPY --from=build ${DOCKER_WORKDIR}/assets/styles/imports ./assets/styles/imports -COPY --from=build ${DOCKER_WORKDIR}/assets/fonts ./assets/fonts -# Copy package.json for script definitions (lock file should not be needed) -COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json - -# Run command -CMD /bin/sh -c "yarn run start" diff --git a/deployment/src/kubernetes/Backup.md b/deployment/src/kubernetes/Backup.md deleted file mode 100644 index 5e4c55ddb..000000000 --- a/deployment/src/kubernetes/Backup.md +++ /dev/null @@ -1,308 +0,0 @@ -# Kubernetes Backup Of Ocelot.Social - -One of the most important tasks in managing a running [ocelot.social](https://github.com/Ocelot-Social-Community/Ocelot-Social) network is backing up the data, e.g. the Neo4j database and the stored image files. - -## Manual Offline Backup - -To prepare, [kubectl](https://kubernetes.io/docs/tasks/tools/) must be installed and ready to use so that you have access to Kubernetes on your server. - -Check if the correct context is used by running the following commands: - -```bash -# check context and set the correct one -$ kubectl config get-contexts -# if the wrong context is chosen use it -$ kubectl config use-context -# if you like check additionally if all pods are running well -$ kubectl -n default get pods -o wide -``` - -The very first step is to put the website into **maintenance mode**. - -### Set Maintenance Mode - -There are two ways to put the network into maintenance mode: - -- via Kubernetes Dashboard -- via `kubectl` - -#### Maintenance Mode Via Kubernetes Dashboard - -In the Kubernetes Dashboard, you can select `Ingresses` from the left side menu under `Service`. - -After that, in the list that appears, you will find the entry `ingress-ocelot-webapp`, which has three dots on the right, where you can click to edit the entry. - -You can scroll to the end of the YAML file, where you will find one or more `host` entries under `rules`, one for each domain of the network. - -In all entries, change the value of the `serviceName` entry from ***ocelot-webapp*** to `ocelot-maintenance` and the value of the `servicePort` entry from ***3000*** to `80`. - -First, check if your website is still online. -After you click `Update`, the new settings will be applied and you will find your website in maintenance mode. - -#### Maintenance Mode Via `kubectl` - -To put the network into maintenance mode, run the following commands in the terminal: - -```bash -# list ingresses -$ kubectl get ingress -n default -# edit ingress -$ kubectl -n default edit ingress ingress-ocelot-webapp -``` - -Change the content of the YAML file for all domains to: - -```yaml - spec: - rules: - - host: network-domain.social - http: - paths: - - backend: - # serviceName: ocelot-webapp - # servicePort: 3000 - serviceName: ocelot-maintenance - servicePort: 80 -``` - -First, check if your website is still online. -After you save the file, the new settings will be applied and you will find your website in maintenance mode. - -### Neo4j Database Offline Backup - -Before we can back up the database, we need to put it into **sleep mode**. - -#### Set Neo4j To Sleep Mode - -Again there are two ways to put the network into sleep mode: - -- via Kubernetes Dashboard -- via `kubectl` - -##### Sleep Mode Via Kubernetes Dashboard - -In the Kubernetes Dashboard, you can select `Deployments` from the left side menu under `Workloads`. - -After that, in the list that appears, you will find the entry `ocelot-neo4j`, which has three dots on the right, where you can click to edit the entry. - -Scroll to the end of the YAML file where you will find the `spec.template.spec.containers` entry. Here you can insert the `command` entry directly after `imagePullPolicy` in a new line. - -```yaml - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - imagePullPolicy: Always - command: ["tail", "-f", "/dev/null"] -``` - -After clicking `Update`, the new settings will be applied and you should check in the `Pods` menu item on the left side if the `ocelot-neo4j-` pod restarts. - -##### Sleep Mode Via `kubectl` - -To put Neo4j into sleep mode, run the following commands in the terminal: - -```bash -# list deployments -$ kubectl get deployments -n default -# edit deployment -$ kubectl -n default edit deployment ocelot-neo4j -``` - -Scroll to the `spec.template.spec.containers` entry. Here you can insert the `command` entry directly after `imagePullPolicy` in a new line. - -```yaml - image: /neo4j-community-branded:latest - imagePullPolicy: Always - command: ["tail", "-f", "/dev/null"] -``` - -After pressing enter, the new settings will be applied and you should check if the `ocelot-neo4j-` pod restarts. -Use command: - -```bash -# check if the old pod restarts -$ kubectl -n default get pods -o wide -``` - -#### Generate Offline Backup - -The offline backup is generated via `kubectl`: - -```bash -# check for the Neo4j pod -$ kubectl -n default get pods -o wide - -# ls: see wish backup dumps are already there -$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-neo4j | awk '{ print $1 }') -- ls - -# bash: enter bash of Neo4j -$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-neo4j | awk '{ print $1 }') -- bash -# generate Dump -neo4j% neo4j-admin dump --to=/var/lib/neo4j/$(date +%F)-neo4j-dump -# exit bash -neo4j% exit - -# ls: see if the new backup dump is there -$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-neo4j | awk '{ print $1 }') -- ls -``` - -If you need a specific database name, add the option `--database=` to the command `neo4j-admin dump`. -To find out the default database name, see the [Neo4j readme](https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/neo4j/README.md). - -Lets copy the dump backup - -```bash -# copy dump onto backup volume direct -$ kubectl cp default/$(kubectl -n default get pods | grep ocelot-neo4j |awk '{ print $1 }'):/var/lib/neo4j/$(date +%F)-neo4j-dump /Volumes//$(date +%F)-neo4j-dump - -``` - -#### Remove Sleep Mode From Neo4j - -Again there are two ways to put the network into working mode: - -- via Kubernetes Dashboard -- via `kubectl` - -##### Remove Sleep Mode Via Kubernetes Dashboard - -In the Kubernetes Dashboard, you can select `Deployments` from the left side menu under `Workloads`. - -After that, in the list that appears, you will find the entry `ocelot-neo4j`, which has three dots on the right, where you can click to edit the entry. - -Scroll to the `spec.template.spec.containers.command` entry and remove the whole `command` entry like: - -```yaml - containers: - - name: container-ocelot-neo4j - image: 'senderfm/neo4j-community-branded:latest' - command: - - tail - - '-f' - - /dev/null - ports: - - containerPort: 7687 - protocol: TCP -``` - -And get: - -```yaml - containers: - - name: container-ocelot-neo4j - image: 'senderfm/neo4j-community-branded:latest' - ports: - - containerPort: 7687 - protocol: TCP -``` - -After clicking `Update`, the new settings will be applied and you should check in the `Pods` menu item on the left side if the `ocelot-neo4j-` pod restarts. - -##### Remove Sleep Mode Via `kubectl` - -To put Neo4j into working mode, run the following commands in the terminal: - -```bash -# list deployments -$ kubectl get deployments -n default -# edit deployment -$ kubectl -n default edit deployment ocelot-neo4j -``` - -Scroll to the `spec.template.spec.containers.command` entry and remove the whole `command` entry like: - -```yaml - spec: - containers: - - command: - - tail - - -f - - /dev/null - envFrom: - - configMapRef: - name: configmap-ocelot-neo4j -``` - -And get: - -```yaml - spec: - containers: - - envFrom: - - configMapRef: - name: configmap-ocelot-neo4j -``` - -After pressing enter, the new settings will be applied and you should check if the `ocelot-neo4j-` pod restarts. -Use command: - -```bash -# check if the old pod restarts -$ kubectl -n default get pods -o wide -``` - -### Backend Backup - -To back up the images from the backend volume, run commands: - -```bash -# ls: backend/public/uploads -$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- ls public/uploads - -# copy all images from upload to backup volume direct -$ kubectl cp default/$(kubectl -n default get pods | grep ocelot-backend |awk '{ print $1 }'):/app/public/uploads /Volumes//$(date +%F)-public-uploads -``` - -### Remove Maintenance Mode - -There are two ways to put the network into working mode: - -- via Kubernetes Dashboard -- via `kubectl` - -#### Remove Maintenance Mode Via Kubernetes Dashboard - -In the Kubernetes Dashboard, you can select `Ingresses` from the left side menu under `Service`. - -After that, in the list that appears, you will find the entry `ingress-ocelot-webapp`, which has three dots on the right, where you can click to edit the entry. - -You can scroll to the end of the YAML file, where you will find one or more `host` entries under `rules`, one for each domain of the network. - -In all entries, change the value of the `serviceName` entry from ***ocelot-maintenance*** to `ocelot-webapp` and the value of the `servicePort` entry from ***80*** to `3000`. - -First, check if your website is still in maintenance mode. -After you click `Update`, the new settings will be applied and you will find your website online again. - -#### Remove Maintenance Mode Via `kubectl` - -To put the network into working mode, run the following commands in the terminal: - -```bash -# list ingresses -$ kubectl get ingress -n default -# edit ingress -$ kubectl -n default edit ingress ingress-ocelot-webapp -``` - -Change the content of the YAML file for all domains to: - -```yaml - spec: - rules: - - host: network-domain.social - http: - paths: - - backend: - serviceName: ocelot-webapp - servicePort: 3000 - # serviceName: ocelot-maintenance - # servicePort: 80 -``` - -First, check if your website is still in maintenance mode. -After you save the file, the new settings will be applied and you will find your website online again. - -XXX - -```bash -# Dump: Create a Backup in Kubernetes: https://docs.human-connection.org/human-connection/deployment/volumes/neo4j-offline-backup#create-a-backup-in-kubernetes -``` diff --git a/deployment/src/kubernetes/Chart.yaml b/deployment/src/kubernetes/Chart.yaml deleted file mode 100644 index 5b953e3e7..000000000 --- a/deployment/src/kubernetes/Chart.yaml +++ /dev/null @@ -1,39 +0,0 @@ -type: application -apiVersion: v2 -name: ocelot-social -version: "1.0.0" -# The appVersion defines which docker image is pulled. -# Having it set to latest will pull the latest build on dockerhub. -# You are free to define a specific version here tho. -# e.g. appVersion: "latest" or "1.0.2-3-ocelot.social1.0.2-79" -# Be aware that this requires all your apps to have the same docker image version available. -appVersion: "latest" -description: The Helm chart for ocelot.social -home: https://ocelot.social -sources: - - https://github.com/Ocelot-Social-Community/ - - https://github.com/Ocelot-Social-Community/Ocelot-Social - - https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding -maintainers: - - name: Ulf Gebhardt - email: ulf.gebhardt@webcraft-media.de - url: https://www.webcraft-media.de/#!ulf_gebhardt -icon: https://github.com/Ocelot-Social-Community/Ocelot-Social/raw/master/webapp/static/img/custom/welcome.svg -deprecated: false - -# Unused Fields -#dependencies: # A list of the chart requirements (optional) -# - name: ingress-nginx -# version: v1.10.0 -# repository: https://kubernetes.github.io/ingress-nginx -# condition: (optional) A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) -# tags: # (optional) -# - Tags can be used to group charts for enabling/disabling together -# import-values: # (optional) -# - ImportValues holds the mapping of source values to parent key to be imported. Each item can be a string or pair of child/parent sublist items. -# alias: (optional) Alias to be used for the chart. Useful when you have to add the same chart multiple times -#kubeVersion: A SemVer range of compatible Kubernetes versions (optional) -#keywords: -# - A list of keywords about this project (optional) -#annotations: -# example: A list of annotations keyed by name (optional). \ No newline at end of file diff --git a/deployment/src/kubernetes/DigitalOcean.md b/deployment/src/kubernetes/DigitalOcean.md deleted file mode 100644 index 251a6d341..000000000 --- a/deployment/src/kubernetes/DigitalOcean.md +++ /dev/null @@ -1,145 +0,0 @@ -# DigitalOcean - -If you want to set up a [Kubernetes](https://kubernetes.io) cluster on [DigitalOcean](https://www.digitalocean.com), follow this guide. - -## Create Account - -Create an account with DigitalOcean. - -## Add Project - -On the left side you will see a menu. Click on `New Project`. Enter a name and click `Create Project`. -Skip moving resources, probably. - -## Create Kubernetes Cluster - -On the right top you find the button `Create`. Click on it and choose `Kubernetes - Create Kubernetes Cluster`. - -- use the latest Kubernetes version -- choose your datacenter region -- name your node pool: e.g. `pool-` -- `2 Basic nodes` with `2.5 GB RAM (total of 4 GB)`, `2 shared CPUs`, and `80 GB Disk` each is optimal for the beginning -- set your cluster name: e.g. `cluster-` -- select your project -- no tags necessary - -## Getting Started - -After your cluster is set up – see progress bar above – click on `Getting started`. Please install the following management tools: - -- [kubectl v1.24.1](https://kubernetes.io/docs/tasks/tools/) -- [doctl v1.78.0](https://github.com/digitalocean/doctl) - -Install the tools as described on the tab or see the links here. - -After the installation, click on `Continue`. - -### Download Configuration File - -Follow the steps to download the configuration file. - -You can skip this step if necessary, as you can download the file later. You can then do this by clicking on `Kubernetes` in the left menu. In the menu to the right of the cluster name in the cluster list, click on `More` and select `Download Config`. - -### Patch & Minor Version Upgrades - -Skip `Patch & Minor Version Upgrades` for now. - -### Install 1-Click Apps - -You don't need a 1-click app. Our helmet script will install the required NGINXs. -Therefore, skip this step as well. - -For a 1-click Kubernetes Dashboard or alternatives, follow the next steps. - -## Install Kubernetes Dashboard - -We recommend installing a Kubernetes Dashboard, as DigitalOcean no longer offers a pre-installed dashboard. - -- 1-click-deployment of [Kubernetes Dashboard on DigitalOcean marketplace](https://marketplace.digitalocean.com/apps/kubernetes-dashboard) - -There you will also find a section entitled `Getting Started`, which describes how you can log in from your local computer. - -Very short description: - -### In your DigitalOcean Account - -For authentication, download the current cluster configuration file from DigitalOcean. - -### In Terminal - -Set the context of the cluster by command: - -```bash -kubectl config use-context -``` - -We seem to have two instances in our DigitalOcean cluster how we need to log into the Kubernetes Dashboard. -It looks like it depends on the Kubernetes Dashboard version, but we are not absolutely sure. - -#### Login with `kubeconfig` File - -Port-forward the Kubernetes Dashboard to your local machine: - -```bash -# save pod name -$ export POD_NAME=$(kubectl get pods -n kubernetes-dashboard -l "app.kubernetes.io/name=kubernetes-dashboard,app.kubernetes.io/instance=kubernetes-dashboard" -o jsonpath="{.items[0].metadata.name}") -# forward port -$ kubectl -n kubernetes-dashboard port-forward $POD_NAME 8443:8443 -``` - -Access the URL in your local web browser at `https://127.0.0.1:8443/`, and log in using your Kubernetes cluster credentials – downloaded config file. -You may encounter a certificate warning, so make sure to override it. - -#### Login with Admin Token - -Port-forward the Kubernetes Dashboard to your local machine: - -```bash -# create your access token -kubectl -n kubernetes-dashboard create token admin-user -# forward port -kubectl -n kubernetes-dashboard port-forward svc/kubernetes-dashboard-kong-proxy 8443:443 -``` - -Access the URL in your local web browser at `https://127.0.0.1:8443/`, and log in using your access token. -You may encounter a certificate warning, so make sure to override it. - -## Alternatives to Kubernetes Dashboard - -DigitalOcean has a website about Kubernetes Dashboard and alternatives: - -- - -## DNS Configuration - -There are the following two ways to set up the DNS. - -### Manage DNS With A Different Domain Provider - -If you have registered your domain or subdomain with another domain provider, add an `A` record there with one of the IP addresses from one of the cluster droplets in the DNS. - -To find the correct IP address to set in the DNS `A` record, click `Droplets` in the left main menu. -A list of all your droplets will be displayed. -Take one of the IPs of perhaps two or more droplets in your cluster from the list and enter it into the `A` record. - -### Manage DNS With DigitalOcean - -***TODO:** How to configure the DigitalOcean DNS management service …* - -To understand what makes sense to do when managing your DNS with DigitalOcean, you need to know how DNS works: - -DNS means `Domain Name System`. It resolves domains like `example.com` into an IP like `123.123.123.123`. -DigitalOcean is not a domain registrar, but provides a DNS management service. If you use DigitalOcean's DNS management service, you can configure [your cluster](./README.md#dns) to always resolve the domain to the correct IP and automatically update it for that. -The IPs of the DigitalOcean machines are not necessarily stable, so the cluster's DNS service will update the DNS records managed by DigitalOcean to the new IP as needed. - -***CAUTION:** If you are using an external DNS, you currently have to do this manually, which can cause downtime.* - -## Deploy - -Yeah, you're done here. Back to [Deployment with Helm for Kubernetes](./README.md). - -## Backups On DigitalOcean - -You can and should do [backups](./Backup.md) with Kubernetes for sure. - -Additional to backup and copying the Neo4j database dump and the backend images you can do a volume snapshot on DigitalOcean at the moment you have the database in sleep mode. diff --git a/deployment/src/kubernetes/README.md b/deployment/src/kubernetes/README.md deleted file mode 100644 index 9b1c6fbe2..000000000 --- a/deployment/src/kubernetes/README.md +++ /dev/null @@ -1,350 +0,0 @@ -# Kubernetes Helm Installation Of Ocelot.Social - -Deploying [ocelot.social](https://github.com/Ocelot-Social-Community/Ocelot-Social) with [Helm](https://helm.sh) for [Kubernetes](https://kubernetes.io) is very straight forward. All you have to do is to change certain parameters, like domain names and API keys, then you just install our provided Helm chart to your cluster. - -## Kubernetes Cloud Hosting - -There are various ways to set up your own or a managed Kubernetes cluster. We will extend the following lists over time. -Please contact us if you are interested in options not listed below. - -Managed Kubernetes: - -- [DigitalOcean](./DigitalOcean.md) - -## Configuration - -You can customize the network server with your configuration by duplicate the `values.template.yaml` to a new `values.yaml` file and change it to your need. All included variables will be available as environment variables in your deployed kubernetes pods. - -Besides the `values.template.yaml` file we provide a `nginx.values.template.yaml` and `dns.values.template.yaml` for a similar procedure. The new `nginx.values.yaml` is the configuration for the ingress-nginx Helm chart, while the `dns.values.yaml` file is for automatically updating the dns values on DigitalOcean and therefore optional. - -## Installation - -Due to the many limitations of Helm you still have to do several manual steps. -Those occur before you run the actual *ocelot.social* Helm chart. -Obviously it is expected of you to have `helm` and `kubectl` installed. -For the cert-manager you may need `cmctl`, see below. -For DigitalOcean you may also need `doctl`. - -Install: - -- [kubectl v1.24.1](https://kubernetes.io/docs/tasks/tools/) -- [doctl v1.78.0](https://docs.digitalocean.com/reference/doctl/how-to/install/) -- [cmctl v1.8.2](https://cert-manager.io/docs/usage/cmctl/#installation) -- [helm v3.9.0](https://helm.sh/docs/intro/install/) - - -### Cert Manager (https) - -Please refer to [cert-manager.io docs](https://cert-manager.io/docs/installation/) for more details. - -***ATTENTION:*** *Be with the Terminal in your repository in the folder of this README.* - -We have three ways to install the cert-manager, purely via `kubectl`, via `cmctl`, or with `helm`. - -We recommend using `helm` because then we do not mix the installation methods. -Please have a look here: - -- [Installing with Helm](https://cert-manager.io/docs/installation/helm/#installing-with-helm) - -Our Helm installation is optimized for cert-manager version `v1.13.1` and `kubectl` version `"v1.28.2`. - -Please search here for cert-manager versions that are compatible with your `kubectl` version on the cluster and on the client: [cert-manager Supported Releases](https://cert-manager.io/docs/installation/supported-releases/#supported-releases). - -***ATTENTION:*** *When uninstalling cert-manager, be sure to use the same method as for installation! Otherwise, we could end up in a broken state, see [Uninstall](https://cert-manager.io/docs/installation/kubectl/#uninstalling).* - - - -### Ingress-Nginx - -#### 1. Add Helm repository for `ingress-nginx` and update - -```bash -$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx -$ helm repo update -``` - -#### 2. Install ingress-nginx - -```bash -# in configuration/ - -# kubeconfig.yaml set globaly -helm install ingress-nginx ingress-nginx/ingress-nginx -f ../../src/kubernetes/nginx.values.yaml - -# or kubeconfig.yaml in your repo, then adjust -helm install \ - ingress-nginx ingress-nginx/ingress-nginx -f ../../src/kubernetes/nginx.values.yaml \ - --kubeconfig ./kubeconfig.yaml -``` - -### DigitalOcean Firewall - -This is only necessary if you run DigitalOcean without load balancer ([see here for more info](https://stackoverflow.com/questions/54119399/expose-port-80-on-digital-oceans-managed-kubernetes-without-a-load-balancer/55968709)) . - -#### 1. Authenticate towards DO with your local `doctl` - -You will need a DO token for that. - -```bash -# without doctl context -$ doctl auth init -# with doctl new context to be filled in -$ doctl auth init --context -``` - -You will need an API token, which you can generate in the control panel at . - -#### 2. Generate DO firewall - - Get the `CLUSTER_UUID` value from the dashboard or from the ID column via `doctl kubernetes cluster list`: - -```bash -# need to apply access token by `doctl auth init` before -$ doctl kubernetes cluster list -``` - -Fill in the `CLUSTER_UUID` and `your-domain`. The latter with hyphens `-` instead of dots `.`: - -```bash -# without doctl context -$ doctl compute firewall create \ ---inbound-rules="protocol:tcp,ports:80,address:0.0.0.0/0,address:::/0 protocol:tcp,ports:443,address:0.0.0.0/0,address:::/0" \ ---tag-names=k8s: \ ---name=-http-https -# with doctl context to be filled in -$ doctl compute firewall create \ ---inbound-rules="protocol:tcp,ports:80,address:0.0.0.0/0,address:::/0 protocol:tcp,ports:443,address:0.0.0.0/0,address:::/0" \ ---tag-names=k8s: \ ---name=-http-https --context -``` - -To get informations about your success use this command. (Fill in the `ID` you got at creation.): - -```bash -# without doctl context -$ doctl compute firewall get -# with doctl context to be filled in -$ doctl compute firewall get --context -``` - -### DNS - -***ATTENTION:** This seems not to work at all so we leave it away at the moment* - -***TODO:** I thought this is necessary if we use the DigitalOcean DNS management service? See [Manage DNS With DigitalOcean](./DigitalOcean.md#manage-dns-with-digitalocean)* - -This chart is only necessary (recommended is more precise) if you run DigitalOcean without load balancer. -You need to generate an access token with read + write for the `dns.values.yaml` at and fill it in. - -#### 1. Add Helm repository for `binami` and update - -```bash -$ helm repo add bitnami https://charts.bitnami.com/bitnami -$ helm repo update -``` - -#### 2. Install DNS - -```bash -# !!! untested for now for new deployment structure !!! - -# kubeconfig.yaml set globaly -$ helm install dns bitnami/external-dns -f dns.values.yaml -# or kubeconfig.yaml in your repo, then adjust -$ helm --kubeconfig=/../kubeconfig.yaml install dns bitnami/external-dns -f dns.values.yaml -``` - -### Ocelot.Social - -***Attention:** Before installing your own ocelot.social network, you need to create a DockerHub (account and) organization, put its name in the `package.json` file, and push your deployment and rebranding code to GitHub so that GitHub Actions can push your Docker images to DockerHub. This is because Kubernetes will pull these images to create PODs from them.* - -All commands for ocelot need to be executed in the kubernetes folder. Therefore `cd deployment/kubernetes/` is expected to be run before every command. Furthermore the given commands will install ocelot into the default namespace. This can be modified to by attaching `--namespace not.default`. - -#### Install - -Only run once for the first time of installation: - -```bash -# in configuration/ - -# kubeconfig.yaml set globaly -helm install ocelot \ - --values ./kubernetes/values.yaml \ - --set appVersion="latest" \ - ../../src/kubernetes/ \ - --timeout 10m - -# or kubeconfig.yaml in your repo, then adjust -helm install ocelot \ - --kubeconfig ./kubeconfig.yaml \ - --values ./kubernetes/values.yaml \ - --set appVersion="latest" \ - ../../src/kubernetes/ \ - --timeout 10m -``` - -#### Upgrade & Update - -Run for all upgrades and updates: - -```bash -# !!! untested for now for new deployment structure !!! - -# in configuration/ - -# kubeconfig.yaml set globaly -helm upgrade ocelot \ - --values ./kubernetes/values.yaml \ - --set appVersion="latest" \ - ../../src/kubernetes/ \ - --timeout 10m - -# or kubeconfig.yaml in your repo, then adjust -helm upgrade ocelot \ - --kubeconfig ./kubeconfig.yaml \ - --values ./kubernetes/values.yaml \ - --set appVersion="latest" \ - ../../src/kubernetes/ \ - --timeout 10m -``` - -#### Rollback - -Run for a rollback, in case something went wrong: - -```bash -# !!! untested for now for new deployment structure !!! - -# in configuration/ - -# kubeconfig.yaml set globaly -helm rollback ocelot --timeout 10m - -# or kubeconfig.yaml in your repo, then adjust -helm rollback ocelot \ - --kubeconfig ./kubeconfig.yaml \ - --timeout 10m -``` - -#### Uninstall - -Be aware that if you uninstall ocelot the formerly bound volumes become unbound. Those volumes contain all data from uploads and database. You have to manually free their reference in order to bind them again when reinstalling. Once unbound from their former container references they should automatically be rebound (considering the sizes did not change) - -```bash -# !!! untested for now for new deployment structure !!! - -# in configuration/ - -# kubeconfig.yaml set globaly -helm uninstall ocelot --timeout 10m - -# or kubeconfig.yaml in your repo, then adjust -helm uninstall ocelot \ - --kubeconfig ./kubeconfig.yaml \ - --timeout 10m -``` - -## Backups - -You can and should do [backups](./Backup.md) with Kubernetes for sure. - - - -## Kubernetes Commands (Without Helm) To Deploy New Docker Images To A Kubernetes Cluster - -### Deploy A Version - -```bash -# !!! be aware of the correct kube context !!! -$ kubectl config get-contexts - -# deploy version '$BUILD_VERSION' -# !!! 'latest' is not recommended on production !!! - -# for easyness set env -$ export BUILD_VERSION=1.0.8-48-ocelot.social1.0.8-184 # example -# check this with -$ echo $BUILD_VERSION -1.0.8-48-ocelot.social1.0.8-184 - -# deploy actual version '$BUILD_VERSION' to Kubernetes cluster -$ kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp:$BUILD_VERSION -$ kubectl -n default rollout restart deployment/ocelot-webapp -$ kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend:$BUILD_VERSION -$ kubectl -n default rollout restart deployment/ocelot-backend -$ kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance:$BUILD_VERSION -$ kubectl -n default rollout restart deployment/ocelot-maintenance -$ kubectl -n default set image deployment/ocelot-neo4j container-ocelot-neo4j=ocelotsocialnetwork/neo4j-community:$BUILD_VERSION -$ kubectl -n default rollout restart deployment/ocelot-neo4j -# verify deployment and wait for the pods of each deployment to get ready for cleaning and seeding of the database -$ kubectl -n default rollout status deployment/ocelot-webapp --timeout=240s -$ kubectl -n default rollout status deployment/ocelot-maintenance --timeout=240s -$ kubectl -n default rollout status deployment/ocelot-backend --timeout=240s -$ kubectl -n default rollout status deployment/ocelot-neo4j --timeout=240s -``` - -### Staging – Clean And Seed Neo4j Database - -***ATTENTION:*** Cleaning and seeding of our Neo4j database is only possible in production if env `PRODUCTION_DB_CLEAN_ALLOW=true` is set in our deployment. - -```bash -# !!! be aware of the correct kube context !!! -$ kubectl config get-contexts - -# for staging: reset and seed Neo4j database via backend -$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "node --experimental-repl-await build/src/db/clean.js && node --experimental-repl-await build/src/db/seed.js" - -# or alternatively - -# for production: set Neo4j database indexes, constrains, and initial admin account plus run migrate up via backend -$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "yarn prod:migrate init && yarn prod:migrate up" -``` diff --git a/deployment/src/kubernetes/nginx.values.yaml b/deployment/src/kubernetes/nginx.values.yaml deleted file mode 100644 index 8035104d2..000000000 --- a/deployment/src/kubernetes/nginx.values.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# please duplicate template file and rename to "nginx.values.yaml" and fill in your value - -controller: - kind: DaemonSet - hostNetwork: true - dnsPolicy: ClusterFirstWithHostNet - ingressClass: nginx - daemonset: - useHostPort: true - service: - type: ClusterIP -rbac: - create: true \ No newline at end of file diff --git a/deployment/src/kubernetes/patches/patch.ingress.maintenance.off.yaml b/deployment/src/kubernetes/patches/patch.ingress.maintenance.off.yaml deleted file mode 100644 index c01745d0a..000000000 --- a/deployment/src/kubernetes/patches/patch.ingress.maintenance.off.yaml +++ /dev/null @@ -1,12 +0,0 @@ -spec: - rules: - - host: - http: - paths: - - path: / - pathType: ImplementationSpecific - backend: - service: - name: ocelot-webapp - port: - number: 3000 \ No newline at end of file diff --git a/deployment/src/kubernetes/patches/patch.ingress.maintenance.on.yaml b/deployment/src/kubernetes/patches/patch.ingress.maintenance.on.yaml deleted file mode 100644 index 8a2c5d45f..000000000 --- a/deployment/src/kubernetes/patches/patch.ingress.maintenance.on.yaml +++ /dev/null @@ -1,12 +0,0 @@ -spec: - rules: - - host: - http: - paths: - - path: / - pathType: ImplementationSpecific - backend: - service: - name: ocelot-maintenance - port: - number: 80 \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/NOTES.txt b/deployment/src/kubernetes/templates/NOTES.txt deleted file mode 100644 index 3db4648ca..000000000 --- a/deployment/src/kubernetes/templates/NOTES.txt +++ /dev/null @@ -1 +0,0 @@ -You installed ocelot-social! Congrats <3 \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/backend/ConfigMap.yml b/deployment/src/kubernetes/templates/backend/ConfigMap.yml deleted file mode 100644 index a421fab41..000000000 --- a/deployment/src/kubernetes/templates/backend/ConfigMap.yml +++ /dev/null @@ -1,31 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: configmap-{{ .Release.Name }}-backend - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "configmap-backend" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -data: - PRODUCTION_DB_CLEAN_ALLOW: "{{ .Values.PRODUCTION_DB_CLEAN_ALLOW }}" - PUBLIC_REGISTRATION: "{{ .Values.PUBLIC_REGISTRATION }}" - INVITE_REGISTRATION: "{{ .Values.INVITE_REGISTRATION }}" - CATEGORIES_ACTIVE: "{{ .Values.CATEGORIES_ACTIVE }}" - CLIENT_URI: "{{ .Values.BACKEND.CLIENT_URI }}" - EMAIL_DEFAULT_SENDER: "{{ .Values.BACKEND.EMAIL_DEFAULT_SENDER }}" - SMTP_HOST: "{{ .Values.BACKEND.SMTP_HOST }}" - SMTP_PORT: "{{ .Values.BACKEND.SMTP_PORT }}" - SMTP_IGNORE_TLS: "{{ .Values.BACKEND.SMTP_IGNORE_TLS }}" - SMTP_SECURE: "{{ .Values.BACKEND.SMTP_SECURE }}" - SMTP_DKIM_DOMAINNAME: "{{ .Values.BACKEND.SMTP_DKIM_DOMAINNAME }}" - SMTP_DKIM_KEYSELECTOR: "{{ .Values.BACKEND.SMTP_DKIM_KEYSELECTOR }}" - GRAPHQL_URI: "http://{{ .Release.Name }}-backend:4000" - NEO4J_URI: "bolt://{{ .Release.Name }}-neo4j:7687" - #REDIS_DOMAIN: ---toBeSet(IP)--- - #REDIS_PORT: "6379" - #SENTRY_DSN_WEBAPP: "---toBeSet---" - #SENTRY_DSN_BACKEND: "---toBeSet---" \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/backend/Deployment.yaml b/deployment/src/kubernetes/templates/backend/Deployment.yaml deleted file mode 100644 index 4d0f66dbc..000000000 --- a/deployment/src/kubernetes/templates/backend/Deployment.yaml +++ /dev/null @@ -1,62 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - name: {{ .Release.Name }}-backend - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "deployment-backend" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - replicas: 1 - minReadySeconds: {{ .Values.BACKEND.MIN_READY_SECONDS }} - progressDeadlineSeconds: {{ .Values.BACKEND.PROGRESS_DEADLINE_SECONDS }} - revisionHistoryLimit: {{ .Values.BACKEND.REVISIONS_HISTORY_LIMIT }} - strategy: - rollingUpdate: - maxUnavailable: 1 - selector: - matchLabels: - app: {{ .Release.Name }}-backend - template: - metadata: - annotations: - backup.velero.io/backup-volumes: uploads - # make sure the pod is redeployed - rollme: {{ randAlphaNum 5 | quote }} - labels: - app: {{ .Release.Name }}-backend - spec: - containers: - - name: container-{{ .Release.Name }}-backend - image: "{{ .Values.BACKEND.DOCKER_IMAGE_REPO }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.BACKEND.DOCKER_IMAGE_PULL_POLICY }} - envFrom: - - configMapRef: - name: configmap-{{ .Release.Name }}-backend - - secretRef: - name: secret-{{ .Release.Name }}-backend - resources: - requests: - memory: {{ .Values.BACKEND.RESOURCE_REQUESTS_MEMORY | default "500M" | quote }} - limits: - memory: {{ .Values.BACKEND.RESOURCE_LIMITS_MEMORY | default "1G" | quote }} - ports: - - containerPort: 4000 - protocol: TCP - terminationMessagePath: /dev/termination-log - terminationMessagePolicy: File - volumeMounts: - - mountPath: /app/public/uploads - name: uploads - dnsPolicy: ClusterFirst - schedulerName: default-scheduler - restartPolicy: {{ .Values.BACKEND.CONTAINER_RESTART_POLICY }} - terminationGracePeriodSeconds: {{ .Values.BACKEND.CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS }} - volumes: - - name: uploads - persistentVolumeClaim: - claimName: volume-claim-{{ .Release.Name }}-uploads \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/backend/PersistentVolumeClaim.yaml b/deployment/src/kubernetes/templates/backend/PersistentVolumeClaim.yaml deleted file mode 100644 index 758e9e18c..000000000 --- a/deployment/src/kubernetes/templates/backend/PersistentVolumeClaim.yaml +++ /dev/null @@ -1,24 +0,0 @@ -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: volume-claim-{{ .Release.Name }}-uploads - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "volume-claim-backend" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - #dataSource: - # name: uploads-snapshot - # kind: VolumeSnapshot - # apiGroup: snapshot.storage.k8s.io - storageClassName: storage-{{ .Release.Name }}-persistent - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.BACKEND.STORAGE_UPLOADS }} - diff --git a/deployment/src/kubernetes/templates/backend/Secret.yaml b/deployment/src/kubernetes/templates/backend/Secret.yaml deleted file mode 100644 index 605a92234..000000000 --- a/deployment/src/kubernetes/templates/backend/Secret.yaml +++ /dev/null @@ -1,22 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: secret-{{ .Release.Name }}-backend - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "secret-backend" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -stringData: - JWT_SECRET: "{{ .Values.BACKEND.JWT_SECRET }}" - MAPBOX_TOKEN: "{{ .Values.MAPBOX_TOKEN }}" - PRIVATE_KEY_PASSPHRASE: "{{ .Values.BACKEND.PRIVATE_KEY_PASSPHRASE }}" - SMTP_USERNAME: "{{ .Values.BACKEND.SMTP_USERNAME }}" - SMTP_PASSWORD: "{{ .Values.BACKEND.SMTP_PASSWORD }}" - SMTP_DKIM_PRIVATKEY: "{{ .Values.BACKEND.SMTP_DKIM_PRIVATKEY }}" - #NEO4J_USERNAME: "" - #NEO4J_PASSWORD: "" - #REDIS_PASSWORD: ---toBeSet--- diff --git a/deployment/src/kubernetes/templates/backend/Service.yaml b/deployment/src/kubernetes/templates/backend/Service.yaml deleted file mode 100644 index 9029be586..000000000 --- a/deployment/src/kubernetes/templates/backend/Service.yaml +++ /dev/null @@ -1,20 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: {{ .Release.Name }}-backend - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "service-backend" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - ports: - - name: {{ .Release.Name }}-graphql - port: 4000 - targetPort: 4000 - protocol: TCP - selector: - app: {{ .Release.Name }}-backend diff --git a/deployment/src/kubernetes/templates/issuer/letsencrypt-production.yaml b/deployment/src/kubernetes/templates/issuer/letsencrypt-production.yaml deleted file mode 100644 index 6f82f3686..000000000 --- a/deployment/src/kubernetes/templates/issuer/letsencrypt-production.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-production - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "letsencrypt-production" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - acme: - server: https://acme-v02.api.letsencrypt.org/directory - email: {{ .Values.LETSENCRYPT.EMAIL }} - privateKeySecretRef: - name: letsencrypt-production - solvers: - - http01: - ingress: - class: nginx diff --git a/deployment/src/kubernetes/templates/issuer/letsencrypt-staging.yaml b/deployment/src/kubernetes/templates/issuer/letsencrypt-staging.yaml deleted file mode 100644 index e488d9335..000000000 --- a/deployment/src/kubernetes/templates/issuer/letsencrypt-staging.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: cert-manager.io/v1 -kind: ClusterIssuer -metadata: - name: letsencrypt-staging - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "letsencrypt-staging" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - acme: - server: https://acme-staging-v02.api.letsencrypt.org/directory - email: {{ .Values.LETSENCRYPT.EMAIL }} - privateKeySecretRef: - name: letsencrypt-staging - solvers: - - http01: - ingress: - class: nginx diff --git a/deployment/src/kubernetes/templates/jobs/job-db-init.yaml b/deployment/src/kubernetes/templates/jobs/job-db-init.yaml deleted file mode 100644 index f207bd8c1..000000000 --- a/deployment/src/kubernetes/templates/jobs/job-db-init.yaml +++ /dev/null @@ -1,29 +0,0 @@ -kind: Job -apiVersion: batch/v1 -metadata: - name: job-{{ .Release.Name }}-db-init - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "job-db-init" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - annotations: - "helm.sh/hook": post-install - "helm.sh/hook-delete-policy": hook-succeeded, hook-failed - "helm.sh/hook-weight": "0" -spec: - template: - spec: - restartPolicy: Never - containers: - - name: job-{{ .Release.Name }}-db-init - image: "{{ .Values.BACKEND.DOCKER_IMAGE_REPO }}:{{ .Chart.AppVersion }}" - command: ["/bin/sh", "-c", "yarn prod:migrate init"] - envFrom: - - configMapRef: - name: configmap-{{ .Release.Name }}-backend - - secretRef: - name: secret-{{ .Release.Name }}-backend \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/jobs/job-db-migrate.yaml b/deployment/src/kubernetes/templates/jobs/job-db-migrate.yaml deleted file mode 100644 index 950793db4..000000000 --- a/deployment/src/kubernetes/templates/jobs/job-db-migrate.yaml +++ /dev/null @@ -1,29 +0,0 @@ -kind: Job -apiVersion: batch/v1 -metadata: - name: job-{{ .Release.Name }}-db-migrate - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "job-db-migrate" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - annotations: - "helm.sh/hook": post-install, post-upgrade - "helm.sh/hook-delete-policy": hook-succeeded, hook-failed - "helm.sh/hook-weight": "5" -spec: - template: - spec: - restartPolicy: Never - containers: - - name: job-{{ .Release.Name }}-db-migrations - image: "{{ .Values.BACKEND.DOCKER_IMAGE_REPO }}:{{ .Chart.AppVersion }}" - command: ["/bin/sh", "-c", "yarn prod:migrate up"] - envFrom: - - configMapRef: - name: configmap-{{ .Release.Name }}-backend - - secretRef: - name: secret-{{ .Release.Name }}-backend \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/maintenance/ConfigMap.yml b/deployment/src/kubernetes/templates/maintenance/ConfigMap.yml deleted file mode 100644 index fe29afbfe..000000000 --- a/deployment/src/kubernetes/templates/maintenance/ConfigMap.yml +++ /dev/null @@ -1,14 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: configmap-{{ .Release.Name }}-maintenance - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "configmap-maintenance" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -data: - HOST: "0.0.0.0" \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/maintenance/Deployment.yaml b/deployment/src/kubernetes/templates/maintenance/Deployment.yaml deleted file mode 100644 index ec37552d1..000000000 --- a/deployment/src/kubernetes/templates/maintenance/Deployment.yaml +++ /dev/null @@ -1,45 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - name: {{ .Release.Name }}-maintenance - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "deployment-maintenance" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - revisionHistoryLimit: {{ .Values.MAINTENANCE.REVISIONS_HISTORY_LIMIT }} - strategy: - rollingUpdate: - maxUnavailable: 1 - selector: - matchLabels: - app: {{ .Release.Name }}-maintenance - template: - metadata: - labels: - app: {{ .Release.Name }}-maintenance - # make sure the pod is redeployed - rollme: {{ randAlphaNum 5 | quote }} - spec: - containers: - - name: container-{{ .Release.Name }}-maintenance - image: "{{ .Values.MAINTENANCE.DOCKER_IMAGE_REPO }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.MAINTENANCE.DOCKER_IMAGE_PULL_POLICY }} - envFrom: - - configMapRef: - name: configmap-{{ .Release.Name }}-webapp - - secretRef: - name: secret-{{ .Release.Name }}-webapp - resources: - requests: - memory: {{ .Values.MAINTENANCE.RESOURCE_REQUESTS_MEMORY | default "500M" | quote }} - limits: - memory: {{ .Values.MAINTENANCE.RESOURCE_LIMITS_MEMORY | default "1G" | quote }} - ports: - - containerPort: 80 - restartPolicy: {{ .Values.MAINTENANCE.CONTAINER_RESTART_POLICY }} - terminationGracePeriodSeconds: {{ .Values.MAINTENANCE.CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS }} diff --git a/deployment/src/kubernetes/templates/maintenance/Secret.yaml b/deployment/src/kubernetes/templates/maintenance/Secret.yaml deleted file mode 100644 index b4752e552..000000000 --- a/deployment/src/kubernetes/templates/maintenance/Secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: secret-{{ .Release.Name }}-maintenance - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "secret-maintenance" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -stringData: diff --git a/deployment/src/kubernetes/templates/maintenance/Service.yaml b/deployment/src/kubernetes/templates/maintenance/Service.yaml deleted file mode 100644 index 95f042df5..000000000 --- a/deployment/src/kubernetes/templates/maintenance/Service.yaml +++ /dev/null @@ -1,20 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: {{ .Release.Name }}-maintenance - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "service-maintenance" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - ports: - - name: {{ .Release.Name }}-http - port: 80 - targetPort: 80 - protocol: TCP - selector: - app: {{ .Release.Name }}-maintenance diff --git a/deployment/src/kubernetes/templates/neo4j/ConfigMap.yml b/deployment/src/kubernetes/templates/neo4j/ConfigMap.yml deleted file mode 100644 index 677218c16..000000000 --- a/deployment/src/kubernetes/templates/neo4j/ConfigMap.yml +++ /dev/null @@ -1,24 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: configmap-{{ .Release.Name }}-neo4j - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "configmap-neo4j" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -data: - NEO4J_ACCEPT_LICENSE_AGREEMENT: "{{ .Values.NEO4J.ACCEPT_LICENSE_AGREEMENT }}" - NEO4J_AUTH: "{{ .Values.NEO4J.AUTH }}" - NEO4J_dbms_connector_bolt_thread__pool__max__size: "{{ .Values.NEO4J.DBMS_CONNECTOR_BOLT_THREAD_POOL_MAX_SIZE }}" - NEO4J_dbms_memory_heap_initial__size: "{{ .Values.NEO4J.DBMS_MEMORY_HEAP_INITIAL_SIZE }}" - NEO4J_dbms_memory_heap_max__size: "{{ .Values.NEO4J.DBMS_MEMORY_HEAP_MAX_SIZE }}" - NEO4J_dbms_memory_pagecache_size: "{{ .Values.NEO4J.DBMS_MEMORY_PAGECACHE_SIZE }}" - NEO4J_dbms_security_procedures_unrestricted: "{{ .Values.NEO4J.DBMS_SECURITY_PROCEDURES_UNRESTRICTED }}" - NEO4J_dbms_allow__format__migration: "true" - NEO4J_dbms_allow__upgrade: "true" - NEO4J_dbms_default__database: "{{ .Values.NEO4J.DBMS_DEFAULT_DATABASE }}" - NEO4J_apoc_import_file_enabled: "{{ .Values.NEO4J.APOC_IMPORT_FILE_ENABLED }}" diff --git a/deployment/src/kubernetes/templates/neo4j/Deployment.yaml b/deployment/src/kubernetes/templates/neo4j/Deployment.yaml deleted file mode 100644 index 96867dbb5..000000000 --- a/deployment/src/kubernetes/templates/neo4j/Deployment.yaml +++ /dev/null @@ -1,57 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Release.Name }}-neo4j - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "deployment-neo4j" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - replicas: 1 - revisionHistoryLimit: {{ .Values.NEO4J.REVISIONS_HISTORY_LIMIT }} - strategy: - rollingUpdate: - maxUnavailable: 1 - selector: - matchLabels: - app: {{ .Release.Name }}-neo4j - template: - metadata: - name: neo4j - annotations: - backup.velero.io/backup-volumes: neo4j-data - # make sure the pod is redeployed - rollme: {{ randAlphaNum 5 | quote }} - labels: - app: {{ .Release.Name }}-neo4j - spec: - containers: - - name: container-{{ .Release.Name }}-neo4j - image: "{{ .Values.NEO4J.DOCKER_IMAGE_REPO }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.NEO4J.DOCKER_IMAGE_PULL_POLICY }} - ports: - - containerPort: 7687 - - containerPort: 7474 - resources: - requests: - memory: {{ .Values.NEO4J.RESOURCE_REQUESTS_MEMORY | default "1G" | quote }} - limits: - memory: {{ .Values.NEO4J.RESOURCE_LIMITS_MEMORY | default "1G" | quote }} - envFrom: - - configMapRef: - name: configmap-{{ .Release.Name }}-neo4j - - secretRef: - name: secret-{{ .Release.Name }}-neo4j - volumeMounts: - - mountPath: /data/ - name: neo4j-data - volumes: - - name: neo4j-data - persistentVolumeClaim: - claimName: volume-claim-{{ .Release.Name }}-neo4j - restartPolicy: {{ .Values.NEO4J.CONTAINER_RESTART_POLICY }} - terminationGracePeriodSeconds: {{ .Values.NEO4J.CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS }} diff --git a/deployment/src/kubernetes/templates/neo4j/PersistentVolumeClaim.yaml b/deployment/src/kubernetes/templates/neo4j/PersistentVolumeClaim.yaml deleted file mode 100644 index 3aab02d9f..000000000 --- a/deployment/src/kubernetes/templates/neo4j/PersistentVolumeClaim.yaml +++ /dev/null @@ -1,19 +0,0 @@ -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: volume-claim-{{ .Release.Name }}-neo4j - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "volume-claim-neo4j" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - storageClassName: storage-{{ .Release.Name }}-persistent - accessModes: - - ReadWriteOnce - resources: - requests: - storage: {{ .Values.NEO4J.STORAGE }} \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/neo4j/Secret.yaml b/deployment/src/kubernetes/templates/neo4j/Secret.yaml deleted file mode 100644 index d8b1c17db..000000000 --- a/deployment/src/kubernetes/templates/neo4j/Secret.yaml +++ /dev/null @@ -1,15 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: secret-{{ .Release.Name }}-neo4j - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "secret-neo4j" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -stringData: - NEO4J_USERNAME: "" - NEO4J_PASSWORD: "" \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/neo4j/Service.yaml b/deployment/src/kubernetes/templates/neo4j/Service.yaml deleted file mode 100644 index 4ed56bd3f..000000000 --- a/deployment/src/kubernetes/templates/neo4j/Service.yaml +++ /dev/null @@ -1,23 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: {{ .Release.Name }}-neo4j - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "service-neo4j" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - ports: - - name: {{ .Release.Name }}-bolt - port: 7687 - targetPort: 7687 - protocol: TCP - #- name: {{ .Release.Name }}-http - # port: 7474 - # targetPort: 7474 - selector: - app: {{ .Release.Name }}-neo4j diff --git a/deployment/src/kubernetes/templates/storage/persistent.yml b/deployment/src/kubernetes/templates/storage/persistent.yml deleted file mode 100644 index 2ac07c5de..000000000 --- a/deployment/src/kubernetes/templates/storage/persistent.yml +++ /dev/null @@ -1,16 +0,0 @@ -kind: StorageClass -apiVersion: storage.k8s.io/v1 -metadata: - name: storage-{{ .Release.Name }}-persistent - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "storage-persistent" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -provisioner: {{ .Values.STORAGE.PROVISIONER }} -reclaimPolicy: {{ .Values.STORAGE.RECLAIM_POLICY }} -volumeBindingMode: {{ .Values.STORAGE.VOLUME_BINDING_MODE }} -allowVolumeExpansion: {{ .Values.STORAGE.ALLOW_VOLUME_EXPANSION }} \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/webapp/ConfigMap.yml b/deployment/src/kubernetes/templates/webapp/ConfigMap.yml deleted file mode 100644 index 762b355cc..000000000 --- a/deployment/src/kubernetes/templates/webapp/ConfigMap.yml +++ /dev/null @@ -1,20 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: configmap-{{ .Release.Name }}-webapp - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "configmap-webapp" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -data: - HOST: "0.0.0.0" - PUBLIC_REGISTRATION: "{{ .Values.PUBLIC_REGISTRATION }}" - INVITE_REGISTRATION: "{{ .Values.INVITE_REGISTRATION }}" - CATEGORIES_ACTIVE: "{{ .Values.CATEGORIES_ACTIVE }}" - COOKIE_EXPIRE_TIME: "{{ .Values.COOKIE_EXPIRE_TIME }}" - WEBSOCKETS_URI: "{{ .Values.WEBAPP.WEBSOCKETS_URI }}" - GRAPHQL_URI: "http://{{ .Release.Name }}-backend:4000" \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/webapp/Deployment.yaml b/deployment/src/kubernetes/templates/webapp/Deployment.yaml deleted file mode 100644 index 41221185c..000000000 --- a/deployment/src/kubernetes/templates/webapp/Deployment.yaml +++ /dev/null @@ -1,49 +0,0 @@ -kind: Deployment -apiVersion: apps/v1 -metadata: - name: {{ .Release.Name }}-webapp - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "deployment-webapp" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - replicas: {{ .Values.WEBAPP.REPLICAS }} - minReadySeconds: {{ .Values.WEBAPP.MIN_READY_SECONDS }} - progressDeadlineSeconds: {{ .Values.WEBAPP.PROGRESS_DEADLINE_SECONDS }} - revisionHistoryLimit: {{ .Values.WEBAPP.REVISIONS_HISTORY_LIMIT }} - strategy: - rollingUpdate: - maxUnavailable: 1 - selector: - matchLabels: - app: {{ .Release.Name }}-webapp - template: - metadata: - annotations: - # make sure the pod is redeployed - rollme: {{ randAlphaNum 5 | quote }} - labels: - app: {{ .Release.Name }}-webapp - spec: - containers: - - name: container-{{ .Release.Name }}-webapp - image: "{{ .Values.WEBAPP.DOCKER_IMAGE_REPO }}:{{ .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.WEBAPP.DOCKER_IMAGE_PULL_POLICY }} - ports: - - containerPort: 3000 - envFrom: - - configMapRef: - name: configmap-{{ .Release.Name }}-webapp - - secretRef: - name: secret-{{ .Release.Name }}-webapp - resources: - requests: - memory: {{ .Values.WEBAPP.RESOURCE_REQUESTS_MEMORY | default "500M" | quote }} - limits: - memory: {{ .Values.WEBAPP.RESOURCE_LIMITS_MEMORY | default "1G" | quote }} - restartPolicy: {{ .Values.WEBAPP.CONTAINER_RESTART_POLICY }} - terminationGracePeriodSeconds: {{ .Values.WEBAPP.CONTAINER_TERMINATION_GRACE_PERIOD_SECONDS }} \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/webapp/Ingress.yaml b/deployment/src/kubernetes/templates/webapp/Ingress.yaml deleted file mode 100644 index d7b12bdc8..000000000 --- a/deployment/src/kubernetes/templates/webapp/Ingress.yaml +++ /dev/null @@ -1,36 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-{{ .Release.Name }}-webapp - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "ingress-webapp" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - annotations: - kubernetes.io/ingress.class: "nginx" - cert-manager.io/cluster-issuer: {{ .Values.LETSENCRYPT.ISSUER }} - nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.NGINX.PROXY_BODY_SIZE }} -spec: - tls: - - hosts: - {{- range .Values.LETSENCRYPT.DOMAINS }} - - {{ . }} - {{- end }} - secretName: tls - rules: - {{- range .Values.LETSENCRYPT.DOMAINS }} - - host: {{ . }} - http: - paths: - - path: / - pathType: ImplementationSpecific - backend: - service: - name: {{ $.Release.Name }}-webapp - port: - number: 3000 - {{- end }} \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/webapp/Secret.yaml b/deployment/src/kubernetes/templates/webapp/Secret.yaml deleted file mode 100644 index 8c0fd9d39..000000000 --- a/deployment/src/kubernetes/templates/webapp/Secret.yaml +++ /dev/null @@ -1,14 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: secret-{{ .Release.Name }}-webapp - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "secret-webapp" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -stringData: - MAPBOX_TOKEN: "{{ .Values.MAPBOX_TOKEN }}" \ No newline at end of file diff --git a/deployment/src/kubernetes/templates/webapp/Service.yaml b/deployment/src/kubernetes/templates/webapp/Service.yaml deleted file mode 100644 index 0c3112e77..000000000 --- a/deployment/src/kubernetes/templates/webapp/Service.yaml +++ /dev/null @@ -1,20 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: {{ .Release.Name }}-webapp - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "service-webapp" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - ports: - - name: {{ .Release.Name }}-http - port: 3000 - targetPort: 3000 - protocol: TCP - selector: - app: {{ .Release.Name }}-webapp diff --git a/deployment/src/old/Maintenance.md b/deployment/src/old/Maintenance.md deleted file mode 100644 index 08a177e65..000000000 --- a/deployment/src/old/Maintenance.md +++ /dev/null @@ -1,45 +0,0 @@ -# Maintenance mode - -> Despite our best efforts, systems sometimes require downtime for a variety of reasons. - -Quote from [here](https://www.nrmitchi.com/2017/11/easy-maintenance-mode-in-kubernetes/) - -We use our maintenance mode for manual database backup and restore. Also we -bring the database into maintenance mode for manual database migrations. - -## Deploy the service - -We prepared sample configuration, so you can simply run: - -```sh -# in folder deployment/ -$ kubectl apply -f ./ocelot-social/maintenance/ -``` - -This will fire up a maintenance service. - -## Bring application into maintenance mode - -Now if you want to have a controlled downtime and you want to bring your -application into maintenance mode, you can edit your global ingress server. - -E.g. copy file [`deployment/digital-ocean/https/templates/ingress.template.yaml`](../../digital-ocean/https/templates/ingress.template.yaml) to new file `deployment/digital-ocean/https/ingress.yaml` and change the following: - -```yaml -... - - - host: develop-k8s.ocelot.social - http: - paths: - - path: / - backend: - # serviceName: web - serviceName: maintenance - # servicePort: 3000 - servicePort: 80 -``` - -Then run `$ kubectl apply -f deployment/digital-ocean/https/ingress.yaml`. If you -want to deactivate the maintenance server, just undo the edit and apply the -configuration again. - diff --git a/deployment/src/old/digital-ocean/README.md b/deployment/src/old/digital-ocean/README.md deleted file mode 100644 index c5893f645..000000000 --- a/deployment/src/old/digital-ocean/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# DigitalOcean - -As a start, read the [introduction into Kubernetes](https://www.digitalocean.com/community/tutorials/an-introduction-to-kubernetes) by the folks at DigitalOcean. The following section should enable you to deploy ocelot.social to your Kubernetes cluster. - -## Connect to your local cluster - -1. Create a cluster at [DigitalOcean](https://www.digitalocean.com/). -2. Download the `***-kubeconfig.yaml` from the Web UI. -3. Move the file to the default location where kubectl expects it to be: `mv ***-kubeconfig.yaml ~/.kube/config`. Alternatively you can set the config on every command: `--kubeconfig ***-kubeconfig.yaml` -4. Now check if you can connect to the cluster and if its your newly created one by running: `kubectl get nodes` - -The output should look about like this: - -```sh -$ kubectl get nodes -NAME STATUS ROLES AGE VERSION -nifty-driscoll-uu1w Ready 69d v1.13.2 -nifty-driscoll-uuiw Ready 69d v1.13.2 -nifty-driscoll-uusn Ready 69d v1.13.2 -``` - -If you got the steps right above and see your nodes you can continue. - -DigitalOcean Kubernetes clusters don't have a graphical interface, so I suggest -to setup the [Kubernetes dashboard](./dashboard/README.md) as a next step. -Configuring [HTTPS](./https/README.md) is bit tricky and therefore I suggest to -do this as a last step. - -## Spaces - -We are storing our images in the s3-compatible [DigitalOcean Spaces](https://www.digitalocean.com/docs/spaces/). - -We still want to take backups of our images in case something happens to the images in the cloud. See these [instructions](https://www.digitalocean.com/docs/spaces/resources/s3cmd-usage/) about getting set up with `s3cmd` to take a copy of all images in a `Spaces` namespace, i.e. `ocelot-social-uploads`. - -After configuring `s3cmd` with your credentials, etc. you should be able to make a backup with this command. - -```sh -s3cmg get --recursive --skip-existing s3://ocelot-social-uploads -``` diff --git a/deployment/src/old/digital-ocean/dashboard/README.md b/deployment/src/old/digital-ocean/dashboard/README.md deleted file mode 100644 index 5f66afe0b..000000000 --- a/deployment/src/old/digital-ocean/dashboard/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Install Kubernetes Dashboard - -The kubernetes dashboard is optional but very helpful for debugging. If you want to install it, you have to do so only **once** per cluster: - -```bash -# in folder deployment/digital-ocean/ -$ kubectl apply -f dashboard/ -$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta4/aio/deploy/recommended.yaml -``` - -### Login to your dashboard - -Proxy the remote kubernetes dashboard to localhost: - -```bash -$ kubectl proxy -``` - -Visit: - -[http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/](http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/) - -You should see a login screen. - -To get your token for the dashboard you can run this command: - -```bash -$ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}') -``` - -It should print something like: - -```text -Name: admin-user-token-6gl6l -Namespace: kube-system -Labels: -Annotations: kubernetes.io/service-account.name=admin-user - kubernetes.io/service-account.uid=b16afba9-dfec-11e7-bbb9-901b0e532516 - -Type: kubernetes.io/service-account-token - -Data -==== -ca.crt: 1025 bytes -namespace: 11 bytes -token: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLTZnbDZsIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMTZhZmJhOS1kZmVjLTExZTctYmJiOS05MDFiMGU1MzI1MTYiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.M70CU3lbu3PP4OjhFms8PVL5pQKj-jj4RNSLA4YmQfTXpPUuxqXjiTf094_Rzr0fgN_IVX6gC4fiNUL5ynx9KU-lkPfk0HnX8scxfJNzypL039mpGt0bbe1IXKSIRaq_9VW59Xz-yBUhycYcKPO9RM2Qa1Ax29nqNVko4vLn1_1wPqJ6XSq3GYI8anTzV8Fku4jasUwjrws6Cn6_sPEGmL54sq5R4Z5afUtv-mItTmqZZdxnkRqcJLlg2Y8WbCPogErbsaCDJoABQ7ppaqHetwfM_0yMun6ABOQbIwwl8pspJhpplKwyo700OSpvTT9zlBsu-b35lzXGBRHzv5g_RA -``` - -Grab the token from above and paste it into the [login screen](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/) - -When you are logged in, you should see sth. like: - -![Dashboard](./dashboard-screenshot.png) - -Feel free to save the login token from above in your password manager. Unlike the `kubeconfig` file, this token does not expire. diff --git a/deployment/src/old/digital-ocean/dashboard/admin-user.yaml b/deployment/src/old/digital-ocean/dashboard/admin-user.yaml deleted file mode 100644 index 27b6bb802..000000000 --- a/deployment/src/old/digital-ocean/dashboard/admin-user.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: admin-user - namespace: kube-system diff --git a/deployment/src/old/digital-ocean/dashboard/dashboard-screenshot.png b/deployment/src/old/digital-ocean/dashboard/dashboard-screenshot.png deleted file mode 100644 index 6aefb5414d26ac3cb69153c0624456e47e4ba88f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181783 zcmeFZX*|?@`!`-n3nyAkibRVRLMas4Qd*QH#@JP2GJ~;%j4gypqOz5>1(`83wz1Dl z2xTuB%P^E>ForPJvHU+%=k>eJ&g;DDe?Pb%-M=UE%E9ULL-GqbFqX@O?@y|8Ad-Tq*N|4@@4DHoYXJlhQp#0_bqx4AmB= zqfC;!4P#1krTW8JFUzCrTNDxP0|81CJ4u31dcr|QgR%~h+{ zZu-@Syk>J<#BZOt;mosDTBvBJ?JZ}2_rSFq(8cS2`AY7B0uOGL9E!*lur2w%m7GIS zPo7*yQnPZLXa!aKS7CL2e$Bn@fp_n&l`%?xD7AmTPJLqhh95_eb5OHcEWEVw`S*>! z)Vn(Iwd!t}VIlI;_Mi5Bu!x7x2MfiTxQM*3{js=vJgbA~2iy0h-}`=?Z+ik(1bF^_ zu$!e^zLl>(_t`i8k3V0yAG{E$+KB$~!Yg-Dm8A_ zc#jWV=&bC6s?|wYW^SY9+l46-U|lLohpwIFI~Tam=*;)sN{aAqK)Y{_)BX8r8*W?x zZ_#xu+wgr$fjhU}yO-)^f$0cqQR21oV>yX>mAI?YX#dPPUb&&LL!XY7yi? z_!;xTyFVV^xN#GEFi{G2`e)gCzXJ$DqHDkO52d;Rm23*4@1Qv6gz4aklsHrm!;;`& z)A`l%HfJu3IWylCECVU9P&v@CQ89dMs`%9EAJ=jd0uGd#WJ~|?m_M~tvmAUaTjW#s zL+V~?OX>$%6X9bn;|fM+$Cn?5Tb(5Y(M4*;i(e|URWL(-lmI&;3�f35BeD2-34X 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 z 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 L0K-54 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 LT8ahoPXhi67M*0s diff --git a/deployment/src/old/digital-ocean/dashboard/role-binding.yaml b/deployment/src/old/digital-ocean/dashboard/role-binding.yaml deleted file mode 100644 index faa8927a2..000000000 --- a/deployment/src/old/digital-ocean/dashboard/role-binding.yaml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: admin-user -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: -- kind: ServiceAccount - name: admin-user - namespace: kube-system diff --git a/deployment/src/old/digital-ocean/https/README.md b/deployment/src/old/digital-ocean/https/README.md deleted file mode 100644 index 2393f3a0f..000000000 --- a/deployment/src/old/digital-ocean/https/README.md +++ /dev/null @@ -1,124 +0,0 @@ -## Create Letsencrypt Issuers and Ingress Services - -Copy the configuration templates and change the file according to your needs. - -```bash -# in folder deployment/digital-ocean/https/ -cp templates/issuer.template.yaml ./issuer.yaml -cp templates/ingress.template.yaml ./ingress.yaml -``` - -At least, **change email addresses** in `issuer.yaml`. For sure you also want -to _change the domain name_ in `ingress.yaml`. - -Once you are done, apply the configuration: - -```bash -# in folder deployment/digital-ocean/https/ -$ kubectl apply -f . -``` - -{% hint style="info" %} -CAUTION: It seems that the behaviour of DigitalOcean has changed and the load balancer is not created automatically anymore. -And to create a load balancer costs money. Please refine the following documentation if required. -{% endhint %} - -::: tabs -@tab:active Without Load Balancer - -A solution without a load balance you can find [here](../no-loadbalancer/README.md). - -@tab With DigitalOcean Load Balancer - -{% hint style="info" %} -CAUTION: It seems that the behaviour of DigitalOcean has changed and the load balancer is not created automatically anymore. -Please refine the following documentation if required. -{% endhint %} - -In earlier days by now, your cluster should have a load balancer assigned with an external IP -address. On DigitalOcean, this is how it should look like: - -![Screenshot of DigitalOcean dashboard showing external ip address](./ip-address.png) - -If the load balancer isn't created automatically you have to create it your self on DigitalOcean under Networks. -In case you don't need a DigitalOcean load balancer (which costs money by the way) have a look in the tab `Without Load Balancer`. - -::: - -Check the ingress server is working correctly: - -```bash -$ curl -kivL -H 'Host: ' 'https://' - -``` - -If the response looks good, configure your domain registrar for the new IP address and the domain. - -Now let's get a valid HTTPS certificate. According to the tutorial above, check your tls certificate for staging: - -```bash -$ kubectl -n ocelot-social describe certificate tls -< -... -Spec: - ... - Issuer Ref: - Group: cert-manager.io - Kind: ClusterIssuer - Name: letsencrypt-staging -... -Events: - -> -$ kubectl -n ocelot-social describe secret tls -< -... -Annotations: ... - cert-manager.io/issuer-kind: ClusterIssuer - cert-manager.io/issuer-name: letsencrypt-staging -... -> -``` - -If everything looks good, update the cluster-issuer of your ingress. Change the annotation `cert-manager.io/cluster-issuer` from `letsencrypt-staging` (for testing by getting a dummy certificate – no blocking by letsencrypt, because of to many request cycles) to `letsencrypt-prod` (for production with a real certificate – possible blocking by letsencrypt for several days, because of to many request cycles) in your ingress configuration in `ingress.yaml`. - -```bash -# in folder deployment/digital-ocean/https/ -$ kubectl apply -f ingress.yaml -``` - -Take a minute and have a look if the certificate is now newly generated by `letsencrypt-prod`, the cluster-issuer for production: - -```bash -$ kubectl -n ocelot-social describe certificate tls -< -... -Spec: - ... - Issuer Ref: - Group: cert-manager.io - Kind: ClusterIssuer - Name: letsencrypt-prod -... -Events: - -> -$ kubectl -n ocelot-social describe secret tls -< -... -Annotations: ... - cert-manager.io/issuer-kind: ClusterIssuer - cert-manager.io/issuer-name: letsencrypt-prod -... -> -``` - -In case the certificate is not newly created delete the former secret to force a refresh: - -```bash -$ kubectl -n ocelot-social delete secret tls -``` - -Now, HTTPS should be configured on your domain. Congrats! - -For troubleshooting have a look at the cert-manager's [Troubleshooting](https://cert-manager.io/docs/faq/troubleshooting/) or [Troubleshooting Issuing ACME Certificates](https://cert-manager.io/docs/faq/acme/). diff --git a/deployment/src/old/digital-ocean/https/ip-address.png b/deployment/src/old/digital-ocean/https/ip-address.png deleted file mode 100644 index db523156adce4de5bd9bb198823c83bda84b795a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 144403 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

hd)T8SBB&CgR2GyvwhxGi)5ks07mk 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 -MERGE (author)-[:WROTE]->(c) -; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/comments/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/comments/delete.cql deleted file mode 100644 index c4a7961c5..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/comments/delete.cql +++ /dev/null @@ -1 +0,0 @@ -MATCH (n:Comment) DETACH DELETE n; \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/contributions.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/contributions.cql deleted file mode 100644 index f09b5ad71..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/contributions.cql +++ /dev/null @@ -1,156 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro -[?] { //Modeled incorrect as Post -[?] userId: { -[X] type: String, -[ ] required: true, // Not required in Nitro -[-] index: true - }, -[ ] organizationId: { -[ ] type: String, -[-] index: true - }, -[X] categoryIds: { -[X] type: Array, -[-] index: true - }, -[X] title: { -[X] type: String, -[X] required: true - }, -[?] slug: { // Generated from title -[X] type: String, -[ ] required: true, // Not required in Nitro -[?] unique: true, // Unique value is not enforced in Nitro? -[-] index: true - }, -[ ] type: { // db.getCollection('contributions').distinct('type') -> 'DELETED', 'cando', 'post' -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] cando: { -[ ] difficulty: { -[ ] type: String, -[ ] enum: ['easy', 'medium', 'hard'] - }, -[ ] reasonTitle: { type: String }, -[ ] reason: { type: String } - }, -[X] content: { -[X] type: String, -[X] required: true - }, -[?] contentExcerpt: { // Generated from content -[X] type: String, -[?] required: true // Not required in Nitro - }, -[ ] hasMore: { type: Boolean }, -[X] teaserImg: { type: String }, -[ ] language: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] shoutCount: { -[ ] type: Number, -[ ] default: 0, -[-] index: true - }, -[ ] meta: { -[ ] hasVideo: { -[ ] type: Boolean, -[ ] default: false - }, -[ ] embedds: { -[ ] type: Object, -[ ] default: {} - } - }, -[?] visibility: { -[X] type: String, -[X] enum: ['public', 'friends', 'private'], -[ ] default: 'public', // Default value is missing in Nitro -[-] index: true - }, -[?] isEnabled: { -[X] type: Boolean, -[ ] default: true, // Default value is missing in Nitro -[-] index: true - }, -[?] tags: { type: Array }, // ensure this is working properly -[ ] emotions: { -[ ] type: Object, -[-] index: true, -[ ] default: { -[ ] angry: { -[ ] count: 0, -[ ] percent: 0 -[ ] }, -[ ] cry: { -[ ] count: 0, -[ ] percent: 0 -[ ] }, -[ ] surprised: { -[ ] count: 0, -[ ] percent: 0 - }, -[ ] happy: { -[ ] count: 0, -[ ] percent: 0 - }, -[ ] funny: { -[ ] count: 0, -[ ] percent: 0 - } - } - }, -[?] deleted: { // THis field is not always present in the alpha-data -[?] type: Boolean, -[ ] default: false, // Default value is missing in Nitro -[-] index: true - }, -[?] createdAt: { -[?] type: Date, // Type is modeled as string in Nitro which is incorrect -[ ] default: Date.now // Default value is missing in Nitro - }, -[?] updatedAt: { -[?] type: Date, // Type is modeled as string in Nitro which is incorrect -[ ] default: Date.now // Default value is missing in Nitro - }, -[ ] wasSeeded: { type: Boolean } - } -*/ -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as post -MERGE (p:Post {id: post._id["$oid"]}) -ON CREATE SET -p.title = post.title, -p.slug = post.slug, -p.image = replace(post.teaserImg, 'https://api-alpha.human-connection.org', ''), -p.content = post.content, -p.contentExcerpt = post.contentExcerpt, -p.visibility = toLower(post.visibility), -p.createdAt = post.createdAt.`$date`, -p.updatedAt = post.updatedAt.`$date`, -p.deleted = COALESCE(post.deleted, false), -p.disabled = COALESCE(NOT post.isEnabled, false) -WITH p, post -MATCH (u:User {id: post.userId}) -MERGE (u)-[:WROTE]->(p) -WITH p, post, post.categoryIds as categoryIds -UNWIND categoryIds AS categoryId -MATCH (c:Category {id: categoryId}) -MERGE (p)-[:CATEGORIZED]->(c) -WITH p, post.tags AS tags -UNWIND tags AS tag -WITH apoc.text.replace(tag, '[^\\p{L}0-9]', '') as tagNoSpacesAllowed -CALL apoc.when(tagNoSpacesAllowed =~ '^((\\p{L}+[\\p{L}0-9]*)|([0-9]+\\p{L}+[\\p{L}0-9]*))$', 'RETURN tagNoSpacesAllowed', '', {tagNoSpacesAllowed: tagNoSpacesAllowed}) -YIELD value as validated -WHERE validated.tagNoSpacesAllowed IS NOT NULL -MERGE (t:Tag { id: validated.tagNoSpacesAllowed, disabled: false, deleted: false }) -MERGE (p)-[:TAGGED]->(t) -; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/delete.cql deleted file mode 100644 index 70adad664..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/contributions/delete.cql +++ /dev/null @@ -1,2 +0,0 @@ -MATCH (n:Post) DETACH DELETE n; -MATCH (n:Tag) DETACH DELETE n; \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/delete_all.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/delete_all.cql deleted file mode 100644 index d01871300..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/delete_all.cql +++ /dev/null @@ -1 +0,0 @@ -MATCH (n) DETACH DELETE n; \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql deleted file mode 100644 index 18fb6699f..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/delete.cql +++ /dev/null @@ -1 +0,0 @@ -MATCH (u:User)-[e:EMOTED]->(c:Post) DETACH DELETE e; \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql deleted file mode 100644 index 06341f277..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/emotions/emotions.cql +++ /dev/null @@ -1,58 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[X] userId: { -[X] type: String, -[X] required: true, -[-] index: true - }, -[X] contributionId: { -[X] type: String, -[X] required: true, -[-] index: true - }, -[?] rated: { -[X] type: String, -[ ] required: true, -[?] enum: ['funny', 'happy', 'surprised', 'cry', 'angry'] - }, -[X] createdAt: { -[X] type: Date, -[X] default: Date.now - }, -[X] updatedAt: { -[X] type: Date, -[X] default: Date.now - }, -[-] wasSeeded: { type: Boolean } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as emotion -MATCH (u:User {id: emotion.userId}), - (c:Post {id: emotion.contributionId}) -MERGE (u)-[e:EMOTED { - id: emotion._id["$oid"], - emotion: emotion.rated, - createdAt: datetime(emotion.createdAt.`$date`), - updatedAt: datetime(emotion.updatedAt.`$date`) - }]->(c) -RETURN e; -/* - // Queries - // user sets an emotion emotion: - // MERGE (u)-[e:EMOTED {id: ..., emotion: "funny", createdAt: ..., updatedAt: ...}]->(c) - // user removes emotion - // MATCH (u)-[e:EMOTED]->(c) DELETE e - // contribution distributions over every `emotion` property value for one post - // MATCH (u:User)-[e:EMOTED]->(c:Post {id: "5a70bbc8508f5b000b443b1a"}) RETURN e.emotion,COUNT(e.emotion) - // contribution distributions over every `emotion` property value for one user (advanced - "whats the primary emotion used by the user?") - // MATCH (u:User{id:"5a663b1ac64291000bf302a1"})-[e:EMOTED]->(c:Post) RETURN e.emotion,COUNT(e.emotion) - // contribution distributions over every `emotion` property value for all posts created by one user (advanced - "how do others react to my contributions?") - // MATCH (u:User)-[e:EMOTED]->(c:Post)<-[w:WROTE]-(a:User{id:"5a663b1ac64291000bf302a1"}) RETURN e.emotion,COUNT(e.emotion) - // if we can filter the above an a variable timescale that would be great (should be possible on createdAt and updatedAt fields) -*/ \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql deleted file mode 100644 index 3de01f8ea..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/delete.cql +++ /dev/null @@ -1 +0,0 @@ -MATCH (u1:User)-[f:FOLLOWS]->(u2:User) DETACH DELETE f; \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/follows.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/follows.cql deleted file mode 100644 index fac858a9a..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/follows/follows.cql +++ /dev/null @@ -1,36 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[?] userId: { -[-] type: String, -[ ] required: true, -[-] index: true - }, -[?] foreignId: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[?] foreignService: { // db.getCollection('follows').distinct('foreignService') returns 'organizations' and 'users' -[ ] type: String, -[ ] required: true, -[ ] index: true - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } - index: -[?] { userId: 1, foreignId: 1, foreignService: 1 },{ unique: true } // is the unique constrain modeled? -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as follow -MATCH (u1:User {id: follow.userId}), (u2:User {id: follow.foreignId}) -MERGE (u1)-[:FOLLOWS]->(u2) -; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/import.sh b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/import.sh deleted file mode 100755 index ccb22dafb..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/import.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env bash -set -e - -# import .env config -set -o allexport -source $(dirname "$0")/.env -set +o allexport - -# Delete collection function defintion -function delete_collection () { - # Delete from Database - echo "Delete $2" - "${IMPORT_CYPHERSHELL_BIN}" < $(dirname "$0")/$1/delete.cql > /dev/null - # Delete index file - rm -f "${IMPORT_PATH}splits/$2.index" -} - -# Import collection function defintion -function import_collection () { - # index file of those chunks we have already imported - INDEX_FILE="${IMPORT_PATH}splits/$1.index" - # load index file - if [ -f "$INDEX_FILE" ]; then - readarray -t IMPORT_INDEX <$INDEX_FILE - else - declare -a IMPORT_INDEX - fi - # for each chunk import data - for chunk in ${IMPORT_PATH}splits/$1/* - do - CHUNK_FILE_NAME=$(basename "${chunk}") - # does the index not contain the chunk file name? - if [[ ! " ${IMPORT_INDEX[@]} " =~ " ${CHUNK_FILE_NAME} " ]]; then - # calculate the path of the chunk - export IMPORT_CHUNK_PATH_CQL_FILE="${IMPORT_CHUNK_PATH_CQL}$1/${CHUNK_FILE_NAME}" - # load the neo4j command and replace file variable with actual path - NEO4J_COMMAND="$(envsubst '${IMPORT_CHUNK_PATH_CQL_FILE}' < $(dirname "$0")/$2)" - # run the import of the chunk - echo "Import $1 ${CHUNK_FILE_NAME} (${chunk})" - echo "${NEO4J_COMMAND}" | "${IMPORT_CYPHERSHELL_BIN}" > /dev/null - # add file to array and file - IMPORT_INDEX+=("${CHUNK_FILE_NAME}") - echo "${CHUNK_FILE_NAME}" >> ${INDEX_FILE} - else - echo "Skipping $1 ${CHUNK_FILE_NAME} (${chunk})" - fi - done -} - -# Time variable -SECONDS=0 - -# Delete all Neo4J Database content -echo "Deleting Database Contents" -delete_collection "badges" "badges" -delete_collection "categories" "categories" -delete_collection "users" "users" -delete_collection "follows" "follows_users" -delete_collection "contributions" "contributions_post" -delete_collection "contributions" "contributions_cando" -delete_collection "shouts" "shouts" -delete_collection "comments" "comments" -delete_collection "emotions" "emotions" - -#delete_collection "invites" -#delete_collection "notifications" -#delete_collection "organizations" -#delete_collection "pages" -#delete_collection "projects" -#delete_collection "settings" -#delete_collection "status" -#delete_collection "systemnotifications" -#delete_collection "userscandos" -#delete_collection "usersettings" -echo "DONE" - -# Import Data -echo "Start Importing Data" -import_collection "badges" "badges/badges.cql" -import_collection "categories" "categories/categories.cql" -import_collection "users_verified" "users/users.cql" -import_collection "follows_users" "follows/follows.cql" -#import_collection "follows_organizations" "follows/follows.cql" -import_collection "contributions_post" "contributions/contributions.cql" -#import_collection "contributions_cando" "contributions/contributions.cql" -#import_collection "contributions_DELETED" "contributions/contributions.cql" -import_collection "shouts" "shouts/shouts.cql" -import_collection "comments" "comments/comments.cql" -import_collection "emotions" "emotions/emotions.cql" - -# import_collection "invites" -# import_collection "notifications" -# import_collection "organizations" -# import_collection "pages" -# import_collection "systemnotifications" -# import_collection "userscandos" -# import_collection "usersettings" - -# does only contain dummy data -# import_collection "projects" - -# does only contain alpha specifc data -# import_collection "status -# import_collection "settings"" - -echo "DONE" - -echo "Time elapsed: $SECONDS seconds" diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/invites.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/invites.cql deleted file mode 100644 index f4a5bf006..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/invites.cql +++ /dev/null @@ -1,39 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] email: { -[ ] type: String, -[ ] required: true, -[-] index: true, -[ ] unique: true - }, -[ ] code: { -[ ] type: String, -[-] index: true, -[ ] required: true - }, -[ ] role: { -[ ] type: String, -[ ] enum: ['admin', 'moderator', 'manager', 'editor', 'user'], -[ ] default: 'user' - }, -[ ] invitedByUserId: { type: String }, -[ ] language: { type: String }, -[ ] badgeIds: [], -[ ] wasUsed: { -[ ] type: Boolean, -[-] index: true - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as invite; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/notifications.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/notifications.cql deleted file mode 100644 index aa6ac8eb9..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/notifications.cql +++ /dev/null @@ -1,48 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] userId: { // User this notification is sent to -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] type: { -[ ] type: String, -[ ] required: true, -[ ] enum: ['comment','comment-mention','contribution-mention','following-contribution'] - }, -[ ] relatedUserId: { -[ ] type: String, -[-] index: true - }, -[ ] relatedContributionId: { -[ ] type: String, -[-] index: true - }, -[ ] relatedOrganizationId: { -[ ] type: String, -[-] index: true - }, -[ ] relatedCommentId: {type: String }, -[ ] unseen: { -[ ] type: Boolean, -[ ] default: true, -[-] index: true - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as notification; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/organizations.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/organizations.cql deleted file mode 100644 index e473e697c..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/organizations/organizations.cql +++ /dev/null @@ -1,137 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] name: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] slug: { -[ ] type: String, -[ ] required: true, -[ ] unique: true, -[-] index: true - }, -[ ] followersCounts: { -[ ] users: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] organizations: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] projects: { -[ ] type: Number, -[ ] default: 0 - } - }, -[ ] followingCounts: { -[ ] users: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] organizations: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] projects: { -[ ] type: Number, -[ ] default: 0 - } - }, -[ ] categoryIds: { -[ ] type: Array, -[ ] required: true, -[-] index: true - }, -[ ] logo: { type: String }, -[ ] coverImg: { type: String }, -[ ] userId: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] description: { -[ ] type: String, -[ ] required: true - }, -[ ] descriptionExcerpt: { type: String }, // will be generated automatically -[ ] publicEmail: { type: String }, -[ ] url: { type: String }, -[ ] type: { -[ ] type: String, -[-] index: true, -[ ] enum: ['ngo', 'npo', 'goodpurpose', 'ev', 'eva'] - }, -[ ] language: { -[ ] type: String, -[ ] required: true, -[ ] default: 'de', -[-] index: true - }, -[ ] addresses: { -[ ] type: [{ -[ ] street: { -[ ] type: String, -[ ] required: true - }, -[ ] zipCode: { -[ ] type: String, -[ ] required: true - }, -[ ] city: { -[ ] type: String, -[ ] required: true - }, -[ ] country: { -[ ] type: String, -[ ] required: true - }, -[ ] lat: { -[ ] type: Number, -[ ] required: true - }, -[ ] lng: { -[ ] type: Number, -[ ] required: true - } - }], -[ ] default: [] - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] isEnabled: { -[ ] type: Boolean, -[ ] default: false, -[-] index: true - }, -[ ] reviewedBy: { -[ ] type: String, -[ ] default: null, -[-] index: true - }, -[ ] tags: { -[ ] type: Array, -[-] index: true - }, -[ ] deleted: { -[ ] type: Boolean, -[ ] default: false, -[-] index: true - }, -[ ] wasSeeded: { type: Boolean } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as organisation; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/pages.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/pages.cql deleted file mode 100644 index 18223136b..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/pages/pages.cql +++ /dev/null @@ -1,55 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] title: { -[ ] type: String, -[ ] required: true - }, -[ ] slug: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] type: { -[ ] type: String, -[ ] required: true, -[ ] default: 'page' - }, -[ ] key: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] content: { -[ ] type: String, -[ ] required: true - }, -[ ] language: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] active: { -[ ] type: Boolean, -[ ] default: true, -[-] index: true - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } - index: -[ ] { slug: 1, language: 1 },{ unique: true } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as page; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/projects.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/projects.cql deleted file mode 100644 index ed859c157..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/projects/projects.cql +++ /dev/null @@ -1,44 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] name: { -[ ] type: String, -[ ] required: true - }, -[ ] slug: { type: String }, -[ ] followerIds: [], -[ ] categoryIds: { type: Array }, -[ ] logo: { type: String }, -[ ] userId: { -[ ] type: String, -[ ] required: true - }, -[ ] description: { -[ ] type: String, -[ ] required: true - }, -[ ] content: { -[ ] type: String, -[ ] required: true - }, -[ ] addresses: { -[ ] type: Array, -[ ] default: [] - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as project; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/settings.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/settings.cql deleted file mode 100644 index 1d557d30c..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/settings/settings.cql +++ /dev/null @@ -1,36 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] key: { -[ ] type: String, -[ ] default: 'system', -[-] index: true, -[ ] unique: true - }, -[ ] invites: { -[ ] userCanInvite: { -[ ] type: Boolean, -[ ] required: true, -[ ] default: false - }, -[ ] maxInvitesByUser: { -[ ] type: Number, -[ ] required: true, -[ ] default: 1 - }, -[ ] onlyUserWithBadgesCanInvite: { -[ ] type: Array, -[ ] default: [] - } - }, -[ ] maintenance: false - }, { -[ ] timestamps: true - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as setting; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/delete.cql deleted file mode 100644 index 21c2e1f90..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/delete.cql +++ /dev/null @@ -1 +0,0 @@ -// this is just a relation between users and contributions - no need to delete \ No newline at end of file diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/shouts.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/shouts.cql deleted file mode 100644 index d370b4b4a..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/shouts/shouts.cql +++ /dev/null @@ -1,36 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[?] userId: { -[X] type: String, -[ ] required: true, // Not required in Nitro -[-] index: true - }, -[?] foreignId: { -[X] type: String, -[ ] required: true, // Not required in Nitro -[-] index: true - }, -[?] foreignService: { // db.getCollection('shots').distinct('foreignService') returns 'contributions' -[X] type: String, -[ ] required: true, // Not required in Nitro -[-] index: true - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } - index: -[?] { userId: 1, foreignId: 1 },{ unique: true } // is the unique constrain modeled? -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as shout -MATCH (u:User {id: shout.userId}), (p:Post {id: shout.foreignId}) -MERGE (u)-[:SHOUTED]->(p) -; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/status.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/status.cql deleted file mode 100644 index 010c2ca09..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/status/status.cql +++ /dev/null @@ -1,19 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] maintenance: { -[ ] type: Boolean, -[ ] default: false - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as status; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/systemnotifications.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/systemnotifications.cql deleted file mode 100644 index 4bd33eb7c..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/systemnotifications/systemnotifications.cql +++ /dev/null @@ -1,61 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] type: { -[ ] type: String, -[ ] default: 'info', -[ ] required: true, -[-] index: true - }, -[ ] title: { -[ ] type: String, -[ ] required: true - }, -[ ] content: { -[ ] type: String, -[ ] required: true - }, -[ ] slot: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] language: { -[ ] type: String, -[ ] required: true, -[-] index: true - }, -[ ] permanent: { -[ ] type: Boolean, -[ ] default: false - }, -[ ] requireConfirmation: { -[ ] type: Boolean, -[ ] default: false - }, -[ ] active: { -[ ] type: Boolean, -[ ] default: true, -[-] index: true - }, -[ ] totalCount: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as systemnotification; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/delete.cql deleted file mode 100644 index 32679f6c8..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/delete.cql +++ /dev/null @@ -1,2 +0,0 @@ -MATCH (n:User) DETACH DELETE n; -MATCH (e:EmailAddress) DETACH DELETE e; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/users.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/users.cql deleted file mode 100644 index 02dff089f..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/users/users.cql +++ /dev/null @@ -1,124 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[?] email: { -[X] type: String, -[-] index: true, -[X] required: true, -[?] unique: true //unique constrain missing in Nitro - }, -[?] password: { // Not required in Alpha -> verify if always present -[X] type: String - }, -[X] name: { type: String }, -[X] slug: { -[X] type: String, -[-] index: true - }, -[ ] gender: { type: String }, -[ ] followersCounts: { -[ ] users: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] organizations: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] projects: { -[ ] type: Number, -[ ] default: 0 - } - }, -[ ] followingCounts: { -[ ] users: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] organizations: { -[ ] type: Number, -[ ] default: 0 - }, -[ ] projects: { -[ ] type: Number, -[ ] default: 0 - } - }, -[ ] timezone: { type: String }, -[X] avatar: { type: String }, -[X] coverImg: { type: String }, -[ ] doiToken: { type: String }, -[ ] confirmedAt: { type: Date }, -[?] badgeIds: [], // Verify this is working properly -[?] deletedAt: { type: Date }, // The Date of deletion is not saved in Nitro -[?] createdAt: { -[?] type: Date, // Modeled as String in Nitro -[ ] default: Date.now // Default value is missing in Nitro - }, -[?] updatedAt: { -[?] type: Date, // Modeled as String in Nitro -[ ] default: Date.now // Default value is missing in Nitro - }, -[ ] lastActiveAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] isVerified: { type: Boolean }, -[?] role: { -[X] type: String, -[-] index: true, -[?] enum: ['admin', 'moderator', 'manager', 'editor', 'user'], // missing roles manager & editor in Nitro -[ ] default: 'user' // Default value is missing in Nitro - }, -[ ] verifyToken: { type: String }, -[ ] verifyShortToken: { type: String }, -[ ] verifyExpires: { type: Date }, -[ ] verifyChanges: { type: Object }, -[ ] resetToken: { type: String }, -[ ] resetShortToken: { type: String }, -[ ] resetExpires: { type: Date }, -[X] wasSeeded: { type: Boolean }, -[X] wasInvited: { type: Boolean }, -[ ] language: { -[ ] type: String, -[ ] default: 'en' - }, -[ ] termsAndConditionsAccepted: { type: Date }, // we display the terms and conditions on registration -[ ] systemNotificationsSeen: { -[ ] type: Array, -[ ] default: [] - } - } -*/ -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as user -MERGE(u:User {id: user._id["$oid"]}) -ON CREATE SET -u.name = user.name, -u.slug = COALESCE(user.slug, apoc.text.random(20, "[A-Za-z]")), -u.email = user.email, -u.encryptedPassword = user.password, -u.avatar = replace(user.avatar, 'https://api-alpha.human-connection.org', ''), -u.coverImg = replace(user.coverImg, 'https://api-alpha.human-connection.org', ''), -u.wasInvited = user.wasInvited, -u.wasSeeded = user.wasSeeded, -u.role = toLower(user.role), -u.createdAt = user.createdAt.`$date`, -u.updatedAt = user.updatedAt.`$date`, -u.deleted = user.deletedAt IS NOT NULL, -u.disabled = false -MERGE (e:EmailAddress { - email: user.email, - createdAt: toString(datetime()), - verifiedAt: toString(datetime()) -}) -MERGE (e)-[:BELONGS_TO]->(u) -MERGE (u)-[:PRIMARY_EMAIL]->(e) -WITH u, user, user.badgeIds AS badgeIds -UNWIND badgeIds AS badgeId -MATCH (b:Badge {id: badgeId}) -MERGE (b)-[:REWARDED]->(u) -; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/userscandos.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/userscandos.cql deleted file mode 100644 index 55f58f171..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/userscandos/userscandos.cql +++ /dev/null @@ -1,35 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] userId: { -[ ] type: String, -[ ] required: true - }, -[ ] contributionId: { -[ ] type: String, -[ ] required: true - }, -[ ] done: { -[ ] type: Boolean, -[ ] default: false - }, -[ ] doneAt: { type: Date }, -[ ] createdAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - }, -[ ] wasSeeded: { type: Boolean } - } - index: -[ ] { userId: 1, contributionId: 1 },{ unique: true } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as usercando; diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/delete.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/delete.cql deleted file mode 100644 index e69de29bb..000000000 diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/usersettings.cql b/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/usersettings.cql deleted file mode 100644 index 722625944..000000000 --- a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/usersettings/usersettings.cql +++ /dev/null @@ -1,43 +0,0 @@ -/* -// Alpha Model -// [ ] Not modeled in Nitro -// [X] Modeled in Nitro -// [-] Omitted in Nitro -// [?] Unclear / has work to be done for Nitro - { -[ ] userId: { -[ ] type: String, -[ ] required: true, -[ ] unique: true - }, -[ ] blacklist: { -[ ] type: Array, -[ ] default: [] - }, -[ ] uiLanguage: { -[ ] type: String, -[ ] required: true - }, -[ ] contentLanguages: { -[ ] type: Array, -[ ] default: [] - }, -[ ] filter: { -[ ] categoryIds: { -[ ] type: Array, -[ ] index: true - }, -[ ] emotions: { -[ ] type: Array, -[ ] index: true - } - }, -[ ] hideUsersWithoutTermsOfUseSigniture: {type: Boolean}, -[ ] updatedAt: { -[ ] type: Date, -[ ] default: Date.now - } - } -*/ - -CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as usersetting; diff --git a/deployment/src/old/mailserver/Deployment.yaml b/deployment/src/old/mailserver/Deployment.yaml deleted file mode 100644 index a36e1652e..000000000 --- a/deployment/src/old/mailserver/Deployment.yaml +++ /dev/null @@ -1,40 +0,0 @@ -{{- if .Values.developmentMailserverDomain }} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ .Release.Name }}-mailserver - labels: - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/name: ocelot-social - app.kubernetes.io/version: {{ .Chart.AppVersion }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - replicas: 1 - minReadySeconds: 15 - progressDeadlineSeconds: 60 - selector: - matchLabels: - ocelot.social/selector: deployment-mailserver - template: - metadata: - labels: - ocelot.social/selector: deployment-mailserver - name: mailserver - spec: - containers: - - name: mailserver - image: djfarrelly/maildev - imagePullPolicy: {{ .Values.image.pullPolicy }} - ports: - - containerPort: 80 - - containerPort: 25 - envFrom: - - configMapRef: - name: {{ .Release.Name }}-configmap - - secretRef: - name: {{ .Release.Name }}-secrets - restartPolicy: Always - terminationGracePeriodSeconds: 30 -status: {} -{{- end}} \ No newline at end of file diff --git a/deployment/src/old/mailserver/README.md b/deployment/src/old/mailserver/README.md deleted file mode 100644 index ed9292d5c..000000000 --- a/deployment/src/old/mailserver/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# Development Mail Server - -You can deploy a fake smtp server which captures all send mails and displays -them in a web interface. The [sample configuration](../templates/configmap.template.yaml) -is assuming such a dummy server in the `SMTP_HOST` configuration and points to -a cluster-internal SMTP server. - -To deploy the SMTP server just uncomment the relevant code in the -[ingress server configuration](../../https/templates/ingress.template.yaml) and -run the following: - -```bash -# in folder deployment/ocelot-social -$ kubectl apply -f mailserver/ -``` - -You might need to refresh the TLS secret to enable HTTPS on the publicly -available web interface. diff --git a/deployment/src/old/mailserver/Service.yaml b/deployment/src/old/mailserver/Service.yaml deleted file mode 100644 index bba734967..000000000 --- a/deployment/src/old/mailserver/Service.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if .Values.developmentMailserverDomain }} -apiVersion: v1 -kind: Service -metadata: - name: {{ .Release.Name }}-mailserver - labels: - app.kubernetes.io/instance: {{ .Release.Name }} - app.kubernetes.io/managed-by: {{ .Release.Service }} - app.kubernetes.io/name: ocelot-social - app.kubernetes.io/version: {{ .Chart.AppVersion }} - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" -spec: - ports: - - name: web - port: 80 - targetPort: 80 - - name: smtp - port: 25 - targetPort: 25 - selector: - ocelot.social/selector: deployment-mailserver -{{- end}} \ No newline at end of file diff --git a/deployment/src/old/mailserver/ingress.yaml b/deployment/src/old/mailserver/ingress.yaml deleted file mode 100644 index 1ea9c58be..000000000 --- a/deployment/src/old/mailserver/ingress.yaml +++ /dev/null @@ -1,42 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-{{ .Release.Name }}-webapp - labels: - app.kubernetes.io/name: "{{ .Chart.Name }}" - app.kubernetes.io/instance: "{{ .Release.Name }}" - app.kubernetes.io/version: "{{ .Chart.AppVersion }}" - app.kubernetes.io/component: "ingress webapp" - app.kubernetes.io/part-of: "{{ .Chart.Name }}" - app.kubernetes.io/managed-by: "{{ .Release.Service }}" - helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" - annotations: - kubernetes.io/ingress.class: "nginx" - cert-manager.io/cluster-issuer: {{ .Values.LETSENCRYPT.ISSUER }} - nginx.ingress.kubernetes.io/proxy-body-size: {{ .Values.NGINX.PROXY_BODY_SIZE }} -spec: - tls: - - hosts: - - {{ .Values.LETSENCRYPT.DOMAIN }} - secretName: tls - rules: - - host: {{ .Values.LETSENCRYPT.DOMAIN }} - http: - paths: - - path: / - pathType: ImplementationSpecific - backend: - service: - name: {{ .Release.Name }}-webapp - port: - number: 3000 - -#{{- if .Values.developmentMailserverDomain }} -# - host: {{ .Values.developmentMailserverDomain }} -# http: -# paths: -# - path: / -# backend: -# serviceName: {{ .Release.Name }}-mailserver -# servicePort: 80 -#{{- end }} diff --git a/deployment/src/old/monitoring/README.md b/deployment/src/old/monitoring/README.md deleted file mode 100644 index 46dfb0301..000000000 --- a/deployment/src/old/monitoring/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Metrics - -You can optionally setup [prometheus](https://prometheus.io/) and -[grafana](https://grafana.com/) for metrics. - -We follow this tutorial [here](https://medium.com/@chris_linguine/how-to-monitor-your-kubernetes-cluster-with-prometheus-and-grafana-2d5704187fc8): - -```bash -kubectl proxy # proxy to your kubernetes dashboard - -helm repo list -# If using helm v3, the stable repository is not set, so you need to manually add it. -helm repo add stable https://kubernetes-charts.storage.googleapis.com -# Create a monitoring namespace for your cluster -kubectl create namespace monitoring -helm --namespace monitoring install prometheus stable/prometheus -kubectl -n monitoring get pods # look for 'server' -kubectl port-forward -n monitoring 9090 -# You can now see your prometheus server on: http://localhost:9090 - -# Make sure you are in folder `deployment/` -kubectl apply -f monitoring/grafana/config.yml -helm --namespace monitoring install grafana stable/grafana -f monitoring/grafana/values.yml -# Get the admin password for grafana from your kubernetes dashboard. -kubectl --namespace monitoring port-forward 3000 -# You can now see your grafana dashboard on: http://localhost:3000 -# Login with user 'admin' and the password you just looked up. -# In your dashboard import this dashboard: -# https://grafana.com/grafana/dashboards/1860 -# Enter ID 180 and choose "Prometheus" as datasource. -# You got metrics! -``` - -Now you should see something like this: - -![Grafana dashboard](./grafana/metrics.png) - -You can set up a grafana dashboard, by visiting https://grafana.com/dashboards, finding one that is suitable and copying it's id. -You then go to the left hand menu in localhost, choose `Dashboard` > `Manage` > `Import` -Paste in the id, click `Load`, select `Prometheus` for the data source, and click `Import` - -When you just installed prometheus and grafana, the data will not be available -immediately, so wait for a couple of minutes and reload. diff --git a/deployment/src/old/monitoring/grafana/config.yml b/deployment/src/old/monitoring/grafana/config.yml deleted file mode 100644 index a338e3480..000000000 --- a/deployment/src/old/monitoring/grafana/config.yml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: prometheus-grafana-datasource - namespace: monitoring - labels: - grafana_datasource: '1' -data: - datasource.yaml: |- - apiVersion: 1 - datasources: - - name: Prometheus - type: prometheus - access: proxy - orgId: 1 - url: http://prometheus-server.monitoring.svc.cluster.local diff --git a/deployment/src/old/monitoring/grafana/metrics.png b/deployment/src/old/monitoring/grafana/metrics.png deleted file mode 100644 index cc68f1bad10efef0abdf03fc1194808c17dc6484..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 211436 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

EF08;%p#ff|dFI5N9t2H`efG+MxXUSYzoO(5rY; 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%0D&^X}H_1K7blDBEZi#$wOX7ECBemaE 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 z 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 d@du_~0kVU{B{F(ZXb bash -# Once you're in the pod, dump the db to a file e.g. `/root/neo4j-backup`. -> neo4j-admin dump --to=/root/neo4j-backup -> exit -# Download the file from the pod to your computer. -$ kubectl cp human-connection/:/root/neo4j-backup ./neo4j-backup -``` - -Revert your changes to deployment `develop-neo4j` which will restart the database. - -## Restore a Backup in Kubernetes - -First stop your Neo4J database. Then: - -```sh -$ kubectl -n ocelot-social get pods -# Copy the ID of the pod running Neo4J. -# Then upload your local backup to the pod. Note that once the pod gets deleted -# e.g. if you change the deployment, the backup file is gone with it. -$ kubectl cp ./neo4j-backup human-connection/:/root/ -$ kubectl -n ocelot-social exec -it bash -# Once you're in the pod restore the backup and overwrite the default database -# called `neo4j` with `--force`. -# This will delete all existing data in database `neo4j`! -> neo4j-admin load --from=/root/neo4j-backup --force -> exit -``` - -Revert your changes to deployment `develop-neo4j` which will restart the database. diff --git a/deployment/src/old/volumes/neo4j-online-backup/README.md b/deployment/src/old/volumes/neo4j-online-backup/README.md deleted file mode 100644 index 602bbd577..000000000 --- a/deployment/src/old/volumes/neo4j-online-backup/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Backup (online) - -## Online backups are only avaible with a Neo4j Enterprise and a license, see https://neo4j.com/licensing/ for the different licenses available - -This tutorial explains how to carry out an online backup of your Neo4J -database in a kubernetes cluster. - -One of the benefits of doing an online backup is that the Neo4j database does not need to be stopped, so there is no downtime. Read [the docs](https://neo4j.com/docs/operations-manual/current/backup/performing/) - -To use Neo4j Enterprise you must add this line to your configmap, if using, or your deployment `develop-neo4j` env. - -```sh -NEO4J_ACCEPT_LICENSE_AGREEMENT: "yes" -``` - -## Create a Backup in Kubernetes - -```sh -# Backup the database with one command, this will get the develop-neo4j pod, ssh into it, and run the backup command -$ kubectl -n=human-connection exec -it $(kubectl -n=human-connection get pods | grep develop-neo4j | awk '{ print $1 }') -- neo4j-admin backup --backup-dir=/var/lib/neo4j --name=neo4j-backup -# Download the file from the pod to your computer. -$ kubectl cp human-connection/$(kubectl -n=human-connection get pods | grep develop-neo4j | awk '{ print $1 }'):/var/lib/neo4j/neo4j-backup ./neo4j-backup/ -``` - -You should now have a backup of the database locally. If you want, you can simulate disaster recovery by sshing into the develop-neo4j pod, deleting all data and restoring from backup - -## Disaster where database data is gone somehow - -```sh -$ kubectl -n=human-connection exec -it $(kubectl -n=human-connection get pods | grep develop-neo4j |awk '{ print $1 }') bash -# Enter cypher-shell -$ cypher-shell -# Delete all data -> MATCH (n) DETACH DELETE (n); - -> exit -``` - -## Restore a backup in Kubernetes - -Restoration must be done while the database is not running, see [our docs](https://docs.human-connection.org/human-connection/deployment/volumes/neo4j-offline-backup#stop-and-restart-neo-4-j-database-in-kubernetes) for how to stop the database, but keep the container running - -After, you have stopped the database, and have the pod running, you can restore the database by running these commands: - -```sh -$ kubectl -n ocelot-social get pods -# Copy the ID of the pod running Neo4J. -# Then upload your local backup to the pod. Note that once the pod gets deleted -# e.g. if you change the deployment, the backup file is gone with it. -$ kubectl cp ./neo4j-backup/ human-connection/:/root/ -$ kubectl -n ocelot-social exec -it bash -# Once you're in the pod restore the backup and overwrite the default database -# called `graph.db` with `--force`. -# This will delete all existing data in database `graph.db`! -> neo4j-admin restore --from=/root/neo4j-backup --force -> exit -``` - -Revert your changes to deployment `develop-neo4j` which will restart the database. diff --git a/deployment/src/old/volumes/uploads.yaml b/deployment/src/old/volumes/uploads.yaml deleted file mode 100644 index 45e1292a8..000000000 --- a/deployment/src/old/volumes/uploads.yaml +++ /dev/null @@ -1,12 +0,0 @@ ---- - kind: PersistentVolumeClaim - apiVersion: v1 - metadata: - name: uploads-claim - namespace: ocelot-social - spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: "10Gi" diff --git a/deployment/src/old/volumes/velero/README.md b/deployment/src/old/volumes/velero/README.md deleted file mode 100644 index bf63f13c8..000000000 --- a/deployment/src/old/volumes/velero/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# Velero - -{% hint style="danger" %} -I tried Velero and it did not work reliably all the time. Sometimes the -kubernetes cluster crashes during recovery or data is not fully recovered. - -Feel free to test it out and update this documentation once you feel that it's -working reliably. It is very likely that DigitalOcean had some bugs when I -tried out the steps below. -{% endhint %} - -We use [velero](https://github.com/heptio/velero) for on premise backups, we -tested on version `v0.11.0`, you can find their -documentation [here](https://heptio.github.io/velero/v0.11.0/). - -Our kubernets configurations adds some annotations to pods. The annotations -define the important persistent volumes that need to be backed up. Velero will -pick them up and store the volumes in the same cluster but in another namespace -`velero`. - -## Prequisites - -You have to install the binary `velero` on your computer and get a tarball of -the latest release. We use `v0.11.0` so visit the -[release](https://github.com/heptio/velero/releases/tag/v0.11.0) page and -download and extract e.g. [velero-v0.11.0-linux-arm64.tar.gz](https://github.com/heptio/velero/releases/download/v0.11.0/velero-v0.11.0-linux-amd64.tar.gz). - - -## Setup Velero Namespace - -Follow their [getting started](https://heptio.github.io/velero/v0.11.0/get-started) -instructions to setup the Velero namespace. We use -[Minio](https://docs.min.io/docs/deploy-minio-on-kubernetes) and -[restic](https://github.com/restic/restic), so check out Velero's instructions -how to setup [restic](https://heptio.github.io/velero/v0.11.0/restic): - -```sh -# run from the extracted folder of the tarball -$ kubectl apply -f config/common/00-prereqs.yaml -$ kubectl apply -f config/minio/ -``` - -Once completed, you should see the namespace in your kubernetes dashboard. - -## Manually Create an On-Premise Backup - -When you create your deployments for Human Connection the required annotations -should already be in place. So when you create a backup of namespace -`human-connection`: - -```sh -$ velero backup create hc-backup --include-namespaces=human-connection -``` - -That should backup your persistent volumes, too. When you enter: - -```sh -$ velero backup describe hc-backup --details -``` - -You should see the persistent volumes at the end of the log: - -```sh -.... - -Restic Backups: - Completed: - human-connection/develop-backend-5b6dd96d6b-q77n6: uploads - human-connection/develop-neo4j-686d768598-z2vhh: neo4j-data -``` - -## Simulate a Disaster - -Feel free to try out if you loose any data when you simulate a disaster and try -to restore the namespace from the backup: - -```sh -$ kubectl delete namespace human-connection -``` - -Wait until the wrongdoing has completed, then: -```sh -$ velero restore create --from-backup hc-backup -``` - -Now, I keep my fingers crossed that everything comes back again. If not, I feel -very sorry for you. - - -## Schedule a Regular Backup - -Check out the [docs](https://heptio.github.io/velero/v0.11.0/get-started). You -can create a regular schedule e.g. with: - -```sh -$ velero schedule create hc-weekly-backup --schedule="@weekly" --include-namespaces=human-connection -``` - -Inspect the created backups: - -```sh -$ velero schedule get -NAME STATUS CREATED SCHEDULE BACKUP TTL LAST BACKUP SELECTOR -hc-weekly-backup Enabled 2019-05-08 17:51:31 +0200 CEST @weekly 720h0m0s 6s ago - -$ velero backup get -NAME STATUS CREATED EXPIRES STORAGE LOCATION SELECTOR -hc-weekly-backup-20190508155132 Completed 2019-05-08 17:51:32 +0200 CEST 29d default - -$ velero backup describe hc-weekly-backup-20190508155132 --details -# see if the persistent volumes are backed up -``` diff --git a/deployment/src/old/volumes/volume-snapshots/README.md b/deployment/src/old/volumes/volume-snapshots/README.md deleted file mode 100644 index 010cfc636..000000000 --- a/deployment/src/old/volumes/volume-snapshots/README.md +++ /dev/null @@ -1,49 +0,0 @@ -# Kubernetes Volume Snapshots - -It is possible to backup persistent volumes through volume snapshots. This is especially handy if you don't want to stop the database to create an [offline backup](../neo4j-offline-backup/README.md) thus having a downtime. - -Kubernetes announced this feature in a [blog post](https://kubernetes.io/blog/2018/10/09/introducing-volume-snapshot-alpha-for-kubernetes/). Please make yourself familiar with it before you continue. - -## Create a Volume Snapshot - -There is an example in this folder how you can e.g. create a volume snapshot for the persistent volume claim of the database `volume-claim-ocelot-neo4j`, or for the uploads of the backend `volume-claim-ocelot-uploads`. - -Replace `YYYY-MM-DD` in the `metadata.name` entry in the yaml files with the actual date before you enter the following commands: - -```bash -# in folder deployment/volumes/volume-snapshots/ -kubectl apply -f neo4j-data-snapshot.yaml -# in case the images are stored on backend and not in S3 storage -kubectl apply -f backen-uploads-snapshot.yaml -``` - -If you are on DigitalOcean the volume snapshot should show up in the Web UI: - -![DigitalOcean Web UI showing a volume snapshot](./digital-ocean-volume-snapshots.png) - -## Provision a Volume based on a Snapshot - -Edit your persistent volume claim configuration and add a `dataSource` pointing -to your volume snapshot. [The blog post](https://kubernetes.io/blog/2018/10/09/introducing-volume-snapshot-alpha-for-kubernetes/) has an example in section "Provision a new volume from a snapshot with -Kubernetes". - -There is also an example in this folder how the configuration could look like. If you apply the configuration new persistent volume claim will be provisioned with the data from the volume snapshot: - -```bash -# in folder deployment/volumes/volume-snapshots/ -kubectl apply -f neo4j-data-provision-snapshot.yaml -``` - -## Data Consistency Warning - -Note that volume snapshots do not guarantee data consistency. Quote from the -[blog post](https://kubernetes.io/blog/2018/10/09/introducing-volume-snapshot-alpha-for-kubernetes/): - -> Please note that the alpha release of Kubernetes Snapshot does not provide -> any consistency guarantees. You have to prepare your application (pause -> application, freeze filesystem etc.) before taking the snapshot for data -> consistency. - -In case of Neo4J this probably means that enterprise edition is required which -supports [online backups](https://neo4j.com/docs/operations-manual/current/backup/). - diff --git a/deployment/src/old/volumes/volume-snapshots/backen-uploads-snapshot.yaml b/deployment/src/old/volumes/volume-snapshots/backen-uploads-snapshot.yaml deleted file mode 100644 index 697346c82..000000000 --- a/deployment/src/old/volumes/volume-snapshots/backen-uploads-snapshot.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -apiVersion: snapshot.storage.k8s.io/v1beta1 -kind: VolumeSnapshot -metadata: - name: YYYY-MM-DD-uploads-snapshot -spec: - source: - persistentVolumeClaimName: volume-claim-ocelot-uploads diff --git a/deployment/src/old/volumes/volume-snapshots/digital-ocean-volume-snapshots.png b/deployment/src/old/volumes/volume-snapshots/digital-ocean-volume-snapshots.png deleted file mode 100644 index cb6599616cad86471f5a990596a0f681470255c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 120810 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 z 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%0ACGdls>#g|@A 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 Jp!aEjgaMMT=M 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 docker-compose -f docker-compose.yml up +# > docker-compose -f docker-compose.yml up services: - - ######################################################## - # WEBAPP ############################################### - ######################################################## webapp: - # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there - image: ocelotsocialnetwork/webapp:local-production - container_name: webapp + image: ghcr.io/ocelot-social-community/ocelot-social/webapp:${OCELOT_VERSION:-latest} build: context: ./webapp target: production - networks: - - external-net depends_on: - backend ports: @@ -27,9 +19,6 @@ services: # Envs used in Dockerfile # - DOCKER_WORKDIR="/app" # - PORT="3000" - - BUILD_DATE - - BUILD_VERSION - - BUILD_COMMIT - NODE_ENV="production" # Application only envs - HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp @@ -37,18 +26,11 @@ services: env_file: - ./webapp/.env - ######################################################## - # FRONTEND ############################################# - ######################################################## frontend: - # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there - image: ocelotsocialnetwork/frontend:local-production - container_name: frontend + image: ghcr.io/ocelot-social-community/ocelot-social/frontend:${OCELOT_VERSION:-latest} build: context: ./frontend target: production - networks: - - external-net depends_on: - backend ports: @@ -57,9 +39,6 @@ services: # Envs used in Dockerfile # - DOCKER_WORKDIR="/app" # - PORT="3002" - - BUILD_DATE - - BUILD_VERSION - - BUILD_COMMIT - NODE_ENV="production" # Application only envs #- HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp @@ -67,19 +46,11 @@ services: env_file: - ./frontend/.env - ######################################################## - # BACKEND ############################################## - ######################################################## backend: - # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there - image: ocelotsocialnetwork/backend:local-production - container_name: backend + image: ghcr.io/ocelot-social-community/ocelot-social/backend:${OCELOT_VERSION:-latest} build: context: ./backend target: production - networks: - - external-net - - internal-net depends_on: - neo4j ports: @@ -90,9 +61,6 @@ services: # Envs used in Dockerfile # - DOCKER_WORKDIR="/app" # - PORT="4000" - - BUILD_DATE - - BUILD_VERSION - - BUILD_COMMIT - NODE_ENV="production" # Application only envs - DEBUG=false @@ -102,35 +70,20 @@ services: env_file: - ./backend/.env - ######################################################## - # MAINTENANCE ########################################## - ######################################################## maintenance: - # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there - image: ocelotsocialnetwork/maintenance:local-production - container_name: maintenance + image: ghcr.io/ocelot-social-community/ocelot-social/maintenance:${OCELOT_VERSION:-latest} build: - # TODO: Separate from webapp, this must be independent context: ./webapp dockerfile: Dockerfile.maintenance - networks: - - external-net ports: - 3001:80 - ######################################################## - # NEO4J ################################################ - ######################################################## neo4j: - # name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there - image: ocelotsocialnetwork/neo4j-community:local-production - container_name: neo4j + image: ghcr.io/ocelot-social-community/ocelot-social/neo4j build: context: ./neo4j # community edition 👆🏼, because we have no enterprise licence 👇🏼 at the moment target: community - networks: - - internal-net ports: - 7687:7687 volumes: @@ -152,11 +105,6 @@ services: # bring the database in offline mode to export or load dumps # command: ["tail", "-f", "/dev/null"] -networks: - external-net: - internal-net: - internal: true - volumes: backend_uploads: neo4j_data: diff --git a/neo4j/Dockerfile b/neo4j/Dockerfile index b1d07080e..a7f52ad36 100644 --- a/neo4j/Dockerfile +++ b/neo4j/Dockerfile @@ -1,45 +1,27 @@ -################################################################################## -# COMMUNITY ###################################################################### -################################################################################## FROM amd64/neo4j:4.4-community AS community - -# ENVs -## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0 -ARG BBUILD_DATE="1970-01-01T00:00:00.00Z" -ENV BUILD_DATE=$BBUILD_DATE -## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0 -ARG BBUILD_VERSION="0.0.0-0" -ENV BUILD_VERSION=$BBUILD_VERSION -## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000 -ARG BBUILD_COMMIT="0000000" -ENV BUILD_COMMIT=$BBUILD_COMMIT - -# Labels -LABEL org.label-schema.build-date="${BUILD_DATE}" LABEL org.label-schema.name="ocelot.social:neo4j" LABEL org.label-schema.description="Neo4J database of the Social Network Software ocelot.social with preinstalled database constraints and indices" LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md" LABEL org.label-schema.url="https://ocelot.social" LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/neo4j" -LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}" LABEL org.label-schema.vendor="ocelot.social Community" -LABEL org.label-schema.version="${BUILD_VERSION}" LABEL org.label-schema.schema-version="1.0" LABEL maintainer="devops@ocelot.social" - -# Install Additional Software ## install: wget, htop (TODO: why do we need htop?) RUN apt-get update && apt-get -y install wget htop ## install: apoc plugin for neo4j RUN wget https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/4.4.0.17/apoc-4.4.0.17-all.jar -P plugins/ -################################################################################## -# ENTERPRISE ##################################################################### -################################################################################## FROM neo4j:4.4-enterprise AS enterprise - -# Install Additional Software +LABEL org.label-schema.name="ocelot.social:neo4j" +LABEL org.label-schema.description="Neo4J database of the Social Network Software ocelot.social with preinstalled database constraints and indices" +LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md" +LABEL org.label-schema.url="https://ocelot.social" +LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/neo4j" +LABEL org.label-schema.vendor="ocelot.social Community" +LABEL org.label-schema.schema-version="1.0" +LABEL maintainer="devops@ocelot.social" ## install: wget, htop (TODO: why do we need htop?) RUN apt-get update && apt-get -y install wget htop ## install: apoc plugin for neo4j -RUN wget https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/4.4.0.17/apoc-4.4.0.17-all.jar -P plugins/ \ No newline at end of file +RUN wget https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/4.4.0.17/apoc-4.4.0.17-all.jar -P plugins/ diff --git a/webapp/Dockerfile b/webapp/Dockerfile index f610713fc..7ec65cbf9 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -1,108 +1,46 @@ -################################################################################## -# BASE (Is pushed to DockerHub for rebranding) ################################### -################################################################################## FROM node:20.12.1-alpine3.19 AS base - -# ENVs -## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame -ENV DOCKER_WORKDIR="/app" -## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0 -ARG BBUILD_DATE="1970-01-01T00:00:00.00Z" -ENV BUILD_DATE=$BBUILD_DATE -## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0 -ARG BBUILD_VERSION="0.0.0-0" -ENV BUILD_VERSION=$BBUILD_VERSION -## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000 -ARG BBUILD_COMMIT="0000000" -ENV BUILD_COMMIT=$BBUILD_COMMIT -## SET NODE_ENV -ENV NODE_ENV="production" -## App relevant Envs -ENV PORT="3000" - -# Labels -LABEL org.label-schema.build-date="${BUILD_DATE}" 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" LABEL org.label-schema.url="https://ocelot.social" LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/webapp" -LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}" LABEL org.label-schema.vendor="ocelot.social Community" -LABEL org.label-schema.version="${BUILD_VERSION}" LABEL org.label-schema.schema-version="1.0" LABEL maintainer="devops@ocelot.social" - -# Install Additional Software -## install: git -RUN apk --no-cache add git python3 make g++ - -# Settings -## Expose Container Port +ENV NODE_ENV="production" +ENV PORT="3000" EXPOSE ${PORT} +RUN apk --no-cache add git python3 make g++ bash jq +RUN mkdir -p /app +WORKDIR /app +CMD ["/bin/bash", "-c", "yarn run start"] -## Workdir -RUN mkdir -p ${DOCKER_WORKDIR} -WORKDIR ${DOCKER_WORKDIR} - -################################################################################## -# DEVELOPMENT (Connected to the local environment, to reload on demand) ########## -################################################################################## FROM base AS development +CMD ["/bin/bash", "-c", "yarn install && yarn run dev"] -# We don't need to copy or build anything since we gonna bind to the -# local filesystem which will need a rebuild anyway - -# Run command -# (for development we need to execute yarn install since the -# node_modules are on another volume and need updating) -CMD /bin/sh -c "yarn install && yarn run dev" - -################################################################################## -# CODE (Does contain all code files and is pushed to DockerHub for rebranding) ### -################################################################################## -FROM base AS code - -# copy everything, but do not build. +FROM base AS build COPY . . - -################################################################################## -# BUILD (Does contain all files and the compilate and is therefore bloated) ###### -################################################################################## -FROM code AS build - -# yarn install -RUN yarn install --production=false --frozen-lockfile --non-interactive -# yarn build -RUN yarn run build - -################################################################################## -# TEST ########################################################################### -################################################################################## -FROM build AS test - -# Run command -CMD /bin/sh -c "yarn run dev" - -################################################################################## -# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) # -################################################################################## -FROM base AS production - -# TODO - do all copying with one COPY command to have one layer -# Copy "binary"-files from build image -COPY --from=build ${DOCKER_WORKDIR}/.nuxt ./.nuxt -COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules -COPY --from=build ${DOCKER_WORKDIR}/nuxt.config.js ./nuxt.config.js +ONBUILD COPY ./branding . +ONBUILD RUN tools/merge-locales.sh +ONBUILD RUN yarn install --production=false --frozen-lockfile --non-interactive +ONBUILD RUN yarn run build +ONBUILD RUN mkdir /build +ONBUILD RUN cp -r ./.nuxt /build +ONBUILD RUN cp -r ./nuxt.config.js /build # Copy static files # TODO - this seems not be needed anymore for the new rebranding # TODO - this should be one Folder containign all stuff needed to be copied -COPY --from=build ${DOCKER_WORKDIR}/config/ ./config/ -COPY --from=build ${DOCKER_WORKDIR}/constants ./constants -COPY --from=build ${DOCKER_WORKDIR}/static ./static -COPY --from=build ${DOCKER_WORKDIR}/locales ./locales -# Copy package.json for script definitions (lock file should not be needed) -COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json +ONBUILD RUN cp -r ./config/ /build +ONBUILD RUN cp -r ./constants /build +ONBUILD RUN cp -r ./static /build +ONBUILD RUN cp -r ./locales /build +ONBUILD RUN cp -r ./package.json ./yarn.lock /build +ONBUILD RUN cd /build && yarn install --production=true --frozen-lockfile --non-interactive -# Run command -CMD /bin/sh -c "yarn run start" +FROM build AS test +CMD ["/bin/bash", "-c", "yarn run dev"] + +FROM build AS production_build + +FROM base AS production +COPY --from=production_build /build . diff --git a/webapp/Dockerfile.maintenance b/webapp/Dockerfile.maintenance index 096995dca..93d104fd5 100644 --- a/webapp/Dockerfile.maintenance +++ b/webapp/Dockerfile.maintenance @@ -1,66 +1,21 @@ -################################################################################## -# BASE ########################################################################### -################################################################################## -FROM node:20.12.1-alpine3.19 AS base - -# ENVs -## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame -ENV DOCKER_WORKDIR="/app" -## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0 -ARG BBUILD_DATE="1970-01-01T00:00:00.00Z" -ENV BUILD_DATE=$BBUILD_DATE -## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0 -ARG BBUILD_VERSION="0.0.0-0" -ENV BUILD_VERSION=$BBUILD_VERSION -## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000 -ARG BBUILD_COMMIT="0000000" -ENV BUILD_COMMIT=$BBUILD_COMMIT -## SET NODE_ENV -ENV NODE_ENV="production" -## App relevant Envs -ENV PORT="3000" - -# Labels -LABEL org.label-schema.build-date="${BUILD_DATE}" +FROM nginx:alpine AS base LABEL org.label-schema.name="ocelot.social:maintenance" LABEL org.label-schema.description="Maintenance page of the Social Network Software ocelot.social" LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md" LABEL org.label-schema.url="https://ocelot.social" LABEL org.label-schema.vcs-url="https://github.com/Ocelot-Social-Community/Ocelot-Social/tree/master/webapp" -LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}" LABEL org.label-schema.vendor="ocelot.social Community" -LABEL org.label-schema.version="${BUILD_VERSION}" LABEL org.label-schema.schema-version="1.0" LABEL maintainer="devops@ocelot.social" -# Install Additional Software -## install: git -RUN apk --no-cache add git python3 make g++ - -# Settings -## Expose Container Port -EXPOSE ${PORT} - -## Workdir -RUN mkdir -p ${DOCKER_WORKDIR} -WORKDIR ${DOCKER_WORKDIR} - -CMD ["yarn", "run", "start"] - -################################################################################## -# CODE (Does contain all code files and is pushed to DockerHub for rebranding) ### -################################################################################## -FROM base AS code - -COPY package.json yarn.lock ./ -# yarn install -RUN yarn install --production=false --frozen-lockfile --non-interactive - +FROM node:20.12.1-alpine3.19 AS build +ENV NODE_ENV="production" +RUN apk --no-cache add git python3 make g++ bash jq +RUN mkdir -p /app +WORKDIR /app COPY assets assets -# COPY components/_new/generic/ components/_new/generic COPY components/LocaleSwitch/ components/LocaleSwitch COPY components/Dropdown.vue components/Dropdown.vue -# COPY components/Logo/ components/Logo COPY layouts/blank.vue layouts/blank.vue COPY locales locales COPY mixins mixins @@ -69,25 +24,18 @@ COPY static static COPY constants constants COPY nuxt.config.js nuxt.config.js COPY config/ config/ - -# this is needed in rebranding +COPY tools/ tools/ COPY maintenance/nginx maintenance/nginx -# this will also ovewrite the existing package.json -COPY maintenance/source ./ +COPY maintenance/source maintenance/source +COPY package.json yarn.lock ./ +ONBUILD COPY ./branding . +ONBUILD RUN tools/merge-locales.sh +ONBUILD RUN yarn install --production=false --frozen-lockfile --non-interactive +ONBUILD RUN cp -r maintenance/source/* ./ +ONBUILD RUN yarn run generate -################################################################################## -# BUILD ### TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO ## -################################################################################## -FROM code AS build +FROM build AS production_build -# yarn generate -RUN yarn run generate - -################################################################################## -# PRODUCTION ### TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO # TODO #### -################################################################################## -FROM nginx:alpine AS production - -COPY --from=build ./app/dist/ /usr/share/nginx/html/ -RUN rm /etc/nginx/conf.d/default.conf -COPY maintenance/nginx/custom.conf /etc/nginx/conf.d/ +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/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/delete.cql b/webapp/branding/.gitkeep similarity index 100% rename from deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/invites/delete.cql rename to webapp/branding/.gitkeep diff --git a/deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/delete.cql b/webapp/locales/tmp/.gitkeep similarity index 100% rename from deployment/src/old/legacy-migration/maintenance-worker/migration/neo4j/notifications/delete.cql rename to webapp/locales/tmp/.gitkeep diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 086a62f1b..4e82e9330 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -302,7 +302,7 @@ export default { modules: [ { preTransformNode(abstractSyntaxTreeElement) { - if (!ctx.isDev) { + if (!ctx.isDev && CONFIG.NODE_ENV !== 'test') { const { attrsMap, attrsList } = abstractSyntaxTreeElement tagAttributesForTesting.forEach((attribute) => { if (attrsMap[attribute]) { diff --git a/webapp/package.json b/webapp/package.json index 70f35761c..486937dfa 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -25,6 +25,7 @@ "@mapbox/mapbox-gl-geocoder": "^5.0.2", "@nuxtjs/apollo": "^4.0.0-rc19", "@nuxtjs/axios": "~5.9.7", + "@nuxtjs/composition-api": "0.32.0", "@nuxtjs/dotenv": "~1.4.1", "@nuxtjs/pwa": "^3.0.0-beta.20", "@nuxtjs/sentry": "^4.0.0", @@ -72,7 +73,6 @@ "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.25.8", "@faker-js/faker": "9.5.0", - "@nuxtjs/composition-api": "0.32.0", "@storybook/addon-a11y": "^8.0.8", "@storybook/addon-actions": "^5.3.21", "@storybook/addon-notes": "^5.3.18", diff --git a/deployment/src/tools/merge-locales.sh b/webapp/tools/merge-locales.sh similarity index 100% rename from deployment/src/tools/merge-locales.sh rename to webapp/tools/merge-locales.sh diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 76b14180a..55873eb69 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -123,6 +123,15 @@ "@babel/highlight" "^7.25.7" picocolors "^1.0.0" +"@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" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.7", "@babel/compat-data@^7.25.8": version "7.25.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.8.tgz#0376e83df5ab0eb0da18885c0140041f0747a402" @@ -133,6 +142,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== +"@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/compat-data@^7.9.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.9.0.tgz#04815556fc90b0c174abd2c0c1bb966faa036a6c" @@ -142,7 +156,7 @@ invariant "^2.2.4" semver "^5.5.0" -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.7", "@babel/core@^7.23.9", "@babel/core@^7.9.0": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.9.0": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.4.tgz#1f758428e88e0d8c563874741bc4ffc4f71a4717" integrity sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg== @@ -163,6 +177,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.16.7": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" + integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.9" + "@babel/types" "^7.26.9" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/core@^7.25.8": version "7.25.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.25.8.tgz#a57137d2a51bbcffcfaeba43cb4dd33ae3e0e1c6" @@ -194,7 +229,18 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.16.8", "@babel/generator@^7.24.1", "@babel/generator@^7.24.4", "@babel/generator@^7.7.2": +"@babel/generator@^7.16.8", "@babel/generator@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== + dependencies: + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + +"@babel/generator@^7.24.1", "@babel/generator@^7.24.4", "@babel/generator@^7.7.2": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.4.tgz#1fc55532b88adf952025d5d2d1e71f946cb1c498" integrity sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw== @@ -282,6 +328,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@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== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz#5d65074c76cae75607421c00d6bd517fe1892d6b" @@ -449,6 +506,14 @@ "@babel/traverse" "^7.25.7" "@babel/types" "^7.25.7" +"@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" + integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-transforms@^7.21.5", "@babel/helper-module-transforms@^7.23.3", "@babel/helper-module-transforms@^7.9.0": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" @@ -470,6 +535,15 @@ "@babel/helper-validator-identifier" "^7.25.7" "@babel/traverse" "^7.25.7" +"@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== + dependencies: + "@babel/helper-module-imports" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/traverse" "^7.25.9" + "@babel/helper-optimise-call-expression@^7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz#1de1b99688e987af723eed44fa7fc0ee7b97d77a" @@ -619,6 +693,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz#d50e8d37b1176207b4fe9acedec386c565a44a54" integrity sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g== +"@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" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" @@ -629,6 +708,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz#77b7f60c40b15c97df735b38a66ba1d7c3e93da5" integrity sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" @@ -639,6 +723,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz#97d1d684448228b30b506d90cace495d6f492729" integrity sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ== +"@babel/helper-validator-option@^7.25.9": + version "7.25.9" + 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.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz#9f6021dd1c4fdf4ad515c809967fc4bac9a70fe7" @@ -675,6 +764,14 @@ "@babel/template" "^7.25.7" "@babel/types" "^7.25.7" +"@babel/helpers@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" + integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + "@babel/highlight@^7.10.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" @@ -704,11 +801,18 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.16.8", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4", "@babel/parser@^7.7.0": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== +"@babel/parser@^7.16.8", "@babel/parser@^7.23.5", "@babel/parser@^7.25.3", "@babel/parser@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== + dependencies: + "@babel/types" "^7.26.9" + "@babel/parser@^7.25.7", "@babel/parser@^7.25.8": version "7.25.8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.25.8.tgz#f6aaf38e80c36129460c1657c0762db584c9d5e2" @@ -1880,7 +1984,29 @@ "@babel/parser" "^7.25.7" "@babel/types" "^7.25.7" -"@babel/traverse@^7.16.8", "@babel/traverse@^7.24.1", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6": +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + +"@babel/traverse@^7.16.8", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + debug "^4.3.1" + globals "^11.1.0" + +"@babel/traverse@^7.24.1", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== @@ -1918,7 +2044,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.16.8", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.6.3", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7", "@babel/types@^7.9.0": +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.6.3", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7", "@babel/types@^7.9.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== @@ -1927,6 +2053,14 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.16.8", "@babel/types@^7.25.9", "@babel/types@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@babel/types@^7.25.7", "@babel/types@^7.25.8": version "7.25.8" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.25.8.tgz#5cf6037258e8a9bcad533f4979025140cb9993e1" @@ -2633,6 +2767,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/sourcemap-codec@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.9": version "0.3.17" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" @@ -4838,23 +4977,34 @@ semver "^6.1.0" strip-ansi "^6.0.0" -"@vue/compiler-core@3.2.45", "@vue/compiler-core@^3.2.26": - version "3.2.45" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz#d9311207d96f6ebd5f4660be129fb99f01ddb41b" - integrity sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A== +"@vue/compiler-core@3.3.13": + version "3.3.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.13.tgz#b3d5f8f84caee5de3f31d95cb568d899fd19c599" + integrity sha512-bwi9HShGu7uaZLOErZgsH2+ojsEdsjerbf2cMXPwmvcgZfVPZ2BVZzCVnwZBxTAYd6Mzbmf6izcUNDkWnBBQ6A== dependencies: - "@babel/parser" "^7.16.4" - "@vue/shared" "3.2.45" + "@babel/parser" "^7.23.5" + "@vue/shared" "3.3.13" estree-walker "^2.0.2" - source-map "^0.6.1" + source-map-js "^1.0.2" + +"@vue/compiler-core@3.5.13", "@vue/compiler-core@^3.2.26": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz#b0ae6c4347f60c03e849a05d34e5bf747c9bda05" + integrity sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q== + dependencies: + "@babel/parser" "^7.25.3" + "@vue/shared" "3.5.13" + entities "^4.5.0" + estree-walker "^2.0.2" + source-map-js "^1.2.0" "@vue/compiler-dom@^3.2.26": - version "3.2.45" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz#c43cc15e50da62ecc16a42f2622d25dc5fd97dce" - integrity sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw== + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz#bb1b8758dbc542b3658dda973b98a1c9311a8a58" + integrity sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA== dependencies: - "@vue/compiler-core" "3.2.45" - "@vue/shared" "3.2.45" + "@vue/compiler-core" "3.5.13" + "@vue/shared" "3.5.13" "@vue/component-compiler-utils@^3.1.0": version "3.1.0" @@ -4872,9 +5022,9 @@ vue-template-es2015-compiler "^1.9.0" "@vue/composition-api@^1.4.6": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-1.7.1.tgz#aa6831be5a12817d93e89e247460c310dd7a3a32" - integrity sha512-xDWoEtxGXhH9Ku3ROYX/rzhcpt4v31hpPU5zF3UeVC/qxA3dChmqU8zvTUYoKh3j7rzpNsoFOwqsWG7XPMlaFA== + version "1.7.2" + resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-1.7.2.tgz#0b656f3ec39fefc2cf40aaa8c12426bcfeae1b44" + integrity sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw== "@vue/eslint-config-prettier@~6.0.0": version "6.0.0" @@ -4884,15 +5034,15 @@ eslint-config-prettier "^6.0.0" "@vue/reactivity-transform@^3.2.26": - version "3.2.45" - resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz#07ac83b8138550c83dfb50db43cde1e0e5e8124d" - integrity sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ== + version "3.3.13" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.13.tgz#dc8e9be961865dc666e367e1aaaea0716afa5c90" + integrity sha512-oWnydGH0bBauhXvh5KXUy61xr9gKaMbtsMHk40IK9M4gMuKPJ342tKFarY0eQ6jef8906m35q37wwA8DMZOm5Q== dependencies: - "@babel/parser" "^7.16.4" - "@vue/compiler-core" "3.2.45" - "@vue/shared" "3.2.45" + "@babel/parser" "^7.23.5" + "@vue/compiler-core" "3.3.13" + "@vue/shared" "3.3.13" estree-walker "^2.0.2" - magic-string "^0.25.7" + magic-string "^0.30.5" "@vue/server-test-utils@~1.0.0-beta.31": version "1.0.0-beta.32" @@ -4902,10 +5052,15 @@ "@types/cheerio" "^0.22.10" cheerio "^1.0.0-rc.2" -"@vue/shared@3.2.45", "@vue/shared@^3.2.26": - version "3.2.45" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2" - integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg== +"@vue/shared@3.3.13": + version "3.3.13" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.13.tgz#4cb73cda958d77ffd389c8640cf7d93a10ac676f" + integrity sha512-/zYUwiHD8j7gKx2argXEMCUXVST6q/21DFU0sTfNX0URJroCe3b1UF6vLJ3lQDfLNIiiRl2ONp7Nh5UVWS6QnA== + +"@vue/shared@3.5.13", "@vue/shared@^3.2.26": + version "3.5.13" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f" + integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ== "@vue/test-utils@1.3.4": version "1.3.4" @@ -8963,6 +9118,11 @@ entities@^4.4.0: resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== +entities@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + env-ci@3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/env-ci/-/env-ci-3.2.2.tgz#06936f1fcfbc999102a2211fc2539df64062b61f" @@ -13316,11 +13476,18 @@ lru_map@^0.3.3: integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= magic-string@^0.25.7: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== dependencies: - sourcemap-codec "^1.4.4" + sourcemap-codec "^1.4.8" + +magic-string@^0.30.5: + version "0.30.17" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" + integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.0" make-dir@^1.0.0: version "1.3.0" @@ -17654,6 +17821,11 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== +source-map-js@^1.0.2, source-map-js@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + source-map-resolve@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" @@ -17701,7 +17873,7 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sourcemap-codec@^1.4.4: +sourcemap-codec@^1.4.8: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== @@ -18853,7 +19025,12 @@ tslib@^1, tslib@^1.10.0, 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.3.1, tslib@^2.6.2: +tslib@^2.3.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== From 87e5fc6bf8b8f343bfd87ee0cb3920ed2b232836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Sat, 1 Mar 2025 08:00:41 +0100 Subject: [PATCH 029/266] feat(webapp): implement configurable custom button in header (#8215) * Implement configurable custom button in header * Implement default values for 'headerMenu' --------- Co-authored-by: mahula --- .../components/CustomButton/CustomButton.vue | 74 +++++++++++++++++++ webapp/components/HeaderMenu/HeaderMenu.vue | 27 ++++++- webapp/constants/headerMenu.js | 14 +--- webapp/constants/headerMenuBranded.js | 27 +++++++ 4 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 webapp/components/CustomButton/CustomButton.vue create mode 100644 webapp/constants/headerMenuBranded.js diff --git a/webapp/components/CustomButton/CustomButton.vue b/webapp/components/CustomButton/CustomButton.vue new file mode 100644 index 000000000..a5fd50c99 --- /dev/null +++ b/webapp/components/CustomButton/CustomButton.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/webapp/components/HeaderMenu/HeaderMenu.vue b/webapp/components/HeaderMenu/HeaderMenu.vue index c1f13581d..26e6aede7 100644 --- a/webapp/components/HeaderMenu/HeaderMenu.vue +++ b/webapp/components/HeaderMenu/HeaderMenu.vue @@ -96,6 +96,10 @@ + + + + @@ -214,6 +218,18 @@ + + + +

+ +
+ + @@ -271,9 +287,10 @@ import { SHOW_CONTENT_FILTER_HEADER_MENU } from '~/constants/filter.js' import LOGOS from '~/constants/logos.js' import AvatarMenu from '~/components/AvatarMenu/AvatarMenu' import ChatNotificationMenu from '~/components/ChatNotificationMenu/ChatNotificationMenu' +import CustomButton from '~/components/CustomButton/CustomButton' import FilterMenu from '~/components/FilterMenu/FilterMenu.vue' import GroupButton from '~/components/Group/GroupButton' -import headerMenu from '~/constants/headerMenu.js' +import headerMenuBranded from '~/constants/headerMenuBranded.js' import InviteButton from '~/components/InviteButton/InviteButton' import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch' import Logo from '~/components/Logo/Logo' @@ -286,6 +303,8 @@ import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParams export default { components: { AvatarMenu, + ChatNotificationMenu, + CustomButton, FilterMenu, GroupButton, InviteButton, @@ -295,7 +314,6 @@ export default { NotificationMenu, PageParamsLink, SearchField, - ChatNotificationMenu, }, props: { showMobileMenu: { type: Boolean, default: false }, @@ -309,8 +327,9 @@ export default { LOGOS, SHOW_GROUP_BUTTON_IN_HEADER, SHOW_CONTENT_FILTER_HEADER_MENU, - isHeaderMenu: headerMenu.MENU.length > 0, - menu: headerMenu.MENU, + isHeaderMenu: headerMenuBranded.MENU.length > 0, + customButton: headerMenuBranded.CUSTOM_BUTTON, + menu: headerMenuBranded.MENU, mobileSearchVisible: false, toggleMobileMenu: false, inviteRegistration: this.$env.INVITE_REGISTRATION === true, // for 'false' in .env INVITE_REGISTRATION is of type undefined and not(!) boolean false, because of internal handling, diff --git a/webapp/constants/headerMenu.js b/webapp/constants/headerMenu.js index aa87a598c..b1c6ea436 100644 --- a/webapp/constants/headerMenu.js +++ b/webapp/constants/headerMenu.js @@ -1,13 +1 @@ -export default { - MENU: [ - // { - // nameIdent: 'nameIdent', - // path: '/', - // }, - // { - // nameIdent: 'nameIdent', - // url: 'https://ocelot.social', - // target: '_blank', - // }, - ], -} +export default {} diff --git a/webapp/constants/headerMenuBranded.js b/webapp/constants/headerMenuBranded.js new file mode 100644 index 000000000..2acba99ed --- /dev/null +++ b/webapp/constants/headerMenuBranded.js @@ -0,0 +1,27 @@ +import { merge } from 'lodash' +import headerMenu from '~/constants/headerMenu.js' + +const defaultHeaderMenu = { + CUSTOM_BUTTON: { + // iconPath: '/img/custom/X', + // iconWidth: '28px', + // iconAltText: 'X', + // toolTipIdent: 'nameIdent', + // path: '/', + // url: 'https://ocelot.social/en/donate', + // target: '_blank', + }, + MENU: [ + // { + // nameIdent: 'nameIdent', + // path: '/', + // }, + // { + // nameIdent: 'nameIdent', + // url: 'https://ocelot.social', + // target: '_blank', + // }, + ], +} + +export default merge(defaultHeaderMenu, headerMenu) From 51b9bb43159b87e41a6474cc13d0feacb551ee39 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 2 Mar 2025 17:42:11 +0100 Subject: [PATCH 030/266] removed dockerhub related stuff (#8249) --- .github/workflows/publish.yml | 228 ---------------------------------- 1 file changed, 228 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4291e5dac..962520300 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -6,234 +6,6 @@ on: - master jobs: - ############################################################################## - # JOB: DOCKER BUILD COMMUNITY NEO4J ########################################## - ############################################################################## - build_production_neo4j: - name: Docker Build Production - Neo4J - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - - name: Setup env - run: | - echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV - echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV - echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV - - name: Neo4J | Build `community` image - run: | - docker build --target community \ - --tag "ocelotsocialnetwork/neo4j-community:latest" \ - --tag "ocelotsocialnetwork/neo4j-community:${VERSION}" \ - --tag "ocelotsocialnetwork/neo4j-community:${BUILD_VERSION}" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - neo4j/ - - name: Neo4J | Save docker image - run: docker save "ocelotsocialnetwork/neo4j-community" > /tmp/neo4j.tar - - name: Upload Artifact - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 - with: - name: docker-neo4j-community - path: /tmp/neo4j.tar - - ############################################################################## - # JOB: DOCKER BUILD PRODUCTION BACKEND ####################################### - ############################################################################## - build_production_backend: - name: Docker Build Production - Backend - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - - name: Setup env - run: | - echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV - echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV - echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV - - name: Backend | Build `production` image - run: | - docker build --target base \ - --tag "ocelotsocialnetwork/backend:latest-base" \ - --tag "ocelotsocialnetwork/backend:${VERSION}-base" \ - --tag "ocelotsocialnetwork/backend:${BUILD_VERSION}-base" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - backend/ - docker build --target code \ - --tag "ocelotsocialnetwork/backend:latest-code" \ - --tag "ocelotsocialnetwork/backend:${VERSION}-code" \ - --tag "ocelotsocialnetwork/backend:${BUILD_VERSION}-code" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - backend/ - docker build --target production \ - --tag "ocelotsocialnetwork/backend:latest" \ - --tag "ocelotsocialnetwork/backend:${VERSION}" \ - --tag "ocelotsocialnetwork/backend:${BUILD_VERSION}" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - backend/ - - name: Backend | Save docker image - run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar - - name: Upload Artifact - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 - with: - name: docker-backend-production - path: /tmp/backend.tar - - ############################################################################## - # JOB: DOCKER BUILD PRODUCTION WEBAPP ######################################## - ############################################################################## - build_production_webapp: - name: Docker Build Production - WebApp - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - - name: Setup env - run: | - echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV - echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV - echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV - - name: Webapp | Build `production` image - run: | - docker build --target base \ - --tag "ocelotsocialnetwork/webapp:latest-base" \ - --tag "ocelotsocialnetwork/webapp:${VERSION}-base" \ - --tag "ocelotsocialnetwork/webapp:${BUILD_VERSION}-base" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - webapp/ - docker build --target code \ - --tag "ocelotsocialnetwork/webapp:latest-code" \ - --tag "ocelotsocialnetwork/webapp:${VERSION}-code" \ - --tag "ocelotsocialnetwork/webapp:${BUILD_VERSION}-code" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - webapp/ - docker build --target production \ - --tag "ocelotsocialnetwork/webapp:latest" \ - --tag "ocelotsocialnetwork/webapp:${VERSION}" \ - --tag "ocelotsocialnetwork/webapp:${BUILD_VERSION}" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - webapp/ - - name: Webapp | Save docker image - run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar - - name: Upload Artifact - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 - with: - name: docker-webapp-production - path: /tmp/webapp.tar - - ############################################################################## - # JOB: DOCKER BUILD PRODUCTION MAINTENANCE ################################### - ############################################################################## - build_production_maintenance: - name: Docker Build Production - Maintenance - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - - name: Setup env - run: | - echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV - echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_ENV - echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV - - name: Maintenance | Build `production` image - run: | - docker build --target base \ - --tag "ocelotsocialnetwork/maintenance:latest-base" \ - --tag "ocelotsocialnetwork/maintenance:${VERSION}-base" \ - --tag "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-base" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - -f webapp/Dockerfile.maintenance \ - webapp/ - docker build --target code \ - --tag "ocelotsocialnetwork/maintenance:latest-code" \ - --tag "ocelotsocialnetwork/maintenance:${VERSION}-code" \ - --tag "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-code" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - -f webapp/Dockerfile.maintenance \ - webapp/ - docker build --target production \ - --tag "ocelotsocialnetwork/maintenance:latest" \ - --tag "ocelotsocialnetwork/maintenance:${VERSION}" \ - --tag "ocelotsocialnetwork/maintenance:${BUILD_VERSION}" \ - --build-arg BBUILD_DATE=$BUILD_DATE \ - --build-arg BBUILD_VERSION=$BUILD_VERSION \ - --build-arg BBUILD_COMMIT=$BUILD_COMMIT \ - -f webapp/Dockerfile.maintenance \ - webapp/ - - name: Maintenance | Save docker image - run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar - - name: Upload Artifact - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 - with: - name: docker-maintenance-production - path: /tmp/maintenance.tar - - ############################################################################## - # JOB: UPLOAD TO DOCKERHUB ################################################### - ############################################################################## - upload_to_dockerhub: - name: Upload to Dockerhub - runs-on: ubuntu-latest - needs: [build_production_neo4j,build_production_backend,build_production_webapp,build_production_maintenance] - env: - DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} - DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} - steps: - - name: Download Docker Image (Neo4J) - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: docker-neo4j-community - path: /tmp - - run: docker load < /tmp/neo4j.tar - - name: Download Docker Image (Backend) - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: docker-backend-production - path: /tmp - - run: docker load < /tmp/backend.tar - - name: Download Docker Image (WebApp) - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: docker-webapp-production - path: /tmp - - run: docker load < /tmp/webapp.tar - - name: Download Docker Image (Maintenance) - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 - with: - name: docker-maintenance-production - path: /tmp - - run: docker load < /tmp/maintenance.tar - - - name: login to dockerhub - run: echo "${DOCKERHUB_TOKEN}" | docker login -u "${DOCKERHUB_USERNAME}" --password-stdin - - name: Push images - run: | - docker push --all-tags ocelotsocialnetwork/neo4j-community - docker push --all-tags ocelotsocialnetwork/backend - docker push --all-tags ocelotsocialnetwork/webapp - docker push --all-tags ocelotsocialnetwork/maintenance - ############################################################################## # JOB: GITHUB TAG LATEST VERSION ############################################# ############################################################################## From cc6aded6852d4ae662eeefa9573b944b77af79e5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 3 Mar 2025 12:59:37 +0100 Subject: [PATCH 031/266] remove the requirement for non-existant job in publish workflow (#8251) --- .github/workflows/publish.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 962520300..29d936d2d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -12,7 +12,6 @@ jobs: github_tag: name: Tag latest version on Github runs-on: ubuntu-latest - needs: [upload_to_dockerhub] steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 From 292155ea104b5234e9bd6af3f801ff99b43fdfcb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 3 Mar 2025 17:04:17 +0100 Subject: [PATCH 032/266] v3.2.1 (#8248) updated changelog after rebase --- CHANGELOG.md | 298 +++++++++++++++++++++++++ backend/package.json | 4 +- frontend/package.json | 2 +- package.json | 2 +- webapp/maintenance/source/package.json | 2 +- webapp/package.json | 2 +- 6 files changed, 304 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b31c96185..f383034d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,306 @@ 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.2.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.2.0...3.2.1) + +- remove the requirement for non-existant job in publish workflow [`#8251`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8251) +- removed dockerhub related stuff [`#8249`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8249) +- feat(webapp): implement configurable custom button in header [`#8215`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8215) +- feat(other): major improvement of deployment [`#7925`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7925) +- Bump the metascraper group across 1 directory with 12 updates [`#8187`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8187) +- Bump peter-evans/repository-dispatch from 39d2331fbbe4be56c4434ca745a23633155f9cdf to b0b38f73c8333be75d585a92b2c630a10d2a78f5 [`#8206`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8206) +- Bump gaurav-nelson/github-action-markdown-link-check from 7d83e59a57f3c201c76eed3d33dff64ec4452d27 to 1b916f2cf6c36510a6059943104e3c42ce6c16bc [`#8178`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8178) +- Bump xregexp from 4.3.0 to 5.1.2 in /webapp [`#8216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8216) +- Bump xregexp from 4.3.0 to 5.1.2 in /backend [`#8225`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8225) +- Bump eslint-import-resolver-typescript from 3.7.0 to 3.8.3 in /backend [`#8222`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8222) +- Bump nodemailer from 6.9.16 to 6.10.0 in /backend [`#8173`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8173) +- Bump @faker-js/faker from 9.3.0 to 9.5.0 in /backend [`#8204`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8204) +- Bump the babel group across 1 directory with 2 updates [`#8205`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8205) +- Bump the babel group across 1 directory with 2 updates [`#8197`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8197) +- Bump @types/node from 22.10.2 to 22.13.5 in /backend [`#8223`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8223) +- Bump eslint-plugin-jest from 28.10.0 to 28.11.0 in /backend [`#8160`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8160) +- Bump @faker-js/faker from 9.3.0 to 9.5.0 in /webapp [`#8211`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8211) +- Bump @faker-js/faker from 9.3.0 to 9.5.0 [`#8196`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8196) +- Bump eslint-config-prettier from 9.1.0 to 10.0.1 in /backend [`#8165`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8165) +- Bump eslint-plugin-prettier from 5.2.1 to 5.2.3 in /backend [`#8170`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8170) +- Bump prettier from 3.4.2 to 3.5.2 in /backend [`#8228`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8228) +- Bump eslint-config-prettier from 6.15.0 to 10.0.1 in /webapp [`#8152`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8152) +- Bump eslint-plugin-prettier from 5.2.1 to 5.2.3 in /webapp [`#8179`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8179) +- Bump prettier from 3.4.2 to 3.5.2 in /webapp [`#8227`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8227) +- Bump sass from 1.83.0 to 1.85.0 in /webapp [`#8207`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8207) +- Bump the cypress group across 1 directory with 3 updates [`#8219`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8219) +- Bump actions/setup-node from 4.1.0 to 4.2.0 [`#8188`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8188) +- Bump actions/upload-artifact from 4.4.3 to 4.6.1 [`#8220`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8220) +- Bump actions/cache from 4.1.2 to 4.2.1 [`#8221`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8221) +- Bump typescript from 5.7.2 to 5.7.3 in /backend [`#8143`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8143) +- Bump the cypress group across 1 directory with 5 updates [`#8175`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8175) +- fix(frontend): hide invite button on mobile if disabled [`#8128`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8128) +- Bump eslint-plugin-promise from 7.1.0 to 7.2.1 in /webapp [`#8096`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8096) +- Bump eslint-plugin-vue from 9.31.0 to 9.32.0 in /webapp [`#8104`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8104) +- Bump sass from 1.75.0 to 1.83.0 in /webapp [`#8121`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8121) +- Bump prettier from 3.3.3 to 3.4.2 in /webapp [`#8100`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8100) +- Bump @faker-js/faker from 9.2.0 to 9.3.0 in /webapp [`#8101`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8101) +- Bump express from 4.21.1 to 4.21.2 in /webapp [`#8105`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8105) +- Bump dotenv from 16.4.5 to 16.4.7 [`#8119`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8119) +- Bump @faker-js/faker from 9.2.0 to 9.3.0 [`#8120`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8120) +- Bump the cypress group across 1 directory with 2 updates [`#8134`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8134) +- Bump sanitize-html from 2.13.1 to 2.14.0 in /backend [`#8132`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8132) +- Bump eslint-import-resolver-typescript from 3.6.3 to 3.7.0 in /backend [`#8113`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8113) +- Bump eslint-plugin-jest from 28.9.0 to 28.10.0 in /backend [`#8133`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8133) +- Bump @babel/cli from 7.25.9 to 7.26.4 in /backend in the babel group [`#8108`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8108) +- Bump express from 4.21.1 to 4.21.2 in /backend [`#8109`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8109) +- Bump linkify-html from 4.1.4 to 4.2.0 in /backend [`#8116`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8116) +- Bump @faker-js/faker from 9.2.0 to 9.3.0 in /backend [`#8115`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8115) +- Bump graphql-redis-subscriptions from 2.2.1 to 2.7.0 in /backend [`#8122`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8122) +- Bump @types/node from 22.9.3 to 22.10.2 in /backend [`#8124`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8124) +- Bump nodemon from 3.1.7 to 3.1.9 in /backend [`#8123`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8123) +- Bump linkifyjs from 4.1.4 to 4.2.0 in /backend [`#8112`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8112) +- Bump prettier from 3.3.3 to 3.4.2 in /backend [`#8111`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8111) +- Bump dotenv from 16.4.5 to 16.4.7 in /backend [`#8110`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8110) +- Bump the cypress group with 2 updates [`#8092`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8092) +- Bump @types/node from 22.9.0 to 22.9.3 in /backend [`#8093`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8093) +- Bump typescript from 5.6.3 to 5.7.2 in /backend [`#8086`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8086) +- Bump linkify-html from 4.1.3 to 4.1.4 in /backend [`#8085`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8085) +- Bump linkifyjs from 4.1.3 to 4.1.4 in /backend [`#8088`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8088) +- Bump eslint-plugin-vue from 9.30.0 to 9.31.0 in /webapp [`#8081`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8081) +- Bump @badeball/cypress-cucumber-preprocessor from 21.0.2 to 21.0.3 in the cypress group [`#8083`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8083) +- Bump amannn/action-semantic-pull-request from 80c0371c57c5142ed6c844270bba1864bac8a4c6 to 40166f00814508ec3201fc8595b393d451c8cd80 [`#8078`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8078) +- Bump peter-evans/repository-dispatch from 28a02cc85a65462275a97039562642d3731c318c to 39d2331fbbe4be56c4434ca745a23633155f9cdf [`#8077`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8077) +- Bump @faker-js/faker from 9.1.0 to 9.2.0 [`#8069`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8069) +- Bump peter-evans/repository-dispatch from af19ba8f0cf25cd177db9cf116a43798bc7446c7 to 28a02cc85a65462275a97039562642d3731c318c [`#8067`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8067) +- Bump cypress from 13.15.1 to 13.15.2 in the cypress group [`#8068`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8068) +- Bump @faker-js/faker from 9.1.0 to 9.2.0 in /webapp [`#8074`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8074) +- Bump eslint-plugin-jest from 28.8.3 to 28.9.0 in /backend [`#8073`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8073) +- Bump aws-sdk from 2.1691.0 to 2.1692.0 in /backend [`#8072`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8072) +- Bump @faker-js/faker from 9.1.0 to 9.2.0 in /backend [`#8071`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8071) +- Bump @types/node from 22.8.7 to 22.9.0 in /backend [`#8070`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8070) +- Bump @types/node from 22.8.4 to 22.8.7 in /backend [`#8066`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8066) +- Bump peter-evans/repository-dispatch from cc7d8686e214c240128fe2680a5c1c5486ae19c4 to af19ba8f0cf25cd177db9cf116a43798bc7446c7 [`#8061`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8061) +- Bump eslint-config-standard from 14.1.1 to 15.0.1 in /webapp [`#8063`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8063) +- Bump eslint-plugin-promise from 4.3.1 to 7.1.0 in /webapp [`#8056`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8056) +- Bump eslint-plugin-import from 2.20.2 to 2.31.0 in /webapp [`#8055`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8055) +- Bump eslint-plugin-prettier from 5.1.3 to 5.2.1 in /webapp [`#8051`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8051) +- Bump eslint-plugin-vue from 9.26.0 to 9.30.0 in /webapp [`#8057`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8057) +- fix(frontend): adapt to different logo sizes, including fixes for Safari [`#8044`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8044) +- refactor(other): dependabot: further package update regroupings [`#8045`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8045) +- Bump @types/node from 22.8.1 to 22.8.4 in /backend [`#8048`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8048) +- Bump the cypress group across 1 directory with 4 updates [`#8019`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8019) +- refactor(maintenance): dependabot: disable frontend dependency checks [`#8041`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8041) +- build(backend): update minor backend dependencies [`#8046`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8046) +- refactor(other): adapt cypress config to cypress-cucumber-preprocessor update [`#8043`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8043) +- Bump crazy-max/ghaction-github-pages from 08f571653184e9ff3d598bdda53ffd4ed00ed562 to fbf0a4fa4e00f45accd6cf3232368436ec06ed59 [`#8025`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8025) +- fix(other): revert a change, which accedentally was merged [`#8039`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8039) +- Bump nodemailer from 6.9.13 to 6.9.16 in /backend [`#8026`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8026) +- Bump @faker-js/faker from 9.0.3 to 9.1.0 [`#8029`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8029) +- Bump @faker-js/faker from 9.0.3 to 9.1.0 in /webapp [`#8036`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8036) +- Bump @faker-js/faker from 9.0.3 to 9.1.0 in /backend [`#8031`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8031) +- refactor(other): dependabot: degroups webapp package updates for babel, graphql, neo4j, and nuxt [`#8024`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8024) +- Bump slug from 9.1.0 to 10.0.0 [`#7962`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7962) +- Bump auto-changelog from 2.4.0 to 2.5.0 [`#7842`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7842) +- Bump helmet from 7.1.0 to 8.0.0 in /backend [`#7916`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7916) +- refactor(webapp): migrate sass devisions [`#7988`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7988) +- Bump the vitest group across 1 directory with 2 updates [`#7963`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7963) +- Bump actions/cache from 8469c94c6a180dfb41a1bd7e1b46ac557ea124f1 to 6849a6489940f00c2f30c0fb92c6274307ccb58a [`#7990`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7990) +- Bump pinia-plugin-persistedstate from 4.1.1 to 4.1.2 in /frontend in the pinia group [`#8010`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8010) +- Bump the babel group across 1 directory with 3 updates [`#8017`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8017) +- Bump sass from 1.77.6 to 1.80.4 in /frontend [`#8023`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8023) +- Bump @types/node from 22.7.7 to 22.8.1 in /backend [`#8021`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8021) +- Bump the babel group across 1 directory with 6 updates [`#8022`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8022) +- Bump @types/jest from 29.5.13 to 29.5.14 in /backend [`#8002`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8002) +- Bump actions/setup-node from aca7b64a59c0063db8564e0ffdadd3887f1cbae5 to 39370e3970a6d050c480ffad4ff0ed4d3fdee5af [`#8020`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8020) +- Bump actions/checkout from 163217dfcd28294438ea1c1c149cfaf66eec283e to 11bd71901bbe5b1630ceea73d27597364c9af683 [`#8018`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8018) +- Bump peter-evans/repository-dispatch from e614736e88253e8888967eafadb9b75b237d52ba to cc7d8686e214c240128fe2680a5c1c5486ae19c4 [`#8016`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8016) +- Bump the vuepress group across 1 directory with 4 updates [`#7999`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7999) +- fix(webapp): change theme color to be brandable [`#8004`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8004) +- fix(webapp): notification page has unbranded button [`#8001`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8001) +- refactor(webapp): degroup sass packages in dependabot [`#7985`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7985) +- refactor(webapp): degroup vue packages in dependabot [`#7984`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7984) +- refactor(other): change set node version in readme files [`#7983`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7983) +- Bump actions/cache from 81382a721fc89d96eca335d0c3ba33144b2baa9d to 8469c94c6a180dfb41a1bd7e1b46ac557ea124f1 [`#7927`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7927) +- Bump @faker-js/faker from 9.0.0 to 9.0.3 [`#7885`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7885) +- Bump sirv from 2.0.4 to 3.0.0 in /frontend [`#7942`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7942) +- Bump the babel group across 1 directory with 3 updates [`#7944`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7944) +- build(webapp): bump the babel group across 1 directory with 2 updates [`#7950`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7950) +- Bump @types/node from 22.5.5 to 22.7.7 in /backend [`#7975`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7975) +- Bump the babel group across 1 directory with 6 updates [`#7938`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7938) +- Bump eslint-plugin-import from 2.30.0 to 2.31.0 in /backend [`#7915`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7915) +- Bump sanitize-html from 2.13.0 to 2.13.1 in /backend [`#7917`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7917) +- Bump @faker-js/faker from 9.0.0 to 9.0.3 in /backend [`#7882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7882) +- Bump nodemon from 3.1.0 to 3.1.7 in /backend [`#7872`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7872) +- Bump actions/checkout from 6d193bf28034eafb982f37bd894289fe649468fc to 163217dfcd28294438ea1c1c149cfaf66eec283e [`#7972`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7972) +- Bump express from 4.21.0 to 4.21.1 in /backend [`#7932`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7932) +- Bump actions/setup-node from 97ca147735c170fb35096b39ef17a0fc5d9270ac to aca7b64a59c0063db8564e0ffdadd3887f1cbae5 [`#7889`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7889) +- Bump typescript from 5.6.2 to 5.6.3 in /backend [`#7940`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7940) +- fix(backend): fix geodata in mapbox-related resolver test [`#7976`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7976) +- Bump the pinia group across 1 directory with 2 updates [`#7905`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7905) +- Bump actions/upload-artifact from 4.4.0 to 4.4.3 [`#7930`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7930) +- Bump express from 4.19.2 to 4.21.1 in /frontend [`#7933`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7933) +- Bump express from 4.21.0 to 4.21.1 in /webapp [`#7949`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7949) +- Bump the stylelint group across 1 directory with 2 updates [`#7937`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7937) +- Bump @vue/compiler-sfc from 3.5.3 to 3.5.12 in /frontend [`#7941`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7941) +- Bump peter-evans/repository-dispatch from c2fad29759e65af76987543a0aabefb8906d379a to e614736e88253e8888967eafadb9b75b237d52ba [`#7973`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7973) +- Bump the storybook group across 1 directory with 8 updates [`#7970`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7970) +- Bump @faker-js/faker from 9.0.0 to 9.0.3 in /webapp [`#7878`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7878) +- Bump eslint from 8.57.0 to 8.57.1 in /backend [`#7870`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7870) +- chore(other): adjust docs for deployment, neo4j cypher commands, and have new scripts [`#7848`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7848) +- refactor(other): remove package wait-on [`#7819`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7819) +- Bump crazy-max/ghaction-github-pages from cda5497acf90563d34489ed832a67c2c50353a16 to 08f571653184e9ff3d598bdda53ffd4ed00ed562 [`#7833`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7833) +- Bump actions/setup-node from 1c7b2db92075f828bee89d7e19d33a911d15e7b3 to 97ca147735c170fb35096b39ef17a0fc5d9270ac [`#7834`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7834) +- refactor(frontend): remove chromatic [`#7818`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7818) +- Bump express from 4.19.2 to 4.21.0 in /webapp [`#7844`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7844) +- Bump express from 4.19.2 to 4.21.0 in /backend [`#7840`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7840) +- Bump @types/jest from 29.5.12 to 29.5.13 in /backend [`#7837`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7837) +- Bump @types/node from 22.5.4 to 22.5.5 in /backend [`#7838`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7838) +- Bump typescript from 5.4.5 to 5.6.2 in /backend [`#7839`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7839) +- build(other): bump cheerio from 1.0.0-rc.12 to 1.0.0 in /backend [`#7709`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7709) +- Bump @faker-js/faker from 8.4.1 to 9.0.0 [`#7791`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7791) +- build(other): bump @faker-js/faker from 8.4.1 to 9.0.0 [`#7817`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7817) +- Bump vike from 0.4.177 to 0.4.195 in /frontend [`#7820`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7820) +- Bump apollo-client from 2.6.8 to 2.6.10 in /webapp in the apollo group [`#7692`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7692) +- Bump date-fns from 3.3.1 to 3.6.0 [`#7139`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7139) +- Bump @types/node from 20.12.7 to 22.5.4 in /backend [`#7812`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7812) +- Bump slug from 9.0.0 to 9.1.0 [`#7457`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7457) +- Bump the vitest group across 1 directory with 2 updates [`#7683`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7683) +- Bump the stylelint group across 1 directory with 2 updates [`#7773`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7773) +- Bump validator from 13.11.0 to 13.12.0 in /webapp [`#7393`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7393) +- Bump peter-evans/repository-dispatch from 733a1daa12a9e7f9b219279836ac0190f48cf46d to c2fad29759e65af76987543a0aabefb8906d379a [`#7805`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7805) +- Bump @vue/compiler-sfc from 3.4.30 to 3.5.3 in /frontend [`#7795`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7795) +- Bump actions/checkout from 9a9194f87191a7e9055e3e9b95b8cfb13023bb08 to 6d193bf28034eafb982f37bd894289fe649468fc [`#7804`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7804) +- Bump actions/cache from 40c3b67b2955d93d83b27ed164edd0756bc24049 to 81382a721fc89d96eca335d0c3ba33144b2baa9d [`#7781`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7781) +- Bump actions/setup-node from 26961cf329f22f6837d5f54c3efd76b480300ace to 1c7b2db92075f828bee89d7e19d33a911d15e7b3 [`#7803`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7803) +- Bump vue-i18n from 9.13.1 to 9.14.0 in /frontend [`#7728`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7728) +- Bump eslint-import-resolver-typescript from 3.6.1 to 3.6.3 in /backend [`#7788`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7788) +- Bump ts-jest from 29.1.2 to 29.2.5 in /backend in the typescript group across 1 directory [`#7758`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7758) +- Bump the babel group across 1 directory with 6 updates [`#7787`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7787) +- Bump eslint-plugin-jest from 28.2.0 to 28.8.3 in /backend [`#7814`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7814) +- Bump eslint-plugin-import from 2.29.1 to 2.30.0 in /backend [`#7815`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7815) +- Bump slug from 9.0.0 to 9.1.0 in /backend [`#7455`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7455) +- Bump peter-evans/repository-dispatch from 1ebfb41781aa0fae446773941d0b3025198fc1a9 to 733a1daa12a9e7f9b219279836ac0190f48cf46d [`#7782`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7782) +- Bump actions/upload-artifact from 4.3.5 to 4.4.0 [`#7780`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7780) +- Bump prettier from 3.2.5 to 3.3.3 in /webapp [`#7629`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7629) +- Bump prettier from 3.3.2 to 3.3.3 in /frontend [`#7641`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7641) +- fix(workflow): fix typo in publishing workflow [`#7770`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7770) +- fix(workflow): reset pkgdeps/git-tag-action to v3.0.0 in publish.yml [`#7769`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7769) +- chore(other): remove obsolete version element from all docker yaml files [`#7564`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7564) +- refactor(workflow): pin all github actions by commit hash [`#7701`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7701) +- fix(webapp): fix maintenance dockerfiles image name [`#7768`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7768) +- refactor(workflow): replace docker-compose by docker compose in workflow files [`#7762`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7762) +- fix(backend): fix backend tests by changing mapbox location coordinates [`#7766`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7766) +- Bump @vue/compiler-sfc from 3.4.29 to 3.4.30 in /frontend [`#7563`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7563) +- Bump prettier from 3.2.5 to 3.3.2 in /frontend [`#7530`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7530) +- chore(frontend): update frontend packages 2024 06 22 [`#7562`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7562) +- Bump the typescript group across 1 directory with 4 updates [`#7487`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7487) +- Bump chromatic from 11.3.2 to 11.5.0 in /frontend [`#7491`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7491) +- Bump vike from 0.4.171 to 0.4.172 in /frontend [`#7489`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7489) +- Bump the vite group in /frontend with 2 updates [`#7486`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7486) +- Bump the vuepress group across 1 directory with 3 updates [`#7485`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7485) +- Bump stylelint from 16.5.0 to 16.6.1 in /frontend in the stylelint group across 1 directory [`#7484`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7484) +- refactor(other): group dependabot updates [`#7431`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7431) +- chore(frontend): update packages 2024 05 12 [`#7401`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7401) +- feat(frontend): implement ocelot color tokens in frontend with themes [`#7144`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7144) +- Bump eslint-plugin-vue from 9.25.0 to 9.26.0 in /webapp [`#7399`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7399) +- refactor(other): bump @faker-js/faker from 5.1.0 to 8.4.1 in /webapp and adapt repo-wide code to deprecation warnings [`#6999`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6999) +- Bump @badeball/cypress-cucumber-preprocessor from 20.0.3 to 20.0.4 [`#7333`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7333) +- Bump cypress from 13.7.3 to 13.8.1 [`#7334`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7334) +- Bump @cucumber/cucumber from 10.4.0 to 10.6.0 [`#7335`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7335) +- Bump cropperjs from 1.6.1 to 1.6.2 in /webapp [`#7307`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7307) +- feat(frontend): add icon components as vuetify assets with icons of actual webapp [`#7129`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7129) +- build(other): bump linkifyjs from 2.1.8 to 4.1.3 in /backend [`#7220`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7220) +- Bump jsonwebtoken from 9.0.0 to 9.0.2 in /webapp [`#7219`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7219) +- Bump uuid from 8.3.2 to 9.0.1 in /backend [`#7206`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7206) +- Bump @faker-js/faker from 7.6.0 to 8.4.1 in /backend [`#7181`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7181) +- Bump slug from 6.0.0 to 9.0.0 in /backend [`#7193`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7193) +- Bump sass from 1.30.0 to 1.75.0 in /webapp [`#7269`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7269) +- Bump express from 4.18.3 to 4.19.2 in /webapp [`#7185`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7185) +- Bump nodemon from 2.0.2 to 3.1.0 in /backend [`#7171`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7171) +- build(other): bump neo4j-driver from 4.4.7 to latest v4 patch available [`#7197`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7197) +- Bump typescript from 4.9.5 to 5.4.5 in /backend [`#7273`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7273) +- Bump validator from 13.0.0 to 13.11.0 in /webapp [`#7208`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7208) +- Bump jest from 29.5.0 to 29.7.0 in /webapp [`#7195`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7195) +- Bump eslint-plugin-vue from 9.24.0 to 9.25.0 in /webapp [`#7282`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7282) +- Bump @typescript-eslint/parser from 5.60.0 to 5.62.0 in /backend [`#7188`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7188) +- Bump eslint-plugin-security from 2.1.1 to 3.0.0 in /backend [`#7275`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7275) +- Bump eslint-plugin-jest from 27.2.2 to 28.2.0 in /backend [`#7204`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7204) +- Bump vue-advanced-chat from 2.0.10 to 2.0.11 in /webapp [`#7175`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7175) +- chore(other): update docker image versions [`#7238`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7238) +- Bump babel-jest from 29.5.0 to 29.7.0 in /webapp [`#7169`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7169) +- Bump @babel/core from 7.23.7 to 7.24.4 in /webapp [`#7226`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7226) +- Bump eslint from 8.43.0 to 8.57.0 in /backend [`#7174`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7174) +- Bump @storybook/addon-a11y from 8.0.5 to 8.0.8 in /webapp [`#7268`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7268) +- chore(frontend): add .vscode to gitignore in /frontend [`#7278`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7278) +- Bump vue-i18n from 9.12.0 to 9.12.1 in /frontend [`#7280`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7280) +- Bump eslint-plugin-vue from 9.24.1 to 9.25.0 in /frontend [`#7279`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7279) +- chore(frontend): update frontend packages 2024 04 14 [`#7277`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7277) +- Bump cypress from 13.7.2 to 13.7.3 [`#7241`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7241) +- Bump aws-sdk from 2.1595.0 to 2.1599.0 in /backend [`#7274`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7274) +- Bump @types/node from 20.12.5 to 20.12.7 in /backend [`#7276`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7276) +- Bump aws-sdk from 2.1594.0 to 2.1595.0 in /backend [`#7236`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7236) +- chore(backend): update packages [`#7235`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7235) +- Bump @cucumber/cucumber from 10.3.1 to 10.4.0 [`#7163`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7163) +- Bump @badeball/cypress-cucumber-preprocessor from 20.0.2 to 20.0.3 [`#7161`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7161) +- Bump minimatch from 9.0.3 to 9.0.4 in /backend [`#7214`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7214) +- Bump sanitize-html from 2.11.0 to 2.13.0 in /backend [`#7218`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7218) +- Bump happy-dom from 14.7.0 to 14.7.1 in /frontend [`#7234`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7234) +- chore(frontend): update boilerplate 07 04 [`#7233`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7233) +- Bump helmet from 7.0.0 to 7.1.0 in /backend [`#7160`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7160) +- build(other): bump vike from 0.4.163 to 0.4.168 in /frontend [`#7153`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7153) +- refactor(other): fix dependabot pr limit problem [`#7159`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7159) +- chore(other): set dependabot package update pr limit to none [`#7158`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7158) +- chore(frontend): update frontend boilerplate 06 04 [`#7157`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7157) +- Bump eslint-plugin-vue from 9.19.2 to 9.24.0 in /webapp [`#7147`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7147) +- Bump @babel/preset-env from 7.24.0 to 7.24.4 [`#7150`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7150) +- Bump cypress from 13.7.0 to 13.7.2 [`#7151`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7151) +- Bump @storybook/addon-a11y from 6.3.6 to 8.0.5 in /webapp [`#7146`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7146) +- Bump actions/cache from 4.0.1 to 4.0.2 [`#7133`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7133) +- Bump multiple-cucumber-html-reporter from 3.6.1 to 3.6.2 [`#7138`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7138) +- Bump dotenv from 16.4.4 to 16.4.5 [`#7137`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7137) +- Bump express from 4.17.1 to 4.18.3 in /webapp [`#7115`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7115) +- Bump slug from 8.2.3 to 9.0.0 [`#7126`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7126) +- Bump vuepress-theme-hope from 2.0.0-rc.29 to 2.0.0-rc.31 [`#7124`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7124) +- Bump migrate from 2.0.1 to 2.1.0 in /backend [`#7118`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7118) +- Bump @badeball/cypress-cucumber-preprocessor from 20.0.1 to 20.0.2 [`#7122`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7122) +- Bump cypress from 13.6.6 to 13.7.0 [`#7123`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7123) +- fix(webapp): remove reported unused locale keys [`#7105`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7105) +- build(other): bump minimatch from 3.0.4 to 9.0.3 in /backend [`#6511`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6511) +- build(other): bump prettier from 2.7.1 to 3.2.5 in /webapp [`#6997`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6997) +- build(other): bump prettier from 2.8.8 to 3.2.5 in /backend [`#7000`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7000) +- Bump eslint-plugin-security from 1.7.1 to 2.1.1 in /backend [`#7041`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7041) +- Bump vuepress-theme-hope from 2.0.0-rc.23 to 2.0.0-rc.29 [`#7047`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7047) +- Bump cypress from 13.6.4 to 13.6.6 [`#7045`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7045) +- Bump @babel/preset-env from 7.23.9 to 7.24.0 [`#7048`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7048) +- Bump dorny/paths-filter from 3.0.1 to 3.0.2 [`#7049`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7049) +- feat(webapp): normalize locale files [`#7038`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7038) +- feat(webapp): update locales event placeholders [`#6793`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6793) +- Bump date-fns from 2.30.0 to 3.3.1 [`#7037`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7037) +- Bump @babel/register from 7.22.15 to 7.23.7 [`#6931`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6931) +- Bump @babel/core from 7.23.7 to 7.24.0 [`#7034`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7034) +- build(other): bump node-fetch from 2.6.1 to 2.7.0 in /backend [`#6960`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6960) +- Bump peter-evans/repository-dispatch from 2 to 3 [`#6966`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6966) +- Bump @faker-js/faker from 8.4.0 to 8.4.1 [`#7026`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7026) +- Bump babel-jest from 25.2.6 to 29.7.0 in /backend [`#6961`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6961) +- Bump actions/cache from 4.0.0 to 4.0.1 [`#7028`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7028) +- chore(frontend): update frontend boilerplate 26 02 [`#7027`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7027) +- fix(docker): docker rework [`#92`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/92) +- chore(docu): update vuepress [`#91`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/91) +- Bump dorny/paths-filter from 3.0.0 to 3.0.1 [`#82`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/82) +- chore(frontend): update packages 04.02 [`#90`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/90) +- chore(frontend): update frontend boilerplate [`#7017`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7017) +- chore(frontend): update packages 19.02 [`#86`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/86) +- refactor(other): set ocelot color for docs website [`#7014`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7014) +- refactor(other): adapt docs website navbar and footer to ocelot.social website [`#7016`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7016) +- refactor(other): replace deprecated testbadgelink in main readme file [`#7012`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7012) +- Bump dotenv from 16.3.1 to 16.4.4 [`#7010`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7010) +- Bump dorny/paths-filter from 3.0.0 to 3.0.1 [`#7006`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7006) +- chore(other): update packages 08.02 [`#75`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/75) +- update frontend packages [`fef8157`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/fef8157b0cdf9081678365e9a7f6339ef787423c) +- update frontend packages [`dce59c1`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/dce59c11089e23f3ed97dfbfea39507e7c098c30) +- Add converted SVG icons with properties 'fill' and 'stroke' on 'svg' tag [`b1a7e91`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/b1a7e916314a0d59a65d2cdb244e6117d796dda1) + #### [3.2.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.1.2...3.2.0) +> 14 February 2024 + +- chore(other): release v3.2.0 – redesign of newsfeed, fix event datetime utc problem etc. [`#7002`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/7002) - feat(webapp): implement config for `date-time` format [`#6985`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6985) - build(other): bump eslint-plugin-n from 15.7.0 to 16.6.2 in /backend [`#6962`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6962) - feat(other): frontend workflows & test suites [`#6987`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/6987) diff --git a/backend/package.json b/backend/package.json index c89a3b49a..6cd363831 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "3.2.0", + "version": "3.2.1", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", @@ -57,8 +57,8 @@ "ioredis": "^4.16.1", "jsonwebtoken": "~8.5.1", "languagedetect": "^2.0.0", - "linkifyjs": "^4.2.0", "linkify-html": "^4.2.0", + "linkifyjs": "^4.2.0", "lodash": "~4.17.21", "merge-graphql-schemas": "^1.7.8", "metascraper": "^5.46.7", diff --git a/frontend/package.json b/frontend/package.json index b53e8c43c..f032f0d0c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-frontend", - "version": "3.2.0", + "version": "3.2.1", "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.json b/package.json index 7315dd55b..ec9a89792 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "3.2.0", + "version": "3.2.1", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json index 58ec9ee67..2c5b898b6 100644 --- a/webapp/maintenance/source/package.json +++ b/webapp/maintenance/source/package.json @@ -1,6 +1,6 @@ { "name": "@ocelot-social/maintenance", - "version": "3.2.0", + "version": "3.2.1", "description": "Maintenance page for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/package.json b/webapp/package.json index 486937dfa..d12672224 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-webapp", - "version": "3.2.0", + "version": "3.2.1", "description": "ocelot.social Frontend", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", From 54c2e5c1316320560b487915ae7b54f09bbca2dc Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 17 Mar 2025 21:49:26 +0100 Subject: [PATCH 033/266] release: also update helmchart versions (#8256) To have congruence a release also updates the helmchart version. This utilizes sed to replace the coresponding string. The `sed` command is not present on windows and could prevent a windows user to use the release functionality out of the box. The release functionality is also externalized into its own script `scripts/release.sh`. --- package.json | 2 +- scripts/release.sh | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100755 scripts/release.sh diff --git a/package.json b/package.json index ec9a89792..44976d25d 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "docs:dev": "NODE_OPTIONS=--openssl-legacy-provider vuepress dev .", "cypress:run": "cypress run --e2e --browser electron --config-file ./cypress/cypress.config.js", "cypress:open": "cypress open --e2e --browser electron --config-file ./cypress/cypress.config.js", - "release": "yarn version --no-git-tag-version --no-commit-hooks --no-commit && auto-changelog --latest-version $(node -p -e \"require('./package.json').version\") && cd backend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../frontend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp/maintenance/source && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../../../package.json').version\")" + "release": "./scripts/release.sh" }, "devDependencies": { "@babel/core": "^7.26.9", diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 000000000..453375203 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# base setup +SCRIPT_PATH=$(realpath $0) +SCRIPT_DIR=$(dirname $SCRIPT_PATH) +ROOT_DIR=$SCRIPT_DIR/.. + +# Update Version +cd $ROOT_DIR +yarn version --no-git-tag-version --no-commit-hooks --no-commit +VERSION_NEW=$(node -p -e "require('$ROOT_DIR/package.json').version") + +## packages +cd backend +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $VERSION_NEW +cd $ROOT_DIR/frontend +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $VERSION_NEW +cd $ROOT_DIR/webapp +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $VERSION_NEW +cd $ROOT_DIR/webapp/maintenance/source +yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $VERSION_NEW + +## helm +sed -i -e 's/appVersion: ".*"/appVersion: "'"$VERSION_NEW"'"/g' $ROOT_DIR/deployment/helm/charts/ocelot-neo4j/Chart.yaml +sed -i -e 's/appVersion: ".*"/appVersion: "'"$VERSION_NEW"'"/g' $ROOT_DIR/deployment/helm/charts/ocelot-social/Chart.yaml + +# generate changelog +cd $ROOT_DIR +yarn run auto-changelog --latest-version $VERSION_NEW \ No newline at end of file From 538f409086ae36dbb1c1850faf78ad72cea3ec5f Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 26 Mar 2025 22:16:06 +0100 Subject: [PATCH 034/266] feat(backend): observe posts (#8292) * After creating the post, the author of it automatically observes it to get notifications when there are interactions * a user that comments a post, automatically observes that post to get notifications when there are more interactions on that post * mutation that switches the state of the observation of a post on and off --- backend/src/graphql/posts.ts | 2 + .../src/middleware/permissionsMiddleware.ts | 1 + .../src/middleware/slugifyMiddleware.spec.ts | 3 + backend/src/schema/resolvers/comments.ts | 6 + .../src/schema/resolvers/observePosts.spec.ts | 240 ++++++++++++++++++ backend/src/schema/resolvers/posts.spec.ts | 3 + backend/src/schema/resolvers/posts.ts | 36 +++ .../schema/resolvers/postsInGroups.spec.ts | 3 + backend/src/schema/types/type/Post.gql | 9 + webapp/graphql/Fragments.js | 2 + webapp/graphql/PostMutations.js | 8 + 11 files changed, 313 insertions(+) create mode 100644 backend/src/schema/resolvers/observePosts.spec.ts diff --git a/backend/src/graphql/posts.ts b/backend/src/graphql/posts.ts index d1dc3ee45..dcd75a4ff 100644 --- a/backend/src/graphql/posts.ts +++ b/backend/src/graphql/posts.ts @@ -46,6 +46,8 @@ export const createPostMutation = () => { lng lat } + isObservedByMe + observingUsersCount } } ` diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index f87f4b079..1fc84b665 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -465,6 +465,7 @@ export default shield( CreateRoom: isAuthenticated, CreateMessage: isAuthenticated, MarkMessagesAsSeen: isAuthenticated, + toggleObservePost: isAuthenticated, }, User: { email: or(isMyOwn, isAdmin), diff --git a/backend/src/middleware/slugifyMiddleware.spec.ts b/backend/src/middleware/slugifyMiddleware.spec.ts index b0b2371b2..26bb2cb96 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.ts +++ b/backend/src/middleware/slugifyMiddleware.spec.ts @@ -21,6 +21,9 @@ const { server } = createServer({ driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) diff --git a/backend/src/schema/resolvers/comments.ts b/backend/src/schema/resolvers/comments.ts index c6f07245c..394695f3a 100644 --- a/backend/src/schema/resolvers/comments.ts +++ b/backend/src/schema/resolvers/comments.ts @@ -25,6 +25,12 @@ export default { SET comment.createdAt = toString(datetime()) SET comment.updatedAt = toString(datetime()) MERGE (post)<-[:COMMENTS]-(comment)<-[:WROTE]-(author) + WITH post, author, comment + MERGE (post)<-[obs:OBSERVES]-(author) + ON CREATE SET + obs.active = true, + obs.createdAt = toString(datetime()), + obs.updatedAt = toString(datetime()) RETURN comment `, { userId: user.id, postId, params }, diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/schema/resolvers/observePosts.spec.ts new file mode 100644 index 000000000..e88bae08a --- /dev/null +++ b/backend/src/schema/resolvers/observePosts.spec.ts @@ -0,0 +1,240 @@ +import { createTestClient } from 'apollo-server-testing' +import Factory, { cleanDatabase } from '../../db/factories' +import gql from 'graphql-tag' +import { getNeode, getDriver } from '../../db/neo4j' +import createServer from '../../server' + +import { createPostMutation } from '../../graphql/posts' +import CONFIG from '../../config' + +CONFIG.CATEGORIES_ACTIVE = false + +const driver = getDriver() +const neode = getNeode() + +let query +let mutate +let authenticatedUser +let user +let otherUser + +const createCommentMutation = gql` + mutation ($id: ID, $postId: ID!, $content: String!) { + CreateComment(id: $id, postId: $postId, content: $content) { + id + } + } +` + +const postQuery = gql` + query Post($id: ID) { + Post(id: $id) { + isObservedByMe + observingUsersCount + } + } +` + +beforeAll(async () => { + await cleanDatabase() + + const { server } = createServer({ + context: () => { + return { + driver, + neode, + user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, + } + }, + }) + query = createTestClient(server).query + mutate = createTestClient(server).mutate +}) + +afterAll(async () => { + await cleanDatabase() + driver.close() +}) + +describe('observing posts', () => { + beforeAll(async () => { + user = await Factory.build('user', { + id: 'user', + name: 'User', + about: 'I am a user', + }) + otherUser = await Factory.build('user', { + id: 'other-user', + name: 'Other User', + about: 'I am another user', + }) + authenticatedUser = await user.toJson() + }) + + describe('creating posts', () => { + it('has the author of the post observing the post', async () => { + await expect( + mutate({ + mutation: createPostMutation(), + variables: { + id: 'p2', + title: 'A post the author should observe', + content: 'The author of this post is expected to observe the post', + }, + }), + ).resolves.toMatchObject({ + data: { + CreatePost: { + isObservedByMe: true, + observingUsersCount: 1, + }, + }, + errors: undefined, + }) + }) + }) + + describe('commenting posts', () => { + beforeAll(async () => { + authenticatedUser = await otherUser.toJson() + }) + + it('has another user NOT observing the post BEFORE commenting it', async () => { + await expect( + query({ + query: postQuery, + variables: { id: 'p2' }, + }), + ).resolves.toMatchObject({ + data: { + Post: [ + { + isObservedByMe: false, + observingUsersCount: 1, + }, + ], + }, + errors: undefined, + }) + }) + + it('has another user observing the post AFTER commenting it', async () => { + await mutate({ + mutation: createCommentMutation, + variables: { + postId: 'p2', + content: 'After commenting the post, I should observe the post automatically', + }, + }) + + await expect( + query({ + query: postQuery, + variables: { id: 'p2' }, + }), + ).resolves.toMatchObject({ + data: { + Post: [ + { + isObservedByMe: true, + observingUsersCount: 2, + }, + ], + }, + errors: undefined, + }) + }) + }) + + describe('toggle observe post', () => { + beforeAll(async () => { + authenticatedUser = await otherUser.toJson() + }) + + const toggleObservePostMutation = gql` + mutation ($id: ID!, $value: Boolean!) { + toggleObservePost(id: $id, value: $value) { + isObservedByMe + observingUsersCount + } + } + ` + + describe('switch off observation', () => { + it('does not observe the post anymore', async () => { + await expect( + mutate({ + mutation: toggleObservePostMutation, + variables: { + id: 'p2', + value: false, + }, + }), + ).resolves.toMatchObject({ + data: { + toggleObservePost: { + isObservedByMe: false, + observingUsersCount: 1, + }, + }, + errors: undefined, + }) + }) + }) + + describe('comment the post again', () => { + it('does NOT alter the observation state', async () => { + await mutate({ + mutation: createCommentMutation, + variables: { + postId: 'p2', + content: + 'After commenting the post I do not observe again, I should NOT observe the post', + }, + }) + + await expect( + query({ + query: postQuery, + variables: { id: 'p2' }, + }), + ).resolves.toMatchObject({ + data: { + Post: [ + { + isObservedByMe: false, + observingUsersCount: 1, + }, + ], + }, + errors: undefined, + }) + }) + }) + + describe('switch on observation', () => { + it('does observe the post again', async () => { + await expect( + mutate({ + mutation: toggleObservePostMutation, + variables: { + id: 'p2', + value: true, + }, + }), + ).resolves.toMatchObject({ + data: { + toggleObservePost: { + isObservedByMe: true, + observingUsersCount: 2, + }, + }, + errors: undefined, + }) + }) + }) + }) +}) diff --git a/backend/src/schema/resolvers/posts.spec.ts b/backend/src/schema/resolvers/posts.spec.ts index 10da1c43e..d7eb063d2 100644 --- a/backend/src/schema/resolvers/posts.spec.ts +++ b/backend/src/schema/resolvers/posts.spec.ts @@ -28,6 +28,9 @@ beforeAll(async () => { driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) diff --git a/backend/src/schema/resolvers/posts.ts b/backend/src/schema/resolvers/posts.ts index 0bd4507b5..b111678c0 100644 --- a/backend/src/schema/resolvers/posts.ts +++ b/backend/src/schema/resolvers/posts.ts @@ -144,6 +144,10 @@ export default { WITH post MATCH (author:User {id: $userId}) MERGE (post)<-[:WROTE]-(author) + MERGE (post)<-[obs:OBSERVES]-(author) + SET obs.active = true + SET obs.createdAt = toString(datetime()) + SET obs.updatedAt = toString(datetime()) ${categoriesCypher} ${groupCypher} RETURN post {.*, postType: [l IN labels(post) WHERE NOT l = 'Post'] } @@ -416,6 +420,35 @@ export default { session.close() } }, + toggleObservePost: async (_parent, params, context, _resolveInfo) => { + const session = context.driver.session() + const writeTxResultPromise = session.writeTransaction(async (transaction) => { + const transactionResponse = await transaction.run( + ` + MATCH (post:Post { id: $params.id }) + MATCH (user:User { id: $userId }) + MERGE (user)-[obs:OBSERVES]->(post) + ON CREATE SET + obs.createdAt = toString(datetime()), + obs.updatedAt = toString(datetime()), + obs.active = $params.value + ON MATCH SET + obs.updatedAt = toString(datetime()), + obs.active = $params.value + RETURN post + `, + { userId: context.user.id, params }, + ) + return transactionResponse.records.map((record) => record.get('post').properties) + }) + try { + const [post] = await writeTxResultPromise + post.viewedTeaserCount = post.viewedTeaserCount.low + return post + } finally { + session.close() + } + }, }, Post: { ...Resolver('Post', { @@ -452,12 +485,15 @@ export default { shoutedCount: '<-[:SHOUTED]-(related:User) WHERE NOT related.deleted = true AND NOT related.disabled = true', emotionsCount: '<-[related:EMOTED]-(:User)', + observingUsersCount: '<-[related:OBSERVES]-(:User) WHERE related.active = true', }, boolean: { shoutedByCurrentUser: 'MATCH(this)<-[:SHOUTED]-(related:User {id: $cypherParams.currentUserId}) RETURN COUNT(related) >= 1', viewedTeaserByCurrentUser: 'MATCH (this)<-[:VIEWED_TEASER]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1', + isObservedByMe: + 'MATCH (this)<-[obs:OBSERVES]-(related:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(related) >= 1', }, }), relatedContributions: async (parent, params, context, resolveInfo) => { diff --git a/backend/src/schema/resolvers/postsInGroups.spec.ts b/backend/src/schema/resolvers/postsInGroups.spec.ts index ba9041090..c7fc34ec7 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.ts +++ b/backend/src/schema/resolvers/postsInGroups.spec.ts @@ -52,6 +52,9 @@ beforeAll(async () => { driver, neode, user: authenticatedUser, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, } }, }) diff --git a/backend/src/schema/types/type/Post.gql b/backend/src/schema/types/type/Post.gql index 7e6d1d0e7..ddf6e557e 100644 --- a/backend/src/schema/types/type/Post.gql +++ b/backend/src/schema/types/type/Post.gql @@ -186,6 +186,13 @@ type Post { eventStart: String eventEnd: String eventIsOnline: Boolean + + isObservedByMe: Boolean! + @cypher( + statement: "MATCH (this)<-[obs:OBSERVES]-(u:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(u) >= 1" + ) + observingUsersCount: Int! + @cypher(statement: "MATCH (this)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true RETURN COUNT(DISTINCT u)") } input _PostInput { @@ -239,6 +246,8 @@ type Mutation { shout(id: ID!, type: ShoutTypeEnum): Boolean! # Unshout the given Type and ID unshout(id: ID!, type: ShoutTypeEnum): Boolean! + + toggleObservePost(id: ID!, value: Boolean!): Post! } type Query { diff --git a/webapp/graphql/Fragments.js b/webapp/graphql/Fragments.js index 4931750b6..da1ed0de3 100644 --- a/webapp/graphql/Fragments.js +++ b/webapp/graphql/Fragments.js @@ -67,6 +67,8 @@ export const postFragment = gql` } pinnedAt pinned + isObservedByMe + observingUsersCount } ` diff --git a/webapp/graphql/PostMutations.js b/webapp/graphql/PostMutations.js index 73e9f8ebe..5f29534a3 100644 --- a/webapp/graphql/PostMutations.js +++ b/webapp/graphql/PostMutations.js @@ -175,5 +175,13 @@ export default () => { } } `, + toggleObservePost: gql` + mutation ($id: ID!, $value: Boolean!) { + toggleObservePost(id: $id, value: $value) { + isObservedByMe + observingUsersCount + } + } + `, } } From 316c12bd35f1990d3c16c9fc3f493b2959da6bb1 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 31 Mar 2025 20:41:39 +0200 Subject: [PATCH 035/266] provide information of the observing status of the post after creating a comment (#8310) --- backend/src/schema/resolvers/comments.ts | 8 ++++ .../src/schema/resolvers/observePosts.spec.ts | 42 ++++++++++++++----- backend/src/schema/types/type/Comment.gql | 6 +++ webapp/graphql/Fragments.js | 2 + 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/backend/src/schema/resolvers/comments.ts b/backend/src/schema/resolvers/comments.ts index 394695f3a..b9c0271c1 100644 --- a/backend/src/schema/resolvers/comments.ts +++ b/backend/src/schema/resolvers/comments.ts @@ -100,6 +100,14 @@ export default { author: '<-[:WROTE]-(related:User)', post: '-[:COMMENTS]->(related:Post)', }, + count: { + postObservingUsersCount: + '-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(related:User) WHERE obs.active = true AND NOT related.deleted AND NOT related.disabled', + }, + boolean: { + isPostObservedByMe: + 'MATCH (this)-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(related:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(related) >= 1', + }, }), }, } diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/schema/resolvers/observePosts.spec.ts index e88bae08a..2d98c33a7 100644 --- a/backend/src/schema/resolvers/observePosts.spec.ts +++ b/backend/src/schema/resolvers/observePosts.spec.ts @@ -22,6 +22,8 @@ const createCommentMutation = gql` mutation ($id: ID, $postId: ID!, $content: String!) { CreateComment(id: $id, postId: $postId, content: $content) { id + isPostObservedByMe + postObservingUsersCount } } ` @@ -122,11 +124,20 @@ describe('observing posts', () => { }) it('has another user observing the post AFTER commenting it', async () => { - await mutate({ - mutation: createCommentMutation, - variables: { - postId: 'p2', - content: 'After commenting the post, I should observe the post automatically', + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'p2', + content: 'After commenting the post, I should observe the post automatically', + }, + }), + ).resolves.toMatchObject({ + data: { + CreateComment: { + isPostObservedByMe: true, + postObservingUsersCount: 2, + }, }, }) @@ -187,12 +198,21 @@ describe('observing posts', () => { describe('comment the post again', () => { it('does NOT alter the observation state', async () => { - await mutate({ - mutation: createCommentMutation, - variables: { - postId: 'p2', - content: - 'After commenting the post I do not observe again, I should NOT observe the post', + await expect( + mutate({ + mutation: createCommentMutation, + variables: { + postId: 'p2', + content: + 'After commenting the post I do not observe again, I should NOT observe the post', + }, + }), + ).resolves.toMatchObject({ + data: { + CreateComment: { + isPostObservedByMe: false, + postObservingUsersCount: 1, + }, }, }) diff --git a/backend/src/schema/types/type/Comment.gql b/backend/src/schema/types/type/Comment.gql index cf53df41d..b1fd7a838 100644 --- a/backend/src/schema/types/type/Comment.gql +++ b/backend/src/schema/types/type/Comment.gql @@ -47,6 +47,12 @@ type Comment { updatedAt: String deleted: Boolean disabled: Boolean + isPostObservedByMe: Boolean! + @cypher( + statement: "MATCH (this)-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(u:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(u) >= 1" + ) + postObservingUsersCount: Int! + @cypher(statement: "MATCH (this)-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true AND NOT u.disabled = true AND NOT u.deleted = true RETURN COUNT(DISTINCT u)") } type Query { diff --git a/webapp/graphql/Fragments.js b/webapp/graphql/Fragments.js index da1ed0de3..32337230b 100644 --- a/webapp/graphql/Fragments.js +++ b/webapp/graphql/Fragments.js @@ -135,5 +135,7 @@ export const commentFragment = gql` deleted content contentExcerpt + isPostObservedByMe + postObservingUsersCount } ` From 01845928b25fc0261af685d97481cba14ca50f7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:23:32 +0200 Subject: [PATCH 036/266] build(deps-dev): Bump cypress from 14.0.3 to 14.1.0 in the cypress group (#8229) Bumps the cypress group with 1 update: [cypress](https://github.com/cypress-io/cypress). Updates `cypress` from 14.0.3 to 14.1.0 - [Release notes](https://github.com/cypress-io/cypress/releases) - [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md) - [Commits](https://github.com/cypress-io/cypress/compare/v14.0.3...v14.1.0) --- updated-dependencies: - dependency-name: cypress dependency-type: direct:development update-type: version-update:semver-minor dependency-group: cypress ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 204 +++++++++++++++++++++++++++++++--------------- package.json | 2 +- yarn.lock | 198 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 301 insertions(+), 103 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2e03e1f2c..6e9ed925a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social", - "version": "3.2.0", + "version": "3.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social", - "version": "3.2.0", + "version": "3.2.1", "license": "MIT", "devDependencies": { "@babel/core": "^7.26.9", @@ -19,7 +19,7 @@ "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", - "cypress": "^14.0.3", + "cypress": "^14.2.1", "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.4.7", @@ -2330,9 +2330,9 @@ } }, "node_modules/@cypress/request": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.7.tgz", - "integrity": "sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.8.tgz", + "integrity": "sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2349,7 +2349,7 @@ "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.13.1", + "qs": "6.14.0", "safe-buffer": "^5.1.2", "tough-cookie": "^5.0.0", "tunnel-agent": "^0.6.0", @@ -7010,6 +7010,23 @@ "node": ">= 0.4" } }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -7741,14 +7758,14 @@ "optional": true }, "node_modules/cypress": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.0.3.tgz", - "integrity": "sha512-yIdvobANw3kS+KF/t5vwjjPNufBA8ux7iQHaWxPTkUw2yCKI72m9mKM24eOwE84Wk4ALPsSvEcGbDrwgmhr4RA==", + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.2.1.tgz", + "integrity": "sha512-5xd0E7fUp0pjjib1D7ljkmCwFDgMkWuW06jWiz8dKrI7MNRrDo0C65i4Sh+oZ9YHjMHZRJBR0XZk1DfekOhOUw==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.7", + "@cypress/request": "^3.0.8", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -7759,9 +7776,9 @@ "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", - "ci-info": "^4.0.0", + "ci-info": "^4.1.0", "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", + "cli-table3": "~0.6.5", "commander": "^6.2.1", "common-tags": "^1.8.0", "dayjs": "^1.10.4", @@ -7785,7 +7802,7 @@ "process": "^0.11.10", "proxy-from-env": "1.0.0", "request-progress": "^3.0.0", - "semver": "^7.5.3", + "semver": "^7.7.1", "supports-color": "^8.1.1", "tmp": "~0.2.3", "tree-kill": "1.2.2", @@ -7874,9 +7891,9 @@ } }, "node_modules/cypress/node_modules/ci-info": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.0.0.tgz", - "integrity": "sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", "dev": true, "funding": [ { @@ -7884,10 +7901,27 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/cypress/node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, "node_modules/cypress/node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", @@ -7897,26 +7931,12 @@ "node": ">= 6" } }, - "node_modules/cypress/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/cypress/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -7924,12 +7944,6 @@ "node": ">=10" } }, - "node_modules/cypress/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/dash-ast": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", @@ -12544,10 +12558,14 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -13763,13 +13781,13 @@ } }, "node_modules/qs": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", - "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -14687,15 +14705,73 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -15429,22 +15505,22 @@ } }, "node_modules/tldts": { - "version": "6.1.78", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.78.tgz", - "integrity": "sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ==", + "version": "6.1.85", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.85.tgz", + "integrity": "sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w==", "dev": true, "license": "MIT", "dependencies": { - "tldts-core": "^6.1.78" + "tldts-core": "^6.1.85" }, "bin": { "tldts": "bin/cli.js" } }, "node_modules/tldts-core": { - "version": "6.1.78", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.78.tgz", - "integrity": "sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw==", + "version": "6.1.85", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.85.tgz", + "integrity": "sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA==", "dev": true, "license": "MIT" }, @@ -15476,9 +15552,9 @@ "dev": true }, "node_modules/tough-cookie": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.1.tgz", - "integrity": "sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", "dependencies": { diff --git a/package.json b/package.json index 44976d25d..a46e3fa85 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", - "cypress": "^14.0.3", + "cypress": "^14.2.1", "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.4.7", diff --git a/yarn.lock b/yarn.lock index a1c343d7d..b3f340b5d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1268,10 +1268,10 @@ through2 "^2.0.0" watchify "^4.0.0" -"@cypress/request@^3.0.7": - version "3.0.7" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.7.tgz#6a74a4da98d9e5ae9121d6e2d9c14780c9b5cf1a" - integrity sha512-LzxlLEMbBOPYB85uXrDqvD4MgcenjRBLIns3zyhx7vTPj/0u2eQhzXvPiGcaJrV38Q9dbkExWp6cOHPJ+EtFYg== +"@cypress/request@^3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.8.tgz#992f1f42ba03ebb14fa5d97290abe9d015ed0815" + integrity sha512-h0NFgh1mJmm1nr4jCwkGHwKneVYKghUyWe6TMNrk0B9zsjAJxpg8C4/+BAcmLgCPa1vj1V8rNUaILl+zYRUWBQ== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -1286,7 +1286,7 @@ json-stringify-safe "~5.0.1" mime-types "~2.1.19" performance-now "^2.1.0" - qs "6.13.1" + qs "6.14.0" safe-buffer "^5.1.2" tough-cookie "^5.0.0" tunnel-agent "^0.6.0" @@ -3515,6 +3515,14 @@ cachedir@^2.3.0: resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d" integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ== +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" @@ -3535,6 +3543,14 @@ call-bind@^1.0.5, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -3668,10 +3684,10 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== -ci-info@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.0.0.tgz#65466f8b280fc019b9f50a5388115d17a63a44f2" - integrity sha512-TdHqgGf9odd8SXNuxtUBVx8Nv+qZOejE6qyqiy5NtbYYQOeFa6zmHkxlPzmaLxWWHsU6nJmB7AETdVPi+2NBUg== +ci-info@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-4.2.0.tgz#cbd21386152ebfe1d56f280a3b5feccbd96764c7" + integrity sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -3710,7 +3726,7 @@ cli-spinners@^2.9.2: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== -cli-table3@0.6.3, cli-table3@^0.6.0, cli-table3@~0.6.1: +cli-table3@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2" integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg== @@ -3719,6 +3735,15 @@ cli-table3@0.6.3, cli-table3@^0.6.0, cli-table3@~0.6.1: optionalDependencies: "@colors/colors" "1.5.0" +cli-table3@^0.6.0, cli-table3@~0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-table@^0.3.11: version "0.3.11" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee" @@ -4049,12 +4074,12 @@ cypress-network-idle@^1.15.0: resolved "https://registry.yarnpkg.com/cypress-network-idle/-/cypress-network-idle-1.15.0.tgz#e249f08695a46f1ddce18a95d5293937f277cbb3" integrity sha512-8zU16zhc7S3nMl1NTEEcNsZYlJy/ZzP2zPTTrngGxyXH32Ipake/xfHLZsgrzeWCieiS2AVhQsakhWqFzO3hpw== -cypress@^14.0.3: - version "14.0.3" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.0.3.tgz#7873d21f61d0559edb9c041316d450cf7ef9d5fa" - integrity sha512-yIdvobANw3kS+KF/t5vwjjPNufBA8ux7iQHaWxPTkUw2yCKI72m9mKM24eOwE84Wk4ALPsSvEcGbDrwgmhr4RA== +cypress@^14.2.1: + version "14.2.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.2.1.tgz#2628696588d3d3961bf0ea0ad87baeb359790dcd" + integrity sha512-5xd0E7fUp0pjjib1D7ljkmCwFDgMkWuW06jWiz8dKrI7MNRrDo0C65i4Sh+oZ9YHjMHZRJBR0XZk1DfekOhOUw== dependencies: - "@cypress/request" "^3.0.7" + "@cypress/request" "^3.0.8" "@cypress/xvfb" "^1.2.4" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" @@ -4065,9 +4090,9 @@ cypress@^14.0.3: cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" - ci-info "^4.0.0" + ci-info "^4.1.0" cli-cursor "^3.1.0" - cli-table3 "~0.6.1" + cli-table3 "~0.6.5" commander "^6.2.1" common-tags "^1.8.0" dayjs "^1.10.4" @@ -4091,7 +4116,7 @@ cypress@^14.0.3: process "^0.11.10" proxy-from-env "1.0.0" request-progress "^3.0.0" - semver "^7.5.3" + semver "^7.7.1" supports-color "^8.1.1" tmp "~0.2.3" tree-kill "1.2.2" @@ -4430,6 +4455,15 @@ dotenv@^4.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" integrity sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -4569,6 +4603,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" @@ -4589,6 +4628,13 @@ es-get-iterator@^1.1.3: isarray "^2.0.5" stop-iteration-iterator "^1.0.0" +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + esbuild@^0.21.3, esbuild@~0.21.5: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" @@ -5103,11 +5149,35 @@ get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-own-enumerable-property-symbols@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stream@^5.0.0, get-stream@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" @@ -5232,6 +5302,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" @@ -5305,6 +5380,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -5353,6 +5433,13 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hast-util-to-html@^9.0.3: version "9.0.3" resolved "https://registry.yarnpkg.com/hast-util-to-html/-/hast-util-to-html-9.0.3.tgz#a9999a0ba6b4919576a9105129fead85d37f302b" @@ -6334,6 +6421,11 @@ markdown-it@^14.1.0: punycode.js "^2.3.1" uc.micro "^2.1.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -6743,10 +6835,10 @@ object-assign@^4.0.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.13.1: - version "1.13.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" - integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== object-inspect@^1.9.0: version "1.13.1" @@ -7291,12 +7383,12 @@ qrcode@^1.5.4: pngjs "^5.0.0" yargs "^15.3.1" -qs@6.13.1: - version "6.13.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e" - integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== +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== dependencies: - side-channel "^1.0.6" + side-channel "^1.1.0" qs@^6.11.2: version "6.11.2" @@ -7749,10 +7841,10 @@ semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +semver@^7.3.5, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== serialize-javascript@^6.0.2: version "6.0.2" @@ -7876,6 +7968,35 @@ shiki@1.22.1, shiki@^1.22.0: "@shikijs/vscode-textmate" "^9.3.0" "@types/hast" "^3.0.4" +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -7885,15 +8006,16 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -side-channel@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" - integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA== +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: - call-bind "^1.0.7" es-errors "^1.3.0" - get-intrinsic "^1.2.4" - object-inspect "^1.13.1" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" signal-exit@^3.0.2: version "3.0.7" From 4f02060fd7de69cfc06b93bff65ca6279f52bfe6 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 2 Apr 2025 23:09:53 +0200 Subject: [PATCH 037/266] Replace argfile by slurpfile (#8309) --- scripts/translations/normalize-locales.jq | 6 +++--- scripts/translations/normalize.sh | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/translations/normalize-locales.jq b/scripts/translations/normalize-locales.jq index 2381f9611..e3b2e68d6 100644 --- a/scripts/translations/normalize-locales.jq +++ b/scripts/translations/normalize-locales.jq @@ -4,7 +4,7 @@ # by the corresponding values of target. If a key does not exist in target, # the value is set to null. # -# jq -n --argfile source en.json --argfile target es.json -f normalize-locales.jq +# jq -n --slurpfile source en.json --slurpfile target es.json -f normalize-locales.jq # # source should be primary or fallback locale file (here en.json) # taget is the locale file to normalize (here es.json) @@ -22,10 +22,10 @@ def keys_to_paths_recursive($path): then with_entries( ($path + [.key]) as $path | if (.value | type == "string") - then .value |= ($target | find_key_by_path($path)) + then .value |= ($target[0] | find_key_by_path($path)) else .value |= keys_to_paths_recursive($path) end) else . end; -$source | keys_to_paths_recursive([]) +$source[0] | keys_to_paths_recursive([]) diff --git a/scripts/translations/normalize.sh b/scripts/translations/normalize.sh index 2f5933977..b98642d21 100755 --- a/scripts/translations/normalize.sh +++ b/scripts/translations/normalize.sh @@ -9,8 +9,8 @@ for locale_file in "${locale_list[@]}" do jq -n \ -f $(dirname "$0")/normalize-locales.jq \ - --argfile source $ROOT_DIR/webapp/locales/en.json \ - --argfile target $ROOT_DIR/webapp/locales/$locale_file \ + --slurpfile source $ROOT_DIR/webapp/locales/en.json \ + --slurpfile target $ROOT_DIR/webapp/locales/$locale_file \ > "$tmp" mv "$tmp" $ROOT_DIR/webapp/locales/$locale_file done From 01027e42cc44c547bf3ddae250c05098eee4bdc4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Apr 2025 17:19:53 +0200 Subject: [PATCH 038/266] feat(webapp): track online status (#8312) * client * backend * tests * also save awaySince timestamp * remove console.log --- .../src/middleware/permissionsMiddleware.ts | 1 + backend/src/models/User.ts | 2 + backend/src/schema/resolvers/users.spec.ts | 117 ++++++++++++++++++ backend/src/schema/resolvers/users.ts | 31 +++++ .../src/schema/types/enum/OnlineStatus.gql | 4 + backend/src/schema/types/type/User.gql | 2 + webapp/graphql/updateOnlineStatus.js | 7 ++ webapp/nuxt.config.js | 1 + webapp/plugins/onlineStatus.js | 27 ++++ 9 files changed, 192 insertions(+) create mode 100644 backend/src/schema/types/enum/OnlineStatus.gql create mode 100644 webapp/graphql/updateOnlineStatus.js create mode 100644 webapp/plugins/onlineStatus.js diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 1fc84b665..a38610efd 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -462,6 +462,7 @@ export default shield( switchUserRole: isAdmin, markTeaserAsViewed: allow, saveCategorySettings: isAuthenticated, + updateOnlineStatus: isAuthenticated, CreateRoom: isAuthenticated, CreateMessage: isAuthenticated, MarkMessagesAsSeen: isAuthenticated, diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index b8d024216..9b828e27e 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -54,6 +54,8 @@ export default { }, invitedBy: { type: 'relationship', relationship: 'INVITED', target: 'User', direction: 'in' }, lastActiveAt: { type: 'string', isoDate: true }, + lastOnlineStatus: { type: 'string' }, + awaySince: { type: 'string', isoDate: true }, createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, updatedAt: { type: 'string', diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index bc976fb24..09f98ad53 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -63,6 +63,12 @@ const saveCategorySettings = gql` } ` +const updateOnlineStatus = gql` + mutation ($status: OnlineStatus!) { + updateOnlineStatus(status: $status) + } +` + beforeAll(async () => { await cleanDatabase() @@ -722,3 +728,114 @@ describe('save category settings', () => { }) }) }) + +describe('updateOnlineStatus', () => { + beforeEach(async () => { + user = await Factory.build('user', { + id: 'user', + role: 'user', + }) + variables = { + status: 'online', + } + }) + + describe('not authenticated', () => { + beforeEach(async () => { + authenticatedUser = undefined + }) + + it('throws an error', async () => { + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [ + expect.objectContaining({ + message: 'Not Authorized!', + }), + ], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeEach(async () => { + authenticatedUser = await user.toJson() + }) + + describe('set online', () => { + it('returns true and saves the user in the database as online', async () => { + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + data: { updateOnlineStatus: true }, + }), + ) + + 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')) + await expect(dbUser.toJson()).resolves.toMatchObject({ + lastOnlineStatus: 'online', + }) + await expect(dbUser.toJson()).resolves.not.toMatchObject({ + awaySince: expect.any(String), + }) + }) + }) + + describe('set away', () => { + beforeEach(() => { + variables = { + status: 'away', + } + }) + + it('returns true and saves the user in the database as away', async () => { + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + data: { updateOnlineStatus: true }, + }), + ) + + 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')) + await expect(dbUser.toJson()).resolves.toMatchObject({ + lastOnlineStatus: 'away', + awaySince: expect.any(String), + }) + }) + + it('stores the timestamp of the first away call', async () => { + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + data: { updateOnlineStatus: true }, + }), + ) + + 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')) + await expect(dbUser.toJson()).resolves.toMatchObject({ + lastOnlineStatus: 'away', + awaySince: expect.any(String), + }) + + const awaySince = (await dbUser.toJson()).awaySince + + await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual( + expect.objectContaining({ + data: { updateOnlineStatus: true }, + }), + ) + + const result2 = await neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser2 = neode.hydrateFirst(result2, 'u', neode.model('User')) + await expect(dbUser2.toJson()).resolves.toMatchObject({ + lastOnlineStatus: 'away', + awaySince, + }) + }) + }) + }) +}) diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index 6f79a4ea9..cab0bc8a3 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -314,6 +314,37 @@ export default { session.close() } }, + updateOnlineStatus: async (object, args, context, resolveInfo) => { + const { status } = args + const { + user: { id }, + } = context + + const CYPHER_AWAY = ` + MATCH (user:User {id: $id}) + WITH user, + CASE user.lastOnlineStatus + WHEN 'away' THEN user.awaySince + ELSE toString(datetime()) + END AS awaySince + SET user.awaySince = awaySince + SET user.lastOnlineStatus = $status + ` + const CYPHER_ONLINE = ` + MATCH (user:User {id: $id}) + SET user.awaySince = null + SET user.lastOnlineStatus = $status + ` + + // Last Online Time is saved as `lastActiveAt` + const session = context.driver.session() + await session.writeTransaction((transaction) => { + // return transaction.run(status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE, { id, status }) + return transaction.run(status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE, { id, status }) + }) + + return true + }, }, User: { email: async (parent, params, context, resolveInfo) => { diff --git a/backend/src/schema/types/enum/OnlineStatus.gql b/backend/src/schema/types/enum/OnlineStatus.gql new file mode 100644 index 000000000..7827ddb0b --- /dev/null +++ b/backend/src/schema/types/enum/OnlineStatus.gql @@ -0,0 +1,4 @@ +enum OnlineStatus { + online + away +} diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index f406e4e45..70b10aa42 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -224,6 +224,8 @@ type Mutation { switchUserRole(role: UserRole!, id: ID!): User saveCategorySettings(activeCategories: [String]): Boolean + + updateOnlineStatus(status: OnlineStatus!): Boolean! requestPasswordReset(email: String!): Boolean! resetPassword(email: String!, nonce: String!, newPassword: String!): Boolean! diff --git a/webapp/graphql/updateOnlineStatus.js b/webapp/graphql/updateOnlineStatus.js new file mode 100644 index 000000000..ee39b0667 --- /dev/null +++ b/webapp/graphql/updateOnlineStatus.js @@ -0,0 +1,7 @@ +import gql from 'graphql-tag' + +export const updateOnlineStatus = gql` + mutation ($status: OnlineStatus!) { + updateOnlineStatus(status: $status) + } +` diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 4e82e9330..9adacd4cc 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -130,6 +130,7 @@ export default { { src: '~/plugins/vue-observe-visibility.js', ssr: false }, { src: '~/plugins/v-mapbox.js', mode: 'client' }, { src: '~/plugins/vue-advanced-chat.js', mode: 'client' }, + { src: '~/plugins/onlineStatus.js', mode: 'client' }, ], router: { diff --git a/webapp/plugins/onlineStatus.js b/webapp/plugins/onlineStatus.js new file mode 100644 index 000000000..81252333c --- /dev/null +++ b/webapp/plugins/onlineStatus.js @@ -0,0 +1,27 @@ +import { updateOnlineStatus as updateOnlineStatusMutation } from '~/graphql/updateOnlineStatus.js' + +let _app = null + +const updateOnlineStatus = async () => { + if (!_app.store.getters['auth/isAuthenticated']) { + return + } + + const status = document.hidden ? 'away' : 'online' + + const client = _app.apolloProvider.defaultClient + + await client.mutate({ + mutation: updateOnlineStatusMutation, + variables: { status }, + }) +} + +export default ({ app }) => { + _app = app + if (process.client) { + window.addEventListener('visibilitychange', updateOnlineStatus) + setInterval(updateOnlineStatus, 30000) + updateOnlineStatus() + } +} From 130da7509b64aeadca9d90e90b23ccd7edbbc186 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 3 Apr 2025 18:03:53 +0200 Subject: [PATCH 039/266] fix(backend): users observing post count (#8295) * do not count users that are deleted or disbled --- backend/src/schema/resolvers/posts.ts | 3 ++- backend/src/schema/types/type/Post.gql | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/schema/resolvers/posts.ts b/backend/src/schema/resolvers/posts.ts index b111678c0..ce342cea7 100644 --- a/backend/src/schema/resolvers/posts.ts +++ b/backend/src/schema/resolvers/posts.ts @@ -485,7 +485,8 @@ export default { shoutedCount: '<-[:SHOUTED]-(related:User) WHERE NOT related.deleted = true AND NOT related.disabled = true', emotionsCount: '<-[related:EMOTED]-(:User)', - observingUsersCount: '<-[related:OBSERVES]-(:User) WHERE related.active = true', + observingUsersCount: + '<-[obs:OBSERVES]-(related:User) WHERE obs.active = true AND NOT related.deleted = true AND NOT related.disabled = true', }, boolean: { shoutedByCurrentUser: diff --git a/backend/src/schema/types/type/Post.gql b/backend/src/schema/types/type/Post.gql index ddf6e557e..fcaa5945a 100644 --- a/backend/src/schema/types/type/Post.gql +++ b/backend/src/schema/types/type/Post.gql @@ -192,7 +192,7 @@ type Post { statement: "MATCH (this)<-[obs:OBSERVES]-(u:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(u) >= 1" ) observingUsersCount: Int! - @cypher(statement: "MATCH (this)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true RETURN COUNT(DISTINCT u)") + @cypher(statement: "MATCH (this)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true AND NOT u.deleted = true AND NOT u.disabled = true RETURN COUNT(DISTINCT u)") } input _PostInput { From 72f469d823bc2eef3bfea1ceedbc57cf2d65fdae Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 3 Apr 2025 18:46:02 +0200 Subject: [PATCH 040/266] migration adds OBSERVES relation between author and post (#8307) --- ...20250331130323-author-observes-own-post.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 backend/src/db/migrations/20250331130323-author-observes-own-post.ts diff --git a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts new file mode 100644 index 000000000..7343d5010 --- /dev/null +++ b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts @@ -0,0 +1,61 @@ +import { getDriver } from '../../db/neo4j' + +export const description = ` +All authors observe their posts. +` + +export async function up(next) { + const driver = getDriver() + const session = driver.session() + const transaction = session.beginTransaction() + + try { + // Implement your migration here. + await transaction.run(` + MATCH (author:User)-[:WROTE]->(post:Post) + MERGE (author)-[obs:OBSERVES]->(post) + ON CREATE SET + obs.active = true, + obs.createdAt = toString(datetime()), + obs.updatedAt = toString(datetime()) + RETURN post + `) + 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 { + 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(` + MATCH (:User)-[obs:OBSERVES]->(p:Post) + DELETE obs + RETURN p + `) + 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 { + session.close() + } +} From ddd46e83047ec21d8cd1a0863c0c0130a9977d1a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Apr 2025 23:22:07 +0200 Subject: [PATCH 041/266] fix frontend package lock (#8316) --- frontend/package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d170ef893..63b54a127 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social-frontend", - "version": "3.2.0", + "version": "3.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.2.0", + "version": "3.2.1", "license": "Apache-2.0", "dependencies": { "@intlify/unplugin-vue-i18n": "^2.0.0", From 5462a5b4c10ab2057d6d22a6c6ff219c73b950ec Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 4 Apr 2025 00:01:10 +0200 Subject: [PATCH 042/266] fix(backend): typo in groups resolver (#8318) * fix typo * fixed all related typos in unittests and found another in groups --------- Co-authored-by: mahula --- backend/src/schema/resolvers/groups.spec.ts | 12 ++++++------ backend/src/schema/resolvers/groups.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/src/schema/resolvers/groups.spec.ts b/backend/src/schema/resolvers/groups.spec.ts index f99133be1..1d66b376c 100644 --- a/backend/src/schema/resolvers/groups.spec.ts +++ b/backend/src/schema/resolvers/groups.spec.ts @@ -377,22 +377,22 @@ describe('in mode', () => { describe('not even one', () => { describe('by "categoryIds: null"', () => { - it('throws error: "Too view categories!"', async () => { + it('throws error: "Too few categories!"', async () => { const { errors } = await mutate({ mutation: createGroupMutation(), variables: { ...variables, categoryIds: null }, }) - expect(errors![0]).toHaveProperty('message', 'Too view categories!') + expect(errors![0]).toHaveProperty('message', 'Too few categories!') }) }) describe('by "categoryIds: []"', () => { - it('throws error: "Too view categories!"', async () => { + it('throws error: "Too few categories!"', async () => { const { errors } = await mutate({ mutation: createGroupMutation(), variables: { ...variables, categoryIds: [] }, }) - expect(errors![0]).toHaveProperty('message', 'Too view categories!') + expect(errors![0]).toHaveProperty('message', 'Too few categories!') }) }) }) @@ -2900,7 +2900,7 @@ describe('in mode', () => { describe('not even one', () => { describe('by "categoryIds: []"', () => { - it('throws error: "Too view categories!"', async () => { + it('throws error: "Too few categories!"', async () => { const { errors } = await mutate({ mutation: updateGroupMutation(), variables: { @@ -2908,7 +2908,7 @@ describe('in mode', () => { categoryIds: [], }, }) - expect(errors![0]).toHaveProperty('message', 'Too view categories!') + expect(errors![0]).toHaveProperty('message', 'Too few categories!') }) }) }) diff --git a/backend/src/schema/resolvers/groups.ts b/backend/src/schema/resolvers/groups.ts index cd1eb8db3..f5282a3bb 100644 --- a/backend/src/schema/resolvers/groups.ts +++ b/backend/src/schema/resolvers/groups.ts @@ -130,7 +130,7 @@ export default { delete params.categoryIds params.locationName = params.locationName === '' ? null : params.locationName if (CONFIG.CATEGORIES_ACTIVE && (!categoryIds || categoryIds.length < CATEGORIES_MIN)) { - throw new UserInputError('Too view categories!') + throw new UserInputError('Too few categories!') } if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length > CATEGORIES_MAX) { throw new UserInputError('Too many categories!') @@ -200,7 +200,7 @@ export default { if (CONFIG.CATEGORIES_ACTIVE && categoryIds) { if (categoryIds.length < CATEGORIES_MIN) { - throw new UserInputError('Too view categories!') + throw new UserInputError('Too few categories!') } if (categoryIds.length > CATEGORIES_MAX) { throw new UserInputError('Too many categories!') From ea972a2c84e940dcf01ac2a10925827413f27465 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 00:42:20 +0000 Subject: [PATCH 043/266] build(deps): bump actions/upload-artifact from 4.6.1 to 4.6.2 (#8289) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1...ea165f8d65b6e75b540449e92b4886f43607fa02) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test-e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index fe8a514dc..8088b9c19 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -93,7 +93,7 @@ jobs: - name: Full stack tests | if tests failed, upload report id: e2e-report if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }} - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: 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 From 6006ed2dbefea30a95ec3c5abdac5a139f1ab7e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 01:21:25 +0000 Subject: [PATCH 044/266] build(deps): bump actions/setup-node from 4.2.0 to 4.3.0 (#8288) Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a...cdca7365b2dadb8aad0a33bc7601856ffabcc48e) --- updated-dependencies: - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/check-documentation.yml | 2 +- .github/workflows/deploy-documentation.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-documentation.yml b/.github/workflows/check-documentation.yml index 7729bc8c5..27e761fa5 100644 --- a/.github/workflows/check-documentation.yml +++ b/.github/workflows/check-documentation.yml @@ -54,7 +54,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Setup Node 20 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.0.3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.0.3 with: node-version: '20' diff --git a/.github/workflows/deploy-documentation.yml b/.github/workflows/deploy-documentation.yml index 220d40aa7..ef4f7d2b7 100644 --- a/.github/workflows/deploy-documentation.yml +++ b/.github/workflows/deploy-documentation.yml @@ -30,7 +30,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Setup Node 20 - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.0.3 + uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4.0.3 with: node-version: 20 From c682bc9b7a0826d5fc0e76dd14c27bc6131ca2d6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 01:49:09 +0000 Subject: [PATCH 045/266] build(deps): bump amannn/action-semantic-pull-request (#8303) Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 40166f00814508ec3201fc8595b393d451c8cd80 to 04501d43b574e4c1d23c629ffe4dcec27acfdeff. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/40166f00814508ec3201fc8595b393d451c8cd80...04501d43b574e4c1d23c629ffe4dcec27acfdeff) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.lint_pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.lint_pr.yml b/.github/workflows/test.lint_pr.yml index 6b91f9d5c..ab9d4b970 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@40166f00814508ec3201fc8595b393d451c8cd80 # v5.5.3 + - uses: amannn/action-semantic-pull-request@04501d43b574e4c1d23c629ffe4dcec27acfdeff # v5.5.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: From 9f67257423642e930250a825f7d1ac7a13e53e28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 02:35:20 +0000 Subject: [PATCH 046/266] build(deps-dev): bump eslint-plugin-prettier in /backend (#8300) Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.2.3 to 5.2.5. - [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases) - [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.3...v5.2.5) --- updated-dependencies: - dependency-name: eslint-plugin-prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/backend/package.json b/backend/package.json index 6cd363831..4f67320df 100644 --- a/backend/package.json +++ b/backend/package.json @@ -108,7 +108,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^16.6.2", - "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-security": "^3.0.1", "jest": "^29.7.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index c8c55819f..13e0cfa21 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1596,10 +1596,10 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@pkgr/core@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" - integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@pkgr/core@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.0.tgz#8dff61038cb5884789d8b323d9869e5363b976f7" + integrity sha512-vsJDAkYR6qCPu+ioGScGiMYR7LvZYIXh/dlQeviqoTWNCVfKTLYD/LkNWH4Mxsv2a5vpIRc77FN5DnmK1eBggQ== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -4466,13 +4466,13 @@ eslint-plugin-n@^16.6.2: resolve "^1.22.2" semver "^7.5.3" -eslint-plugin-prettier@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5" - integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw== +eslint-plugin-prettier@^5.2.6: + version "5.2.6" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz#be39e3bb23bb3eeb7e7df0927cdb46e4d7945096" + integrity sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ== dependencies: prettier-linter-helpers "^1.0.0" - synckit "^0.9.1" + synckit "^0.11.0" eslint-plugin-promise@^6.1.1: version "6.1.1" @@ -9066,13 +9066,13 @@ synchronous-promise@^2.0.10: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.10.tgz#e64c6fd3afd25f423963353043f4a68ebd397fd8" integrity sha512-6PC+JRGmNjiG3kJ56ZMNWDPL8hjyghF5cMXIFOKg+NiwwEZZIvxTWd0pinWKyD227odg9ygF8xVhhz7gb8Uq7A== -synckit@^0.9.1: - version "0.9.2" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62" - integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw== +synckit@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.1.tgz#8ea44544e9d9c0540963c6bddb8f14616fef5425" + integrity sha512-fWZqNBZNNFp/7mTUy1fSsydhKsAKJ+u90Nk7kOK5Gcq9vObaqLBLjWFDBkyVU9Vvc6Y71VbOevMuGhqv02bT+Q== dependencies: - "@pkgr/core" "^0.1.0" - tslib "^2.6.2" + "@pkgr/core" "^0.2.0" + tslib "^2.8.1" tapable@^2.2.0: version "2.2.1" @@ -9330,10 +9330,10 @@ 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.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +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== tsutils@^3.21.0: version "3.21.0" From 69508cfb98663e96474fe5e6eebd4d85a1817a52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 03:28:40 +0000 Subject: [PATCH 047/266] build(deps-dev): bump eslint-config-prettier in /backend (#8269) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 10.0.1 to 10.1.1. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v10.0.1...v10.1.1) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4f67320df..12ed9ce20 100644 --- a/backend/package.json +++ b/backend/package.json @@ -102,7 +102,7 @@ "@typescript-eslint/parser": "^5.62.0", "apollo-server-testing": "~2.11.0", "eslint": "^8.57.1", - "eslint-config-prettier": "^10.0.1", + "eslint-config-prettier": "^10.1.1", "eslint-config-standard": "^17.1.0", "eslint-import-resolver-typescript": "^3.8.3", "eslint-plugin-import": "^2.31.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 13e0cfa21..45abd673e 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4369,10 +4369,10 @@ eslint-compat-utils@^0.1.2: resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz#f45e3b5ced4c746c127cf724fb074cd4e730d653" integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg== -eslint-config-prettier@^10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf" - integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw== +eslint-config-prettier@^10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132" + integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw== eslint-config-standard@^17.1.0: version "17.1.0" From 81b7d4a09c2b7e9e65af19ff34737ab3f8760a59 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 4 Apr 2025 10:28:35 +0200 Subject: [PATCH 048/266] expose port so the mailserver can actually be used (#8319) --- docker-compose.override.yml | 1 + docker-compose.test.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index fed2ae70a..53f7e7b4e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -43,3 +43,4 @@ services: container_name: mailserver ports: - 1080:80 + - 25:25 diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 542a6c187..119cd20eb 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -40,3 +40,4 @@ services: image: djfarrelly/maildev ports: - 1080:80 + - 25:25 From 1e6a74b8ce874681fe586f9f1efe4b2d8c6d3611 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 4 Apr 2025 13:54:43 +0200 Subject: [PATCH 049/266] feat(frontend): observe posts (#8293) * After creating the post, the author of it automatically observes it to get notifications when there are interactions * a user that comments a post, automatically observes that post to get notifications when there are more interactions on that post * mutation that switches the state of the obeservation of a post on and off * remove duplicate code * fix unit tests * add metric observed users count to posts * change naming * Add follow post entry to post menu * Add FollowButton (WIP), show unfollow in menu when already followed * Follow/unfollow post => observe * Update slashed bell * Add requests to observe/unobserve posts * Add ObserveButton functionality * Rename isObservedByMe * Add observingUsersCount; simplify ObserveButton and menu entries * Fix locales * Add snapshot test for ObserveButton (WIP) * Remove empty routes push * Add test for ObserveButton * Add test for ContentMenu, improve ObserveButton test * Remove unneeded fields from PostQuery --------- Co-authored-by: Moriz Wahl --- .../assets/_new/icons/svgs/bell-slashed.svg | 1 + .../ContentMenu/ContentMenu.spec.js | 38 +++++++++ webapp/components/ContentMenu/ContentMenu.vue | 18 +++++ webapp/components/ObserveButton.spec.js | 60 ++++++++++++++ webapp/components/ObserveButton.vue | 39 +++++++++ webapp/components/PostTeaser/PostTeaser.vue | 6 +- .../__snapshots__/ObserveButton.spec.js.snap | 81 +++++++++++++++++++ .../features/SearchResults/SearchResults.vue | 3 + webapp/graphql/CommentMutations.js | 2 + webapp/jest.config.js | 1 + webapp/locales/de.json | 7 ++ webapp/locales/en.json | 7 ++ webapp/locales/es.json | 7 ++ webapp/locales/fr.json | 7 ++ webapp/locales/it.json | 7 ++ webapp/locales/nl.json | 7 ++ webapp/locales/pl.json | 7 ++ webapp/locales/pt.json | 7 ++ webapp/locales/ru.json | 7 ++ webapp/mixins/postListActions.js | 18 +++++ webapp/package.json | 1 + webapp/pages/groups/_id/_slug.vue | 3 + webapp/pages/index.vue | 3 + webapp/pages/post/_id/_slug/index.vue | 37 ++++++++- webapp/pages/profile/_id/_slug.vue | 3 + webapp/yarn.lock | 9 ++- 26 files changed, 383 insertions(+), 3 deletions(-) create mode 100644 webapp/assets/_new/icons/svgs/bell-slashed.svg create mode 100644 webapp/components/ObserveButton.spec.js create mode 100644 webapp/components/ObserveButton.vue create mode 100644 webapp/components/__snapshots__/ObserveButton.spec.js.snap diff --git a/webapp/assets/_new/icons/svgs/bell-slashed.svg b/webapp/assets/_new/icons/svgs/bell-slashed.svg new file mode 100644 index 000000000..0aae3ff97 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/bell-slashed.svg @@ -0,0 +1 @@ + diff --git a/webapp/components/ContentMenu/ContentMenu.spec.js b/webapp/components/ContentMenu/ContentMenu.spec.js index 0bd398e41..ce7a45a42 100644 --- a/webapp/components/ContentMenu/ContentMenu.spec.js +++ b/webapp/components/ContentMenu/ContentMenu.spec.js @@ -500,6 +500,44 @@ describe('ContentMenu.vue', () => { ], ]) }) + + it('can observe posts', async () => { + const wrapper = await openContentMenu({ + isOwner: false, + resourceType: 'contribution', + resource: { + id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8', + isObservedByMe: false, + }, + }) + wrapper + .findAll('.ds-menu-item') + .filter((item) => item.text() === 'post.menu.observe') + .at(0) + .trigger('click') + expect(wrapper.emitted('toggleObservePost')).toEqual([ + ['d23a4265-f5f7-4e17-9f86-85f714b4b9f8', true], + ]) + }) + + it('can unobserve posts', async () => { + const wrapper = await openContentMenu({ + isOwner: false, + resourceType: 'contribution', + resource: { + id: 'd23a4265-f5f7-4e17-9f86-85f714b4b9f8', + isObservedByMe: true, + }, + }) + wrapper + .findAll('.ds-menu-item') + .filter((item) => item.text() === 'post.menu.unobserve') + .at(0) + .trigger('click') + expect(wrapper.emitted('toggleObservePost')).toEqual([ + ['d23a4265-f5f7-4e17-9f86-85f714b4b9f8', false], + ]) + }) }) }) }) diff --git a/webapp/components/ContentMenu/ContentMenu.vue b/webapp/components/ContentMenu/ContentMenu.vue index d723a9667..627e5d982 100644 --- a/webapp/components/ContentMenu/ContentMenu.vue +++ b/webapp/components/ContentMenu/ContentMenu.vue @@ -99,6 +99,24 @@ export default { }) } } + + if (this.resource.isObservedByMe) { + routes.push({ + label: this.$t(`post.menu.unobserve`), + callback: () => { + this.$emit('toggleObservePost', this.resource.id, false) + }, + icon: 'bell-slashed', + }) + } else { + routes.push({ + label: this.$t(`post.menu.observe`), + callback: () => { + this.$emit('toggleObservePost', this.resource.id, true) + }, + icon: 'bell', + }) + } } if (this.isOwner && this.resourceType === 'comment') { diff --git a/webapp/components/ObserveButton.spec.js b/webapp/components/ObserveButton.spec.js new file mode 100644 index 000000000..3ecfc40b6 --- /dev/null +++ b/webapp/components/ObserveButton.spec.js @@ -0,0 +1,60 @@ +import { mount } from '@vue/test-utils' +import ObserveButton from './ObserveButton.vue' + +const localVue = global.localVue + +describe('ObserveButton', () => { + let mocks + + const Wrapper = (count = 1, postId = '123', isObserved = true) => { + return mount(ObserveButton, { + mocks, + localVue, + propsData: { + count, + postId, + isObserved, + }, + }) + } + + let wrapper + + beforeEach(() => { + mocks = { + $t: jest.fn(), + } + }) + + describe('observed', () => { + beforeEach(() => { + wrapper = Wrapper(1, '123', true) + }) + + it('renders', () => { + expect(wrapper.element).toMatchSnapshot() + }) + + it('emits toggleObservePost with false when clicked', () => { + const button = wrapper.find('.base-button') + button.trigger('click') + expect(wrapper.emitted('toggleObservePost')).toEqual([['123', false]]) + }) + }) + + describe('unobserved', () => { + beforeEach(() => { + wrapper = Wrapper(1, '123', false) + }) + + it('renders', () => { + expect(wrapper.element).toMatchSnapshot() + }) + + it('emits toggleObservePost with true when clicked', () => { + const button = wrapper.find('.base-button') + button.trigger('click') + expect(wrapper.emitted('toggleObservePost')).toEqual([['123', true]]) + }) + }) +}) diff --git a/webapp/components/ObserveButton.vue b/webapp/components/ObserveButton.vue new file mode 100644 index 000000000..2c275709b --- /dev/null +++ b/webapp/components/ObserveButton.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/webapp/components/PostTeaser/PostTeaser.vue b/webapp/components/PostTeaser/PostTeaser.vue index 3c7d23c03..ad43a9d31 100644 --- a/webapp/components/PostTeaser/PostTeaser.vue +++ b/webapp/components/PostTeaser/PostTeaser.vue @@ -59,7 +59,7 @@ :key="category.id" v-tooltip="{ content: ` - ${$t(`contribution.category.name.${category.slug}`)}: + ${$t(`contribution.category.name.${category.slug}`)}: ${$t(`contribution.category.description.${category.slug}`)} `, placement: 'bottom-start', @@ -97,6 +97,7 @@ :is-owner="isAuthor" @pinPost="pinPost" @unpinPost="unpinPost" + @toggleObservePost="toggleObservePost" /> @@ -212,6 +213,9 @@ export default { unpinPost(post) { this.$emit('unpinPost', post) }, + toggleObservePost(postId, value) { + this.$emit('toggleObservePost', postId, value) + }, visibilityChanged(isVisible, entry, id) { if (!this.post.viewedTeaserByCurrentUser && isVisible) { this.$apollo diff --git a/webapp/components/__snapshots__/ObserveButton.spec.js.snap b/webapp/components/__snapshots__/ObserveButton.spec.js.snap new file mode 100644 index 000000000..c3ba629be --- /dev/null +++ b/webapp/components/__snapshots__/ObserveButton.spec.js.snap @@ -0,0 +1,81 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ObserveButton observed renders 1`] = ` +
+ + +
+ +

+

+ 1x +

+ + + +

+
+`; + +exports[`ObserveButton unobserved renders 1`] = ` +
+ + +
+ +

+

+ 1x +

+ + + +

+
+`; diff --git a/webapp/components/_new/features/SearchResults/SearchResults.vue b/webapp/components/_new/features/SearchResults/SearchResults.vue index 20385ce64..94d569e70 100644 --- a/webapp/components/_new/features/SearchResults/SearchResults.vue +++ b/webapp/components/_new/features/SearchResults/SearchResults.vue @@ -48,6 +48,9 @@ @removePostFromList="posts = removePostFromList(post, posts)" @pinPost="pinPost(post, refetchPostList)" @unpinPost="unpinPost(post, refetchPostList)" + @toggleObservePost=" + (postId, value) => toggleObservePost(postId, value, refetchPostList) + " /> diff --git a/webapp/graphql/CommentMutations.js b/webapp/graphql/CommentMutations.js index 191edf217..dd00527be 100644 --- a/webapp/graphql/CommentMutations.js +++ b/webapp/graphql/CommentMutations.js @@ -13,6 +13,8 @@ export default (i18n) => { updatedAt disabled deleted + isPostObservedByMe + postObservingUsersCount author { id slug diff --git a/webapp/jest.config.js b/webapp/jest.config.js index e9185f60e..947d6019d 100644 --- a/webapp/jest.config.js +++ b/webapp/jest.config.js @@ -38,4 +38,5 @@ module.exports = { }, moduleFileExtensions: ['js', 'json', 'vue'], testEnvironment: 'jest-environment-jsdom', + snapshotSerializers: ['jest-serializer-vue'], } diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 4e5d42ac4..518ba99a9 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -747,6 +747,9 @@ "title": "Benachrichtigungen", "user": "Nutzer" }, + "observeButton": { + "observed": "beobachtet" + }, "post": { "comment": { "reply": "Antworten", @@ -778,8 +781,12 @@ "menu": { "delete": "Beitrag löschen", "edit": "Beitrag bearbeiten", + "observe": "Beitrag beobachten", + "observedSuccessfully": "Du beobachtest diesen Beitrag!", "pin": "Beitrag anheften", "pinnedSuccessfully": "Beitrag erfolgreich angeheftet!", + "unobserve": "Beitrag nicht mehr beobachten", + "unobservedSuccessfully": "Du beobachtest diesen Beitrag nicht mehr!", "unpin": "Beitrag loslösen", "unpinnedSuccessfully": "Angehefteten Beitrag erfolgreich losgelöst!" }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 534a376cf..f78728c4f 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -747,6 +747,9 @@ "title": "Notifications", "user": "User" }, + "observeButton": { + "observed": "observed" + }, "post": { "comment": { "reply": "Reply", @@ -778,8 +781,12 @@ "menu": { "delete": "Delete post", "edit": "Edit post", + "observe": "Observe post", + "observedSuccessfully": "You are now observing this post!", "pin": "Pin post", "pinnedSuccessfully": "Post pinned successfully!", + "unobserve": "Stop to observe post", + "unobservedSuccessfully": "You are no longer observing this post!", "unpin": "Unpin post", "unpinnedSuccessfully": "Post unpinned successfully!" }, diff --git a/webapp/locales/es.json b/webapp/locales/es.json index c33169f66..a085a53e0 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -747,6 +747,9 @@ "title": "Notificaciones", "user": "Usuario" }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": "Contestar", @@ -778,8 +781,12 @@ "menu": { "delete": "Borrar contribución", "edit": "Editar contribución", + "observe": "Observar contribución", + "observedSuccessfully": null, "pin": "Anclar contribución", "pinnedSuccessfully": "¡Contribución anclado con éxito!", + "unobserve": "Dejar de observar contribución", + "unobservedSuccessfully": null, "unpin": "Desanclar contribución", "unpinnedSuccessfully": "¡Contribución desanclado con éxito!" }, diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index 889093b8c..f1b5642de 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -747,6 +747,9 @@ "title": "Notifications", "user": "Utilisateur" }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": null, @@ -778,8 +781,12 @@ "menu": { "delete": "Supprimer le Post", "edit": "Modifier le Post", + "observe": "Observer le Post", + "observedSuccessfully": null, "pin": "Épingler le Post", "pinnedSuccessfully": "Poste épinglé avec succès!", + "unobserve": "Ne plus observer le Post", + "unobservedSuccessfully": null, "unpin": "Retirer l'épingle du poste", "unpinnedSuccessfully": "Épingle retirer du Post avec succès!" }, diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 222ac94b2..54248e6ee 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -747,6 +747,9 @@ "title": null, "user": null }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": null, @@ -778,8 +781,12 @@ "menu": { "delete": null, "edit": null, + "observe": null, + "observedSuccessfully": null, "pin": null, "pinnedSuccessfully": null, + "unobserve": null, + "unobservedSuccessfully": null, "unpin": null, "unpinnedSuccessfully": null }, diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 6ec9b4c2a..7907ce052 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -747,6 +747,9 @@ "title": null, "user": null }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": null, @@ -778,8 +781,12 @@ "menu": { "delete": null, "edit": null, + "observe": null, + "observedSuccessfully": null, "pin": null, "pinnedSuccessfully": null, + "unobserve": null, + "unobservedSuccessfully": null, "unpin": null, "unpinnedSuccessfully": null }, diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index 1ed0fa050..7a800b3d0 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -747,6 +747,9 @@ "title": null, "user": null }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": null, @@ -778,8 +781,12 @@ "menu": { "delete": "Usuń wpis", "edit": "Edytuj wpis", + "observe": null, + "observedSuccessfully": null, "pin": null, "pinnedSuccessfully": null, + "unobserve": null, + "unobservedSuccessfully": null, "unpin": null, "unpinnedSuccessfully": null }, diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index c2e38fbc6..c0bb8a500 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -747,6 +747,9 @@ "title": "Notificações", "user": "Usuário" }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": null, @@ -778,8 +781,12 @@ "menu": { "delete": "Excluir publicação", "edit": "Editar publicação", + "observe": "Observar publicação", + "observedSuccessfully": null, "pin": "Fixar publicação", "pinnedSuccessfully": "Publicação fixada com sucesso!", + "unobserve": "Deixar de observar publicação", + "unobservedSuccessfully": null, "unpin": "Desafixar publicação", "unpinnedSuccessfully": "Publicação desafixada com sucesso!" }, diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index 91a8b30bc..bebae1012 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -747,6 +747,9 @@ "title": "Уведомления", "user": "Пользователь" }, + "observeButton": { + "observed": null + }, "post": { "comment": { "reply": "Ответ", @@ -778,8 +781,12 @@ "menu": { "delete": "Удалить пост", "edit": "Редактировать пост", + "observe": null, + "observedSuccessfully": null, "pin": "Закрепить пост", "pinnedSuccessfully": "Пост больше не закреплен!", + "unobserve": null, + "unobservedSuccessfully": null, "unpin": "Открепить пост", "unpinnedSuccessfully": "Пост успешно не закреплено!" }, diff --git a/webapp/mixins/postListActions.js b/webapp/mixins/postListActions.js index 57fd28bd1..808af3ff7 100644 --- a/webapp/mixins/postListActions.js +++ b/webapp/mixins/postListActions.js @@ -35,5 +35,23 @@ export default { }) .catch((error) => this.$toast.error(error.message)) }, + toggleObservePost(postId, value, refetchPostList = () => {}) { + this.$apollo + .mutate({ + mutation: PostMutations().toggleObservePost, + variables: { + value, + id: postId, + }, + }) + .then(() => { + const message = this.$t( + `post.menu.${value ? 'observedSuccessfully' : 'unobservedSuccessfully'}`, + ) + this.$toast.success(message) + refetchPostList() + }) + .catch((error) => this.$toast.error(error.message)) + }, }, } diff --git a/webapp/package.json b/webapp/package.json index d12672224..03ad85d30 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -40,6 +40,7 @@ "express": "~4.21.2", "graphql": "~14.7.0", "intersection-observer": "^0.12.0", + "jest-serializer-vue": "^3.1.0", "jsonwebtoken": "~9.0.2", "linkify-it": "~5.0.0", "mapbox-gl": "1.13.2", diff --git a/webapp/pages/groups/_id/_slug.vue b/webapp/pages/groups/_id/_slug.vue index f261f2ef3..10d2ca8d2 100644 --- a/webapp/pages/groups/_id/_slug.vue +++ b/webapp/pages/groups/_id/_slug.vue @@ -283,6 +283,9 @@ @removePostFromList="posts = removePostFromList(post, posts)" @pinPost="pinPost(post, refetchPostList)" @unpinPost="unpinPost(post, refetchPostList)" + @toggleObservePost=" + (postId, value) => toggleObservePost(postId, value, refetchPostList) + " /> diff --git a/webapp/pages/index.vue b/webapp/pages/index.vue index 6067119e1..c780b0ae3 100644 --- a/webapp/pages/index.vue +++ b/webapp/pages/index.vue @@ -116,6 +116,9 @@ @removePostFromList="posts = removePostFromList(post, posts)" @pinPost="pinPost(post, refetchPostList)" @unpinPost="unpinPost(post, refetchPostList)" + @toggleObservePost=" + (postId, value) => toggleObservePost(postId, value, refetchPostList) + " /> diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index 1e9622c13..b84486eec 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -49,6 +49,7 @@ :is-owner="isAuthor" @pinPost="pinPost" @unpinPost="unpinPost" + @toggleObservePost="toggleObservePost" /> @@ -111,6 +112,18 @@ :post-id="post.id" /> + + + + @@ -156,6 +169,7 @@ import ContentMenu from '~/components/ContentMenu/ContentMenu' import DateTimeRange from '~/components/DateTimeRange/DateTimeRange' import UserTeaser from '~/components/UserTeaser/UserTeaser' import HcShoutButton from '~/components/ShoutButton.vue' +import ObserveButton from '~/components/ObserveButton.vue' import LocationTeaser from '~/components/LocationTeaser/LocationTeaser' import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParamsLink.vue' import { @@ -184,6 +198,7 @@ export default { HcCategory, HcHashtag, HcShoutButton, + ObserveButton, LocationTeaser, PageParamsLink, UserTeaser, @@ -302,6 +317,8 @@ export default { }, async createComment(comment) { this.post.comments.push(comment) + this.post.isObservedByMe = comment.isPostObservedByMe + this.post.observingUsersCount = comment.postObservingUsersCount }, pinPost(post) { this.$apollo @@ -325,6 +342,24 @@ export default { }) .catch((error) => this.$toast.error(error.message)) }, + toggleObservePost(postId, value) { + this.$apollo + .mutate({ + mutation: PostMutations().toggleObservePost, + variables: { + value, + id: postId, + }, + }) + .then(() => { + const message = this.$t( + `post.menu.${value ? 'observedSuccessfully' : 'unobservedSuccessfully'}`, + ) + this.$toast.success(message) + this.$apollo.queries.Post.refetch() + }) + .catch((error) => this.$toast.error(error.message)) + }, toggleNewCommentForm(showNewCommentForm) { this.showNewCommentForm = showNewCommentForm }, @@ -379,7 +414,7 @@ export default { position: relative; /* The padding top makes sure the correct height is set (according to the hero image aspect ratio) before the hero image loads so - the autoscroll works correctly when following a comment link. + the autoscroll works correctly when following a comment link. */ padding-top: calc(var(--hero-image-aspect-ratio) * (100% + 48px)); diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue index cef3a5d45..e60ba1098 100644 --- a/webapp/pages/profile/_id/_slug.vue +++ b/webapp/pages/profile/_id/_slug.vue @@ -156,6 +156,9 @@ @removePostFromList="posts = removePostFromList(post, posts)" @pinPost="pinPost(post, refetchPostList)" @unpinPost="unpinPost(post, refetchPostList)" + @toggleObservePost=" + (postId, value) => toggleObservePost(postId, value, refetchPostList) + " /> diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 55873eb69..250492601 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -12609,6 +12609,13 @@ jest-runtime@^29.7.0: slash "^3.0.0" strip-bom "^4.0.0" +jest-serializer-vue@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jest-serializer-vue/-/jest-serializer-vue-3.1.0.tgz#af65817aa416d019f837b6cc53f121a3222846f4" + integrity sha512-vXz9/3IgBbLhsaVANYLG4ROCQd+Wg3qbB6ICofzFL+fbhSFPlqb0/MMGXcueVsjaovdWlYiRaLQLpdi1PTcoRQ== + dependencies: + pretty "2.0.0" + jest-snapshot@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5" @@ -16155,7 +16162,7 @@ pretty-time@^1.1.0: resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== -pretty@^2.0.0: +pretty@2.0.0, pretty@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pretty/-/pretty-2.0.0.tgz#adbc7960b7bbfe289a557dc5f737619a220d06a5" integrity sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w== From 0835057cc7027b750d7b641814a6db73fb4068d7 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 4 Apr 2025 19:16:50 +0200 Subject: [PATCH 050/266] refactor(backend): comment on observed post notification (#8311) * all users that observe a post are notified when the post is commented, except of the author of the comment, or users that blocked the commenter * test to illustrate the behavior of notifications for observed posts --- .../notificationsMiddleware.spec.ts | 1 - .../notifications/notificationsMiddleware.ts | 35 +- .../notifications/observing-posts.spec.ts | 377 ++++++++++++++++++ 3 files changed, 400 insertions(+), 13 deletions(-) create mode 100644 backend/src/middleware/notifications/observing-posts.spec.ts diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 6cec5c940..57354d13f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -238,7 +238,6 @@ describe('notifications', () => { }) it('sends me no notification', async () => { - await notifiedUser.relateTo(commentAuthor, 'blocked') await createCommentOnPostAction() const expected = expect.objectContaining({ data: { notifications: [] }, diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 706e46c51..aa2cee06e 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -108,13 +108,19 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo const handleContentDataOfComment = async (resolve, root, args, context, resolveInfo) => { const { content } = args - let idsOfUsers = extractMentionedUsers(content) + let idsOfMentionedUsers = extractMentionedUsers(content) const comment = await resolve(root, args, context, resolveInfo) const [postAuthor] = await postAuthorOfComment(comment.id, { context }) - idsOfUsers = idsOfUsers.filter((id) => id !== postAuthor.id) + idsOfMentionedUsers = idsOfMentionedUsers.filter((id) => id !== postAuthor.id) await publishNotifications(context, [ - notifyUsersOfMention('Comment', comment.id, idsOfUsers, 'mentioned_in_comment', context), - notifyUsersOfComment('Comment', comment.id, postAuthor.id, 'commented_on_post', context), + notifyUsersOfMention( + 'Comment', + comment.id, + idsOfMentionedUsers, + 'mentioned_in_comment', + context, + ), + notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context), ]) return comment } @@ -269,29 +275,34 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => { } } -const notifyUsersOfComment = async (label, commentId, postAuthorId, reason, context) => { - if (context.user.id === postAuthorId) return [] +const notifyUsersOfComment = async (label, commentId, reason, context) => { await validateNotifyUsers(label, reason) const session = context.driver.session() const writeTxResultPromise = await session.writeTransaction(async (transaction) => { const notificationTransactionResponse = await transaction.run( ` - MATCH (postAuthor:User {id: $postAuthorId})-[:WROTE]->(post:Post)<-[:COMMENTS]-(comment:Comment { id: $commentId })<-[:WROTE]-(commenter:User) - WHERE NOT (postAuthor)-[:BLOCKED]-(commenter) - MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(postAuthor) + MATCH (observingUser:User)-[:OBSERVES { active: true }]->(post:Post)<-[:COMMENTS]-(comment:Comment { id: $commentId })<-[:WROTE]-(commenter:User) + WHERE NOT (observingUser)-[:BLOCKED]-(commenter) AND NOT observingUser.id = $userId + WITH observingUser, post, comment, commenter + MATCH (postAuthor:User)-[:WROTE]->(post) + MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(observingUser) SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) - WITH notification, postAuthor, post, commenter, + WITH notification, observingUser, post, commenter, postAuthor, comment {.*, __typename: labels(comment)[0], author: properties(commenter), post: post {.*, author: properties(postAuthor) } } AS finalResource RETURN notification { .*, from: finalResource, - to: properties(postAuthor), + to: properties(observingUser), relatedUser: properties(commenter) } `, - { commentId, postAuthorId, reason }, + { + commentId, + reason, + userId: context.user.id, + }, ) return notificationTransactionResponse.records.map((record) => record.get('notification')) }) diff --git a/backend/src/middleware/notifications/observing-posts.spec.ts b/backend/src/middleware/notifications/observing-posts.spec.ts new file mode 100644 index 000000000..13b971ed8 --- /dev/null +++ b/backend/src/middleware/notifications/observing-posts.spec.ts @@ -0,0 +1,377 @@ +import gql from 'graphql-tag' +import { cleanDatabase } from '../../db/factories' +import { getNeode, getDriver } from '../../db/neo4j' +import createServer from '../../server' +import { createTestClient } from 'apollo-server-testing' + +import CONFIG from '../../config' + +CONFIG.CATEGORIES_ACTIVE = false + +let server, query, mutate, authenticatedUser + +let postAuthor, firstCommenter, secondCommenter + +const driver = getDriver() +const neode = getNeode() + +const createPostMutation = gql` + mutation ($id: ID, $title: String!, $content: String!) { + CreatePost(id: $id, title: $title, content: $content) { + id + title + content + } + } +` + +const createCommentMutation = gql` + mutation ($id: ID, $postId: ID!, $content: String!) { + CreateComment(id: $id, postId: $postId, content: $content) { + id + content + } + } +` + +const notificationQuery = gql` + query ($read: Boolean) { + notifications(read: $read, orderBy: updatedAt_desc) { + read + reason + createdAt + relatedUser { + id + } + from { + __typename + ... on Post { + id + content + } + ... on Comment { + id + content + } + ... on Group { + id + } + } + } + } +` + +const toggleObservePostMutation = gql` + mutation ($id: ID!, $value: Boolean!) { + toggleObservePost(id: $id, value: $value) { + isObservedByMe + observingUsersCount + } + } +` + +beforeAll(async () => { + await cleanDatabase() + + const createServerResult = createServer({ + context: () => { + return { + user: authenticatedUser, + neode, + driver, + cypherParams: { + currentUserId: authenticatedUser ? authenticatedUser.id : null, + }, + } + }, + }) + server = createServerResult.server + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate +}) + +afterAll(async () => { + await cleanDatabase() + driver.close() +}) + +describe('notifications for users that observe a post', () => { + beforeAll(async () => { + postAuthor = await neode.create( + 'User', + { + id: 'post-author', + name: 'Post Author', + slug: 'post-author', + }, + { + email: 'test@example.org', + password: '1234', + }, + ) + firstCommenter = await neode.create( + 'User', + { + id: 'first-commenter', + name: 'First Commenter', + slug: 'first-commenter', + }, + { + email: 'test2@example.org', + password: '1234', + }, + ) + secondCommenter = await neode.create( + 'User', + { + id: 'second-commenter', + name: 'Second Commenter', + slug: 'second-commenter', + }, + { + email: 'test3@example.org', + password: '1234', + }, + ) + authenticatedUser = await postAuthor.toJson() + await mutate({ + mutation: createPostMutation, + variables: { + id: 'post', + title: 'This is the post', + content: 'This is the content of the post', + }, + }) + }) + + describe('first comment on the post', () => { + beforeAll(async () => { + authenticatedUser = await firstCommenter.toJson() + await mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post', + id: 'c-1', + content: 'first comment of first commenter', + }, + }) + }) + + it('sends NO notification to the commenter', async () => { + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [], + }, + errors: undefined, + }) + }) + + it('sends notification to the author', async () => { + authenticatedUser = await postAuthor.toJson() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Comment', + id: 'c-1', + }, + read: false, + reason: 'commented_on_post', + }, + ], + }, + errors: undefined, + }) + }) + + describe('second comment on post', () => { + beforeAll(async () => { + authenticatedUser = await secondCommenter.toJson() + await mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post', + id: 'c-2', + content: 'first comment of second commenter', + }, + }) + }) + + it('sends NO notification to the commenter', async () => { + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [], + }, + errors: undefined, + }) + }) + + it('sends notification to the author', async () => { + authenticatedUser = await postAuthor.toJson() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Comment', + id: 'c-2', + }, + read: false, + reason: 'commented_on_post', + }, + { + from: { + __typename: 'Comment', + id: 'c-1', + }, + read: false, + reason: 'commented_on_post', + }, + ], + }, + errors: undefined, + }) + }) + + it('sends notification to first commenter', async () => { + authenticatedUser = await firstCommenter.toJson() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Comment', + id: 'c-2', + }, + read: false, + reason: 'commented_on_post', + }, + ], + }, + errors: undefined, + }) + }) + }) + + describe('first commenter unfollows the post and post author comments post', () => { + beforeAll(async () => { + authenticatedUser = await firstCommenter.toJson() + await mutate({ + mutation: toggleObservePostMutation, + variables: { + id: 'post', + value: false, + }, + }) + + authenticatedUser = await postAuthor.toJson() + await mutate({ + mutation: createCommentMutation, + variables: { + postId: 'post', + id: 'c-3', + content: 'first comment of post author', + }, + }) + }) + + it('sends no new notification to the post author', async () => { + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Comment', + id: 'c-2', + }, + read: false, + reason: 'commented_on_post', + }, + { + from: { + __typename: 'Comment', + id: 'c-1', + }, + read: false, + reason: 'commented_on_post', + }, + ], + }, + errors: undefined, + }) + }) + + it('sends no new notification to first commenter', async () => { + authenticatedUser = await firstCommenter.toJson() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Comment', + id: 'c-2', + }, + read: false, + reason: 'commented_on_post', + }, + ], + }, + errors: undefined, + }) + }) + + it('sends notification to second commenter', async () => { + authenticatedUser = await secondCommenter.toJson() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Comment', + id: 'c-3', + }, + read: false, + reason: 'commented_on_post', + }, + ], + }, + errors: undefined, + }) + }) + }) + }) +}) From 16bd62f949cb65fab7359396c518ff2fd1238de9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Fri, 4 Apr 2025 19:59:05 +0200 Subject: [PATCH 051/266] fix(other): leftover hetzner deployment (#8254) * Ignore old configuration, still * Add old branding value descriptions again and refine --- .gitignore | 1 + deployment/deployment-values.md | 73 +++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 deployment/deployment-values.md diff --git a/.gitignore b/.gitignore index 25c7b3134..d9d081e31 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ node_modules/ cypress/videos cypress/screenshots/ cypress.env.json +deployment/configurations/ .vuepress/.cache/ .vuepress/.temp/ diff --git a/deployment/deployment-values.md b/deployment/deployment-values.md new file mode 100644 index 000000000..96e5770fa --- /dev/null +++ b/deployment/deployment-values.md @@ -0,0 +1,73 @@ +# Deployment Values + +For each deployment, you need to set the environment variables and configurations. +Here is some specific information on how to set the values. + +## Webapp + +We have several configuration possibilities just in the frontend. + +### Date Time + +In file `branding/constants/dateTime.js`. + +- `RELATIVE_DATETIME` + - `true` (default) or `false` +- `ABSOLUT_DATETIME_FORMAT` + - definition see [date-fns, format](https://date-fns.org/v3.3.1/docs/format): + - `P`: just localized date + - `Pp`: just localized date and time + +## E-Mails + +You need to set environment variables to send registration and invitation information or notifications to users, for example. + +### SPF and DKIM + +More and more e-mail providers require settings for authorization and verification of e-mail senders. + +### SPF + +Sometimes it is enough to create an SPF record in your DNS. + +### DKIM + +However, if you need DKIM authorization and verification, you must set the appropriate environment variables in: `.env`, `docker-compose.yml` or Helm script `values.yaml`: + +```bash +SMTP_DKIM_DOMAINNAME= +SMTP_DKIM_KEYSELECTOR=ocelot # "free" name used in DNS as selector. we recommend this +SMTP_DKIM_PRIVATKEY="-----BEGIN RSA PRIVATE KEY-----\\n\\n-----END RSA PRIVATE KEY-----\\n" +``` + +You can find out how DKIM works here: + + + +To create the private and public DKIM key as DNS records with selector, see here: + + + +Information about the required PEM format can be found here: + + + +## Neo4j Database + +We have several configuration options for our Neo4j database. + +### DBMS_DEFAULT_DATABASE – Default Database Name to be Used + +If you need to set the default database name in Neo4j to be used for all operations and terminal commands like our backup scripts, you must set the appropriate environment variable in: `.env`, `docker-compose.yml` or Helm script `values.yaml`: + +```yaml +DBMS_DEFAULT_DATABASE: "graph.db" +``` + +The default value is `neo4j` if it is not set. + +As example see files: + +- `neo4j/.env.template` +- `deployment/docker-compose.yml` +- `deployment/configurations/stage.ocelot.social/kubernetes/values.yaml.template` From 2b74c76bad3d70a8a349b485a2d47f62e939d443 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 19:58:32 +0000 Subject: [PATCH 052/266] build(deps-dev): bump prettier from 3.5.2 to 3.5.3 in /backend (#8266) Bumps [prettier](https://github.com/prettier/prettier) from 3.5.2 to 3.5.3. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.5.2...3.5.3) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 12ed9ce20..e7f745b29 100644 --- a/backend/package.json +++ b/backend/package.json @@ -113,7 +113,7 @@ "eslint-plugin-security": "^3.0.1", "jest": "^29.7.0", "nodemon": "~3.1.9", - "prettier": "^3.5.2", + "prettier": "^3.5.3", "rosie": "^2.1.1", "ts-jest": "^29.2.5", "ts-node": "^10.9.2", diff --git a/backend/yarn.lock b/backend/yarn.lock index 45abd673e..6cf8589b3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -8037,10 +8037,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.2.tgz#d066c6053200da0234bf8fa1ef45168abed8b914" - integrity sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg== +prettier@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== pretty-format@^29.0.0, pretty-format@^29.7.0: version "29.7.0" From 16ecfab216f78a122ab1220752369e17f78a7d9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:00:22 +0000 Subject: [PATCH 053/266] build(deps-dev): bump eslint-plugin-vue from 9.32.0 to 9.33.0 in /webapp (#8264) Bumps [eslint-plugin-vue](https://github.com/vuejs/eslint-plugin-vue) from 9.32.0 to 9.33.0. - [Release notes](https://github.com/vuejs/eslint-plugin-vue/releases) - [Commits](https://github.com/vuejs/eslint-plugin-vue/compare/v9.32.0...v9.33.0) --- updated-dependencies: - dependency-name: eslint-plugin-vue dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 03ad85d30..f351a71fd 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -102,7 +102,7 @@ "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-promise": "~7.2.1", "eslint-plugin-standard": "~5.0.0", - "eslint-plugin-vue": "~9.32.0", + "eslint-plugin-vue": "~9.33.0", "flush-promises": "^1.0.2", "identity-obj-proxy": "^3.0.0", "jest": "29.7", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 250492601..c6cf074c9 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -9534,10 +9534,10 @@ eslint-plugin-standard@~5.0.0: resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-5.0.0.tgz#c43f6925d669f177db46f095ea30be95476b1ee4" integrity sha512-eSIXPc9wBM4BrniMzJRBm2uoVuXz2EPa+NXPk2+itrVt+r5SbKFERx/IgrK/HmfjddyKVz2f+j+7gBRvu19xLg== -eslint-plugin-vue@~9.32.0: - version "9.32.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz#2b558e827886b567dfaa156cc1cad0f596461fab" - integrity sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug== +eslint-plugin-vue@~9.33.0: + version "9.33.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz#de33eba8f78e1d172c59c8ec7fbfd60c6ca35c39" + integrity sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw== dependencies: "@eslint-community/eslint-utils" "^4.4.0" globals "^13.24.0" From cfd290f886c48d2fd17a497422a7457af441ed7d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:58:07 +0000 Subject: [PATCH 054/266] build(deps-dev): bump prettier from 3.5.2 to 3.5.3 in /webapp (#8260) Bumps [prettier](https://github.com/prettier/prettier) from 3.5.2 to 3.5.3. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.5.2...3.5.3) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index f351a71fd..1cace96c3 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -108,7 +108,7 @@ "jest": "29.7", "jest-environment-jsdom": "^29.7.0", "mutation-observer": "^1.0.3", - "prettier": "~3.5.2", + "prettier": "~3.5.3", "sass-loader": "~10.1.1", "storybook-design-token": "^0.8.1", "storybook-vue-router": "^1.0.7", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index c6cf074c9..6b11556b1 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -16125,10 +16125,10 @@ prettier@^1.18.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== -prettier@~3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.2.tgz#d066c6053200da0234bf8fa1ef45168abed8b914" - integrity sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg== +prettier@~3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== pretty-bytes@^5.3.0: version "5.3.0" From f6e6954b5103eb24e600d3047cff0e34865b7b46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 08:43:51 +0200 Subject: [PATCH 055/266] build(deps-dev): bump @babel/core in the babel group (#8277) Bumps the babel group with 1 update: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core). Updates `@babel/core` from 7.26.9 to 7.26.10 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/core" dependency-type: direct:development update-type: version-update:semver-patch dependency-group: babel ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 76 ++++++++++++++++++++-------------------- package.json | 2 +- yarn.lock | 88 +++++++++++++++++++++++------------------------ 3 files changed, 83 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6e9ed925a..5a083c2c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "3.2.1", "license": "MIT", "devDependencies": { - "@babel/core": "^7.26.9", + "@babel/core": "^7.26.10", "@babel/preset-env": "^7.26.9", "@babel/register": "^7.25.9", "@badeball/cypress-cucumber-preprocessor": "^22.0.1", @@ -127,22 +127,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", + "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", + "@babel/generator": "^7.26.10", "@babel/helper-compilation-targets": "^7.26.5", "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", + "@babel/helpers": "^7.26.10", + "@babel/parser": "^7.26.10", "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/traverse": "^7.26.10", + "@babel/types": "^7.26.10", "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.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", + "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -411,27 +411,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", - "integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", + "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", + "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.27.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -1714,32 +1714,32 @@ } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", + "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/parser": "^7.27.0", + "@babel/types": "^7.27.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", + "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/generator": "^7.27.0", + "@babel/parser": "^7.27.0", + "@babel/template": "^7.27.0", + "@babel/types": "^7.27.0", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1748,9 +1748,9 @@ } }, "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", + "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", "devOptional": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index a46e3fa85..b90501c4b 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "release": "./scripts/release.sh" }, "devDependencies": { - "@babel/core": "^7.26.9", + "@babel/core": "^7.26.10", "@babel/preset-env": "^7.26.9", "@babel/register": "^7.25.9", "@badeball/cypress-cucumber-preprocessor": "^22.0.1", diff --git a/yarn.lock b/yarn.lock index b3f340b5d..f01c83a18 100644 --- a/yarn.lock +++ b/yarn.lock @@ -52,34 +52,34 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== -"@babel/core@^7.16.0", "@babel/core@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" - integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== +"@babel/core@^7.16.0", "@babel/core@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.9" + "@babel/generator" "^7.26.10" "@babel/helper-compilation-targets" "^7.26.5" "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.9" - "@babel/parser" "^7.26.9" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" "@babel/template" "^7.26.9" - "@babel/traverse" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" - integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== +"@babel/generator@^7.26.10", "@babel/generator@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" + integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== dependencies: - "@babel/parser" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" @@ -343,20 +343,20 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helpers@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" - integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== +"@babel/helpers@^7.26.10": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" + integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== dependencies: - "@babel/template" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" -"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" - integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== +"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0": + 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.26.9" + "@babel/types" "^7.27.0" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" @@ -1015,32 +1015,32 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15", "@babel/template@^7.25.9", "@babel/template@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" - integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== +"@babel/template@^7.22.15", "@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" + integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== dependencies: "@babel/code-frame" "^7.26.2" - "@babel/parser" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" -"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.8", "@babel/traverse@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" - integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.8": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" + integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== dependencies: "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.9" - "@babel/parser" "^7.26.9" - "@babel/template" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/generator" "^7.27.0" + "@babel/parser" "^7.27.0" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.9", "@babel/types@^7.4.4": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" - integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.4.4": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" + integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" From 784211e19c4ee24fce3b2dafd0cfbace55e4b7e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 07:15:06 +0000 Subject: [PATCH 056/266] build(deps): bump the metascraper group across 1 directory with 12 updates (#8327) Bumps the metascraper group with 12 updates in the /backend directory: | Package | From | To | | --- | --- | --- | | [metascraper](https://github.com/microlinkhq/metascraper) | `5.46.7` | `5.46.11` | | [metascraper-author](https://github.com/microlinkhq/metascraper) | `5.46.5` | `5.46.11` | | [metascraper-date](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-date) | `5.46.5` | `5.46.11` | | [metascraper-description](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-description) | `5.46.5` | `5.46.11` | | [metascraper-image](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-image) | `5.46.5` | `5.46.11` | | [metascraper-lang](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-lang) | `5.46.5` | `5.46.11` | | [metascraper-logo](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-logo) | `5.46.5` | `5.46.11` | | [metascraper-publisher](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-publisher) | `5.46.5` | `5.46.11` | | [metascraper-title](https://github.com/microlinkhq/metascraper) | `5.46.5` | `5.46.11` | | [metascraper-url](https://github.com/microlinkhq/metascraper) | `5.46.5` | `5.46.11` | | [metascraper-video](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-video) | `5.46.5` | `5.46.11` | | [metascraper-youtube](https://github.com/microlinkhq/metascraper/tree/HEAD/packages/metascraper-youtube) | `5.46.5` | `5.46.11` | Updates `metascraper` from 5.46.7 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.46.7...v5.46.11) Updates `metascraper-author` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.46.5...v5.46.11) Updates `metascraper-date` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-date/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-date) Updates `metascraper-description` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-description/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-description) Updates `metascraper-image` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-image/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-image) Updates `metascraper-lang` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-lang/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-lang) Updates `metascraper-logo` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-logo/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-logo) Updates `metascraper-publisher` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-publisher/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-publisher) Updates `metascraper-title` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.46.5...v5.46.11) Updates `metascraper-url` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/compare/v5.46.5...v5.46.11) Updates `metascraper-video` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-video/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-video) Updates `metascraper-youtube` from 5.46.5 to 5.46.11 - [Release notes](https://github.com/microlinkhq/metascraper/releases) - [Changelog](https://github.com/microlinkhq/metascraper/blob/master/packages/metascraper-youtube/CHANGELOG.md) - [Commits](https://github.com/microlinkhq/metascraper/commits/v5.46.11/packages/metascraper-youtube) --- updated-dependencies: - dependency-name: metascraper dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-author dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-date dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-description dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-image dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-lang dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-logo dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-publisher dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-title dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-url dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-video dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper - dependency-name: metascraper-youtube dependency-version: 5.46.11 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: metascraper ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 24 +-- backend/yarn.lock | 338 ++++++++++++++++++++++++++++--------------- 2 files changed, 235 insertions(+), 127 deletions(-) diff --git a/backend/package.json b/backend/package.json index e7f745b29..93dd7e0be 100644 --- a/backend/package.json +++ b/backend/package.json @@ -61,20 +61,20 @@ "linkifyjs": "^4.2.0", "lodash": "~4.17.21", "merge-graphql-schemas": "^1.7.8", - "metascraper": "^5.46.7", - "metascraper-author": "^5.46.5", - "metascraper-date": "^5.46.5", - "metascraper-description": "^5.46.5", - "metascraper-image": "^5.46.5", - "metascraper-lang": "^5.46.5", + "metascraper": "^5.46.11", + "metascraper-author": "^5.46.11", + "metascraper-date": "^5.46.11", + "metascraper-description": "^5.46.11", + "metascraper-image": "^5.46.11", + "metascraper-lang": "^5.46.11", "metascraper-lang-detector": "^4.10.2", - "metascraper-logo": "^5.46.5", - "metascraper-publisher": "^5.46.5", + "metascraper-logo": "^5.46.11", + "metascraper-publisher": "^5.46.11", "metascraper-soundcloud": "^5.34.4", - "metascraper-title": "^5.46.5", - "metascraper-url": "^5.46.5", - "metascraper-video": "^5.46.5", - "metascraper-youtube": "^5.46.5", + "metascraper-title": "^5.46.11", + "metascraper-url": "^5.46.11", + "metascraper-video": "^5.46.11", + "metascraper-youtube": "^5.46.11", "migrate": "^2.1.0", "mime-types": "^2.1.35", "minimatch": "^9.0.4", diff --git a/backend/yarn.lock b/backend/yarn.lock index 6cf8589b3..87c016367 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -53,13 +53,13 @@ dependencies: xss "^1.0.6" -"@asamuzakjp/css-color@^2.8.2": - version "2.8.3" - resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-2.8.3.tgz#665f0f5e8edb95d8f543847529e30fe5cc437ef7" - integrity sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw== +"@asamuzakjp/css-color@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@asamuzakjp/css-color/-/css-color-3.1.1.tgz#41a612834dafd9353b89855b37baa8a03fb67bf2" + integrity sha512-hpRD68SV2OMcZCsrbdkccTw5FXjNDLo5OuqSHyHZfwweGsDWZwDJ2+gONyNAbazZclobMirACLw0lk8WVxIqxA== dependencies: - "@csstools/css-calc" "^2.1.1" - "@csstools/css-color-parser" "^3.0.7" + "@csstools/css-calc" "^2.1.2" + "@csstools/css-color-parser" "^3.0.8" "@csstools/css-parser-algorithms" "^3.0.4" "@csstools/css-tokenizer" "^3.0.3" lru-cache "^10.4.3" @@ -1046,12 +1046,12 @@ resolved "https://registry.yarnpkg.com/@csstools/color-helpers/-/color-helpers-5.0.2.tgz#82592c9a7c2b83c293d9161894e2a6471feb97b8" integrity sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA== -"@csstools/css-calc@^2.1.1", "@csstools/css-calc@^2.1.2": +"@csstools/css-calc@^2.1.2": version "2.1.2" resolved "https://registry.yarnpkg.com/@csstools/css-calc/-/css-calc-2.1.2.tgz#bffd55f002dab119b76d4023f95cd943e6c8c11e" integrity sha512-TklMyb3uBB28b5uQdxjReG4L80NxAqgrECqLZFQbyLekwwlcDDS8r3f07DKqeo8C4926Br0gf/ZDe17Zv4wIuw== -"@csstools/css-color-parser@^3.0.7": +"@csstools/css-color-parser@^3.0.8": version "3.0.8" resolved "https://registry.yarnpkg.com/@csstools/css-color-parser/-/css-color-parser-3.0.8.tgz#5fe9322920851450bf5e065c2b0e731b9e165394" integrity sha512-pdwotQjCCnRPuNi06jFuP68cykU1f3ZWExLe/8MQ1LOs8Xq+fTkYgd+2V8mWUWMrOn9iS2HftPVaMZDaXzGbhQ== @@ -1484,13 +1484,13 @@ resolved "https://registry.yarnpkg.com/@kikobeats/time-span/-/time-span-1.0.5.tgz#9f7c5d48b08da02115dbf3d85ca11a6a6f8bfdeb" integrity sha512-txRAdmi35N1wnsLS1AO5mTlbY5Cv5/61WXqek2y3L9Q7u4mgdUVq819so5xe753hL5gYeLzlWoJ/VJfXg9nx8g== -"@metascraper/helpers@5.46.5", "@metascraper/helpers@^5.34.4": - version "5.46.5" - resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.46.5.tgz#db9db60835495d2865af1c99f758d32b8aac8f10" - integrity sha512-SuFJ7qu0MK1yaV9VnVDVY0eMsJ6hU3/4XYPhjrdZL9GejJUzzDhTTzNoi1V5NYF68YF+VlGEwQL101wDc/zo+A== +"@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" + integrity sha512-NmuJBy84CuXDeRT0PlKyM2To++jEHlqDTX8YajoL+S0FQpwvrB8kB3gsSaLzTgCzrZh0SABiicK1GVxtXFdrVA== dependencies: audio-extensions "0.0.0" - chrono-node "~2.7.7" + chrono-node "~2.7.9" condense-whitespace "~2.0.0" data-uri-utils "~1.0.8" debug-logfmt "~1.2.3" @@ -1499,10 +1499,10 @@ has-values "~2.0.1" image-extensions "~1.1.0" is-relative-url "~3.0.0" - is-uri "~1.2.6" + is-uri "~1.2.7" iso-639-3 "~2.2.0" isostring "0.0.1" - jsdom "~25.0.1" + jsdom "~26.0.0" lodash "~4.17.21" memoize-one "~6.0.0" microsoft-capitalize "~1.0.5" @@ -1510,7 +1510,7 @@ normalize-url "~6.1.0" re2 "~1.21.4" smartquotes "~2.3.2" - tldts "~6.1.70" + tldts "~6.1.85" url-regex-safe "~4.0.0" video-extensions "1" @@ -3197,6 +3197,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" @@ -3341,10 +3349,10 @@ chrono-node@~1.3.11: dependencies: moment "2.21.0" -chrono-node@~2.7.7: - version "2.7.8" - resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.7.8.tgz#629a96b955ea20b1fba9811328c69cbc932c8e39" - integrity sha512-pzxemrTKu6jFVyAfkNxUckp9nlrmRFtr5lGrEJcVKyeKV9WSeGT78Oysazlzd/H0BdMv7EzACtJrw0pi2KODBQ== +chrono-node@~2.7.9: + version "2.7.9" + resolved "https://registry.yarnpkg.com/chrono-node/-/chrono-node-2.7.9.tgz#198c1360558b7a8913f3d3788ed5ee90b69c65b1" + integrity sha512-PW3tzuztH7OFbwdCCwv1k8F6ALFs5Yet1Neh5JJBL1GGj8zsLj3ZgZU6StUyM6gSsVRMv8EE6LqpTjM52Mshrw== dependencies: dayjs "^1.10.0" @@ -3617,12 +3625,12 @@ cssfilter@0.0.10: resolved "https://registry.yarnpkg.com/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae" integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4= -cssstyle@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.2.1.tgz#5142782410fea95db66fb68147714a652a7c2381" - integrity sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw== +cssstyle@^4.2.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-4.3.0.tgz#83db22d1aec8eb7e5ecd812b4d14a17fb3dd243d" + integrity sha512-6r0NiY0xizYqfBvWp1G7WXJ06/bZyrk7Dc6PHql82C/pKGUTKu4yAX4Y8JPamb1ob9nBKuxWzCGTRuGwU3yxJQ== dependencies: - "@asamuzakjp/css-color" "^2.8.2" + "@asamuzakjp/css-color" "^3.1.1" rrweb-cssom "^0.8.0" d@1, d@^1.0.1, d@^1.0.2: @@ -3968,6 +3976,15 @@ dotenv@^4.0.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-4.0.0.tgz#864ef1379aced55ce6f95debecdce179f7a0cd1d" integrity sha512-XcaMACOr3JMVcEv0Y/iUM2XaOsATRZ3U1In41/1jjK6vJZ2PZbQ1bzCG8uvaByfaBpl9gqc9QWJovpUGBXLLYQ== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^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" @@ -4235,6 +4252,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" @@ -4247,6 +4269,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -4265,6 +4294,16 @@ es-set-tostringtag@^2.0.3: has-tostringtag "^1.0.2" hasown "^2.0.1" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -4918,13 +4957,14 @@ form-data@^3.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== +form-data@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" + integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" mime-types "^2.1.12" form-data@~2.3.2: @@ -5058,11 +5098,35 @@ get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -5204,6 +5268,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + got@~11.8.0: version "11.8.6" resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" @@ -5399,6 +5468,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -5573,7 +5647,7 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" -https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.5: +https-proxy-agent@^7.0.1, https-proxy-agent@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== @@ -6065,10 +6139,10 @@ is-uri@~1.2.0: parse-uri "~1.0.0" punycode2 "~1.0.0" -is-uri@~1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/is-uri/-/is-uri-1.2.6.tgz#f8f5f55d9b997493f470bf10ca214a3838b1e818" - integrity sha512-kNciklu//Ki8BUmRseLTfG/WW55qDHavf3MKUic8wvXR3d7etbSMoQPTpjvDeLVekESSgJM4AG+BESIKU02u3A== +is-uri@~1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-uri/-/is-uri-1.2.7.tgz#cfb25d3123ff859a044ef54797551a911a450821" + integrity sha512-drze7nnbh2ZTHU0InyskqNcyCRH7G6SpC/2F3yFXTa38IL+aeUmYvz/9TRfR1Z4tuMzFGos1tolX8QJmgKEiqA== dependencies: parse-uri "~1.0.3" punycode2 "~1.0.0" @@ -6600,22 +6674,22 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== -jsdom@~25.0.1: - version "25.0.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-25.0.1.tgz#536ec685c288fc8a5773a65f82d8b44badcc73ef" - integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw== +jsdom@~26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-26.0.0.tgz#446dd1ad8cfc50df7e714e58f1f972c1763b354c" + integrity sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw== dependencies: - cssstyle "^4.1.0" + cssstyle "^4.2.1" data-urls "^5.0.0" decimal.js "^10.4.3" - form-data "^4.0.0" + form-data "^4.0.1" html-encoding-sniffer "^4.0.0" http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.5" + https-proxy-agent "^7.0.6" is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.12" - parse5 "^7.1.2" - rrweb-cssom "^0.7.1" + nwsapi "^2.2.16" + parse5 "^7.2.1" + rrweb-cssom "^0.8.0" saxes "^6.0.0" symbol-tree "^3.2.4" tough-cookie "^5.0.0" @@ -6623,7 +6697,7 @@ jsdom@~25.0.1: webidl-conversions "^7.0.0" whatwg-encoding "^3.1.1" whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" + whatwg-url "^14.1.0" ws "^8.18.0" xml-name-validator "^5.0.0" @@ -6978,6 +7052,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -7031,33 +7110,33 @@ merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -metascraper-author@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.46.5.tgz#64fa4e593ece8aa3d663b9fb0a101e2cd18f55d2" - integrity sha512-TKlqY7BVVzqphew842MwdtV5cNx3TG6l6rtswSzYOM2EhyuZuQPQUgrk6s20hRbZrCJM9XM+oc1PvzdmV7hXcg== +metascraper-author@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-5.46.11.tgz#ddef50ae941ba3b46f0f24cc8f78ab819934344a" + integrity sha512-PyL0jzDfdBR5b4RjIRp64L4kolhyLuVG7XVHUMZ3+3epbxu8j1enUah6iErb9dQ0nlqAfdtAlNXZ63IU5COs+g== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" -metascraper-date@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.46.5.tgz#df63486c1db3d6bf360dff32b29446bb9e1b128b" - integrity sha512-PrgJioyTXm1ZqPpo0ybpKZ4u3QUDr3PQEWuKmQ5Xfe886haKF9N4b23KVDrME87nI975bwi3aH+3lo1msxL7ig== +metascraper-date@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-date/-/metascraper-date-5.46.11.tgz#bcecc35b23c329787f8486b72cf26a43c2aa923b" + integrity sha512-N3ToXyaZ3SMg3jtgExr6jLX7JCaVPZ2GD6o4ojE3v2zCa3IZBTsIhIw2mw8eiM6A4EqYMMdzGAXpkZXo4tbEuw== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" -metascraper-description@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.46.5.tgz#10c74065e8c6227a2e19bcbfb498fd117dd0b7a9" - integrity sha512-RJKfwVPXRy8+w7mSjJM+6oe1ws3AtPA5CspcJty1fdKD1S4jF0nFzgwtrTgOCtggMK8iHG01/qAunPobFEbs+w== +metascraper-description@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-description/-/metascraper-description-5.46.11.tgz#ab5df1ec24b9f662618f496ea19d5ade6669e35e" + integrity sha512-kJ9Er7XptGiZ502l3uCaMXVkaAKB//7FDylOHs+Dx4Irzp8SrWzKQgWX2XLg99oXdy7F/reNLdpPuV877Cw43Q== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" -metascraper-image@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.46.5.tgz#c011ddccbdbf380851a7a36cd9ebd12f9f7e9558" - integrity sha512-wgfilKtxeiwBjNYsMXr4AKVIu3jqScDZnWILl2MkILnn6BttLetgNRXsAvE5bT9LD8Upnf5ufJebLK+hoHAiwg== +metascraper-image@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-image/-/metascraper-image-5.46.11.tgz#ca9f8ebaf60c68cbccc022bbc474da4eb86dbc6c" + integrity sha512-R6u3jaYr7w4DEkpv3dhvdqoCEslbTrFDcV6CsAmqy62l33hGW1pfqxrCVMKoSj7NTVnmA5PqmSdzSqbvCinU4A== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" metascraper-lang-detector@^4.10.2: version "4.10.2" @@ -7068,27 +7147,27 @@ metascraper-lang-detector@^4.10.2: franc "~4.0.0" iso-639-3 "~1.1.0" -metascraper-lang@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-lang/-/metascraper-lang-5.46.5.tgz#a47c9433341b4a31e5bde9e4f094c34ce5de3f42" - integrity sha512-CMa4D23kBVa+1DbCnpiiCgGvPQqx6b0TaSd9qLIeGaqhWQw7dUxGAB6x5KzjhoABGgKzTIpy+Xmc9cPforT+SQ== +metascraper-lang@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-lang/-/metascraper-lang-5.46.11.tgz#4956f2e18d3b52e1945ff2f329c9d76de1d13123" + integrity sha512-mS/6rmfIDiLhV8x/Io7WLtVLLUwJFo9jP8SU00GePbqWgOjxpeDlqcaC6WUBIV/rVdZzg0Sv86vcn09+qyvHhg== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" -metascraper-logo@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.46.5.tgz#5e57677ee6e9c21bcb7288257d751a3fb302141d" - integrity sha512-Wp7u7CEXjo7PA7kRKPG9EtD57xXWkv0E9klVgkQeSzBaVmXCTcEMYhHMfyBumpeZn02+Gmj/cuEuHjuMtbJ/uQ== +metascraper-logo@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-logo/-/metascraper-logo-5.46.11.tgz#6b601ab93e40b147ff873011f8066f03f2af10a7" + integrity sha512-NMilSVAtQccVm3iW1DLMiOPdQh0OSWGVjDzXmL7fXZLUCkvXYYeDX14eQWV1gvKoY3Iw00K/CSmgchAfK+8hVg== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" lodash "~4.17.21" -metascraper-publisher@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.46.5.tgz#f9ac000a358df7ff1f826ecaac58c0aada152687" - integrity sha512-vmc6tMshN/0n9DrMlOBt1jxktNIAqLJTP28Z2HStOadlNg4m6L+hdmp0mFAjfTxCuzR3bJYmWRfba4b51ugIiA== +metascraper-publisher@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-publisher/-/metascraper-publisher-5.46.11.tgz#6754dfa271ce89b44b055f134aea40953eba5869" + integrity sha512-1kdNc0aJDimP3Wc3kuRlKseASqm3nvrteBuCsraZgltnYTT8O82kehaxkY+OPijNV7mfrhnZFnE9X2Qh5VpfDg== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" metascraper-soundcloud@^5.34.4: version "5.34.4" @@ -7097,45 +7176,45 @@ metascraper-soundcloud@^5.34.4: dependencies: "@metascraper/helpers" "^5.34.4" -metascraper-title@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.46.5.tgz#b1a5027b3d051cba5a3cefcbb97a58bdfb0c00f5" - integrity sha512-AciK4SgOCdkRgXQ6isYcV/JzxomUd7hJ7BrikXqIfZME+UK6OHUtjQV3gtdB0SHBbIBE7Q+h1X5r8j5nWwBALg== +metascraper-title@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-title/-/metascraper-title-5.46.11.tgz#bedd7f891ed56697f1ddf48f5bd286997e430e95" + integrity sha512-xb/A5EvvSTh4Ux+VoLpF7Q4/7Yy6PpWq6RCH8QhuzxUXAFQIdCbAK0UUkgWtpmUarbxGK0UK6koX0fFKGu4CXQ== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" -metascraper-url@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.46.5.tgz#8673db754a613951b9a870c3533ee3a4fee04d22" - integrity sha512-DsF80xrH3qoyhxsZISbGQ6krs+BQO+3c19FA3oGfs88cjhpGSr0gMr662xL5NBveFatHSCCaaKYl2/OTb3N1VQ== +metascraper-url@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-url/-/metascraper-url-5.46.11.tgz#23fd921b7ea063e4cb52d14e36b3b8a3fd5fc62c" + integrity sha512-EHs+lKR1LYASN0dNq5dSfUXWGqI2kNRfKIe0XySdFFZfcODGkEjgm0+T8jUDuf+qQVGsrJrwG83Hs9w6p0G7+A== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" -metascraper-video@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-video/-/metascraper-video-5.46.5.tgz#44a246622e92f499d9da9e567e4e65da7c002520" - integrity sha512-+tQNDglDBJU4jpIc9N8BwZ0w5OZBOO0jobigp8OHLJWPQTC9zfcZTsBRr7zTbrAfXF3Hu51ZO4VkapxTwTUQsw== +metascraper-video@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-video/-/metascraper-video-5.46.11.tgz#268b71a70cf917d176262f55df374e7001342964" + integrity sha512-yb8AOYsOTFidBiX4dyFYxBNhnbKnbw1NYJx/nrmYaHemuM3JSTGt4u1blYwK7ZGhobhb/I2CIhogI0b3meG/dA== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" lodash "~4.17.21" p-reflect "~2.1.0" -metascraper-youtube@^5.46.5: - version "5.46.5" - resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.46.5.tgz#20998d86ccc56b93dfd57b619bff3c936980c47d" - integrity sha512-cc3/BaqbcEpvBjOP0MWFxBlDHsdqCeksD0QQX/gwW/2LT8KfUL+qZl6rHqmYPk1RH4WDVtjAzFwq8Z6bQ+/yxQ== +metascraper-youtube@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper-youtube/-/metascraper-youtube-5.46.11.tgz#5dd2d226cb446df4c2a357024024a41c90cbeade" + integrity sha512-Lr2AJ86apYNGw5MQ4nhP+NPV5BiePv/T4xHWLEtPkpXDooJPSXSVf/BAdLmQ7s6lGtSqwSUxlIY3Y3P3Rwt9FA== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" get-video-id "3" p-locate "~5.0.0" reachable-url "~1.8.1" -metascraper@^5.46.7: - version "5.46.7" - resolved "https://registry.yarnpkg.com/metascraper/-/metascraper-5.46.7.tgz#cdc7d7fa3206cab5b69d6d69db6d473f61eec09b" - integrity sha512-fBWVQQH3blesqRRxO8lfOh5lxnp4gMBld7uz+hFElY5Kk9sJZIhzFW2TJW+rqqvnjbbmDiaYvGfBEzfZ5sC1Sw== +metascraper@^5.46.11: + version "5.46.11" + resolved "https://registry.yarnpkg.com/metascraper/-/metascraper-5.46.11.tgz#ee63bdbc94440c33d714b006e50a1712e2f2c25d" + integrity sha512-EdwpSK+P5l+oS5X7yRjnFhVq6s0m7f9mWfRykZKCHEeoLFmNVpUrA94S4q5PG6jAla5YMh2PAGGPS5EYyZ4YrQ== dependencies: - "@metascraper/helpers" "5.46.5" + "@metascraper/helpers" "5.46.11" cheerio "~1.0.0" debug-logfmt "~1.2.3" lodash "~4.17.21" @@ -7594,10 +7673,10 @@ nth-check@^2.0.1: dependencies: boolbase "^1.0.0" -nwsapi@^2.2.12: - version "2.2.16" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.16.tgz#177760bba02c351df1d2644e220c31dfec8cdb43" - integrity sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ== +nwsapi@^2.2.16: + version "2.2.20" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.20.tgz#22e53253c61e7b0e7e93cef42c891154bcca11ef" + integrity sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA== oauth-sign@~0.9.0: version "0.9.0" @@ -7891,6 +7970,13 @@ parse5@^7.0.0, parse5@^7.1.2: dependencies: entities "^4.4.0" +parse5@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.2.1.tgz#8928f55915e6125f430cc44309765bf17556a33a" + integrity sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ== + dependencies: + entities "^4.5.0" + parseurl@^1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -8437,11 +8523,6 @@ rosie@^2.1.1: resolved "https://registry.yarnpkg.com/rosie/-/rosie-2.1.1.tgz#f8c9b8145d581d19fb1c933cf6ac1c554ad68798" integrity sha512-2AXB7WrIZXtKMZ6Q/PlozqPF5nu/x7NEvRJZOblrJuprrPfm5gL8JVvJPj9aaib9F8IUALnLUFhzXrwEtnI5cQ== -rrweb-cssom@^0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz#c73451a484b86dd7cfb1e0b2898df4b703183e4b" - integrity sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg== - rrweb-cssom@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" @@ -9163,13 +9244,25 @@ tldts-core@^6.1.78: resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.78.tgz#47b477d9742870daa01dbd5ff9a598a48379728c" integrity sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw== -tldts@^6.1.32, tldts@~6.1.70: +tldts-core@^6.1.85: + version "6.1.85" + resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.85.tgz#6f6b795468c0b5f7660a11c7306ff2766ceaea7e" + integrity sha512-DTjUVvxckL1fIoPSb3KE7ISNtkWSawZdpfxGxwiIrZoO6EbHVDXXUIlIuWympPaeS+BLGyggozX/HTMsRAdsoA== + +tldts@^6.1.32: version "6.1.78" resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.78.tgz#ee94576653a60d421ff94162c4e9060f2e62467b" integrity sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ== dependencies: tldts-core "^6.1.78" +tldts@~6.1.85: + version "6.1.85" + resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.85.tgz#c7636ad5ec2f02591264360da51c6b584ef6f7bb" + integrity sha512-gBdZ1RjCSevRPFix/hpaUWeak2/RNUZB4/8frF1r5uYMHjFptkiT0JXIebWvgI/0ZHXvxaUDDJshiA0j6GdL3w== + dependencies: + tldts-core "^6.1.85" + tmpl@1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" @@ -9226,6 +9319,13 @@ tr46@^5.0.0: dependencies: punycode "^2.3.1" +tr46@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.1.0.tgz#4a077922360ae807e172075ce5beb79b36e4a101" + integrity sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw== + dependencies: + punycode "^2.3.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -9750,6 +9850,14 @@ whatwg-url@^14.0.0: tr46 "^5.0.0" webidl-conversions "^7.0.0" +whatwg-url@^14.1.0: + version "14.2.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-14.2.0.tgz#4ee02d5d725155dae004f6ae95c73e7ef5d95663" + integrity sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw== + dependencies: + tr46 "^5.1.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" From f101ba48cbec47b7e9abdcbb90dbd328816bc020 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 16:12:34 +0200 Subject: [PATCH 057/266] build(deps): bump sanitize-html from 2.14.0 to 2.15.0 in /backend (#8285) Bumps [sanitize-html](https://github.com/apostrophecms/sanitize-html) from 2.14.0 to 2.15.0. - [Changelog](https://github.com/apostrophecms/sanitize-html/blob/main/CHANGELOG.md) - [Commits](https://github.com/apostrophecms/sanitize-html/compare/2.14.0...2.15.0) --- updated-dependencies: - dependency-name: sanitize-html dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 93dd7e0be..b6de74fc4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -86,7 +86,7 @@ "nodemailer": "^6.10.0", "nodemailer-html-to-text": "^3.2.0", "request": "~2.88.2", - "sanitize-html": "~2.14.0", + "sanitize-html": "~2.15.0", "slug": "~9.1.0", "subscriptions-transport-ws": "^0.9.19", "trunc-html": "~1.1.2", diff --git a/backend/yarn.lock b/backend/yarn.lock index 87c016367..ebdc17aa8 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -8595,10 +8595,10 @@ safe-regex@^2.1.1: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sanitize-html@~2.14.0: - version "2.14.0" - resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.14.0.tgz#bd2a7b97ee1d86a7f0e0babf3a4468f639c3a429" - integrity sha512-CafX+IUPxZshXqqRaG9ZClSlfPVjSxI0td7n07hk8QO2oO+9JDnlcL8iM8TWeOXOIBFgIOx6zioTzM53AOMn3g== +sanitize-html@~2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.15.0.tgz#8e7f97ee1fecdec1bb1fb2e37f6d2c6acfdbabe3" + integrity sha512-wIjst57vJGpLyBP8ioUbg6ThwJie5SuSIjHxJg53v5Fg+kUK+AXlb7bK3RNXpp315MvwM+0OBGCV6h5pPHsVhA== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" From 31b285a3fa2edfc70c1ead13cc66dbf59b0f6f08 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 15:14:43 +0000 Subject: [PATCH 058/266] build(deps): bump validator from 13.12.0 to 13.15.0 in /backend (#8297) Bumps [validator](https://github.com/validatorjs/validator.js) from 13.12.0 to 13.15.0. - [Release notes](https://github.com/validatorjs/validator.js/releases) - [Changelog](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/validatorjs/validator.js/compare/13.12.0...13.15.0) --- updated-dependencies: - dependency-name: validator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index b6de74fc4..82ddab57a 100644 --- a/backend/package.json +++ b/backend/package.json @@ -91,7 +91,7 @@ "subscriptions-transport-ws": "^0.9.19", "trunc-html": "~1.1.2", "uuid": "~9.0.1", - "validator": "^13.12.0", + "validator": "^13.15.0", "xregexp": "^5.1.2" }, "devDependencies": { diff --git a/backend/yarn.lock b/backend/yarn.lock index ebdc17aa8..4998223bb 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -9777,10 +9777,10 @@ v8flags@^3.1.1: dependencies: homedir-polyfill "^1.0.1" -validator@^13.12.0: - version "13.12.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" - integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== +validator@^13.15.0: + version "13.15.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" + integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA== vary@^1, vary@~1.1.2: version "1.1.2" From 40de045c03bd52bc87ffbea5f731e1795c283f2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 16:03:45 +0000 Subject: [PATCH 059/266] build(deps): bump helmet from 8.0.0 to 8.1.0 in /backend (#8284) Bumps [helmet](https://github.com/helmetjs/helmet) from 8.0.0 to 8.1.0. - [Changelog](https://github.com/helmetjs/helmet/blob/main/CHANGELOG.md) - [Commits](https://github.com/helmetjs/helmet/compare/v8.0.0...v8.1.0) --- updated-dependencies: - dependency-name: helmet dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 82ddab57a..7992fc1ad 100644 --- a/backend/package.json +++ b/backend/package.json @@ -53,7 +53,7 @@ "graphql-redis-subscriptions": "^2.7.0", "graphql-shield": "~7.2.2", "graphql-tag": "~2.10.3", - "helmet": "~8.0.0", + "helmet": "~8.1.0", "ioredis": "^4.16.1", "jsonwebtoken": "~8.5.1", "languagedetect": "^2.0.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 4998223bb..afa0b4639 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -5518,10 +5518,10 @@ he@^1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -helmet@~8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/helmet/-/helmet-8.0.0.tgz#05370fb1953aa7b81bd0ddfa459221247be6ea5c" - integrity sha512-VyusHLEIIO5mjQPUI1wpOAEu+wl6Q0998jzTxqUYGE45xCIcAxy3MsbEK/yyJUJ3ADeMoB6MornPH6GMWAf+Pw== +helmet@~8.1.0: + version "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" From 8f4bae87a960586d52856533dfeee4e852b8d9c7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 16:32:20 +0000 Subject: [PATCH 060/266] build(deps-dev): Bump ts-jest in /backend in the typescript group (#8237) Bumps the typescript group in /backend with 1 update: [ts-jest](https://github.com/kulshekhar/ts-jest). Updates `ts-jest` from 29.2.5 to 29.2.6 - [Release notes](https://github.com/kulshekhar/ts-jest/releases) - [Changelog](https://github.com/kulshekhar/ts-jest/blob/main/CHANGELOG.md) - [Commits](https://github.com/kulshekhar/ts-jest/compare/v29.2.5...v29.2.6) --- updated-dependencies: - dependency-name: ts-jest dependency-type: direct:development update-type: version-update:semver-patch dependency-group: typescript ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/backend/package.json b/backend/package.json index 7992fc1ad..0647b214f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -115,7 +115,7 @@ "nodemon": "~3.1.9", "prettier": "^3.5.3", "rosie": "^2.1.1", - "ts-jest": "^29.2.5", + "ts-jest": "^29.3.1", "ts-node": "^10.9.2", "typescript": "^5.7.3" }, diff --git a/backend/yarn.lock b/backend/yarn.lock index afa0b4639..2773c005a 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -8634,10 +8634,10 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3: - version "7.6.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" - integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== send@0.19.0: version "0.19.0" @@ -9376,10 +9376,10 @@ ts-invariant@^0.4.0: dependencies: tslib "^1.9.3" -ts-jest@^29.2.5: - version "29.2.5" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.5.tgz#591a3c108e1f5ebd013d3152142cb5472b399d63" - integrity sha512-KD8zB2aAZrcKIdGk4OwpJggeLcH1FgrICqDSROWqlnJXGCXK4Mn6FcdK2B6670Xr73lHMG1kHw8R87A0ecZ+vA== +ts-jest@^29.3.1: + version "29.3.1" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.3.1.tgz#2e459e1f94a833bd8216ba4b045fac948e265937" + integrity sha512-FT2PIRtZABwl6+ZCry8IY7JZ3xMuppsEV9qFVHOVe8jDzggwUZ9TsM4chyJxL9yi6LvkqcZYU3LmapEE454zBQ== dependencies: bs-logger "^0.2.6" ejs "^3.1.10" @@ -9388,7 +9388,8 @@ ts-jest@^29.2.5: json5 "^2.2.3" lodash.memoize "^4.1.2" make-error "^1.3.6" - semver "^7.6.3" + semver "^7.7.1" + type-fest "^4.38.0" yargs-parser "^21.1.1" ts-node@^10.9.2: @@ -9476,6 +9477,11 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== +type-fest@^4.38.0: + version "4.39.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.39.1.tgz#7521f6944e279abaf79cf60cfbc4823f4858083e" + integrity sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w== + type-is@^1.6.16, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" From 620617a7aace33cd8c69633a8c98fa318c679d45 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:00:48 +0000 Subject: [PATCH 061/266] build(deps-dev): bump @faker-js/faker from 9.5.0 to 9.6.0 in /backend (#8268) Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.5.0 to 9.6.0. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v9.5.0...v9.6.0) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 0647b214f..2931633a6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -95,7 +95,7 @@ "xregexp": "^5.1.2" }, "devDependencies": { - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "@types/jest": "^29.5.14", "@types/node": "^22.13.5", "@typescript-eslint/eslint-plugin": "^5.62.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 2773c005a..30bf74644 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1101,10 +1101,10 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.1.tgz#de633db3ec2ef6a3c89e2f19038063e8a122e2c2" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== -"@faker-js/faker@9.5.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" - integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== +"@faker-js/faker@9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d" + integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw== "@graphql-toolkit/common@0.10.4": version "0.10.4" From e70e0a5bac229199cc6b8e02fc934a245fb32522 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:30:11 +0000 Subject: [PATCH 062/266] build(deps): bump the babel group across 1 directory with 2 updates (#8296) Bumps the babel group with 2 updates in the /backend directory: [@babel/cli](https://github.com/babel/babel/tree/HEAD/packages/babel-cli) and [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core). Updates `@babel/cli` from 7.26.4 to 7.27.0 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-cli) Updates `@babel/core` from 7.26.9 to 7.26.10 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.10/packages/babel-core) --- updated-dependencies: - dependency-name: "@babel/cli" dependency-type: direct:production update-type: version-update:semver-minor dependency-group: babel - dependency-name: "@babel/core" dependency-type: direct:production update-type: version-update:semver-patch dependency-group: babel ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 4 +- backend/yarn.lock | 96 ++++++++++++++++++++++---------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/backend/package.json b/backend/package.json index 2931633a6..13c82a9b3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -23,8 +23,8 @@ "db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create" }, "dependencies": { - "@babel/cli": "~7.26.4", - "@babel/core": "^7.26.9", + "@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", diff --git a/backend/yarn.lock b/backend/yarn.lock index 30bf74644..52ca57229 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -64,10 +64,10 @@ "@csstools/css-tokenizer" "^3.0.3" lru-cache "^10.4.3" -"@babel/cli@~7.26.4": - version "7.26.4" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.26.4.tgz#4101ff8ee5de8447a6c395397a97921056411d20" - integrity sha512-+mORf3ezU3p3qr+82WvJSnQNE1GAYeoCfEv4fik6B5/2cvKZ75AX8oawWQdXtM9MmndooQj15Jr9kelRFWsuRw== +"@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" @@ -94,34 +94,34 @@ 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.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" - integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.26.10": + version "7.26.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" + integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== dependencies: "@ampproject/remapping" "^2.2.0" "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.9" + "@babel/generator" "^7.26.10" "@babel/helper-compilation-targets" "^7.26.5" "@babel/helper-module-transforms" "^7.26.0" - "@babel/helpers" "^7.26.9" - "@babel/parser" "^7.26.9" + "@babel/helpers" "^7.26.10" + "@babel/parser" "^7.26.10" "@babel/template" "^7.26.9" - "@babel/traverse" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/traverse" "^7.26.10" + "@babel/types" "^7.26.10" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.26.9", "@babel/generator@^7.7.2": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" - integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== +"@babel/generator@^7.26.10", "@babel/generator@^7.27.0", "@babel/generator@^7.7.2": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" + integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== dependencies: - "@babel/parser" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" @@ -291,13 +291,13 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helpers@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" - integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== +"@babel/helpers@^7.26.10": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" + integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== dependencies: - "@babel/template" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" "@babel/node@~7.26.0": version "7.26.0" @@ -311,12 +311,12 @@ 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.9", "@babel/parser@^7.7.0": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" - integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== +"@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": + 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.26.9" + "@babel/types" "^7.27.0" "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": version "7.25.9" @@ -999,32 +999,32 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.3.3": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" - integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== +"@babel/template@^7.25.9", "@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== dependencies: "@babel/code-frame" "^7.26.2" - "@babel/parser" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/parser" "^7.27.0" + "@babel/types" "^7.27.0" -"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.8", "@babel/traverse@^7.26.9", "@babel/traverse@^7.7.0": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" - integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.8", "@babel/traverse@^7.7.0": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" + integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== dependencies: "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.9" - "@babel/parser" "^7.26.9" - "@babel/template" "^7.26.9" - "@babel/types" "^7.26.9" + "@babel/generator" "^7.27.0" + "@babel/parser" "^7.27.0" + "@babel/template" "^7.27.0" + "@babel/types" "^7.27.0" 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.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" - integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== +"@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": + version "7.27.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" + integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" From f9a2f0462756ab596576b4dc32c285763cf71d0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:58:28 +0000 Subject: [PATCH 063/266] build(deps-dev): bump typescript from 5.7.3 to 5.8.3 in /backend (#8324) Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.7.3 to 5.8.3. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Changelog](https://github.com/microsoft/TypeScript/blob/main/azure-pipelines.release-publish.yml) - [Commits](https://github.com/microsoft/TypeScript/commits) --- updated-dependencies: - dependency-name: typescript dependency-version: 5.8.3 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 13c82a9b3..b6e47f115 100644 --- a/backend/package.json +++ b/backend/package.json @@ -117,7 +117,7 @@ "rosie": "^2.1.1", "ts-jest": "^29.3.1", "ts-node": "^10.9.2", - "typescript": "^5.7.3" + "typescript": "^5.8.3" }, "resolutions": { "**/**/fs-capacitor": "^6.2.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 52ca57229..cec38d291 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -9578,10 +9578,10 @@ typed-array-length@^1.0.6: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" -typescript@^5.7.3: - version "5.7.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.3.tgz#919b44a7dbb8583a9b856d162be24a54bf80073e" - integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== +typescript@^5.8.3: + version "5.8.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" + integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== unbox-primitive@^1.0.2: version "1.0.2" From 110f52f47f164f7c682cd030b6785b8b01d42f6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 18:38:55 +0000 Subject: [PATCH 064/266] build(deps-dev): bump @types/node from 22.13.5 to 22.14.0 in /backend (#8325) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.13.5 to 22.14.0. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 22.14.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/package.json b/backend/package.json index b6e47f115..21344b26f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -97,7 +97,7 @@ "devDependencies": { "@faker-js/faker": "9.6.0", "@types/jest": "^29.5.14", - "@types/node": "^22.13.5", + "@types/node": "^22.14.0", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "apollo-server-testing": "~2.11.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index cec38d291..bedf9be1a 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2026,12 +2026,12 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>=6", "@types/node@^22.13.5": - version "22.13.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.5.tgz#23add1d71acddab2c6a4d31db89c0f98d330b511" - integrity sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg== +"@types/node@*", "@types/node@>=6", "@types/node@^22.14.0": + version "22.14.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.0.tgz#d3bfa3936fef0dbacd79ea3eb17d521c628bb47e" + integrity sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA== dependencies: - undici-types "~6.20.0" + undici-types "~6.21.0" "@types/node@^10.1.0": version "10.17.26" @@ -9598,10 +9598,10 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== -undici-types@~6.20.0: - version "6.20.0" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.20.0.tgz#8171bf22c1f588d1554d55bf204bc624af388433" - integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== undici@^6.19.5: version "6.19.8" From 1461f979ba171d875c5a89bcc8c9cf4150d358f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:13:01 +0000 Subject: [PATCH 065/266] build(deps): bump actions/cache from 4.2.1 to 4.2.3 (#8287) Bumps [actions/cache](https://github.com/actions/cache) from 4.2.1 to 4.2.3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/0c907a75c2c80ebcb7f088228285e798b750cf8f...5a3ec84eff668545956fd18022155c47e93e2684) --- updated-dependencies: - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test-backend.yml | 8 ++++---- .github/workflows/test-e2e.yml | 4 ++-- .github/workflows/test-webapp.yml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test-backend.yml b/.github/workflows/test-backend.yml index ba0bb3c74..766717a97 100644 --- a/.github/workflows/test-backend.yml +++ b/.github/workflows/test-backend.yml @@ -37,7 +37,7 @@ jobs: - name: Cache docker images id: cache-neo4j - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: /tmp/neo4j.tar key: ${{ github.run_id }}-backend-neo4j-cache @@ -58,7 +58,7 @@ jobs: - name: Cache docker images id: cache-backend - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: /tmp/backend.tar key: ${{ github.run_id }}-backend-cache @@ -87,14 +87,14 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Restore Neo4J cache - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: /tmp/neo4j.tar key: ${{ github.run_id }}-backend-neo4j-cache fail-on-cache-miss: true - name: Restore Backend cache - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: /tmp/backend.tar key: ${{ github.run_id }}-backend-cache diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 8088b9c19..8e3570d95 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -37,7 +37,7 @@ jobs: - name: Cache docker images id: cache - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: | /opt/cucumber-json-formatter @@ -59,7 +59,7 @@ jobs: job: [1, 2, 3, 4, 5, 6, 7, 8] steps: - name: Restore cache - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 id: cache with: path: | diff --git a/.github/workflows/test-webapp.yml b/.github/workflows/test-webapp.yml index 21473f3fe..937153060 100644 --- a/.github/workflows/test-webapp.yml +++ b/.github/workflows/test-webapp.yml @@ -50,7 +50,7 @@ jobs: docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar - name: Cache docker image - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: /tmp/webapp.tar key: ${{ github.run_id }}-webapp-cache @@ -79,7 +79,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Restore webapp cache - uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.0.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: /tmp/webapp.tar key: ${{ github.run_id }}-webapp-cache From ad2c4cdc4faf8dc8a42c7aa8c15f3c0474cc3e0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:41:41 +0000 Subject: [PATCH 066/266] build(deps-dev): bump eslint-import-resolver-typescript in /backend (#8326) Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 3.8.3 to 4.3.1. - [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases) - [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v3.8.3...v4.3.1) --- updated-dependencies: - dependency-name: eslint-import-resolver-typescript dependency-version: 4.3.1 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 197 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 158 insertions(+), 41 deletions(-) diff --git a/backend/package.json b/backend/package.json index 21344b26f..9c52815a1 100644 --- a/backend/package.json +++ b/backend/package.json @@ -104,7 +104,7 @@ "eslint": "^8.57.1", "eslint-config-prettier": "^10.1.1", "eslint-config-standard": "^17.1.0", - "eslint-import-resolver-typescript": "^3.8.3", + "eslint-import-resolver-typescript": "^4.3.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^16.6.2", diff --git a/backend/yarn.lock b/backend/yarn.lock index bedf9be1a..ab611aea5 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1069,6 +1069,28 @@ resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz#a5502c8539265fecbd873c1e395a890339f119c2" integrity sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw== +"@emnapi/core@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.4.0.tgz#8844b02d799198158ac1fea21ae2bc81b881da9a" + integrity sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg== + dependencies: + "@emnapi/wasi-threads" "1.0.1" + tslib "^2.4.0" + +"@emnapi/runtime@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.4.0.tgz#8f509bf1059a5551c8fe829a1c4e91db35fdfbee" + integrity sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw== + dependencies: + tslib "^2.4.0" + +"@emnapi/wasi-threads@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.0.1.tgz#d7ae71fd2166b1c916c6cd2d0df2ef565a2e1a5b" + integrity sha512-iIBu7mwkq4UQGeMEM8bLwNK962nXdhodeScX4slfQnRhEMMzvYivHhutCIk8uojvmASXXPC2WNEjwxFWk72Oqw== + dependencies: + tslib "^2.4.0" + "@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -1537,6 +1559,15 @@ url-regex "~4.1.1" video-extensions "~1.1.0" +"@napi-rs/wasm-runtime@^0.2.7": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.8.tgz#642e8390ee78ed21d6b79c467aa610e249224ed6" + integrity sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg== + dependencies: + "@emnapi/core" "^1.4.0" + "@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" @@ -1568,11 +1599,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nolyfill/is-core-module@1.0.39": - version "1.0.39" - resolved "https://registry.yarnpkg.com/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz#3dc35ba0f1e66b403c00b39344f870298ebb1c8e" - integrity sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA== - "@npmcli/agent@^2.0.0": version "2.2.2" resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" @@ -1789,6 +1815,13 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@tybys/wasm-util@^0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@tybys/wasm-util/-/wasm-util-0.9.0.tgz#3e75eb00604c8d6db470bf18c37b7d984a0e3355" + integrity sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw== + dependencies: + tslib "^2.4.0" + "@types/accepts@*", "@types/accepts@^1.3.5": version "1.3.5" resolved "https://registry.yarnpkg.com/@types/accepts/-/accepts-1.3.5.tgz#c34bec115cfc746e04fe5a059df4ce7e7b391575" @@ -2236,6 +2269,83 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@unrs/resolver-binding-darwin-arm64@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.3.3.tgz#394065916f98cdc1897cf7234adfdee395725fa8" + integrity sha512-EpRILdWr3/xDa/7MoyfO7JuBIJqpBMphtu4+80BK1bRfFcniVT74h3Z7q1+WOc92FuIAYatB1vn9TJR67sORGw== + +"@unrs/resolver-binding-darwin-x64@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.3.3.tgz#6a3c75ca984342261c7346db53293b0002e8cde1" + integrity sha512-ntj/g7lPyqwinMJWZ+DKHBse8HhVxswGTmNgFKJtdgGub3M3zp5BSZ3bvMP+kBT6dnYJLSVlDqdwOq1P8i0+/g== + +"@unrs/resolver-binding-freebsd-x64@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.3.3.tgz#6532b8d4fecaca6c4424791c82f7a27aac94fcd5" + integrity sha512-l6BT8f2CU821EW7U8hSUK8XPq4bmyTlt9Mn4ERrfjJNoCw0/JoHAh9amZZtV3cwC3bwwIat+GUnrcHTG9+qixw== + +"@unrs/resolver-binding-linux-arm-gnueabihf@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.3.3.tgz#69a8e430095fcf6a76f7350cc27b83464f8cbb91" + integrity sha512-8ScEc5a4y7oE2BonRvzJ+2GSkBaYWyh0/Ko4Q25e/ix6ANpJNhwEPZvCR6GVRmsQAYMIfQvYLdM6YEN+qRjnAQ== + +"@unrs/resolver-binding-linux-arm-musleabihf@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.3.3.tgz#e1fc8440e54929b1f0f6aff6f6e3e9e19ac4a73c" + integrity sha512-8qQ6l1VTzLNd3xb2IEXISOKwMGXDCzY/UNy/7SovFW2Sp0K3YbL7Ao7R18v6SQkLqQlhhqSBIFRk+u6+qu5R5A== + +"@unrs/resolver-binding-linux-arm64-gnu@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.3.3.tgz#1249e18b5fa1419addda637d62ef201ce9bcf5a4" + integrity sha512-v81R2wjqcWXJlQY23byqYHt9221h4anQ6wwN64oMD/WAE+FmxPHFZee5bhRkNVtzqO/q7wki33VFWlhiADwUeQ== + +"@unrs/resolver-binding-linux-arm64-musl@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.3.3.tgz#9af549ce9dde57b31c32a36cbe9eafa05f96befd" + integrity sha512-cAOx/j0u5coMg4oct/BwMzvWJdVciVauUvsd+GQB/1FZYKQZmqPy0EjJzJGbVzFc6gbnfEcSqvQE6gvbGf2N8Q== + +"@unrs/resolver-binding-linux-ppc64-gnu@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.3.3.tgz#45aab52319f3e3b2627038a80c0331b0793a4be3" + integrity sha512-mq2blqwErgDJD4gtFDlTX/HZ7lNP8YCHYFij2gkXPtMzrXxPW1hOtxL6xg4NWxvnj4bppppb0W3s/buvM55yfg== + +"@unrs/resolver-binding-linux-s390x-gnu@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.3.3.tgz#7d2fe5c43e291d42e66d74fce07d9cf0050b4241" + integrity sha512-u0VRzfFYysarYHnztj2k2xr+eu9rmgoTUUgCCIT37Nr+j0A05Xk2c3RY8Mh5+DhCl2aYibihnaAEJHeR0UOFIQ== + +"@unrs/resolver-binding-linux-x64-gnu@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.3.3.tgz#be54ff88c581610c42d8614475c0560f043d7ded" + integrity sha512-OrVo5ZsG29kBF0Ug95a2KidS16PqAMmQNozM6InbquOfW/udouk063e25JVLqIBhHLB2WyBnixOQ19tmeC/hIg== + +"@unrs/resolver-binding-linux-x64-musl@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.3.3.tgz#4efa7a1e4f7bf231098ed23df1e19174d360c24f" + integrity sha512-PYnmrwZ4HMp9SkrOhqPghY/aoL+Rtd4CQbr93GlrRTjK6kDzfMfgz3UH3jt6elrQAfupa1qyr1uXzeVmoEAxUA== + +"@unrs/resolver-binding-wasm32-wasi@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.3.3.tgz#6df454b4a9b28d47850bcb665d243f09101b782c" + integrity sha512-81AnQY6fShmktQw4hWDUIilsKSdvr/acdJ5azAreu2IWNlaJOKphJSsUVWE+yCk6kBMoQyG9ZHCb/krb5K0PEA== + dependencies: + "@napi-rs/wasm-runtime" "^0.2.7" + +"@unrs/resolver-binding-win32-arm64-msvc@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.3.3.tgz#fb19e118350e1392993a0a6565b427d38c1c1760" + integrity sha512-X/42BMNw7cW6xrB9syuP5RusRnWGoq+IqvJO8IDpp/BZg64J1uuIW6qA/1Cl13Y4LyLXbJVYbYNSKwR/FiHEng== + +"@unrs/resolver-binding-win32-ia32-msvc@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.3.3.tgz#23a9c4b5621bba2d472bc78fadde7273a8c4548d" + integrity sha512-EGNnNGQxMU5aTN7js3ETYvuw882zcO+dsVjs+DwO2j/fRVKth87C8e2GzxW1L3+iWAXMyJhvFBKRavk9Og1Z6A== + +"@unrs/resolver-binding-win32-x64-msvc@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.3.3.tgz#eee226e5b4c4d91c862248afd24452c8698ed542" + integrity sha512-GraLbYqOJcmW1qY3osB+2YIiD62nVf2/bVLHZmrb4t/YSUwE03l7TwcDJl08T/Tm3SVhepX8RQkpzWbag/Sb4w== + "@wry/context@^0.4.0": version "0.4.4" resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.4.4.tgz#e50f5fa1d6cfaabf2977d1fda5ae91717f8815f8" @@ -3730,7 +3840,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.7: +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.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== @@ -4074,14 +4184,6 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enhanced-resolve@^5.15.0: - version "5.17.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz#67bfbbcc2f81d511be77d686a90267ef7f898a15" - integrity sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" @@ -4427,18 +4529,17 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-import-resolver-typescript@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.8.3.tgz#1721a1e4417e57a8fe6bf9463d0db8e220285eef" - integrity sha512-A0bu4Ks2QqDWNpeEgTQMPTngaMhuDu4yv6xpftBMAf+1ziXnpx+eSR1WRfoPTe2BAiAjHFZ7kSNx1fvr5g5pmQ== +eslint-import-resolver-typescript@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.1.tgz#6721c639716de3685363ddb284e2cec60cee60ee" + integrity sha512-/dR9YMomeBlvfuvX5q0C3Y/2PHC9OCRdT2ijFwdfq/4Bq+4m5/lqstEp9k3P6ocha1pCbhoY9fkwVYLmOqR0VQ== dependencies: - "@nolyfill/is-core-module" "1.0.39" - debug "^4.3.7" - enhanced-resolve "^5.15.0" + debug "^4.4.0" get-tsconfig "^4.10.0" - is-bun-module "^1.0.2" - stable-hash "^0.0.4" + is-bun-module "^2.0.0" + stable-hash "^0.0.5" tinyglobby "^0.2.12" + unrs-resolver "^1.3.3" eslint-module-utils@^2.12.0: version "2.12.0" @@ -5290,7 +5391,7 @@ got@~11.8.0: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: +graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== @@ -5901,12 +6002,12 @@ is-builtin-module@^3.2.1: dependencies: builtin-modules "^3.3.0" -is-bun-module@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-1.1.0.tgz#a66b9830869437f6cdad440ba49ab6e4dc837269" - integrity sha512-4mTAVPlrXpaN3jtF0lsnPCMGnq4+qZjVIKq0HCpfcqf8OC1SM5oATCIAPM5V5FN05qp2NNnFndphmdZS9CV3hA== +is-bun-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" + integrity sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ== dependencies: - semver "^7.6.3" + semver "^7.7.1" is-callable@^1.1.3, is-callable@^1.2.7: version "1.2.7" @@ -8634,7 +8735,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: +semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== @@ -8909,10 +9010,10 @@ ssri@^10.0.0: dependencies: minipass "^7.0.3" -stable-hash@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.4.tgz#55ae7dadc13e4b3faed13601587cec41859b42f7" - integrity sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g== +stable-hash@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stable-hash/-/stable-hash-0.0.5.tgz#94e8837aaeac5b4d0f631d2972adef2924b40269" + integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA== stack-utils@^2.0.3: version "2.0.6" @@ -9155,11 +9256,6 @@ synckit@^0.11.0: "@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" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - tar@^6.1.11: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" @@ -9431,7 +9527,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.8.1: +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== @@ -9657,6 +9753,27 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +unrs-resolver@^1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.3.3.tgz#46bd5dd2ecc650365e050055fc208b5f4ae57803" + integrity sha512-PFLAGQzYlyjniXdbmQ3dnGMZJXX5yrl2YS4DLRfR3BhgUsE1zpRIrccp9XMOGRfIHpdFvCn/nr5N1KMVda4x3A== + optionalDependencies: + "@unrs/resolver-binding-darwin-arm64" "1.3.3" + "@unrs/resolver-binding-darwin-x64" "1.3.3" + "@unrs/resolver-binding-freebsd-x64" "1.3.3" + "@unrs/resolver-binding-linux-arm-gnueabihf" "1.3.3" + "@unrs/resolver-binding-linux-arm-musleabihf" "1.3.3" + "@unrs/resolver-binding-linux-arm64-gnu" "1.3.3" + "@unrs/resolver-binding-linux-arm64-musl" "1.3.3" + "@unrs/resolver-binding-linux-ppc64-gnu" "1.3.3" + "@unrs/resolver-binding-linux-s390x-gnu" "1.3.3" + "@unrs/resolver-binding-linux-x64-gnu" "1.3.3" + "@unrs/resolver-binding-linux-x64-musl" "1.3.3" + "@unrs/resolver-binding-wasm32-wasi" "1.3.3" + "@unrs/resolver-binding-win32-arm64-msvc" "1.3.3" + "@unrs/resolver-binding-win32-ia32-msvc" "1.3.3" + "@unrs/resolver-binding-win32-x64-msvc" "1.3.3" + update-browserslist-db@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz#7ca61c0d8650766090728046e416a8cde682859e" From 5617799d7927d12c0395b126da157f8f170ef6b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 03:30:26 +0000 Subject: [PATCH 067/266] build(deps-dev): bump eslint-config-prettier in /webapp (#8259) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 10.0.1 to 10.1.1. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v10.0.1...v10.1.1) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 1cace96c3..edcc620eb 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -93,7 +93,7 @@ "core-js": "~2.6.10", "css-loader": "~3.5.2", "eslint": "^7.28.0", - "eslint-config-prettier": "~10.0.1", + "eslint-config-prettier": "~10.1.1", "eslint-config-standard": "~15.0.1", "eslint-loader": "~4.0.0", "eslint-plugin-import": "~2.31.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 6b11556b1..f318a66d2 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -9425,10 +9425,10 @@ eslint-config-prettier@^6.0.0: dependencies: get-stdin "^6.0.0" -eslint-config-prettier@~10.0.1: - version "10.0.1" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz#fbb03bfc8db0651df9ce4e8b7150d11c5fe3addf" - integrity sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw== +eslint-config-prettier@~10.1.1: + version "10.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132" + integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw== eslint-config-standard@~15.0.1: version "15.0.1" From b0aebeafae8ea8f3e62e90a3b723c3d197946804 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 04:15:14 +0000 Subject: [PATCH 068/266] build(deps): Bump actions/checkout from 4.2.1 to 4.2.2 (#8247) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.1 to 4.2.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4.2.1...11bd71901bbe5b1630ceea73d27597364c9af683) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml index 93dc45bd3..643c5436d 100644 --- a/.github/workflows/docker-push.yml +++ b/.github/workflows/docker-push.yml @@ -59,7 +59,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.1.7 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Log in to the Container registry uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 with: From fc9aa59ccaada57961a0de83cbe05972ed199fcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 07:00:07 +0000 Subject: [PATCH 069/266] build(deps): bump peter-evans/repository-dispatch (#8330) Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from b0b38f73c8333be75d585a92b2c630a10d2a78f5 to 7d980a9b9f8ecf8955ea90507b3ed89122f53215. - [Release notes](https://github.com/peter-evans/repository-dispatch/releases) - [Commits](https://github.com/peter-evans/repository-dispatch/compare/b0b38f73c8333be75d585a92b2c630a10d2a78f5...7d980a9b9f8ecf8955ea90507b3ed89122f53215) --- updated-dependencies: - dependency-name: peter-evans/repository-dispatch dependency-version: 7d980a9b9f8ecf8955ea90507b3ed89122f53215 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 29d936d2d..b66413f22 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -64,7 +64,7 @@ jobs: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV #- name: Repository Dispatch - # uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0 + # uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0 # with: # token: ${{ github.token }} # event-type: trigger-ocelot-build-success @@ -72,7 +72,7 @@ jobs: # client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}' - name: Repository Dispatch stage.ocelot.social - uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0 + uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0 with: token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository event-type: trigger-ocelot-build-success @@ -80,7 +80,7 @@ jobs: client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}' - name: Repository Dispatch stage.yunite.me - uses: peter-evans/repository-dispatch@b0b38f73c8333be75d585a92b2c630a10d2a78f5 # v3.0.0 + uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0 with: token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository event-type: trigger-ocelot-build-success From a39223d8d0174c4eb68268d01897368bf297b740 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 07:30:29 +0000 Subject: [PATCH 070/266] build(deps): bump crazy-max/ghaction-github-pages from 4.1.0 to 4.2.0 (#8328) Bumps [crazy-max/ghaction-github-pages](https://github.com/crazy-max/ghaction-github-pages) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/crazy-max/ghaction-github-pages/releases) - [Commits](https://github.com/crazy-max/ghaction-github-pages/compare/fbf0a4fa4e00f45accd6cf3232368436ec06ed59...df5cc2bfa78282ded844b354faee141f06b41865) --- updated-dependencies: - dependency-name: crazy-max/ghaction-github-pages dependency-version: 4.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/deploy-documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-documentation.yml b/.github/workflows/deploy-documentation.yml index ef4f7d2b7..d1816bddd 100644 --- a/.github/workflows/deploy-documentation.yml +++ b/.github/workflows/deploy-documentation.yml @@ -38,7 +38,7 @@ jobs: run: npm install && npm run docs:build - name: Deploy Vuepress to Github Pages - uses: crazy-max/ghaction-github-pages@fbf0a4fa4e00f45accd6cf3232368436ec06ed59 # v4.0.0 + uses: crazy-max/ghaction-github-pages@df5cc2bfa78282ded844b354faee141f06b41865 # v4.0.0 with: target_branch: gh-pages build_dir: .vuepress/dist From ca3b075397e751630905b03666502312e1217763 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 07:59:14 +0000 Subject: [PATCH 071/266] build(deps-dev): bump @faker-js/faker from 9.5.0 to 9.6.0 in /webapp (#8262) Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.5.0 to 9.6.0. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v9.5.0...v9.6.0) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index edcc620eb..c59f03126 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -73,7 +73,7 @@ "@babel/core": "^7.25.8", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.25.8", - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "@storybook/addon-a11y": "^8.0.8", "@storybook/addon-actions": "^5.3.21", "@storybook/addon-notes": "^5.3.18", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index f318a66d2..9359cd872 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2447,10 +2447,10 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@faker-js/faker@9.5.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" - integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== +"@faker-js/faker@9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d" + integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw== "@hapi/address@2.x.x": version "2.0.0" From 6c422f49e21803dc2780afb737127185850dc81d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 08:33:00 +0000 Subject: [PATCH 072/266] build(deps-dev): bump @faker-js/faker from 9.5.0 to 9.6.0 (#8257) Bumps [@faker-js/faker](https://github.com/faker-js/faker) from 9.5.0 to 9.6.0. - [Release notes](https://github.com/faker-js/faker/releases) - [Changelog](https://github.com/faker-js/faker/blob/next/CHANGELOG.md) - [Commits](https://github.com/faker-js/faker/compare/v9.5.0...v9.6.0) --- updated-dependencies: - dependency-name: "@faker-js/faker" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5a083c2c1..0b708bace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", @@ -2786,9 +2786,9 @@ } }, "node_modules/@faker-js/faker": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.5.0.tgz", - "integrity": "sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-9.6.0.tgz", + "integrity": "sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index b90501c4b..dd27c53ca 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", - "@faker-js/faker": "9.5.0", + "@faker-js/faker": "9.6.0", "auto-changelog": "^2.5.0", "bcryptjs": "^2.4.3", "cross-env": "^7.0.3", diff --git a/yarn.lock b/yarn.lock index f01c83a18..4e1450d9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1543,10 +1543,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== -"@faker-js/faker@9.5.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.5.0.tgz#ce254c83706250ca8a5a0e05683608160610dd84" - integrity sha512-3qbjLv+fzuuCg3umxc9/7YjrEXNaKwHgmig949nfyaTx8eL4FAsvFbu+1JcFUj1YAXofhaDn6JdEUBTYuk0Ssw== +"@faker-js/faker@9.6.0": + version "9.6.0" + resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.6.0.tgz#64235d20330b142eef3d1d1638ba56c083b4bf1d" + integrity sha512-3vm4by+B5lvsFPSyep3ELWmZfE3kicDtmemVpuwl1yH7tqtnHdsA6hG8fbXedMVdkzgtvzWoRgjSB4Q+FHnZiw== "@fastify/busboy@^2.0.0": version "2.1.1" From 175fd60034307b2f58dba50c421357f6bc87ba6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 09:05:42 +0000 Subject: [PATCH 073/266] build(deps): bump validator from 13.12.0 to 13.15.0 in /webapp (#8305) Bumps [validator](https://github.com/validatorjs/validator.js) from 13.12.0 to 13.15.0. - [Release notes](https://github.com/validatorjs/validator.js/releases) - [Changelog](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md) - [Commits](https://github.com/validatorjs/validator.js/compare/13.12.0...13.15.0) --- updated-dependencies: - dependency-name: validator dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index c59f03126..4da555417 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -56,7 +56,7 @@ "trunc-html": "^1.1.2", "v-mapbox": "^1.11.2", "v-tooltip": "~2.1.3", - "validator": "^13.12.0", + "validator": "^13.15.0", "vue-advanced-chat": "^2.0.11", "vue-count-to": "~1.0.13", "vue-infinite-loading": "^2.4.5", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 9359cd872..595b80429 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -19666,10 +19666,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" -validator@^13.12.0: - version "13.12.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.12.0.tgz#7d78e76ba85504da3fee4fd1922b385914d4b35f" - integrity sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg== +validator@^13.15.0: + version "13.15.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" + integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA== vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" From c2eae7a8c3973a65c789ba879a45c8616101795c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 09:35:05 +0000 Subject: [PATCH 074/266] build(deps): bump sass from 1.85.0 to 1.86.3 in /webapp (#8333) Bumps [sass](https://github.com/sass/dart-sass) from 1.85.0 to 1.86.3. - [Release notes](https://github.com/sass/dart-sass/releases) - [Changelog](https://github.com/sass/dart-sass/blob/main/CHANGELOG.md) - [Commits](https://github.com/sass/dart-sass/compare/1.85.0...1.86.3) --- updated-dependencies: - dependency-name: sass dependency-version: 1.86.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 4da555417..5c1d946fc 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -48,7 +48,7 @@ "nuxt": "~2.12.1", "nuxt-dropzone": "^1.0.4", "nuxt-env": "~0.1.0", - "sass": "^1.85.0", + "sass": "^1.86.3", "stack-utils": "^2.0.3", "tippy.js": "^4.3.5", "tiptap": "~1.26.6", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 595b80429..87e849383 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -17426,10 +17426,10 @@ sass-resources-loader@^2.2.1: glob "^7.1.6" loader-utils "^2.0.0" -sass@^1.85.0: - version "1.85.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.85.0.tgz#0127ef697d83144496401553f0a0e87be83df45d" - integrity sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww== +sass@^1.86.3: + version "1.86.3" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.86.3.tgz#0a0d9ea97cb6665e73f409639f8533ce057464c9" + integrity sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw== dependencies: chokidar "^4.0.0" immutable "^5.0.2" @@ -17823,12 +17823,7 @@ source-list-map@^2.0.0: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" - integrity sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg== - -source-map-js@^1.0.2, source-map-js@^1.2.0: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.0.2, source-map-js@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== From 46f380eafa31e067af214adfc886c39fda03f888 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 10:02:37 +0000 Subject: [PATCH 075/266] build(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#8271) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/9780b0c442fbb1117ed29e0efdff1e18412f7567...74a5d142397b4f367a81961eba4e8cd7edddf772) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml index 643c5436d..de1d74d15 100644 --- a/.github/workflows/docker-push.yml +++ b/.github/workflows/docker-push.yml @@ -61,7 +61,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 - name: Log in to the Container registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} From bcff4d393dc17fc52f4aaa68d40a8622506212ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 10:33:36 +0000 Subject: [PATCH 076/266] build(deps): Bump docker/build-push-action from 6.9.0 to 6.15.0 (#8244) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.9.0 to 6.15.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/4f58ea79222b3b9dc2c8bbdd6debcef730109a75...471d1dc4e07e5cdedd4c2171150001c434f0b7a4) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml index de1d74d15..9e95fabe5 100644 --- a/.github/workflows/docker-push.yml +++ b/.github/workflows/docker-push.yml @@ -81,7 +81,7 @@ jobs: type=sha - name: Build and push Docker images id: push - uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 + uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 with: context: ${{ matrix.app.context }} target: ${{ matrix.app.target }} From 4e827de29d5fe3e3c7f5f9523b79db307cbfdfdc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 11:39:34 +0000 Subject: [PATCH 077/266] build(deps): Bump docker/metadata-action (#8245) Bumps [docker/metadata-action](https://github.com/docker/metadata-action) from 70b2cdc6480c1a8b86edf1777157f8f437de2166 to 902fa8ec7d6ecbf8d84d538b9b233a880e428804. - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/70b2cdc6480c1a8b86edf1777157f8f437de2166...902fa8ec7d6ecbf8d84d538b9b233a880e428804) --- updated-dependencies: - dependency-name: docker/metadata-action dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml index 9e95fabe5..406d8304b 100644 --- a/.github/workflows/docker-push.yml +++ b/.github/workflows/docker-push.yml @@ -68,7 +68,7 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@70b2cdc6480c1a8b86edf1777157f8f437de2166 + uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | From 2eaaa7af3977307c0da47037ebf4b6c4a3544a51 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 7 Apr 2025 12:23:36 +0200 Subject: [PATCH 078/266] feat(backend): chat notify via email (#8314) * client * backend * tests * also save awaySince timestamp * remove console.log * chat notification logic * send notification mails for chat messages * externalize online check, resolver resover first * prevent email notifications for blocked users comment * respect user email notification settings * properly handle null case for email destructuring * tests * corrected mail style --------- Co-authored-by: mahula --- .../helpers/email/templateBuilder.spec.ts | 32 +++++ .../helpers/email/templateBuilder.ts | 13 ++ .../helpers/email/templates/chatMessage.html | 105 ++++++++++++++ .../helpers/email/templates/index.ts | 1 + .../middleware/helpers/isUserOnline.spec.ts | 46 +++++++ .../src/middleware/helpers/isUserOnline.ts | 16 +++ .../notificationsMiddleware.spec.ts | 128 +++++++++++++++++- .../notifications/notificationsMiddleware.ts | 54 +++++++- 8 files changed, 393 insertions(+), 2 deletions(-) create mode 100644 backend/src/middleware/helpers/email/templates/chatMessage.html create mode 100644 backend/src/middleware/helpers/isUserOnline.spec.ts create mode 100644 backend/src/middleware/helpers/isUserOnline.ts diff --git a/backend/src/middleware/helpers/email/templateBuilder.spec.ts b/backend/src/middleware/helpers/email/templateBuilder.spec.ts index cb516c0a9..437672a9a 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.spec.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.spec.ts @@ -6,6 +6,7 @@ import { resetPasswordTemplate, wrongAccountTemplate, notificationTemplate, + chatMessageTemplate, } from './templateBuilder' const englishHint = 'English version below!' @@ -34,6 +35,12 @@ const resetPasswordTemplateData = () => ({ name: 'Mr Example', }, }) +const chatMessageTemplateData = { + email: 'test@example.org', + variables: { + name: 'Mr Example', + }, +} const wrongAccountTemplateData = () => ({ email: 'test@example.org', variables: {}, @@ -163,6 +170,31 @@ describe('templateBuilder', () => { }) }) + describe('chatMessageTemplate', () => { + describe('multi language', () => { + it('e-mail is build with all data', () => { + const subject = 'Neue Chatnachricht | New chat message' + const actionUrl = new URL('/chat', CONFIG.CLIENT_URI).toString() + const enContent = 'You have received a new chat message.' + const deContent = 'Du hast eine neue Chatnachricht erhalten.' + testEmailData(null, chatMessageTemplate, chatMessageTemplateData, [ + ...textsStandard, + { + templPropName: 'subject', + isContaining: false, + text: subject, + }, + englishHint, + actionUrl, + chatMessageTemplateData.variables.name, + enContent, + deContent, + supportUrl, + ]) + }) + }) + }) + describe('wrongAccountTemplate', () => { describe('multi language', () => { it('e-mail is build with all data', () => { diff --git a/backend/src/middleware/helpers/email/templateBuilder.ts b/backend/src/middleware/helpers/email/templateBuilder.ts index 78d7a9bf9..431048336 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.ts @@ -71,6 +71,19 @@ export const resetPasswordTemplate = ({ email, variables: { nonce, name } }) => } } +export const chatMessageTemplate = ({ email, variables: { name } }) => { + const subject = 'Neue Chatnachricht | New chat message' + const actionUrl = new URL('/chat', CONFIG.CLIENT_URI) + const renderParams = { ...defaultParams, englishHint, actionUrl, name, subject } + + 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) diff --git a/backend/src/middleware/helpers/email/templates/chatMessage.html b/backend/src/middleware/helpers/email/templates/chatMessage.html new file mode 100644 index 000000000..0b1bacb08 --- /dev/null +++ b/backend/src/middleware/helpers/email/templates/chatMessage.html @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/backend/src/middleware/helpers/email/templates/index.ts b/backend/src/middleware/helpers/email/templates/index.ts index b8ae01bdb..9c32c6d3e 100644 --- a/backend/src/middleware/helpers/email/templates/index.ts +++ b/backend/src/middleware/helpers/email/templates/index.ts @@ -7,5 +7,6 @@ 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/isUserOnline.spec.ts b/backend/src/middleware/helpers/isUserOnline.spec.ts new file mode 100644 index 000000000..bf2cb8d17 --- /dev/null +++ b/backend/src/middleware/helpers/isUserOnline.spec.ts @@ -0,0 +1,46 @@ +import { isUserOnline } from './isUserOnline' + +let user + +describe('isUserOnline', () => { + beforeEach(() => { + user = { + properties: { + lastActiveAt: null, + awaySince: null, + lastOnlineStatus: null, + }, + } + }) + describe('user has lastOnlineStatus `online`', () => { + it('returns true if he was active within the last 90 seconds', () => { + user.properties.lastOnlineStatus = 'online' + user.properties.lastActiveAt = new Date() + expect(isUserOnline(user)).toBe(true) + }) + it('returns false if he was not active within the last 90 seconds', () => { + user.properties.lastOnlineStatus = 'online' + user.properties.lastActiveAt = new Date().getTime() - 90001 + expect(isUserOnline(user)).toBe(false) + }) + }) + + describe('user has lastOnlineStatus `away`', () => { + it('returns true if he went away less then 180 seconds ago', () => { + user.properties.lastOnlineStatus = 'away' + user.properties.awaySince = new Date() + expect(isUserOnline(user)).toBe(true) + }) + it('returns false if he went away more then 180 seconds ago', () => { + user.properties.lastOnlineStatus = 'away' + user.properties.awaySince = new Date().getTime() - 180001 + expect(isUserOnline(user)).toBe(false) + }) + }) + + describe('user is freshly created and has never logged in', () => { + it('returns false', () => { + expect(isUserOnline(user)).toBe(false) + }) + }) +}) diff --git a/backend/src/middleware/helpers/isUserOnline.ts b/backend/src/middleware/helpers/isUserOnline.ts new file mode 100644 index 000000000..679953f81 --- /dev/null +++ b/backend/src/middleware/helpers/isUserOnline.ts @@ -0,0 +1,16 @@ +export const isUserOnline = (user) => { + // Is Recipient considered online + const lastActive = new Date(user.properties.lastActiveAt).getTime() + const awaySince = new Date(user.properties.awaySince).getTime() + const now = new Date().getTime() + const status = user.properties.lastOnlineStatus + if ( + // online & last active less than 1.5min -> online + (status === 'online' && now - lastActive < 90000) || + // away for less then 3min -> online + (status === 'away' && now - awaySince < 180000) + ) { + return true + } + return false +} diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 57354d13f..50d655484 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -1,5 +1,5 @@ import gql from 'graphql-tag' -import { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '../../db/factories' import { createTestClient } from 'apollo-server-testing' import { getNeode, getDriver } from '../../db/neo4j' import createServer, { pubsub } from '../../server' @@ -10,6 +10,23 @@ import { changeGroupMemberRoleMutation, removeUserFromGroupMutation, } from '../../graphql/groups' +import { createMessageMutation } from '../../graphql/messages' +import { createRoomMutation } from '../../graphql/rooms' + +const sendMailMock = jest.fn() +jest.mock('../helpers/email/sendMail', () => ({ + sendMail: () => sendMailMock(), +})) + +const chatMessageTemplateMock = jest.fn() +jest.mock('../helpers/email/templateBuilder', () => ({ + chatMessageTemplate: () => chatMessageTemplateMock(), +})) + +let isUserOnlineMock = jest.fn() +jest.mock('../helpers/isUserOnline', () => ({ + isUserOnline: () => isUserOnlineMock(), +})) let server, query, mutate, notifiedUser, authenticatedUser let publishSpy @@ -633,6 +650,115 @@ describe('notifications', () => { }) }) + describe('chat email notifications', () => { + let chatSender + let chatReceiver + let roomId + + beforeEach(async () => { + jest.clearAllMocks() + + chatSender = await neode.create( + 'User', + { + id: 'chatSender', + name: 'chatSender', + slug: 'chatSender', + }, + { + email: 'chatSender@example.org', + password: '1234', + }, + ) + + chatReceiver = await Factory.build( + 'user', + { id: 'chatReceiver', name: 'chatReceiver', slug: 'chatReceiver' }, + { email: 'user@example.org' }, + ) + + authenticatedUser = await chatSender.toJson() + + const room = await mutate({ + mutation: createRoomMutation(), + variables: { + userId: 'chatReceiver', + }, + }) + roomId = room.data.CreateRoom.id + }) + + describe('chatReceiver is online', () => { + it('sends no email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(true) + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).not.toHaveBeenCalled() + expect(chatMessageTemplateMock).not.toHaveBeenCalled() + }) + }) + + describe('chatReceiver is offline', () => { + it('sends an email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(false) + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(chatMessageTemplateMock).toHaveBeenCalledTimes(1) + }) + }) + + describe('chatReceiver has blocked chatSender', () => { + it('sends no email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(false) + await chatReceiver.relateTo(chatSender, 'blocked') + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).not.toHaveBeenCalled() + expect(chatMessageTemplateMock).not.toHaveBeenCalled() + }) + }) + + describe('chatReceiver has disabled email notifications', () => { + it('sends no email', async () => { + isUserOnlineMock = jest.fn().mockReturnValue(false) + await chatReceiver.update({ sendNotificationEmails: false }) + + await mutate({ + mutation: createMessageMutation(), + variables: { + roomId, + content: 'Some nice message to chatReceiver', + }, + }) + + expect(sendMailMock).not.toHaveBeenCalled() + expect(chatMessageTemplateMock).not.toHaveBeenCalled() + }) + }) + }) + describe('group notifications', () => { let groupOwner diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index aa2cee06e..7ecbf8181 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -2,7 +2,8 @@ import { pubsub, NOTIFICATION_ADDED } from '../../server' import extractMentionedUsers from './mentions/extractMentionedUsers' import { validateNotifyUsers } from '../validation/validationMiddleware' import { sendMail } from '../helpers/email/sendMail' -import { notificationTemplate } from '../helpers/email/templateBuilder' +import { chatMessageTemplate, notificationTemplate } from '../helpers/email/templateBuilder' +import { isUserOnline } from '../helpers/isUserOnline' const queryNotificationEmails = async (context, notificationUserIds) => { if (!(notificationUserIds && notificationUserIds.length)) return [] @@ -314,6 +315,56 @@ const notifyUsersOfComment = async (label, commentId, reason, context) => { } } +const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => { + // Execute resolver + const result = await resolve(root, args, context, resolveInfo) + + // Query Parameters + const { roomId } = args + const { + user: { id: currentUserId }, + } = context + + // Find Recipient + const session = context.driver.session() + const messageRecipient = session.readTransaction(async (transaction) => { + const messageRecipientCypher = ` + MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId }) + MATCH (room)<-[:CHATS_IN]-(recipientUser:User)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) + WHERE NOT recipientUser.id = $currentUserId + AND NOT (recipientUser)-[:BLOCKED]-(currentUser) + AND recipientUser.sendNotificationEmails = true + RETURN recipientUser, emailAddress {.email} + ` + const txResponse = await transaction.run(messageRecipientCypher, { + currentUserId, + roomId, + }) + + return { + user: await txResponse.records.map((record) => record.get('recipientUser'))[0], + email: await txResponse.records.map((record) => record.get('emailAddress'))[0]?.email, + } + }) + + try { + // Execute Query + const { user, email } = await messageRecipient + + // Send EMail if we found a user(not blocked) and he is not considered online + if (user && !isUserOnline(user)) { + void sendMail(chatMessageTemplate({ email, variables: { name: user.properties.name } })) + } + + // Return resolver result to client + return result + } catch (error) { + throw new Error(error) + } finally { + session.close() + } +} + export default { Mutation: { CreatePost: handleContentDataOfPost, @@ -324,5 +375,6 @@ export default { LeaveGroup: handleLeaveGroup, ChangeGroupMemberRole: handleChangeGroupMemberRole, RemoveUserFromGroup: handleRemoveUserFromGroup, + CreateMessage: handleCreateMessage, }, } From d6cb9b51c3566e55381732cc771b4433e6cfda8f Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 7 Apr 2025 12:53:44 +0200 Subject: [PATCH 079/266] feat(backend): lint rules (#8339) * eslint import * eslint comments * eslint security * eslint import - rules * eslint n * eslint promise * eslint no-catch-all * eslint jest * missing ignore * disable import/unambiguous as conflicting --- backend/.eslintrc.cjs | 209 +++++++++++++++++ backend/.eslintrc.js | 219 ------------------ backend/package.json | 8 +- backend/src/config/index.ts | 7 +- backend/src/db/clean.ts | 4 +- backend/src/db/compiler.ts | 3 + backend/src/db/migrate/store.ts | 10 +- backend/src/db/migrate/template.ts | 2 +- ...123150105-merge_duplicate_user_accounts.ts | 3 +- ...23150110-merge_duplicate_location_nodes.ts | 1 + ..._between_existing_blocked_relationships.ts | 4 +- ...0206190233-swap_latitude_with_longitude.ts | 2 +- .../20200207080200-fulltext_index_for_tags.ts | 2 +- ...213230248-add_unique_index_to_image_url.ts | 3 +- .../20200312140328-bulk_upload_to_s3.ts | 4 +- ...15-refactor_all_images_to_separate_type.ts | 2 +- ...emove_deleted_users_obsolete_attributes.ts | 2 +- ...emove_deleted_posts_obsolete_attributes.ts | 2 +- ...200326160326-remove_dangling_image_urls.ts | 3 +- .../migrations/1613589876420-null_mutation.ts | 4 +- ...1614023644903-add-clickedCount-to-posts.ts | 6 +- ...77130817-add-viewedTeaserCount-to-posts.ts | 6 +- .../20210506150512-add-donations-node.ts | 2 +- ...otificationEmails-property-to-all-users.ts | 2 +- ...text_indices_and_unique_keys_for_groups.ts | 2 +- .../20230320130345-fulltext-search-indexes.ts | 2 +- .../20230329150329-article-label-for-posts.ts | 2 +- .../20230608130637-add-postType-property.ts | 2 +- .../20231017141022-fix-event-dates.ts | 2 +- ...20250331130323-author-observes-own-post.ts | 2 +- backend/src/db/neo4j.ts | 3 +- backend/src/db/seed.ts | 6 +- backend/src/helpers/asyncForEach.ts | 2 + backend/src/helpers/jest.ts | 1 + backend/src/helpers/walkRecursive.ts | 2 + backend/src/jwt/decode.ts | 3 +- backend/src/jwt/encode.spec.ts | 2 +- backend/src/jwt/encode.ts | 2 +- .../middleware/hashtags/extractHashtags.ts | 5 +- .../middleware/hashtags/hashtagsMiddleware.ts | 2 +- backend/src/middleware/helpers/cleanHtml.ts | 1 + .../src/middleware/helpers/email/sendMail.ts | 2 +- .../helpers/email/templateBuilder.ts | 1 + .../helpers/email/templates/de/index.ts | 1 + .../helpers/email/templates/en/index.ts | 1 + .../helpers/email/templates/index.ts | 1 + backend/src/middleware/index.ts | 4 +- .../mentions/extractMentionedUsers.ts | 4 +- .../notifications/notificationsMiddleware.ts | 2 + backend/src/middleware/sentryMiddleware.ts | 1 + backend/src/middleware/xssMiddleware.ts | 2 +- backend/src/models/index.ts | 2 + backend/src/schema/resolvers/emails.ts | 1 + .../src/schema/resolvers/embeds/scraper.ts | 5 + .../src/schema/resolvers/helpers/Resolver.ts | 1 + .../resolvers/helpers/databaseLogger.ts | 2 + .../resolvers/helpers/generateInviteCode.ts | 2 +- .../schema/resolvers/helpers/generateNonce.ts | 2 +- backend/src/schema/resolvers/images.ts | 1 + .../schema/resolvers/images/images.spec.ts | 3 + backend/src/schema/resolvers/images/images.ts | 2 + .../src/schema/resolvers/inviteCodes.spec.ts | 3 +- .../schema/resolvers/notifications.spec.ts | 2 +- .../schema/resolvers/passwordReset.spec.ts | 2 +- backend/src/schema/resolvers/passwordReset.ts | 2 +- backend/src/schema/resolvers/reports.spec.ts | 2 +- backend/src/schema/resolvers/statistics.ts | 1 + .../schema/resolvers/user_management.spec.ts | 3 +- .../src/schema/resolvers/users/location.ts | 3 + backend/src/server.ts | 2 + backend/yarn.lock | 21 +- 71 files changed, 353 insertions(+), 279 deletions(-) create mode 100644 backend/.eslintrc.cjs delete mode 100644 backend/.eslintrc.js diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs new file mode 100644 index 000000000..e781e15b7 --- /dev/null +++ b/backend/.eslintrc.cjs @@ -0,0 +1,209 @@ +// eslint-disable-next-line import/no-commonjs +module.exports = { + root: true, + env: { + node: true, + }, + parser: '@typescript-eslint/parser', + plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise', 'security', 'no-catch-all',], + extends: [ + 'standard', + 'eslint:recommended', + 'plugin:prettier/recommended', + 'plugin:import/recommended', + 'plugin:import/typescript', + 'plugin:security/recommended-legacy', + 'plugin:@eslint-community/eslint-comments/recommended', + ], + settings: { + 'import/parsers': { + '@typescript-eslint/parser': ['.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + project: ['./tsconfig.json'], + }, + node: true, + }, + }, + rules: { + 'no-catch-all/no-catch-all': 'error', + 'no-console': 'error', + camelcase: 'error', + 'no-debugger': 'error', + 'prettier/prettier': [ + 'error', + { + htmlWhitespaceSensitivity: 'ignore', + }, + ], + // import + 'import/export': 'error', + // 'import/no-deprecated': 'error', + 'import/no-empty-named-blocks': 'error', + // 'import/no-extraneous-dependencies': 'error', + 'import/no-mutable-exports': 'error', + 'import/no-unused-modules': 'error', + 'import/no-named-as-default': 'error', + 'import/no-named-as-default-member': 'error', + 'import/no-amd': 'error', + 'import/no-commonjs': 'error', + 'import/no-import-module-exports': 'error', + 'import/no-nodejs-modules': 'off', + 'import/unambiguous': 'off', // not compatible with scriptless vue files + 'import/default': 'error', + // 'import/named': 'error', + 'import/namespace': 'error', + 'import/no-absolute-path': 'error', + 'import/no-cycle': 'error', + 'import/no-dynamic-require': 'error', + 'import/no-internal-modules': 'off', + 'import/no-relative-packages': 'error', + // 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], + 'import/no-self-import': 'error', + 'import/no-unresolved': 'error', + 'import/no-useless-path-segments': 'error', + 'import/no-webpack-loader-syntax': 'error', + 'import/consistent-type-specifier-style': 'error', + 'import/exports-last': 'off', + 'import/extensions': 'error', + 'import/first': 'error', + 'import/group-exports': 'off', + 'import/newline-after-import': 'error', + // 'import/no-anonymous-default-export': 'error', + // 'import/no-default-export': 'error', + 'import/no-duplicates': 'error', + 'import/no-named-default': 'error', + 'import/no-namespace': 'error', + 'import/no-unassigned-import': 'error', + // 'import/order': [ + // 'error', + // { + // groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + // 'newlines-between': 'always', + // pathGroups: [ + // { + // pattern: '@?*/**', + // group: 'external', + // position: 'after', + // }, + // { + // pattern: '@/**', + // group: 'external', + // position: 'after', + // }, + // ], + // alphabetize: { + // order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, + // caseInsensitive: true /* ignore case. Options: [true, false] */, + // }, + // distinctGroup: true, + // }, + // ], + 'import/prefer-default-export': 'off', + // n + 'n/handle-callback-err': 'error', + 'n/no-callback-literal': 'error', + 'n/no-exports-assign': 'error', + // 'n/no-extraneous-import': 'error', + 'n/no-extraneous-require': 'error', + 'n/no-hide-core-modules': 'error', + 'n/no-missing-import': 'off', // not compatible with typescript + 'n/no-missing-require': 'error', + 'n/no-new-require': 'error', + 'n/no-path-concat': 'error', + 'n/no-process-exit': 'error', + 'n/no-unpublished-bin': 'error', + 'n/no-unpublished-import': 'off', // TODO need to exclude seeds + 'n/no-unpublished-require': 'error', + 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], + 'n/no-unsupported-features/es-builtins': 'error', + 'n/no-unsupported-features/es-syntax': 'error', + 'n/no-unsupported-features/node-builtins': 'error', + 'n/process-exit-as-throw': 'error', + 'n/shebang': 'error', + //'n/callback-return': 'error', + 'n/exports-style': 'error', + 'n/file-extension-in-import': 'off', + 'n/global-require': 'error', + 'n/no-mixed-requires': 'error', + 'n/no-process-env': 'error', + 'n/no-restricted-import': 'error', + 'n/no-restricted-require': 'error', + // 'n/no-sync': 'error', + 'n/prefer-global/buffer': 'error', + 'n/prefer-global/console': 'error', + 'n/prefer-global/process': 'error', + 'n/prefer-global/text-decoder': 'error', + 'n/prefer-global/text-encoder': 'error', + 'n/prefer-global/url': 'error', + 'n/prefer-global/url-search-params': 'error', + 'n/prefer-promises/dns': 'error', + 'n/prefer-promises/fs': 'error', + // promise + 'promise/catch-or-return': 'error', + 'promise/no-return-wrap': 'error', + 'promise/param-names': 'error', + 'promise/always-return': 'error', + 'promise/no-native': 'off', + 'promise/no-nesting': 'warn', + 'promise/no-promise-in-callback': 'warn', + 'promise/no-callback-in-promise': 'warn', + 'promise/avoid-new': 'warn', + 'promise/no-new-statics': 'error', + 'promise/no-return-in-finally': 'warn', + 'promise/valid-params': 'warn', + 'promise/prefer-await-to-callbacks': 'error', + 'promise/no-multiple-resolved': 'error', + // eslint comments + '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], + '@eslint-community/eslint-comments/no-restricted-disable': 'error', + '@eslint-community/eslint-comments/no-use': 'off', + '@eslint-community/eslint-comments/require-description': 'off', + }, + overrides: [ + // only for ts files + { + files: ['*.ts', '*.tsx'], + extends: [ + // 'plugin:@typescript-eslint/recommended', + // 'plugin:@typescript-eslint/recommended-requiring-type-checking', + // 'plugin:@typescript-eslint/strict', + ], + rules: { + // allow explicitly defined dangling promises + // '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], + 'no-void': ['error', { allowAsStatement: true }], + // ignore prefer-regexp-exec rule to allow string.match(regex) + '@typescript-eslint/prefer-regexp-exec': 'off', + // this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486 + 'import/unambiguous': 'off', + // this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable + '@typescript-eslint/no-unnecessary-condition': 'off', + }, + parserOptions: { + 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, + }, + }, + { + files: ['*.spec.ts'], + plugins: ['jest'], + env: { + jest: true, + }, + rules: { + 'jest/no-disabled-tests': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/valid-expect': 'error', + '@typescript-eslint/unbound-method': 'off', + 'jest/unbound-method': 'error', + }, + }, + ], +}; diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js deleted file mode 100644 index cc5440d82..000000000 --- a/backend/.eslintrc.js +++ /dev/null @@ -1,219 +0,0 @@ -module.exports = { - root: true, - env: { - // es6: true, - node: true, - }, - /* parserOptions: { - parser: 'babel-eslint' - },*/ - parser: '@typescript-eslint/parser', - plugins: ['prettier', '@typescript-eslint' /*, 'import', 'n', 'promise'*/], - extends: [ - 'standard', - // 'eslint:recommended', - 'plugin:prettier/recommended', - // 'plugin:import/recommended', - // 'plugin:import/typescript', - // 'plugin:security/recommended', - // 'plugin:@eslint-community/eslint-comments/recommended', - ], - settings: { - 'import/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx'], - }, - 'import/resolver': { - typescript: { - project: ['./tsconfig.json'], - }, - node: true, - }, - }, - /* rules: { - //'indent': [ 'error', 2 ], - //'quotes': [ "error", "single"], - // 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', - > 'no-console': ['error'], - > 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', - > 'prettier/prettier': ['error'], - }, */ - rules: { - 'no-console': 'error', - camelcase: 'error', - 'no-debugger': 'error', - 'prettier/prettier': [ - 'error', - { - htmlWhitespaceSensitivity: 'ignore', - }, - ], - // import - // 'import/export': 'error', - // 'import/no-deprecated': 'error', - // 'import/no-empty-named-blocks': 'error', - // 'import/no-extraneous-dependencies': 'error', - // 'import/no-mutable-exports': 'error', - // 'import/no-unused-modules': 'error', - // 'import/no-named-as-default': 'error', - // 'import/no-named-as-default-member': 'error', - // 'import/no-amd': 'error', - // 'import/no-commonjs': 'error', - // 'import/no-import-module-exports': 'error', - // 'import/no-nodejs-modules': 'off', - // 'import/unambiguous': 'error', - // 'import/default': 'error', - // 'import/named': 'error', - // 'import/namespace': 'error', - // 'import/no-absolute-path': 'error', - // 'import/no-cycle': 'error', - // 'import/no-dynamic-require': 'error', - // 'import/no-internal-modules': 'off', - // 'import/no-relative-packages': 'error', - // 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], - // 'import/no-self-import': 'error', - // 'import/no-unresolved': 'error', - // 'import/no-useless-path-segments': 'error', - // 'import/no-webpack-loader-syntax': 'error', - // 'import/consistent-type-specifier-style': 'error', - // 'import/exports-last': 'off', - // 'import/extensions': 'error', - // 'import/first': 'error', - // 'import/group-exports': 'off', - // 'import/newline-after-import': 'error', - // 'import/no-anonymous-default-export': 'error', - // 'import/no-default-export': 'error', - // 'import/no-duplicates': 'error', - // 'import/no-named-default': 'error', - // 'import/no-namespace': 'error', - // 'import/no-unassigned-import': 'error', - // 'import/order': [ - // 'error', - // { - // groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], - // 'newlines-between': 'always', - // pathGroups: [ - // { - // pattern: '@?*/**', - // group: 'external', - // position: 'after', - // }, - // { - // pattern: '@/**', - // group: 'external', - // position: 'after', - // }, - // ], - // alphabetize: { - // order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, - // caseInsensitive: true /* ignore case. Options: [true, false] */, - // }, - // distinctGroup: true, - // }, - // ], - // 'import/prefer-default-export': 'off', - // n - // 'n/handle-callback-err': 'error', - // 'n/no-callback-literal': 'error', - // 'n/no-exports-assign': 'error', - // 'n/no-extraneous-import': 'error', - // 'n/no-extraneous-require': 'error', - // 'n/no-hide-core-modules': 'error', - // 'n/no-missing-import': 'off', // not compatible with typescript - // 'n/no-missing-require': 'error', - // 'n/no-new-require': 'error', - // 'n/no-path-concat': 'error', - // 'n/no-process-exit': 'error', - // 'n/no-unpublished-bin': 'error', - // 'n/no-unpublished-import': 'off', // TODO need to exclude seeds - // 'n/no-unpublished-require': 'error', - // 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], - // 'n/no-unsupported-features/es-builtins': 'error', - // 'n/no-unsupported-features/es-syntax': 'error', - // 'n/no-unsupported-features/node-builtins': 'error', - // 'n/process-exit-as-throw': 'error', - // 'n/shebang': 'error', - // 'n/callback-return': 'error', - // 'n/exports-style': 'error', - // 'n/file-extension-in-import': 'off', - // 'n/global-require': 'error', - // 'n/no-mixed-requires': 'error', - // 'n/no-process-env': 'error', - // 'n/no-restricted-import': 'error', - // 'n/no-restricted-require': 'error', - // 'n/no-sync': 'error', - // 'n/prefer-global/buffer': 'error', - // 'n/prefer-global/console': 'error', - // 'n/prefer-global/process': 'error', - // 'n/prefer-global/text-decoder': 'error', - // 'n/prefer-global/text-encoder': 'error', - // 'n/prefer-global/url': 'error', - // 'n/prefer-global/url-search-params': 'error', - // 'n/prefer-promises/dns': 'error', - // 'n/prefer-promises/fs': 'error', - // promise - // 'promise/catch-or-return': 'error', - // 'promise/no-return-wrap': 'error', - // 'promise/param-names': 'error', - // 'promise/always-return': 'error', - // 'promise/no-native': 'off', - // 'promise/no-nesting': 'warn', - // 'promise/no-promise-in-callback': 'warn', - // 'promise/no-callback-in-promise': 'warn', - // 'promise/avoid-new': 'warn', - // 'promise/no-new-statics': 'error', - // 'promise/no-return-in-finally': 'warn', - // 'promise/valid-params': 'warn', - // 'promise/prefer-await-to-callbacks': 'error', - // 'promise/no-multiple-resolved': 'error', - // eslint comments - // '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], - // '@eslint-community/eslint-comments/no-restricted-disable': 'error', - // '@eslint-community/eslint-comments/no-use': 'off', - // '@eslint-community/eslint-comments/require-description': 'off', - }, - overrides: [ - // only for ts files - { - files: ['*.ts', '*.tsx'], - extends: [ - // 'plugin:@typescript-eslint/recommended', - // 'plugin:@typescript-eslint/recommended-requiring-type-checking', - // 'plugin:@typescript-eslint/strict', - ], - rules: { - // allow explicitly defined dangling promises - // '@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }], - 'no-void': ['error', { allowAsStatement: true }], - // ignore prefer-regexp-exec rule to allow string.match(regex) - '@typescript-eslint/prefer-regexp-exec': 'off', - // this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486 - 'import/unambiguous': 'off', - // this is not compatible with typeorm, due to joined tables can be null, but are not defined as nullable - '@typescript-eslint/no-unnecessary-condition': 'off', - }, - parserOptions: { - 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, - }, - }, - { - files: ['*.spec.ts'], - plugins: ['jest'], - env: { - jest: true, - }, - rules: { - 'jest/no-disabled-tests': 'error', - 'jest/no-focused-tests': 'error', - 'jest/no-identical-title': 'error', - 'jest/prefer-to-have-length': 'error', - 'jest/valid-expect': 'error', - '@typescript-eslint/unbound-method': 'off', - // 'jest/unbound-method': 'error', - }, - }, - ], -}; diff --git a/backend/package.json b/backend/package.json index 9c52815a1..58162de29 100644 --- a/backend/package.json +++ b/backend/package.json @@ -95,6 +95,7 @@ "xregexp": "^5.1.2" }, "devDependencies": { + "@eslint-community/eslint-plugin-eslint-comments": "^4.4.1", "@faker-js/faker": "9.6.0", "@types/jest": "^29.5.14", "@types/node": "^22.14.0", @@ -108,6 +109,7 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^16.6.2", + "eslint-plugin-no-catch-all": "^1.1.0", "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^6.1.1", "eslint-plugin-security": "^3.0.1", @@ -121,7 +123,9 @@ }, "resolutions": { "**/**/fs-capacitor": "^6.2.0", - "**/graphql-upload": "^11.0.0", - "nan": "2.17.0" + "**/graphql-upload": "^11.0.0" + }, + "engines": { + "node": ">=20.12.1" } } diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 9f03622a5..e6a02a87d 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,11 +1,14 @@ -import dotenv from 'dotenv' +/* eslint-disable n/no-process-env */ +/* eslint-disable n/no-unpublished-require */ +/* eslint-disable n/no-missing-require */ +import { config } from 'dotenv' import emails from './emails' import metadata from './metadata' // Load env file if (require.resolve) { try { - dotenv.config({ path: require.resolve('../../.env') }) + config({ path: require.resolve('../../.env') }) } catch (error) { // This error is thrown when the .env is not found if (error.code !== 'MODULE_NOT_FOUND') { diff --git a/backend/src/db/clean.ts b/backend/src/db/clean.ts index eac26036c..ae5ce7320 100644 --- a/backend/src/db/clean.ts +++ b/backend/src/db/clean.ts @@ -1,5 +1,6 @@ +/* eslint-disable n/no-process-exit */ import CONFIG from '../config' -import { cleanDatabase } from '../db/factories' +import { cleanDatabase } from './factories' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot clean the database in a non-staging and real production environment!`) @@ -10,6 +11,7 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { await cleanDatabase() console.log('Successfully deleted all nodes and relations!') // eslint-disable-line no-console process.exit(0) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { console.log(`Error occurred deleting the nodes and relations (reset the db)\n\n${err}`) // eslint-disable-line no-console process.exit(1) diff --git a/backend/src/db/compiler.ts b/backend/src/db/compiler.ts index 8b09ac9c3..4dd36f16b 100644 --- a/backend/src/db/compiler.ts +++ b/backend/src/db/compiler.ts @@ -1,2 +1,5 @@ +/* eslint-disable import/no-commonjs */ +// eslint-disable-next-line n/no-unpublished-require const tsNode = require('ts-node') + module.exports = tsNode.register diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index 0c0b63943..b5dd43e16 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -1,4 +1,4 @@ -import { getDriver, getNeode } from '../../db/neo4j' +import { getDriver, getNeode } from '../neo4j' import { hashSync } from 'bcryptjs' import { v4 as uuid } from 'uuid' import { categories } from '../../constants/categories' @@ -30,6 +30,7 @@ const createCategories = async (session) => { try { await createCategoriesTxResultPromise console.log('Successfully created categories!') // eslint-disable-line no-console + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(`Error creating categories: ${error}`) // eslint-disable-line no-console } @@ -44,6 +45,7 @@ const createDefaultAdminUser = async (session) => { try { const userCount = parseInt(String(await readTxResultPromise)) if (userCount === 0) createAdmin = true + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console } @@ -71,6 +73,7 @@ const createDefaultAdminUser = async (session) => { try { await createAdminTxResultPromise console.log('Successfully created default admin user!') // eslint-disable-line no-console + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console } @@ -93,6 +96,7 @@ class Store { // eslint-disable-next-line no-console console.log('Successfully created database indices and constraints!') next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console next(error, null) @@ -122,6 +126,7 @@ class Store { } const [{ title: lastRun }] = migrations next(null, { lastRun, migrations }) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console next(error) @@ -157,6 +162,7 @@ class Store { try { await writeTxResultPromise next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { console.log(error) // eslint-disable-line no-console next(error) @@ -166,4 +172,4 @@ class Store { } } -module.exports = Store +export default Store diff --git a/backend/src/db/migrate/template.ts b/backend/src/db/migrate/template.ts index 72bfc9b1b..9661dcf9c 100644 --- a/backend/src/db/migrate/template.ts +++ b/backend/src/db/migrate/template.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = '' 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 7d98d9dcc..6eb9e0ed0 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 @@ -1,7 +1,8 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators' import { getDriver } from '../neo4j' -import normalizeEmail from '../../schema/resolvers//helpers/normalizeEmail' +import normalizeEmail from '../../schema/resolvers/helpers/normalizeEmail' export const description = ` This migration merges duplicate :User and :EmailAddress nodes. It became diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index 10b77c6dd..23d1d55bc 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -1,3 +1,4 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError } from 'rxjs/operators' import { getDriver } from '../neo4j' diff --git a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts index ce46be9d6..49506aae3 100644 --- a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts +++ b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration creates a MUTED relationship between two edges(:User) that have a pre-existing BLOCKED relationship. @@ -21,6 +21,7 @@ export async function up(next) { `, ) await transaction.commit() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (error) { // eslint-disable-next-line no-console console.log(error) @@ -38,6 +39,7 @@ export function down(next) { try { // Rollback your migration here. next() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { next(err) } finally { diff --git a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts index 94a2f442d..73c329bfc 100644 --- a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts +++ b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration swaps the value stored in Location.lat with the value diff --git a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts index ffcd3d4b6..8ef6976a3 100644 --- a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts +++ b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'This migration adds a fulltext index for the tags in order to search for Hasthags.' diff --git a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts index 4582d938c..e949713b8 100644 --- a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts +++ b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` We introduced a new node label 'Image' and we need a primary key for it. Best @@ -48,6 +48,7 @@ export async function down(next) { `) 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) 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 index 356004237..7818001fb 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -1,4 +1,5 @@ -import { getDriver } from '../../db/neo4j' +/* eslint-disable security/detect-non-literal-fs-filename */ +import { getDriver } from '../neo4j' import { existsSync, createReadStream } from 'fs' import path from 'path' import { S3 } from 'aws-sdk' @@ -95,6 +96,7 @@ export async function down(next) { 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) diff --git a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts index 1ad5e645d..6f347b99b 100644 --- a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts +++ b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` Refactor all our image properties on posts and users to a dedicated type diff --git a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts index e4852f79c..a8880d8e8 100644 --- a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'We should not maintain obsolete attributes for users who have been deleted.' diff --git a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts index 8c1efe5c6..70d81e5c0 100644 --- a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'We should not maintain obsolete attributes for posts which have been deleted.' diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index a77ac360c..9d0d44f26 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -1,4 +1,5 @@ -import { getDriver } from '../../db/neo4j' +/* eslint-disable security/detect-non-literal-fs-filename */ +import { getDriver } from '../neo4j' import { existsSync } from 'fs' export const description = ` diff --git a/backend/src/db/migrations/1613589876420-null_mutation.ts b/backend/src/db/migrations/1613589876420-null_mutation.ts index f158549de..8efe667be 100644 --- a/backend/src/db/migrations/1613589876420-null_mutation.ts +++ b/backend/src/db/migrations/1613589876420-null_mutation.ts @@ -1,9 +1,9 @@ 'use strict' -module.exports.up = function (next) { +export async function up(next) { next() } -module.exports.down = function (next) { +export async function down(next) { next() } diff --git a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts index ff95a25df..0d8f28e1b 100644 --- a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts +++ b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts @@ -1,10 +1,10 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration adds the clickedCount property to all posts, setting it to 0. ` -module.exports.up = async function (next) { +export async function up(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() @@ -28,7 +28,7 @@ module.exports.up = async function (next) { } } -module.exports.down = async function (next) { +export async function down(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() diff --git a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts index ee1fad124..31b9d69ff 100644 --- a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts +++ b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts @@ -1,10 +1,10 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` This migration adds the viewedTeaserCount property to all posts, setting it to 0. ` -module.exports.up = async function (next) { +export async function up(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() @@ -28,7 +28,7 @@ module.exports.up = async function (next) { } } -module.exports.down = async function (next) { +export async function down(next) { const driver = getDriver() const session = driver.session() const transaction = session.beginTransaction() diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.ts b/backend/src/db/migrations/20210506150512-add-donations-node.ts index 6cbc1e897..b7e0e026a 100644 --- a/backend/src/db/migrations/20210506150512-add-donations-node.ts +++ b/backend/src/db/migrations/20210506150512-add-donations-node.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' import { v4 as uuid } from 'uuid' export const description = diff --git a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts index 0d1f4fb91..a555efa3a 100644 --- a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts +++ b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = '' diff --git a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts index 63e40c72b..586a090f4 100644 --- a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts +++ b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` We introduced a new node label 'Group' and we need two primary keys 'id' and 'slug' for it. diff --git a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts index 40ebc6c2e..34cf7b7a2 100644 --- a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts +++ b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = '' diff --git a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts index 3cf435203..2ca705bf4 100644 --- a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts +++ b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'Add to all existing posts the Article label' diff --git a/backend/src/db/migrations/20230608130637-add-postType-property.ts b/backend/src/db/migrations/20230608130637-add-postType-property.ts index 433577715..83c2f4ed3 100644 --- a/backend/src/db/migrations/20230608130637-add-postType-property.ts +++ b/backend/src/db/migrations/20230608130637-add-postType-property.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = 'Add postType property Article to all posts' diff --git a/backend/src/db/migrations/20231017141022-fix-event-dates.ts b/backend/src/db/migrations/20231017141022-fix-event-dates.ts index 3c4302f13..e793e173c 100644 --- a/backend/src/db/migrations/20231017141022-fix-event-dates.ts +++ b/backend/src/db/migrations/20231017141022-fix-event-dates.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` Transform event start and end date of format 'YYYY-MM-DD HH:MM:SS' in CEST diff --git a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts index 7343d5010..026f7f29c 100644 --- a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts +++ b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../../db/neo4j' +import { getDriver } from '../neo4j' export const description = ` All authors observe their posts. diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index 78b52237e..dc5bf2764 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,5 +1,6 @@ +/* eslint-disable import/no-named-as-default-member */ import neo4j from 'neo4j-driver' -import CONFIG from './../config' +import CONFIG from '../config' import Neode from 'neode' import models from '../models' diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index bff236f64..4183b8ce5 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -1,10 +1,11 @@ +/* eslint-disable n/no-process-exit */ import sample from 'lodash/sample' import { createTestClient } from 'apollo-server-testing' import CONFIG from '../config' import createServer from '../server' import { faker } from '@faker-js/faker' -import Factory from '../db/factories' -import { getNeode, getDriver } from '../db/neo4j' +import Factory from './factories' +import { getNeode, getDriver } from './neo4j' import { createGroupMutation, joinGroupMutation, @@ -1565,6 +1566,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] await driver.close() await neode.close() process.exit(0) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { /* eslint-disable-next-line no-console */ console.error(err) diff --git a/backend/src/helpers/asyncForEach.ts b/backend/src/helpers/asyncForEach.ts index 5577cce14..00b0f85a3 100644 --- a/backend/src/helpers/asyncForEach.ts +++ b/backend/src/helpers/asyncForEach.ts @@ -1,3 +1,5 @@ +/* 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 * diff --git a/backend/src/helpers/jest.ts b/backend/src/helpers/jest.ts index 09744e9f2..f1a0deb15 100644 --- a/backend/src/helpers/jest.ts +++ b/backend/src/helpers/jest.ts @@ -1,3 +1,4 @@ +/* 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/ diff --git a/backend/src/helpers/walkRecursive.ts b/backend/src/helpers/walkRecursive.ts index f3be67575..4937f61bb 100644 --- a/backend/src/helpers/walkRecursive.ts +++ b/backend/src/helpers/walkRecursive.ts @@ -1,3 +1,5 @@ +/* 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 diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts index e02dcc8d4..45888dead 100644 --- a/backend/src/jwt/decode.ts +++ b/backend/src/jwt/decode.ts @@ -1,5 +1,5 @@ import jwt from 'jsonwebtoken' -import CONFIG from './../config' +import CONFIG from '../config' export default async (driver, authorizationHeader) => { if (!authorizationHeader) return null @@ -8,6 +8,7 @@ export default async (driver, authorizationHeader) => { try { const decoded = await jwt.verify(token, CONFIG.JWT_SECRET) id = decoded.sub + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { return null } diff --git a/backend/src/jwt/encode.spec.ts b/backend/src/jwt/encode.spec.ts index 21ebdffec..37775eb55 100644 --- a/backend/src/jwt/encode.spec.ts +++ b/backend/src/jwt/encode.spec.ts @@ -1,6 +1,6 @@ import encode from './encode' import jwt from 'jsonwebtoken' -import CONFIG from './../config' +import CONFIG from '../config' describe('encode', () => { let payload diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts index baeb62d3d..0df81fa02 100644 --- a/backend/src/jwt/encode.ts +++ b/backend/src/jwt/encode.ts @@ -1,5 +1,5 @@ import jwt from 'jsonwebtoken' -import CONFIG from './../config' +import CONFIG from '../config' // Generate an Access Token for the given User ID export default function encode(user) { diff --git a/backend/src/middleware/hashtags/extractHashtags.ts b/backend/src/middleware/hashtags/extractHashtags.ts index 670673bf4..fc7a93d17 100644 --- a/backend/src/middleware/hashtags/extractHashtags.ts +++ b/backend/src/middleware/hashtags/extractHashtags.ts @@ -1,4 +1,5 @@ -import * as cheerio from 'cheerio' +import { load } from 'cheerio' +// eslint-disable-next-line import/extensions import { exec, build } from 'xregexp/xregexp-all.js' // formats of a Hashtag: // https://en.wikipedia.org/w/index.php?title=Hashtag&oldid=905141980#Style @@ -10,7 +11,7 @@ const regX = build('^((\\pL+[\\pL0-9]*)|([0-9]+\\pL+[\\pL0-9]*))$') export default function (content?) { if (!content) return [] - const $ = cheerio.load(content) + const $ = load(content) // We can not search for class '.hashtag', because the classes are removed at the 'xss' middleware. // But we have to know, which Hashtags are removed from the content as well, so we search for the 'a' html-tag. const ids = $('a[data-hashtag-id]') diff --git a/backend/src/middleware/hashtags/hashtagsMiddleware.ts b/backend/src/middleware/hashtags/hashtagsMiddleware.ts index 985cd3c92..76939d59d 100644 --- a/backend/src/middleware/hashtags/hashtagsMiddleware.ts +++ b/backend/src/middleware/hashtags/hashtagsMiddleware.ts @@ -1,4 +1,4 @@ -import extractHashtags from '../hashtags/extractHashtags' +import extractHashtags from './extractHashtags' const updateHashtagsOfPost = async (postId, hashtags, context) => { if (!hashtags.length) return diff --git a/backend/src/middleware/helpers/cleanHtml.ts b/backend/src/middleware/helpers/cleanHtml.ts index 72129274c..04d6deae4 100644 --- a/backend/src/middleware/helpers/cleanHtml.ts +++ b/backend/src/middleware/helpers/cleanHtml.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-unsafe-regex */ import sanitizeHtml from 'sanitize-html' import linkifyHtml from 'linkify-html' diff --git a/backend/src/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts index c0e54e7f7..6c1e0d8ba 100644 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ b/backend/src/middleware/helpers/email/sendMail.ts @@ -1,5 +1,5 @@ import CONFIG from '../../../config' -import { cleanHtml } from '../../../middleware/helpers/cleanHtml' +import { cleanHtml } from '../cleanHtml' import nodemailer from 'nodemailer' import { htmlToText } from 'nodemailer-html-to-text' diff --git a/backend/src/middleware/helpers/email/templateBuilder.ts b/backend/src/middleware/helpers/email/templateBuilder.ts index 431048336..398cbabf9 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-namespace */ import mustache from 'mustache' import CONFIG from '../../../config' import metadata from '../../../config/metadata' diff --git a/backend/src/middleware/helpers/email/templates/de/index.ts b/backend/src/middleware/helpers/email/templates/de/index.ts index 0f9d13c36..f29e2c485 100644 --- a/backend/src/middleware/helpers/email/templates/de/index.ts +++ b/backend/src/middleware/helpers/email/templates/de/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ import fs from 'fs' import path from 'path' diff --git a/backend/src/middleware/helpers/email/templates/en/index.ts b/backend/src/middleware/helpers/email/templates/en/index.ts index 0f9d13c36..f29e2c485 100644 --- a/backend/src/middleware/helpers/email/templates/en/index.ts +++ b/backend/src/middleware/helpers/email/templates/en/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ import fs from 'fs' import path from 'path' diff --git a/backend/src/middleware/helpers/email/templates/index.ts b/backend/src/middleware/helpers/email/templates/index.ts index 9c32c6d3e..bcb5c2b64 100644 --- a/backend/src/middleware/helpers/email/templates/index.ts +++ b/backend/src/middleware/helpers/email/templates/index.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-non-literal-fs-filename */ import fs from 'fs' import path from 'path' diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 08c872db7..8eca3c8e8 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -1,5 +1,6 @@ +/* eslint-disable security/detect-object-injection */ import { applyMiddleware } from 'graphql-middleware' -import CONFIG from './../config' +import CONFIG from '../config' import softDelete from './softDelete/softDeleteMiddleware' import sluggify from './sluggifyMiddleware' import excerpt from './excerptMiddleware' @@ -8,6 +9,7 @@ import permissions from './permissionsMiddleware' import includedFields from './includedFieldsMiddleware' import orderBy from './orderByMiddleware' import validation from './validation/validationMiddleware' +// eslint-disable-next-line import/no-cycle import notifications from './notifications/notificationsMiddleware' import hashtags from './hashtags/hashtagsMiddleware' import login from './login/loginMiddleware' diff --git a/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts b/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts index ff80bb77a..b7dc0fed1 100644 --- a/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts +++ b/backend/src/middleware/notifications/mentions/extractMentionedUsers.ts @@ -1,8 +1,8 @@ -import * as cheerio from 'cheerio' +import { load } from 'cheerio' export default (content?) => { if (!content) return [] - const $ = cheerio.load(content) + const $ = load(content) const userIds = $('a.mention[data-mention-id]') .map((_, el) => { return $(el).attr('data-mention-id') diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 7ecbf8181..09212a29d 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,3 +1,5 @@ +/* eslint-disable security/detect-object-injection */ +// eslint-disable-next-line import/no-cycle import { pubsub, NOTIFICATION_ADDED } from '../../server' import extractMentionedUsers from './mentions/extractMentionedUsers' import { validateNotifyUsers } from '../validation/validationMiddleware' diff --git a/backend/src/middleware/sentryMiddleware.ts b/backend/src/middleware/sentryMiddleware.ts index 73f393eef..ace2c4eeb 100644 --- a/backend/src/middleware/sentryMiddleware.ts +++ b/backend/src/middleware/sentryMiddleware.ts @@ -1,6 +1,7 @@ import { sentry } from 'graphql-middleware-sentry' import CONFIG from '../config' +// eslint-disable-next-line import/no-mutable-exports let sentryMiddleware: any = (resolve, root, args, context, resolveInfo) => resolve(root, args, context, resolveInfo) diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index c10997e8d..7b1b66145 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -1,5 +1,5 @@ import walkRecursive from '../helpers/walkRecursive' -import { cleanHtml } from '../middleware/helpers/cleanHtml' +import { cleanHtml } from './helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized const fields = [ diff --git a/backend/src/models/index.ts b/backend/src/models/index.ts index f7d338684..e02cbc242 100644 --- a/backend/src/models/index.ts +++ b/backend/src/models/index.ts @@ -1,3 +1,5 @@ +/* eslint-disable n/no-missing-require */ +/* eslint-disable n/global-require */ // NOTE: We cannot use `fs` here to clean up the code. Cypress breaks on any npm // module that is not browser-compatible. Node's `fs` module is server-side only declare let Cypress: any | undefined diff --git a/backend/src/schema/resolvers/emails.ts b/backend/src/schema/resolvers/emails.ts index d705781ca..ff37948f2 100644 --- a/backend/src/schema/resolvers/emails.ts +++ b/backend/src/schema/resolvers/emails.ts @@ -2,6 +2,7 @@ import generateNonce from './helpers/generateNonce' import Resolver from './helpers/Resolver' import existingEmailAddress from './helpers/existingEmailAddress' import { UserInputError } from 'apollo-server' +// eslint-disable-next-line import/extensions import Validator from 'neode/build/Services/Validator.js' import normalizeEmail from './helpers/normalizeEmail' diff --git a/backend/src/schema/resolvers/embeds/scraper.ts b/backend/src/schema/resolvers/embeds/scraper.ts index 79dd5a368..afc2b1df6 100644 --- a/backend/src/schema/resolvers/embeds/scraper.ts +++ b/backend/src/schema/resolvers/embeds/scraper.ts @@ -1,3 +1,7 @@ +/* eslint-disable n/no-extraneous-require */ +/* eslint-disable n/global-require */ +/* eslint-disable import/no-commonjs */ +/* eslint-disable import/no-named-as-default */ import Metascraper from 'metascraper' import fetch from 'node-fetch' @@ -37,6 +41,7 @@ const fetchEmbed = async (url) => { try { const response = await fetch(endpointUrl) json = await response.json() + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { error(`Error fetching embed data: ${err.message}`) return {} diff --git a/backend/src/schema/resolvers/helpers/Resolver.ts b/backend/src/schema/resolvers/helpers/Resolver.ts index 58d1512d7..a21893f7d 100644 --- a/backend/src/schema/resolvers/helpers/Resolver.ts +++ b/backend/src/schema/resolvers/helpers/Resolver.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-object-injection */ import log from './databaseLogger' export const undefinedToNullResolver = (list) => { diff --git a/backend/src/schema/resolvers/helpers/databaseLogger.ts b/backend/src/schema/resolvers/helpers/databaseLogger.ts index fac1a5c4a..98544087b 100644 --- a/backend/src/schema/resolvers/helpers/databaseLogger.ts +++ b/backend/src/schema/resolvers/helpers/databaseLogger.ts @@ -1,4 +1,6 @@ +/* eslint-disable import/no-named-as-default */ import Debug from 'debug' + const debugCypher = Debug('human-connection:neo4j:cypher') const debugStats = Debug('human-connection:neo4j:stats') diff --git a/backend/src/schema/resolvers/helpers/generateInviteCode.ts b/backend/src/schema/resolvers/helpers/generateInviteCode.ts index 5a123ff88..e3f555931 100644 --- a/backend/src/schema/resolvers/helpers/generateInviteCode.ts +++ b/backend/src/schema/resolvers/helpers/generateInviteCode.ts @@ -1,4 +1,4 @@ -import CONSTANTS_REGISTRATION from './../../../constants/registration' +import CONSTANTS_REGISTRATION from '../../../constants/registration' export default function generateInviteCode() { // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) diff --git a/backend/src/schema/resolvers/helpers/generateNonce.ts b/backend/src/schema/resolvers/helpers/generateNonce.ts index f08b3ccd6..de1294567 100644 --- a/backend/src/schema/resolvers/helpers/generateNonce.ts +++ b/backend/src/schema/resolvers/helpers/generateNonce.ts @@ -1,4 +1,4 @@ -import CONSTANTS_REGISTRATION from './../../../constants/registration' +import CONSTANTS_REGISTRATION from '../../../constants/registration' // TODO: why this is not used in resolver 'requestPasswordReset'? export default function generateNonce() { diff --git a/backend/src/schema/resolvers/images.ts b/backend/src/schema/resolvers/images.ts index 111f84888..ea596a183 100644 --- a/backend/src/schema/resolvers/images.ts +++ b/backend/src/schema/resolvers/images.ts @@ -1,4 +1,5 @@ import Resolver from './helpers/Resolver' + export default { Image: { ...Resolver('Image', { diff --git a/backend/src/schema/resolvers/images/images.spec.ts b/backend/src/schema/resolvers/images/images.spec.ts index d46972ce0..94602ccd8 100644 --- a/backend/src/schema/resolvers/images/images.spec.ts +++ b/backend/src/schema/resolvers/images/images.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import { deleteImage, mergeImage } from './images' import { getNeode, getDriver } from '../../../db/neo4j' import Factory, { cleanDatabase } from '../../../db/factories' @@ -90,6 +91,7 @@ describe('deleteImage', () => { }) throw new Error('Ouch!') }) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { // nothing has been deleted await expect(neode.all('Image')).resolves.toHaveLength(1) @@ -251,6 +253,7 @@ describe('mergeImage', () => { }) return transaction.run('Ooops invalid cypher!', { image }) }) + // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { // nothing has been created await expect(neode.all('Image')).resolves.toHaveLength(0) diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index b99b13a10..4566aa5bf 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -1,3 +1,5 @@ +/* eslint-disable promise/avoid-new */ +/* eslint-disable security/detect-non-literal-fs-filename */ import path from 'path' import { v4 as uuid } from 'uuid' import { S3 } from 'aws-sdk' diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/schema/resolvers/inviteCodes.spec.ts index 1df791ba6..bd6a55bc8 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.ts +++ b/backend/src/schema/resolvers/inviteCodes.spec.ts @@ -1,9 +1,10 @@ +/* eslint-disable security/detect-non-literal-regexp */ import Factory, { cleanDatabase } from '../../db/factories' import { getDriver } from '../../db/neo4j' import gql from 'graphql-tag' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' -import CONSTANTS_REGISTRATION from './../../constants/registration' +import CONSTANTS_REGISTRATION from '../../constants/registration' let user let query diff --git a/backend/src/schema/resolvers/notifications.spec.ts b/backend/src/schema/resolvers/notifications.spec.ts index 58757c92d..e3bcb9489 100644 --- a/backend/src/schema/resolvers/notifications.spec.ts +++ b/backend/src/schema/resolvers/notifications.spec.ts @@ -2,7 +2,7 @@ import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { getDriver } from '../../db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '../.././server' +import createServer from '../../server' import { markAsReadMutation, markAllAsReadMutation, diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/schema/resolvers/passwordReset.spec.ts index 3d17ff481..d0ca3e4a8 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/schema/resolvers/passwordReset.spec.ts @@ -1,7 +1,7 @@ import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { getNeode, getDriver } from '../../db/neo4j' -import CONSTANTS_REGISTRATION from './../../constants/registration' +import CONSTANTS_REGISTRATION from '../../constants/registration' import createPasswordReset from './helpers/createPasswordReset' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index 6fea020dd..4adca11d3 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from 'uuid' import bcrypt from 'bcryptjs' -import CONSTANTS_REGISTRATION from './../../constants/registration' +import CONSTANTS_REGISTRATION from '../../constants/registration' import createPasswordReset from './helpers/createPasswordReset' export default { diff --git a/backend/src/schema/resolvers/reports.spec.ts b/backend/src/schema/resolvers/reports.spec.ts index bc47778c1..2e6b4d302 100644 --- a/backend/src/schema/resolvers/reports.spec.ts +++ b/backend/src/schema/resolvers/reports.spec.ts @@ -1,5 +1,5 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '../.././server' +import createServer from '../../server' import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { getDriver, getNeode } from '../../db/neo4j' diff --git a/backend/src/schema/resolvers/statistics.ts b/backend/src/schema/resolvers/statistics.ts index b454ce8f4..6bf73b0b2 100644 --- a/backend/src/schema/resolvers/statistics.ts +++ b/backend/src/schema/resolvers/statistics.ts @@ -1,3 +1,4 @@ +/* eslint-disable security/detect-object-injection */ import log from './helpers/databaseLogger' export default { diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/schema/resolvers/user_management.spec.ts index 546c7a748..797f08126 100644 --- a/backend/src/schema/resolvers/user_management.spec.ts +++ b/backend/src/schema/resolvers/user_management.spec.ts @@ -1,5 +1,6 @@ +/* eslint-disable promise/prefer-await-to-callbacks */ import jwt from 'jsonwebtoken' -import CONFIG from './../../config' +import CONFIG from '../../config' import Factory, { cleanDatabase } from '../../db/factories' import gql from 'graphql-tag' import { loginMutation } from '../../graphql/userManagement' diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/schema/resolvers/users/location.ts index 0c3f55595..9a8b5430b 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/schema/resolvers/users/location.ts @@ -1,3 +1,6 @@ +/* eslint-disable promise/avoid-new */ +/* eslint-disable promise/prefer-await-to-callbacks */ +/* eslint-disable import/no-named-as-default */ import request from 'request' import { UserInputError } from 'apollo-server' import Debug from 'debug' diff --git a/backend/src/server.ts b/backend/src/server.ts index 0522f5fc8..7451e3e4a 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,8 +1,10 @@ +/* eslint-disable import/no-named-as-default-member */ import express from 'express' import http from 'http' import helmet from 'helmet' import { ApolloServer } from 'apollo-server-express' import CONFIG from './config' +// eslint-disable-next-line import/no-cycle import middleware from './middleware' import { getNeode, getDriver } from './db/neo4j' import decode from './jwt/decode' diff --git a/backend/yarn.lock b/backend/yarn.lock index ab611aea5..a77d4d7d3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1091,6 +1091,14 @@ dependencies: tslib "^2.4.0" +"@eslint-community/eslint-plugin-eslint-comments@^4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.4.1.tgz#dbfab6f2447c22be8758a0a9a9c80e56d2e2b93f" + integrity sha512-lb/Z/MzbTf7CaVYM9WCFNQZ4L1yi3ev2fsFPF99h31ljhSEyUoyEsKsNWiU+qD1glbYTDJdqgyaLKtyTkkqtuQ== + dependencies: + escape-string-regexp "^4.0.0" + ignore "^5.2.4" + "@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -4606,6 +4614,11 @@ eslint-plugin-n@^16.6.2: resolve "^1.22.2" semver "^7.5.3" +eslint-plugin-no-catch-all@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-no-catch-all/-/eslint-plugin-no-catch-all-1.1.0.tgz#f2e8950cc2b0bdde5faa4ab339d0986c6ae32fb0" + integrity sha512-VkP62jLTmccPrFGN/W6V7a3SEwdtTZm+Su2k4T3uyJirtkm0OMMm97h7qd8pRFAHus/jQg9FpUpLRc7sAylBEQ== + eslint-plugin-prettier@^5.2.6: version "5.2.6" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz#be39e3bb23bb3eeb7e7df0927cdb46e4d7945096" @@ -7534,10 +7547,10 @@ n-gram@^1.0.0: resolved "https://registry.yarnpkg.com/n-gram/-/n-gram-1.1.1.tgz#a374dc176a9063a2388d1be18ed7c35828be2a97" integrity sha512-qibRqvUghLIVsq+RTwVuwOzgOxf0l4DDZKVYAK0bMam5sG9ZzaJ6BUSJyG2Td8kTc7c/HcMUtjiN5ShobZA2bA== -nan@2.17.0, nan@^2.20.0: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nan@^2.20.0: + version "2.22.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" + integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== nanoid@^3.3.6: version "3.3.7" From f01d2b43e2627bea8a09b846ec50d3c8a8a77953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 11:39:44 +0000 Subject: [PATCH 080/266] build(deps): bump mime-types from 2.1.35 to 3.0.1 in /backend (#8298) Bumps [mime-types](https://github.com/jshttp/mime-types) from 2.1.35 to 3.0.1. - [Release notes](https://github.com/jshttp/mime-types/releases) - [Changelog](https://github.com/jshttp/mime-types/blob/master/HISTORY.md) - [Commits](https://github.com/jshttp/mime-types/compare/2.1.35...v3.0.1) --- updated-dependencies: - dependency-name: mime-types dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/package.json b/backend/package.json index 58162de29..2cff584d2 100644 --- a/backend/package.json +++ b/backend/package.json @@ -76,7 +76,7 @@ "metascraper-video": "^5.46.11", "metascraper-youtube": "^5.46.11", "migrate": "^2.1.0", - "mime-types": "^2.1.35", + "mime-types": "^3.0.1", "minimatch": "^9.0.4", "mustache": "^4.2.0", "neo4j-driver": "^4.4.11", diff --git a/backend/yarn.lock b/backend/yarn.lock index a77d4d7d3..41a80a121 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -7366,29 +7366,29 @@ migrate@^2.1.0: mkdirp "^3.0.1" slug "^8.2.2" -mime-db@1.43.0: - version "1.43.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.43.0.tgz#0a12e0502650e473d735535050e7c8f4eb4fae58" - integrity sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.35, mime-types@~2.1.19, mime-types@~2.1.34: +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + +mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.22, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime-types@~2.1.22, mime-types@~2.1.24: - version "2.1.26" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.26.tgz#9c921fc09b7e149a65dfdc0da4d20997200b0a06" - integrity sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ== +mime-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" + integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== dependencies: - mime-db "1.43.0" + mime-db "^1.54.0" mime@1.6.0: version "1.6.0" From 666d2b67d7bf16e244c9742ecdefd2d0c6d25713 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 02:14:56 +0200 Subject: [PATCH 081/266] build(deps): bump express from 4.21.2 to 5.1.0 in /backend (#8322) Bumps [express](https://github.com/expressjs/express) from 4.21.2 to 5.1.0. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.2...v5.1.0) --- updated-dependencies: - dependency-name: express dependency-version: 5.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 274 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 254 insertions(+), 22 deletions(-) diff --git a/backend/package.json b/backend/package.json index 2cff584d2..1949c23af 100644 --- a/backend/package.json +++ b/backend/package.json @@ -46,7 +46,7 @@ "cors": "~2.8.5", "cross-env": "~7.0.3", "dotenv": "~16.4.7", - "express": "^4.21.2", + "express": "^5.1.0", "graphql": "^14.6.0", "graphql-middleware": "~4.0.2", "graphql-middleware-sentry": "^3.2.1", diff --git a/backend/yarn.lock b/backend/yarn.lock index 41a80a121..76418da2c 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2387,6 +2387,14 @@ accepts@^1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -3167,6 +3175,21 @@ body-parser@1.20.3, body-parser@^1.18.3: type-is "~1.6.18" unpipe "1.0.0" +body-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" + integrity sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.0" + http-errors "^2.0.0" + iconv-lite "^0.6.3" + on-finished "^2.4.1" + qs "^6.14.0" + raw-body "^3.0.0" + type-is "^2.0.0" + boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -3274,7 +3297,7 @@ busboy@^0.3.1: dependencies: dicer "0.3.0" -bytes@3.1.2: +bytes@3.1.2, bytes@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -3351,6 +3374,14 @@ call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -3610,7 +3641,14 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-type@~1.0.4, content-type@~1.0.5: +content-disposition@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2" + integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -3625,6 +3663,11 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + cookie@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" @@ -3635,6 +3678,11 @@ cookie@^0.3.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= +cookie@^0.7.1: + version "0.7.2" + 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" @@ -3848,7 +3896,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.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.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== @@ -3956,7 +4004,7 @@ denque@^2.1.0: resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== -depd@2.0.0: +depd@2.0.0, depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -4160,16 +4208,16 @@ emoji-regex@^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" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - encoding-sniffer@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" @@ -4493,7 +4541,7 @@ escalade@^3.1.2, escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +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= @@ -4762,7 +4810,7 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== -etag@~1.8.1: +etag@^1.8.1, etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= @@ -4834,7 +4882,7 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.2.tgz#a8f26adb96bf78e8cd8ad1037928d5e5c0679d91" integrity sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA== -express@^4.0.0, express@^4.17.1, express@^4.21.2: +express@^4.0.0, express@^4.17.1: version "4.21.2" resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== @@ -4871,6 +4919,39 @@ express@^4.0.0, express@^4.17.1, express@^4.21.2: utils-merge "1.0.1" vary "~1.1.2" +express@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/express/-/express-5.1.0.tgz#d31beaf715a0016f0d53f47d3b4d7acf28c75cc9" + integrity sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.0" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + ext@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" @@ -4992,6 +5073,18 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" +finalhandler@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f" + integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + 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" @@ -5107,6 +5200,11 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + fs-capacitor@^6.1.0, fs-capacitor@^6.2.0: version "6.2.0" resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-6.2.0.tgz#fa79ac6576629163cb84561995602d8999afb7f5" @@ -5212,7 +5310,7 @@ get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: has-symbols "^1.0.3" hasown "^2.0.0" -get-intrinsic@^1.2.6: +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -5706,7 +5804,7 @@ http-cache-semantics@^4.1.1: resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-errors@2.0.0: +http-errors@2.0.0, http-errors@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== @@ -6134,6 +6232,11 @@ is-promise@^2.2.2: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== +is-promise@^4.0.0: + version "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" @@ -7176,6 +7279,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + memoize-one@~6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045" @@ -7200,6 +7308,11 @@ merge-descriptors@1.0.3: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-graphql-schemas@^1.7.8: version "1.7.8" resolved "https://registry.yarnpkg.com/merge-graphql-schemas/-/merge-graphql-schemas-1.7.8.tgz#11a0a672a38a61d988c09ffdebe1bd4f8418de48" @@ -7383,7 +7496,7 @@ mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.22, mime-types@~2.1.24, dependencies: mime-db "1.52.0" -mime-types@^3.0.1: +mime-types@^3.0.0, mime-types@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== @@ -7572,6 +7685,11 @@ negotiator@0.6.3, negotiator@^0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + neo4j-driver-bolt-connection@4.4.11: version "4.4.11" resolved "https://registry.yarnpkg.com/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-4.4.11.tgz#aeaee9faa620e6309698b4cedf5b354d8898ea05" @@ -7812,6 +7930,11 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-inspect@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" @@ -7906,7 +8029,7 @@ object.values@^1.2.0: define-properties "^1.2.1" es-object-atoms "^1.0.0" -on-finished@2.4.1: +on-finished@2.4.1, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -8091,7 +8214,7 @@ parse5@^7.2.1: dependencies: entities "^4.5.0" -parseurl@^1.3.2, parseurl@~1.3.3: +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" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -8147,6 +8270,11 @@ path-to-regexp@0.1.12: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== +path-to-regexp@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" + integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -8284,7 +8412,7 @@ property-expr@^2.0.0: resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330" integrity sha512-bc/5ggaYZxNkFKj374aLbEDqVADdYaLcFo8XBkishUWbaAdjlphaBFns9TvRA2pUseVL/wMFmui9X3IdNDU37g== -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -8347,6 +8475,13 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" +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== + dependencies: + side-channel "^1.1.0" + qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -8362,7 +8497,7 @@ quick-lru@^5.1.1: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== -range-parser@~1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== @@ -8377,6 +8512,16 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f" + integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.6.3" + unpipe "1.0.0" + re2@~1.21.4: version "1.21.4" resolved "https://registry.yarnpkg.com/re2/-/re2-1.21.4.tgz#d688edcc40da3cf542ee3a480a8b60e5900dd24d" @@ -8637,6 +8782,17 @@ rosie@^2.1.1: resolved "https://registry.yarnpkg.com/rosie/-/rosie-2.1.1.tgz#f8c9b8145d581d19fb1c933cf6ac1c554ad68798" integrity sha512-2AXB7WrIZXtKMZ6Q/PlozqPF5nu/x7NEvRJZOblrJuprrPfm5gL8JVvJPj9aaib9F8IUALnLUFhzXrwEtnI5cQ== +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + rrweb-cssom@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" @@ -8772,6 +8928,23 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@^1.1.0, send@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" + integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== + dependencies: + debug "^4.3.5" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.0" + mime-types "^3.0.1" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.1" + serve-static@1.16.2: version "1.16.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.2.tgz#b6a5343da47f6bdd2673848bf45754941e803296" @@ -8782,6 +8955,16 @@ serve-static@1.16.2: parseurl "~1.3.3" send "0.19.0" +serve-static@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" + integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + set-function-length@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" @@ -8872,6 +9055,35 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -8891,6 +9103,17 @@ side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + signal-exit@^3.0.0, signal-exit@^3.0.3, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -9045,7 +9268,7 @@ standard-as-callback@^2.1.0: resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== -statuses@2.0.1: +statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== @@ -9599,6 +9822,15 @@ type-is@^1.6.16, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type-is@^2.0.0, type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + type@^2.7.2: version "2.7.3" resolved "https://registry.yarnpkg.com/type/-/type-2.7.3.tgz#436981652129285cc3ba94f392886c2637ea0486" @@ -9918,7 +10150,7 @@ validator@^13.15.0: resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" integrity sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA== -vary@^1, vary@~1.1.2: +vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= From be0de5b7619374043a4184c9cba18cfb46cabb0c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 00:53:47 +0000 Subject: [PATCH 082/266] build(deps): bump express from 4.21.2 to 5.1.0 in /webapp (#8334) Bumps [express](https://github.com/expressjs/express) from 4.21.2 to 5.1.0. - [Release notes](https://github.com/expressjs/express/releases) - [Changelog](https://github.com/expressjs/express/blob/master/History.md) - [Commits](https://github.com/expressjs/express/compare/4.21.2...v5.1.0) --- updated-dependencies: - dependency-name: express dependency-version: 5.1.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 390 ++++++++++++++++++++++++++++++++++++++------ 2 files changed, 340 insertions(+), 52 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 5c1d946fc..8ae97ec3e 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -37,7 +37,7 @@ "cropperjs": "^1.6.2", "cross-env": "~7.0.3", "date-fns": "2.22.1", - "express": "~4.21.2", + "express": "~5.1.0", "graphql": "~14.7.0", "intersection-observer": "^0.12.0", "jest-serializer-vue": "^3.1.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 87e849383..117099c54 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5276,6 +5276,14 @@ accepts@^1.3.5, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +accepts@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" + integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== + dependencies: + mime-types "^3.0.0" + negotiator "^1.0.0" + accounting@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/accounting/-/accounting-0.4.1.tgz#87dd4103eff7f4460f1e186f5c677ed6cf566883" @@ -6740,6 +6748,21 @@ body-parser@1.20.3, body-parser@^1.18.3: type-is "~1.6.18" unpipe "1.0.0" +body-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" + integrity sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg== + dependencies: + bytes "^3.1.2" + content-type "^1.0.5" + debug "^4.4.0" + http-errors "^2.0.0" + iconv-lite "^0.6.3" + on-finished "^2.4.1" + qs "^6.14.0" + raw-body "^3.0.0" + type-is "^2.0.0" + boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -6973,7 +6996,7 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.2: +bytes@3.1.2, bytes@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -7068,6 +7091,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" @@ -7087,6 +7118,14 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +call-bound@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a" + integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== + dependencies: + call-bind-apply-helpers "^1.0.2" + get-intrinsic "^1.3.0" + caller-callsite@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" @@ -7791,7 +7830,14 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: +content-disposition@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2" + integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg== + dependencies: + safe-buffer "5.2.1" + +content-type@^1.0.4, content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -7811,6 +7857,11 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= +cookie-signature@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" + integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== + cookie-universal-nuxt@~2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.2.2.tgz#107815f03f5b769de7018670d6370368205387bb" @@ -7842,6 +7893,11 @@ cookie@^0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@^0.7.1: + version "0.7.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" + integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -8448,12 +8504,12 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, 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== dependencies: - ms "2.1.2" + ms "^2.1.3" debug@=3.1.0: version "3.1.0" @@ -8625,7 +8681,7 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= -depd@2.0.0: +depd@2.0.0, depd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -8890,6 +8946,15 @@ dropzone@^5.5.1: resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-5.5.1.tgz#06e2f513e61d6aa363d4b556f18574f47cf7ba26" integrity sha512-3VduRWLxx9hbVr42QieQN25mx/I61/mRdUSuxAmDGdDqZIN8qtP7tcKMa3KfpJjuGjOJGYYUzzeq6eGDnkzesA== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -9046,16 +9111,16 @@ emotion-theming@^10.0.19: "@emotion/weak-memoize" "0.2.5" hoist-non-react-statics "^3.3.0" +encodeurl@^2.0.0, encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= -encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -9266,6 +9331,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" @@ -9278,6 +9348,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" @@ -9374,7 +9451,7 @@ escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@~1.0.3: +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= @@ -9858,7 +9935,7 @@ expect@^29.7.0: jest-message-util "^29.7.0" jest-util "^29.7.0" -express@^4.16.3, express@^4.17.1, express@~4.21.2: +express@^4.16.3, express@^4.17.1: version "4.21.2" resolved "https://registry.yarnpkg.com/express/-/express-4.21.2.tgz#cf250e48362174ead6cea4a566abef0162c1ec32" integrity sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA== @@ -9895,6 +9972,39 @@ express@^4.16.3, express@^4.17.1, express@~4.21.2: utils-merge "1.0.1" vary "~1.1.2" +express@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/express/-/express-5.1.0.tgz#d31beaf715a0016f0d53f47d3b4d7acf28c75cc9" + integrity sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA== + dependencies: + accepts "^2.0.0" + body-parser "^2.2.0" + content-disposition "^1.0.0" + content-type "^1.0.5" + cookie "^0.7.1" + cookie-signature "^1.2.1" + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + finalhandler "^2.1.0" + fresh "^2.0.0" + http-errors "^2.0.0" + merge-descriptors "^2.0.0" + mime-types "^3.0.0" + on-finished "^2.4.1" + once "^1.4.0" + parseurl "^1.3.3" + proxy-addr "^2.0.7" + qs "^6.14.0" + range-parser "^1.2.1" + router "^2.2.0" + send "^1.1.0" + serve-static "^2.2.0" + statuses "^2.0.1" + type-is "^2.0.1" + vary "^1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -10163,6 +10273,18 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" +finalhandler@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f" + integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q== + dependencies: + debug "^4.4.0" + encodeurl "^2.0.0" + escape-html "^1.0.3" + on-finished "^2.4.1" + parseurl "^1.3.3" + statuses "^2.0.1" + find-cache-dir@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" @@ -10359,6 +10481,11 @@ fresh@0.5.2, fresh@^0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= +fresh@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" + integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== + from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -10571,11 +10698,35 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.5, get-intrinsic@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -10821,6 +10972,11 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + got@^11.8.5: version "11.8.6" resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a" @@ -11063,6 +11219,11 @@ has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" @@ -11355,7 +11516,7 @@ http-call@^5.2.2: parse-json "^4.0.0" tunnel-agent "^0.6.0" -http-errors@2.0.0: +http-errors@2.0.0, http-errors@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== @@ -11473,7 +11634,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -12063,6 +12224,11 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= +is-promise@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" + integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== + is-redirect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" @@ -13599,6 +13765,11 @@ markdown-to-jsx@^6.10.3, markdown-to-jsx@^6.11.4, markdown-to-jsx@^6.9.1: prop-types "^15.6.2" unquote "^1.1.0" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -13628,6 +13799,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +media-typer@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" + integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== + memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -13678,6 +13854,11 @@ merge-descriptors@1.0.3: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== +merge-descriptors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" + integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== + merge-source-map@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" @@ -14006,42 +14187,35 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.40.0, "mime-db@>= 1.40.0 < 2": - version "1.40.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" - integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== - -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== - dependencies: - mime-db "1.44.0" +"mime-db@>= 1.40.0 < 2": + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== -mime-types@^2.1.19, mime-types@~2.1.19: - version "2.1.24" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" - integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== - dependencies: - mime-db "1.40.0" +mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== -mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.19, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime-types@^3.0.0, mime-types@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" + integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== + dependencies: + mime-db "^1.54.0" + mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -14269,12 +14443,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.1.1: +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -14345,6 +14514,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" + integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== + neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -14722,6 +14896,11 @@ object-inspect@^1.13.1: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-inspect@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" @@ -14830,7 +15009,7 @@ object.values@^1.1.0, object.values@^1.2.0: define-properties "^1.2.1" es-object-atoms "^1.0.0" -on-finished@2.4.1, on-finished@^2.3.0: +on-finished@2.4.1, on-finished@^2.3.0, on-finished@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -15177,7 +15356,7 @@ parse5@^7.0.0, parse5@^7.1.1: dependencies: entities "^4.4.0" -parseurl@^1.3.2, parseurl@~1.3.3: +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" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -15275,6 +15454,11 @@ path-to-regexp@0.1.12: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== +path-to-regexp@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" + integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== + path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -16381,7 +16565,7 @@ protocol-buffers-schema@^3.3.1: resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== -proxy-addr@~2.0.7: +proxy-addr@^2.0.7, proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -16489,13 +16673,20 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= -qs@6.13.0, qs@^6.10.0, qs@^6.6.0: +qs@6.13.0: version "6.13.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== dependencies: side-channel "^1.0.6" +qs@^6.10.0, qs@^6.14.0, qs@^6.6.0: + version "6.14.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" + integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== + dependencies: + side-channel "^1.1.0" + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -16581,6 +16772,16 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f" + integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.6.3" + unpipe "1.0.0" + raw-loader@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" @@ -17333,6 +17534,17 @@ rope-sequence@^1.3.0: resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b" integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg== +router@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" + integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== + dependencies: + debug "^4.4.0" + depd "^2.0.0" + is-promise "^4.0.0" + parseurl "^1.3.3" + path-to-regexp "^8.0.0" + run-parallel@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" @@ -17534,6 +17746,23 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" +send@^1.1.0, send@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" + integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== + dependencies: + debug "^4.3.5" + encodeurl "^2.0.0" + escape-html "^1.0.3" + etag "^1.8.1" + fresh "^2.0.0" + http-errors "^2.0.0" + mime-types "^3.0.1" + ms "^2.1.3" + on-finished "^2.4.1" + range-parser "^1.2.1" + statuses "^2.0.1" + sentence-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" @@ -17569,6 +17798,16 @@ serve-static@1.16.2, serve-static@^1.14.1: parseurl "~1.3.3" send "0.19.0" +serve-static@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" + integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== + dependencies: + encodeurl "^2.0.0" + escape-html "^1.0.3" + parseurl "^1.3.3" + send "^1.2.0" + server-destroy@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" @@ -17682,6 +17921,35 @@ shelljs@0.7.7: interpret "^1.0.0" rechoir "^0.6.2" +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4, side-channel@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2" @@ -17692,6 +17960,17 @@ side-channel@^1.0.4, side-channel@^1.0.6: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +side-channel@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + sigmund@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" @@ -17995,7 +18274,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -statuses@2.0.1: +statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== @@ -19128,6 +19407,15 @@ type-is@^1.6.16, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +type-is@^2.0.0, type-is@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" + integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== + dependencies: + content-type "^1.0.5" + media-typer "^1.1.0" + mime-types "^3.0.0" + typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" From f7c371c9ce565cb6301ac861d44109035e6545de Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 8 Apr 2025 09:35:11 +0200 Subject: [PATCH 083/266] lint import/no-extraneous-dependencies (#8341) --- backend/.eslintrc.cjs | 2 +- backend/package.json | 3 +++ .../20200123150105-merge_duplicate_user_accounts.ts | 1 + .../20200123150110-merge_duplicate_location_nodes.ts | 1 + backend/src/schema/resolvers/embeds/scraper.ts | 1 + backend/src/schema/resolvers/helpers/databaseLogger.ts | 1 + backend/src/schema/resolvers/users/location.ts | 1 + backend/yarn.lock | 9 ++++++++- 8 files changed, 17 insertions(+), 2 deletions(-) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index e781e15b7..46a356681 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -41,7 +41,7 @@ module.exports = { 'import/export': 'error', // 'import/no-deprecated': 'error', 'import/no-empty-named-blocks': 'error', - // 'import/no-extraneous-dependencies': 'error', + 'import/no-extraneous-dependencies': 'error', 'import/no-mutable-exports': 'error', 'import/no-unused-modules': 'error', 'import/no-named-as-default': 'error', diff --git a/backend/package.json b/backend/package.json index 1949c23af..b0509c3c7 100644 --- a/backend/package.json +++ b/backend/package.json @@ -42,6 +42,7 @@ "babel-jest": "~29.7.0", "babel-plugin-transform-runtime": "^6.23.0", "bcryptjs": "~2.4.3", + "body-parser": "^1.20.3", "cheerio": "~1.0.0", "cors": "~2.8.5", "cross-env": "~7.0.3", @@ -52,7 +53,9 @@ "graphql-middleware-sentry": "^3.2.1", "graphql-redis-subscriptions": "^2.7.0", "graphql-shield": "~7.2.2", + "graphql-subscriptions": "^1.1.0", "graphql-tag": "~2.10.3", + "graphql-upload": "^11.0.0", "helmet": "~8.1.0", "ioredis": "^4.16.1", "jsonwebtoken": "~8.5.1", 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 6eb9e0ed0..1480715ae 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 @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators' diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index 23d1d55bc..f56389045 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -1,3 +1,4 @@ +/* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError } from 'rxjs/operators' diff --git a/backend/src/schema/resolvers/embeds/scraper.ts b/backend/src/schema/resolvers/embeds/scraper.ts index afc2b1df6..4771ba160 100644 --- a/backend/src/schema/resolvers/embeds/scraper.ts +++ b/backend/src/schema/resolvers/embeds/scraper.ts @@ -11,6 +11,7 @@ import isArray from 'lodash/isArray' import mergeWith from 'lodash/mergeWith' import findProvider from './findProvider' +// eslint-disable-next-line import/no-extraneous-dependencies const error = require('debug')('embed:error') const metascraper = Metascraper([ diff --git a/backend/src/schema/resolvers/helpers/databaseLogger.ts b/backend/src/schema/resolvers/helpers/databaseLogger.ts index 98544087b..f2db22965 100644 --- a/backend/src/schema/resolvers/helpers/databaseLogger.ts +++ b/backend/src/schema/resolvers/helpers/databaseLogger.ts @@ -1,4 +1,5 @@ /* eslint-disable import/no-named-as-default */ +// eslint-disable-next-line import/no-extraneous-dependencies import Debug from 'debug' const debugCypher = Debug('human-connection:neo4j:cypher') diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/schema/resolvers/users/location.ts index 9a8b5430b..49b7c6b1c 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/schema/resolvers/users/location.ts @@ -3,6 +3,7 @@ /* eslint-disable import/no-named-as-default */ import request from 'request' import { UserInputError } from 'apollo-server' +// eslint-disable-next-line import/no-extraneous-dependencies import Debug from 'debug' import asyncForEach from '../../../helpers/asyncForEach' import CONFIG from '../../../config' diff --git a/backend/yarn.lock b/backend/yarn.lock index 76418da2c..2f9f02349 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3157,7 +3157,7 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== -body-parser@1.20.3, body-parser@^1.18.3: +body-parser@1.20.3, body-parser@^1.18.3, body-parser@^1.20.3: version "1.20.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6" integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g== @@ -5565,6 +5565,13 @@ graphql-subscriptions@^1.0.0: dependencies: iterall "^1.2.1" +graphql-subscriptions@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-1.2.1.tgz#2142b2d729661ddf967b7388f7cf1dd4cf2e061d" + integrity sha512-95yD/tKi24q8xYa7Q9rhQN16AYj5wPbrb8tmHGM3WRc9EBmWrG/0kkMl+tQG8wcEuE9ibR4zyOM31p5Sdr2v4g== + dependencies: + iterall "^1.3.0" + graphql-tag@^2.9.2, graphql-tag@~2.10.3: version "2.10.3" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03" From 59c145c1f93621202bb4e7e040b4ba9453acf118 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 8 Apr 2025 10:35:49 +0200 Subject: [PATCH 084/266] lint plugin n - update and cleanup (#8342) - Update plugin n to fix a deprecation warning - Order n rules according to docu - use n:recommended - comment out all rules from recommened - enable some rules --- backend/.eslintrc.cjs | 49 ++++--- backend/package.json | 2 +- .../20200312140328-bulk_upload_to_s3.ts | 4 +- ...200326160326-remove_dangling_image_urls.ts | 2 +- .../helpers/email/templates/de/index.ts | 4 +- .../helpers/email/templates/en/index.ts | 4 +- .../helpers/email/templates/index.ts | 4 +- backend/src/schema/resolvers/embeds.spec.ts | 4 +- .../schema/resolvers/embeds/findProvider.ts | 4 +- backend/src/schema/resolvers/images/images.ts | 4 +- backend/src/schema/resolvers/index.ts | 2 +- backend/src/schema/types/index.ts | 2 +- backend/src/server.ts | 2 +- backend/yarn.lock | 127 +++++++++++------- 14 files changed, 123 insertions(+), 91 deletions(-) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 46a356681..3556bd36d 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -5,10 +5,11 @@ module.exports = { node: true, }, parser: '@typescript-eslint/parser', - plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise', 'security', 'no-catch-all',], + plugins: ['prettier', '@typescript-eslint', 'import', 'n', 'promise', 'security', 'no-catch-all'], extends: [ 'standard', 'eslint:recommended', + 'plugin:n/recommended', 'plugin:prettier/recommended', 'plugin:import/recommended', 'plugin:import/typescript', @@ -101,36 +102,36 @@ module.exports = { // }, // ], 'import/prefer-default-export': 'off', + // n + // 'n/callback-return': 'error', + 'n/exports-style': 'error', + 'n/file-extension-in-import': ['error', 'never'], + 'n/global-require': 'error', 'n/handle-callback-err': 'error', + // 'n/hashbang': 'error', // part of n/recommended 'n/no-callback-literal': 'error', - 'n/no-exports-assign': 'error', - // 'n/no-extraneous-import': 'error', - 'n/no-extraneous-require': 'error', + // 'n/no-deprecated-api': 'error', // part of n/recommended + // 'n/no-exports-assign': 'error', // part of n/recommended + 'n/no-extraneous-import': 'off', // TODO // part of n/recommended + // 'n/no-extraneous-require': 'error', // part of n/recommended 'n/no-hide-core-modules': 'error', - 'n/no-missing-import': 'off', // not compatible with typescript - 'n/no-missing-require': 'error', + 'n/no-missing-import': 'off', // not compatible with typescript // part of n/recommended + // 'n/no-missing-require': 'error', // part of n/recommended + 'n/no-mixed-requires': 'error', 'n/no-new-require': 'error', 'n/no-path-concat': 'error', - 'n/no-process-exit': 'error', - 'n/no-unpublished-bin': 'error', - 'n/no-unpublished-import': 'off', // TODO need to exclude seeds - 'n/no-unpublished-require': 'error', - 'n/no-unsupported-features': ['error', { ignores: ['modules'] }], - 'n/no-unsupported-features/es-builtins': 'error', - 'n/no-unsupported-features/es-syntax': 'error', - 'n/no-unsupported-features/node-builtins': 'error', - 'n/process-exit-as-throw': 'error', - 'n/shebang': 'error', - //'n/callback-return': 'error', - 'n/exports-style': 'error', - 'n/file-extension-in-import': 'off', - 'n/global-require': 'error', - 'n/no-mixed-requires': 'error', 'n/no-process-env': 'error', + // 'n/no-process-exit': 'error', // part of n/recommended 'n/no-restricted-import': 'error', 'n/no-restricted-require': 'error', // 'n/no-sync': 'error', + // 'n/no-unpublished-bin': 'error', // part of n/recommended + 'n/no-unpublished-import': ['error', { 'allowModules': ['apollo-server-testing', 'rosie', '@faker-js/faker'] }], // part of n/recommended + // 'n/no-unpublished-require': 'error', // 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 'n/prefer-global/buffer': 'error', 'n/prefer-global/console': 'error', 'n/prefer-global/process': 'error', @@ -138,8 +139,12 @@ module.exports = { 'n/prefer-global/text-encoder': 'error', 'n/prefer-global/url': 'error', 'n/prefer-global/url-search-params': 'error', + 'n/prefer-node-protocol': 'error', 'n/prefer-promises/dns': 'error', 'n/prefer-promises/fs': 'error', + // 'n/process-exit-as-throw': 'error', // part of n/recommended + 'n/shebang': 'error', + // promise 'promise/catch-or-return': 'error', 'promise/no-return-wrap': 'error', @@ -206,4 +211,4 @@ module.exports = { }, }, ], -}; +} diff --git a/backend/package.json b/backend/package.json index b0509c3c7..fdc33616d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -111,7 +111,7 @@ "eslint-import-resolver-typescript": "^4.3.1", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", - "eslint-plugin-n": "^16.6.2", + "eslint-plugin-n": "^17.17.0", "eslint-plugin-no-catch-all": "^1.1.0", "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "^6.1.1", 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 index 7818001fb..6643e3540 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -1,7 +1,7 @@ /* eslint-disable security/detect-non-literal-fs-filename */ import { getDriver } from '../neo4j' -import { existsSync, createReadStream } from 'fs' -import path from 'path' +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' diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index 9d0d44f26..765c7919b 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-non-literal-fs-filename */ import { getDriver } from '../neo4j' -import { existsSync } from 'fs' +import { existsSync } from 'node:fs' export const description = ` In this review: diff --git a/backend/src/middleware/helpers/email/templates/de/index.ts b/backend/src/middleware/helpers/email/templates/de/index.ts index f29e2c485..6f0803bc7 100644 --- a/backend/src/middleware/helpers/email/templates/de/index.ts +++ b/backend/src/middleware/helpers/email/templates/de/index.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import fs from 'fs' -import path from 'path' +import fs from 'node:fs' +import path from 'node:path' const readFile = (fileName) => fs.readFileSync(path.join(__dirname, fileName), 'utf-8') diff --git a/backend/src/middleware/helpers/email/templates/en/index.ts b/backend/src/middleware/helpers/email/templates/en/index.ts index f29e2c485..6f0803bc7 100644 --- a/backend/src/middleware/helpers/email/templates/en/index.ts +++ b/backend/src/middleware/helpers/email/templates/en/index.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import fs from 'fs' -import path from 'path' +import fs from 'node:fs' +import path from 'node:path' const readFile = (fileName) => fs.readFileSync(path.join(__dirname, fileName), 'utf-8') diff --git a/backend/src/middleware/helpers/email/templates/index.ts b/backend/src/middleware/helpers/email/templates/index.ts index bcb5c2b64..79de6b8ae 100644 --- a/backend/src/middleware/helpers/email/templates/index.ts +++ b/backend/src/middleware/helpers/email/templates/index.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import fs from 'fs' -import path from 'path' +import fs from 'node:fs' +import path from 'node:path' const readFile = (fileName) => fs.readFileSync(path.join(__dirname, fileName), 'utf-8') diff --git a/backend/src/schema/resolvers/embeds.spec.ts b/backend/src/schema/resolvers/embeds.spec.ts index 8e7a69891..51dc18bd8 100644 --- a/backend/src/schema/resolvers/embeds.spec.ts +++ b/backend/src/schema/resolvers/embeds.spec.ts @@ -1,6 +1,6 @@ import fetch from 'node-fetch' -import fs from 'fs' -import path from 'path' +import fs from 'node:fs' +import path from 'node:path' import { createTestClient } from 'apollo-server-testing' import createServer from '../../server' import gql from 'graphql-tag' diff --git a/backend/src/schema/resolvers/embeds/findProvider.ts b/backend/src/schema/resolvers/embeds/findProvider.ts index 1b875b180..7bedf2a77 100644 --- a/backend/src/schema/resolvers/embeds/findProvider.ts +++ b/backend/src/schema/resolvers/embeds/findProvider.ts @@ -1,5 +1,5 @@ -import fs from 'fs' -import path from 'path' +import fs from 'node:fs' +import path from 'node:path' import { minimatch } from 'minimatch' let oEmbedProvidersFile = fs.readFileSync( diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index 4566aa5bf..5d19d96f7 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -1,10 +1,10 @@ /* eslint-disable promise/avoid-new */ /* eslint-disable security/detect-non-literal-fs-filename */ -import path from 'path' +import path from 'node:path' import { v4 as uuid } from 'uuid' import { S3 } from 'aws-sdk' import slug from 'slug' -import { existsSync, unlinkSync, createWriteStream } from 'fs' +import { existsSync, unlinkSync, createWriteStream } from 'node:fs' import { UserInputError } from 'apollo-server' import { getDriver } from '../../../db/neo4j' import CONFIG from '../../../config' diff --git a/backend/src/schema/resolvers/index.ts b/backend/src/schema/resolvers/index.ts index 1aeadbea2..bc028f0db 100644 --- a/backend/src/schema/resolvers/index.ts +++ b/backend/src/schema/resolvers/index.ts @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' import { fileLoader, mergeResolvers } from 'merge-graphql-schemas' // the files must be correctly evaluated in built and dev state - therefore accept both js & ts files diff --git a/backend/src/schema/types/index.ts b/backend/src/schema/types/index.ts index d49becffc..fe8a6315e 100644 --- a/backend/src/schema/types/index.ts +++ b/backend/src/schema/types/index.ts @@ -1,4 +1,4 @@ -import path from 'path' +import path from 'node:path' import { mergeTypes, fileLoader } from 'merge-graphql-schemas' const typeDefs = fileLoader(path.join(__dirname, './**/*.gql')) diff --git a/backend/src/server.ts b/backend/src/server.ts index 7451e3e4a..86c0c3658 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,6 +1,6 @@ /* eslint-disable import/no-named-as-default-member */ import express from 'express' -import http from 'http' +import http from 'node:http' import helmet from 'helmet' import { ApolloServer } from 'apollo-server-express' import CONFIG from './config' diff --git a/backend/yarn.lock b/backend/yarn.lock index 2f9f02349..5d81dab96 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1106,7 +1106,19 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1": +"@eslint-community/eslint-utils@^4.5.0": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz#b0fc7e06d0c94f801537fd4237edc2706d3b8e4c" + integrity sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w== + 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" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.10.0" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== @@ -3278,18 +3290,6 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -builtin-modules@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== - -builtins@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" - integrity sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ== - dependencies: - semver "^7.0.0" - busboy@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" @@ -4240,6 +4240,14 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +enhanced-resolve@^5.17.1: + version "5.18.1" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz#728ab082f8b7b6836de51f1637aab5d3b9568faf" + integrity sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + entities@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" @@ -4561,10 +4569,12 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-compat-utils@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.1.2.tgz#f45e3b5ced4c746c127cf724fb074cd4e730d653" - integrity sha512-Jia4JDldWnFNIru1Ehx1H5s9/yxiRHY/TimCuUc0jNexew3cF1gI6CYZil1ociakfWO3rRqFjl1mskBblB3RYg== +eslint-compat-utils@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.5.1.tgz#7fc92b776d185a70c4070d03fd26fde3d59652e4" + integrity sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q== + dependencies: + semver "^7.5.4" eslint-config-prettier@^10.1.1: version "10.1.1" @@ -4604,14 +4614,14 @@ eslint-module-utils@^2.12.0: dependencies: debug "^3.2.7" -eslint-plugin-es-x@^7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.5.0.tgz#d08d9cd155383e35156c48f736eb06561d07ba92" - integrity sha512-ODswlDSO0HJDzXU0XvgZ3lF3lS3XAZEossh15Q2UHjwrJggWeBoKqqEsLTZLXl+dh5eOAozG0zRcYtuE35oTuQ== +eslint-plugin-es-x@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.8.0.tgz#a207aa08da37a7923f2a9599e6d3eb73f3f92b74" + integrity sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ== dependencies: "@eslint-community/eslint-utils" "^4.1.2" - "@eslint-community/regexpp" "^4.6.0" - eslint-compat-utils "^0.1.2" + "@eslint-community/regexpp" "^4.11.0" + eslint-compat-utils "^0.5.1" eslint-plugin-import@^2.31.0: version "2.31.0" @@ -4645,22 +4655,19 @@ eslint-plugin-jest@^28.11.0: dependencies: "@typescript-eslint/utils" "^6.0.0 || ^7.0.0 || ^8.0.0" -eslint-plugin-n@^16.6.2: - version "16.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz#6a60a1a376870064c906742272074d5d0b412b0b" - integrity sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ== +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" + integrity sha512-2VvPK7Mo73z1rDFb6pTvkH6kFibAmnTubFq5l83vePxu0WiY1s0LOtj2WHb6Sa40R3w4mnh8GFYbHBQyMlotKw== dependencies: - "@eslint-community/eslint-utils" "^4.4.0" - builtins "^5.0.1" - eslint-plugin-es-x "^7.5.0" - get-tsconfig "^4.7.0" - globals "^13.24.0" - ignore "^5.2.4" - is-builtin-module "^3.2.1" - is-core-module "^2.12.1" - minimatch "^3.1.2" - resolve "^1.22.2" - semver "^7.5.3" + "@eslint-community/eslint-utils" "^4.5.0" + enhanced-resolve "^5.17.1" + eslint-plugin-es-x "^7.8.0" + get-tsconfig "^4.8.1" + globals "^15.11.0" + ignore "^5.3.2" + minimatch "^9.0.5" + semver "^7.6.3" eslint-plugin-no-catch-all@^1.1.0: version "1.1.0" @@ -5373,7 +5380,7 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" -get-tsconfig@^4.10.0, get-tsconfig@^4.7.0: +get-tsconfig@^4.10.0, get-tsconfig@^4.8.1: version "4.10.0" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.10.0.tgz#403a682b373a823612475a4c2928c7326fc0f6bb" integrity sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A== @@ -5435,13 +5442,18 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.19.0, globals@^13.24.0: +globals@^13.19.0: version "13.24.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" +globals@^15.11.0: + version "15.15.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-15.15.0.tgz#7c4761299d41c32b075715a4ce1ede7897ff72a8" + integrity sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg== + globalthis@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" @@ -5502,6 +5514,11 @@ got@~11.8.0: p-cancelable "^2.0.0" responselike "^2.0.0" +graceful-fs@^4.2.4: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -5913,6 +5930,11 @@ ignore@^5.1.4, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== +ignore@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + image-extensions@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/image-extensions/-/image-extensions-1.1.0.tgz#b8e6bf6039df0056e333502a00b6637a3105d894" @@ -6113,13 +6135,6 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-builtin-module@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" - integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== - dependencies: - builtin-modules "^3.3.0" - is-bun-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-bun-module/-/is-bun-module-2.0.0.tgz#4d7859a87c0fcac950c95e666730e745eae8bddd" @@ -6137,7 +6152,7 @@ is-callable@^1.1.4, is-callable@^1.2.0: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb" integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw== -is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.15.1: +is-core-module@^2.13.0, is-core-module@^2.15.1: version "2.15.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.1.tgz#a7363a25bee942fefab0de13bf6aa372c82dcc37" integrity sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ== @@ -7556,6 +7571,13 @@ minimatch@^9.0.1, minimatch@^9.0.4: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -8751,7 +8773,7 @@ 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.2, resolve@^1.22.4: +resolve@^1.12.0, resolve@^1.14.2, 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== @@ -8911,7 +8933,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.1: +semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.3, semver@^7.7.1: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== @@ -9499,6 +9521,11 @@ synckit@^0.11.0: "@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" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + tar@^6.1.11: version "6.1.13" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.13.tgz#46e22529000f612180601a6fe0680e7da508847b" From 16ada68a3ac3b770b884f54d07f717da079a4384 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 8 Apr 2025 11:05:17 +0200 Subject: [PATCH 085/266] lint - update plugin promise and cleanup rules (#8343) - use of promise:recommended - update plugin - apply new rules --- backend/.eslintrc.cjs | 30 +++++++++++++++++------------- backend/package.json | 2 +- backend/yarn.lock | 10 ++++++---- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 3556bd36d..d214ae761 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -13,6 +13,7 @@ module.exports = { 'plugin:prettier/recommended', 'plugin:import/recommended', 'plugin:import/typescript', + 'plugin:promise/recommended', 'plugin:security/recommended-legacy', 'plugin:@eslint-community/eslint-comments/recommended', ], @@ -146,20 +147,23 @@ module.exports = { 'n/shebang': 'error', // promise - 'promise/catch-or-return': 'error', - 'promise/no-return-wrap': 'error', - 'promise/param-names': 'error', - 'promise/always-return': 'error', - 'promise/no-native': 'off', - 'promise/no-nesting': 'warn', - 'promise/no-promise-in-callback': 'warn', - 'promise/no-callback-in-promise': 'warn', - 'promise/avoid-new': 'warn', - 'promise/no-new-statics': 'error', - 'promise/no-return-in-finally': 'warn', - 'promise/valid-params': 'warn', - 'promise/prefer-await-to-callbacks': 'error', + // 'promise/always-return': 'error', // part of promise/recommended + 'promise/avoid-new': 'error', + // 'promise/catch-or-return': 'error', // part of promise/recommended + // 'promise/no-callback-in-promise': 'warn', // part of promise/recommended 'promise/no-multiple-resolved': 'error', + 'promise/no-native': 'off', // ES5 only + // 'promise/no-nesting': 'warn', // part of promise/recommended + // 'promise/no-new-statics': 'error', // part of promise/recommended + // 'promise/no-promise-in-callback': 'warn', // part of promise/recommended + // 'promise/no-return-in-finally': 'warn', // part of promise/recommended + // 'promise/no-return-wrap': 'error', // part of promise/recommended + // 'promise/param-names': 'error', // part of promise/recommended + 'promise/prefer-await-to-callbacks': 'error', + 'promise/prefer-catch': 'error', + 'promise/spec-only': 'error', + // 'promise/valid-params': 'error', // part of promise/recommended + // eslint comments '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], '@eslint-community/eslint-comments/no-restricted-disable': 'error', diff --git a/backend/package.json b/backend/package.json index fdc33616d..76c85c683 100644 --- a/backend/package.json +++ b/backend/package.json @@ -114,7 +114,7 @@ "eslint-plugin-n": "^17.17.0", "eslint-plugin-no-catch-all": "^1.1.0", "eslint-plugin-prettier": "^5.2.6", - "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-promise": "^7.2.1", "eslint-plugin-security": "^3.0.1", "jest": "^29.7.0", "nodemon": "~3.1.9", diff --git a/backend/yarn.lock b/backend/yarn.lock index 5d81dab96..204fe73bd 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4682,10 +4682,12 @@ eslint-plugin-prettier@^5.2.6: prettier-linter-helpers "^1.0.0" synckit "^0.11.0" -eslint-plugin-promise@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz#269a3e2772f62875661220631bd4dafcb4083816" - integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== +eslint-plugin-promise@^7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-7.2.1.tgz#a0652195700aea40b926dc3c74b38e373377bfb0" + integrity sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" eslint-plugin-security@^3.0.1: version "3.0.1" From dfe9205bf6ec3fa9d85b30ae66cd98ef36960316 Mon Sep 17 00:00:00 2001 From: mahula Date: Tue, 8 Apr 2025 22:20:41 +0200 Subject: [PATCH 086/266] refactor(other): cleanup cypress configuration (#8345) * cypress:remove redundant comment from command config * cypress: move command and factory imports from global to local * cypress: linting * cypress: set support file to false in config --- cypress/cypress.config.js | 2 +- cypress/support/commands.js | 13 -------- cypress/support/e2e.js | 33 ------------------- .../somebody_reported_the_following_posts.js | 6 ++-- .../common/I_am_logged_in_as_{string}.js | 6 ++-- ..._following_{string}_are_in_the_database.js | 25 +++++++------- .../common/{string}_wrote_a_post_{string}.js | 5 +-- 7 files changed, 24 insertions(+), 66 deletions(-) delete mode 100644 cypress/support/e2e.js diff --git a/cypress/cypress.config.js b/cypress/cypress.config.js index d08749370..52a765bf0 100644 --- a/cypress/cypress.config.js +++ b/cypress/cypress.config.js @@ -44,7 +44,7 @@ module.exports = defineConfig({ chromeWebSecurity: false, baseUrl: 'http://localhost:3000', specPattern: '**/*.feature', - supportFile: 'cypress/support/e2e.js', + supportFile: false, retries: 0, video: false, viewportHeight: 720, diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 92e8bf1f7..f75fe9b6b 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -53,16 +53,3 @@ Cypress.Commands.add( }) }) - -// -// -// -- This is a child command -- -// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) -// -// -// -- This is a dual command -- -// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) -// -// -// -- This is will overwrite an existing command -- -// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) diff --git a/cypress/support/e2e.js b/cypress/support/e2e.js deleted file mode 100644 index 453c8476f..000000000 --- a/cypress/support/e2e.js +++ /dev/null @@ -1,33 +0,0 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: - -import './commands' -import './factories' - -// intermittent failing tests -// import 'cypress-plugin-retries' - -// Alternatively you can use CommonJS syntax: -// require('./commands') -import { WebSocket } from 'mock-socket' -before(() => { - cy.visit('/', { - onBeforeLoad(win) { - cy.stub(win, "WebSocket", url => new WebSocket(url)) - } - }) -}) 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 38cdbee09..e924acdeb 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,5 +1,7 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; -import 'cypress-network-idle'; +import { Given } from '@badeball/cypress-cucumber-preprocessor' +import './../../commands' +import './../../factories' +import 'cypress-network-idle' Given('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}$/ 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 1dbaa3d94..833a21c6a 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,9 +1,9 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { Given } from '@badeball/cypress-cucumber-preprocessor' import encode from '../../../../backend/build/src/jwt/encode' -Given("I am logged in as {string}", slug => { +Given('I am logged in as {string}', slug => { cy.neode() - .firstOf("User", { slug }) + .firstOf('User', { slug }) .then(user => { return new Cypress.Promise((resolve, reject) => { if(!user) { 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 8e41afa2a..066b5ef3d 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,10 +1,11 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { Given } from '@badeball/cypress-cucumber-preprocessor' +import './../../factories' -Given("the following {string} are in the database:", (table,data) => { +Given('the following {string} are in the database:', (table,data) => { switch(table){ - case "posts": + case 'posts': data.hashes().forEach( entry => { - cy.factory().build("post", { + cy.factory().build('post', { ...entry, deleted: Boolean(entry.deleted), disabled: Boolean(entry.disabled), @@ -15,25 +16,25 @@ Given("the following {string} are in the database:", (table,data) => { }); }) break - case "comments": + case 'comments': data.hashes().forEach( entry => { cy.factory() - .build("comment", entry, entry); + .build('comment', entry, entry); }) break - case "users": + case 'users': data.hashes().forEach( entry => { - cy.factory().build("user", entry, entry); + cy.factory().build('user', entry, entry); }); break - case "tags": + case 'tags': data.hashes().forEach( entry => { - cy.factory().build("tag", entry, entry) + cy.factory().build('tag', entry, entry) }); break - case "donations": + case 'donations': data.hashes().forEach( entry => { - cy.factory().build("donations", entry, 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 0da055951..086432b30 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,8 +1,9 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { Given } from '@badeball/cypress-cucumber-preprocessor' +import './../../factories' Given('{string} wrote a post {string}', (author, title) => { cy.factory() - .build("post", { + .build('post', { title, }, { authorId: author, From a4f391930caddef2f74ec969f76c4ea931ddbcba Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 8 Apr 2025 23:18:07 +0200 Subject: [PATCH 087/266] lint - import/no-relative-parent-imports (#8349) enable import/no-relative-parent-imports --- backend/.eslintrc.cjs | 21 ++++--- backend/jest.config.cjs | 26 +++++++++ backend/jest.config.js | 20 ------- backend/package.json | 11 ++-- backend/scripts/build.copy.files.sh | 3 + backend/src/db/clean.ts | 2 +- backend/src/db/compiler.ts | 2 + backend/src/db/factories.ts | 4 +- backend/src/db/migrate/store.ts | 6 +- backend/src/db/migrate/template.ts | 2 +- ...123150105-merge_duplicate_user_accounts.ts | 4 +- ...23150110-merge_duplicate_location_nodes.ts | 2 +- ..._between_existing_blocked_relationships.ts | 2 +- ...0206190233-swap_latitude_with_longitude.ts | 2 +- .../20200207080200-fulltext_index_for_tags.ts | 2 +- ...213230248-add_unique_index_to_image_url.ts | 2 +- .../20200312140328-bulk_upload_to_s3.ts | 4 +- ...15-refactor_all_images_to_separate_type.ts | 2 +- ...emove_deleted_users_obsolete_attributes.ts | 2 +- ...emove_deleted_posts_obsolete_attributes.ts | 2 +- ...200326160326-remove_dangling_image_urls.ts | 2 +- ...1614023644903-add-clickedCount-to-posts.ts | 2 +- ...77130817-add-viewedTeaserCount-to-posts.ts | 2 +- .../20210506150512-add-donations-node.ts | 2 +- ...otificationEmails-property-to-all-users.ts | 2 +- ...text_indices_and_unique_keys_for_groups.ts | 2 +- .../20230320130345-fulltext-search-indexes.ts | 2 +- .../20230329150329-article-label-for-posts.ts | 2 +- .../20230608130637-add-postType-property.ts | 2 +- .../20231017141022-fix-event-dates.ts | 2 +- ...20250331130323-author-observes-own-post.ts | 2 +- backend/src/db/neo4j.ts | 4 +- backend/src/db/seed.ts | 16 +++--- backend/src/jwt/decode.spec.ts | 4 +- backend/src/jwt/decode.ts | 2 +- backend/src/jwt/encode.spec.ts | 2 +- backend/src/jwt/encode.ts | 2 +- backend/src/middleware/excerptMiddleware.ts | 2 +- .../hashtags/hashtagsMiddleware.spec.ts | 6 +- .../src/middleware/helpers/email/sendMail.ts | 4 +- .../helpers/email/templateBuilder.spec.ts | 4 +- .../helpers/email/templateBuilder.ts | 6 +- backend/src/middleware/index.ts | 2 +- .../middleware/languages/languages.spec.ts | 6 +- backend/src/middleware/languages/languages.ts | 2 +- .../src/middleware/login/loginMiddleware.ts | 4 +- .../notificationsMiddleware.spec.ts | 12 ++-- .../notifications/notificationsMiddleware.ts | 13 +++-- .../notifications/observing-posts.spec.ts | 8 +-- .../src/middleware/orderByMiddleware.spec.ts | 6 +- .../middleware/permissionsMiddleware.spec.ts | 8 +-- .../src/middleware/permissionsMiddleware.ts | 6 +- backend/src/middleware/sentryMiddleware.ts | 2 +- .../src/middleware/slugifyMiddleware.spec.ts | 12 ++-- .../softDelete/softDeleteMiddleware.spec.ts | 6 +- .../src/middleware/userInteractions.spec.ts | 6 +- .../validation/validationMiddleware.spec.ts | 6 +- backend/src/middleware/xssMiddleware.ts | 2 +- backend/src/models/User.spec.ts | 4 +- backend/src/schema/resolvers/comments.spec.ts | 6 +- .../src/schema/resolvers/donations.spec.ts | 6 +- backend/src/schema/resolvers/emails.spec.ts | 6 +- backend/src/schema/resolvers/embeds.spec.ts | 2 +- .../src/schema/resolvers/filter-posts.spec.ts | 10 ++-- backend/src/schema/resolvers/follow.spec.ts | 6 +- backend/src/schema/resolvers/follow.ts | 2 +- backend/src/schema/resolvers/groups.spec.ts | 10 ++-- backend/src/schema/resolvers/groups.ts | 8 +-- .../resolvers/helpers/filterForMutedUsers.ts | 2 +- .../resolvers/helpers/generateInviteCode.ts | 2 +- .../schema/resolvers/helpers/generateNonce.ts | 2 +- .../schema/resolvers/images/images.spec.ts | 4 +- backend/src/schema/resolvers/images/images.ts | 4 +- .../src/schema/resolvers/inviteCodes.spec.ts | 8 +-- .../src/schema/resolvers/locations.spec.ts | 6 +- backend/src/schema/resolvers/messages.spec.ts | 10 ++-- backend/src/schema/resolvers/messages.ts | 2 +- .../src/schema/resolvers/moderation.spec.ts | 6 +- .../schema/resolvers/notifications.spec.ts | 8 +-- backend/src/schema/resolvers/notifications.ts | 2 +- .../src/schema/resolvers/observePosts.spec.ts | 10 ++-- .../schema/resolvers/passwordReset.spec.ts | 8 +-- backend/src/schema/resolvers/passwordReset.ts | 2 +- backend/src/schema/resolvers/posts.spec.ts | 10 ++-- backend/src/schema/resolvers/posts.ts | 2 +- .../schema/resolvers/postsInGroups.spec.ts | 20 +++---- .../src/schema/resolvers/registration.spec.ts | 8 +-- backend/src/schema/resolvers/registration.ts | 4 +- backend/src/schema/resolvers/reports.spec.ts | 6 +- backend/src/schema/resolvers/rewards.spec.ts | 6 +- backend/src/schema/resolvers/rewards.ts | 2 +- backend/src/schema/resolvers/rooms.spec.ts | 10 ++-- backend/src/schema/resolvers/rooms.ts | 2 +- backend/src/schema/resolvers/searches.spec.ts | 6 +- backend/src/schema/resolvers/shout.spec.ts | 6 +- .../src/schema/resolvers/socialMedia.spec.ts | 6 +- backend/src/schema/resolvers/socialMedia.ts | 2 +- .../src/schema/resolvers/statistics.spec.ts | 6 +- backend/src/schema/resolvers/userData.spec.ts | 6 +- .../schema/resolvers/user_management.spec.ts | 14 ++--- .../src/schema/resolvers/user_management.ts | 4 +- backend/src/schema/resolvers/users.spec.ts | 8 +-- backend/src/schema/resolvers/users.ts | 2 +- .../schema/resolvers/users/location.spec.ts | 6 +- .../src/schema/resolvers/users/location.ts | 4 +- .../schema/resolvers/users/mutedUsers.spec.ts | 6 +- .../resolvers/viewedTeaserCount.spec.ts | 6 +- backend/tsconfig.json | 14 ++++- backend/yarn.lock | 57 ++++++++++++++++++- 109 files changed, 369 insertions(+), 286 deletions(-) create mode 100644 backend/jest.config.cjs delete mode 100644 backend/jest.config.js diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index d214ae761..5cdee6690 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -23,7 +23,7 @@ module.exports = { }, 'import/resolver': { typescript: { - project: ['./tsconfig.json'], + project: ['./tsconfig.json', './backend/tsconfig.json'], }, node: true, }, @@ -52,16 +52,16 @@ module.exports = { 'import/no-commonjs': 'error', 'import/no-import-module-exports': 'error', 'import/no-nodejs-modules': 'off', - 'import/unambiguous': 'off', // not compatible with scriptless vue files + 'import/unambiguous': 'off', // not compatible with .eslintrc.cjs 'import/default': 'error', - // 'import/named': 'error', + 'import/named': 'off', // has false positives 'import/namespace': 'error', 'import/no-absolute-path': 'error', 'import/no-cycle': 'error', 'import/no-dynamic-require': 'error', 'import/no-internal-modules': 'off', 'import/no-relative-packages': 'error', - // 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], + 'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }], 'import/no-self-import': 'error', 'import/no-unresolved': 'error', 'import/no-useless-path-segments': 'error', @@ -72,8 +72,8 @@ module.exports = { 'import/first': 'error', 'import/group-exports': 'off', 'import/newline-after-import': 'error', - // 'import/no-anonymous-default-export': 'error', - // 'import/no-default-export': 'error', + 'import/no-anonymous-default-export': 'off', // not compatible with neode + 'import/no-default-export': 'off', // not compatible with neode 'import/no-duplicates': 'error', 'import/no-named-default': 'error', 'import/no-namespace': 'error', @@ -128,7 +128,10 @@ module.exports = { 'n/no-restricted-require': 'error', // 'n/no-sync': 'error', // 'n/no-unpublished-bin': 'error', // part of n/recommended - 'n/no-unpublished-import': ['error', { 'allowModules': ['apollo-server-testing', 'rosie', '@faker-js/faker'] }], // part of n/recommended + 'n/no-unpublished-import': [ + '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-unsupported-features/es-builtins': 'error', // part of n/recommended // 'n/no-unsupported-features/es-syntax': 'error', // part of n/recommended @@ -148,7 +151,7 @@ module.exports = { // promise // 'promise/always-return': 'error', // part of promise/recommended - 'promise/avoid-new': 'error', + 'promise/avoid-new': 'error', // 'promise/catch-or-return': 'error', // part of promise/recommended // 'promise/no-callback-in-promise': 'warn', // part of promise/recommended 'promise/no-multiple-resolved': 'error', @@ -163,7 +166,7 @@ module.exports = { 'promise/prefer-catch': 'error', 'promise/spec-only': 'error', // 'promise/valid-params': 'error', // part of promise/recommended - + // eslint comments '@eslint-community/eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }], '@eslint-community/eslint-comments/no-restricted-disable': 'error', diff --git a/backend/jest.config.cjs b/backend/jest.config.cjs new file mode 100644 index 000000000..8d322ff08 --- /dev/null +++ b/backend/jest.config.cjs @@ -0,0 +1,26 @@ +/* eslint-disable import/no-commonjs */ +const { pathsToModuleNameMapper } = require('ts-jest') +const requireJSON5 = require('require-json5') +const { compilerOptions } = requireJSON5('./tsconfig.json') + +module.exports = { + verbose: true, + preset: 'ts-jest', + collectCoverage: true, + collectCoverageFrom: [ + '**/*.ts', + '!**/node_modules/**', + '!**/test/**', + '!**/build/**', + '!**/src/**/?(*.)+(spec|test).ts?(x)', + '!**/src/db/**', + ], + coverageThreshold: { + global: { + lines: 90, + }, + }, + testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'], + setupFilesAfterEnv: ['/test/setup.ts'], + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/' }), +} diff --git a/backend/jest.config.js b/backend/jest.config.js deleted file mode 100644 index 15eb22477..000000000 --- a/backend/jest.config.js +++ /dev/null @@ -1,20 +0,0 @@ -module.exports = { - verbose: true, - preset: 'ts-jest', - collectCoverage: true, - collectCoverageFrom: [ - '**/*.ts', - '!**/node_modules/**', - '!**/test/**', - '!**/build/**', - '!**/src/**/?(*.)+(spec|test).ts?(x)', - '!**/src/db/**' - ], - coverageThreshold: { - global: { - lines: 90, - }, - }, - testMatch: ['**/src/**/?(*.)+(spec|test).ts?(x)'], - setupFilesAfterEnv: ['/test/setup.ts'] -} diff --git a/backend/package.json b/backend/package.json index 76c85c683..f093fff62 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,14 +11,14 @@ "__migrate": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations", "prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js", "start": "node build/src/", - "build": "tsc && ./scripts/build.copy.files.sh", - "dev": "nodemon --exec ts-node src/ -e js,ts,gql", + "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 --ext .js,.ts ./src", "test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles", - "db:clean": "ts-node src/db/clean.ts", + "db:clean": "ts-node --require tsconfig-paths/register src/db/clean.ts", "db:reset": "yarn run db:clean", - "db:seed": "ts-node src/db/seed.ts", + "db:seed": "ts-node --require tsconfig-paths/register src/db/seed.ts", "db:migrate": "yarn run __migrate --store ./src/db/migrate/store.ts", "db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create" }, @@ -119,9 +119,12 @@ "jest": "^29.7.0", "nodemon": "~3.1.9", "prettier": "^3.5.3", + "require-json5": "^1.3.0", "rosie": "^2.1.1", "ts-jest": "^29.3.1", "ts-node": "^10.9.2", + "tsc-alias": "^1.8.14", + "tsconfig-paths": "^4.2.0", "typescript": "^5.8.3" }, "resolutions": { diff --git a/backend/scripts/build.copy.files.sh b/backend/scripts/build.copy.files.sh index 9d17f46ae..da76a623c 100755 --- a/backend/scripts/build.copy.files.sh +++ b/backend/scripts/build.copy.files.sh @@ -1,5 +1,8 @@ #!/bin/sh +# 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/ diff --git a/backend/src/db/clean.ts b/backend/src/db/clean.ts index ae5ce7320..64ef91c31 100644 --- a/backend/src/db/clean.ts +++ b/backend/src/db/clean.ts @@ -1,5 +1,5 @@ /* eslint-disable n/no-process-exit */ -import CONFIG from '../config' +import CONFIG from '@config/index' import { cleanDatabase } from './factories' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { diff --git a/backend/src/db/compiler.ts b/backend/src/db/compiler.ts index 4dd36f16b..2d897762f 100644 --- a/backend/src/db/compiler.ts +++ b/backend/src/db/compiler.ts @@ -1,5 +1,7 @@ /* eslint-disable import/no-commonjs */ // eslint-disable-next-line n/no-unpublished-require const tsNode = require('ts-node') +// eslint-disable-next-line import/no-unassigned-import, import/no-extraneous-dependencies, n/no-unpublished-require +require('tsconfig-paths/register') module.exports = tsNode.register diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index c75c92fdd..5cb573f12 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -4,8 +4,8 @@ import { hashSync } from 'bcryptjs' import { Factory } from 'rosie' import { faker } from '@faker-js/faker' import { getDriver, getNeode } from './neo4j' -import CONFIG from '../config/index' -import generateInviteCode from '../schema/resolvers/helpers/generateInviteCode' +import CONFIG from '@config/index' +import generateInviteCode from '@schema/resolvers/helpers/generateInviteCode' const neode = getNeode() diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index b5dd43e16..742c9f11e 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -1,8 +1,8 @@ -import { getDriver, getNeode } from '../neo4j' +import { getDriver, getNeode } from '@db/neo4j' import { hashSync } from 'bcryptjs' import { v4 as uuid } from 'uuid' -import { categories } from '../../constants/categories' -import CONFIG from '../../config' +import { categories } from '@constants/categories' +import CONFIG from '@config/index' const defaultAdmin = { email: 'admin@example.org', diff --git a/backend/src/db/migrate/template.ts b/backend/src/db/migrate/template.ts index 9661dcf9c..f9eb1a338 100644 --- a/backend/src/db/migrate/template.ts +++ b/backend/src/db/migrate/template.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = '' 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 1480715ae..d1be6542b 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 @@ -2,8 +2,8 @@ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators' -import { getDriver } from '../neo4j' -import normalizeEmail from '../../schema/resolvers/helpers/normalizeEmail' +import { getDriver } from '@db/neo4j' +import normalizeEmail from '@schema/resolvers/helpers/normalizeEmail' export const description = ` This migration merges duplicate :User and :EmailAddress nodes. It became diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index f56389045..083bfa420 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -2,7 +2,7 @@ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError } from 'rxjs/operators' -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` This migration merges duplicate :Location nodes. It became diff --git a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts index 49506aae3..4743ff175 100644 --- a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts +++ b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` This migration creates a MUTED relationship between two edges(:User) that have a pre-existing BLOCKED relationship. diff --git a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts index 73c329bfc..84e15f9fb 100644 --- a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts +++ b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` This migration swaps the value stored in Location.lat with the value diff --git a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts index 8ef6976a3..8eee22318 100644 --- a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts +++ b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = 'This migration adds a fulltext index for the tags in order to search for Hasthags.' diff --git a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts index e949713b8..2a30d769e 100644 --- a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts +++ b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` We introduced a new node label 'Image' and we need a primary key for it. Best 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 index 6643e3540..bf154e3d1 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -1,10 +1,10 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' 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' +import s3Configs from '@config/index' import https from 'https' export const description = ` diff --git a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts index 6f347b99b..355eb8476 100644 --- a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts +++ b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts @@ -1,5 +1,5 @@ /* eslint-disable no-console */ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` Refactor all our image properties on posts and users to a dedicated type diff --git a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts index a8880d8e8..5ce75ab28 100644 --- a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = 'We should not maintain obsolete attributes for users who have been deleted.' diff --git a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts index 70d81e5c0..a2b5ff159 100644 --- a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = 'We should not maintain obsolete attributes for posts which have been deleted.' diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index 765c7919b..b79d74b9a 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -1,5 +1,5 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' import { existsSync } from 'node:fs' export const description = ` diff --git a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts index 0d8f28e1b..ce3515ac7 100644 --- a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts +++ b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` This migration adds the clickedCount property to all posts, setting it to 0. diff --git a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts index 31b9d69ff..5615aa4e0 100644 --- a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts +++ b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` This migration adds the viewedTeaserCount property to all posts, setting it to 0. diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.ts b/backend/src/db/migrations/20210506150512-add-donations-node.ts index b7e0e026a..95b7e9664 100644 --- a/backend/src/db/migrations/20210506150512-add-donations-node.ts +++ b/backend/src/db/migrations/20210506150512-add-donations-node.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' import { v4 as uuid } from 'uuid' export const description = diff --git a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts index a555efa3a..bd886db02 100644 --- a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts +++ b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = '' diff --git a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts index 586a090f4..08dc558fb 100644 --- a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts +++ b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` We introduced a new node label 'Group' and we need two primary keys 'id' and 'slug' for it. diff --git a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts index 34cf7b7a2..2239d6d06 100644 --- a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts +++ b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = '' diff --git a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts index 2ca705bf4..f33aa818a 100644 --- a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts +++ b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = 'Add to all existing posts the Article label' diff --git a/backend/src/db/migrations/20230608130637-add-postType-property.ts b/backend/src/db/migrations/20230608130637-add-postType-property.ts index 83c2f4ed3..26c99ce48 100644 --- a/backend/src/db/migrations/20230608130637-add-postType-property.ts +++ b/backend/src/db/migrations/20230608130637-add-postType-property.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = 'Add postType property Article to all posts' diff --git a/backend/src/db/migrations/20231017141022-fix-event-dates.ts b/backend/src/db/migrations/20231017141022-fix-event-dates.ts index e793e173c..b2edf17dc 100644 --- a/backend/src/db/migrations/20231017141022-fix-event-dates.ts +++ b/backend/src/db/migrations/20231017141022-fix-event-dates.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` Transform event start and end date of format 'YYYY-MM-DD HH:MM:SS' in CEST diff --git a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts index 026f7f29c..619b5f1fa 100644 --- a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts +++ b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts @@ -1,4 +1,4 @@ -import { getDriver } from '../neo4j' +import { getDriver } from '@db/neo4j' export const description = ` All authors observe their posts. diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index dc5bf2764..a83c9972d 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,8 +1,8 @@ /* eslint-disable import/no-named-as-default-member */ import neo4j from 'neo4j-driver' -import CONFIG from '../config' +import CONFIG from '@config/index' import Neode from 'neode' -import models from '../models' +import models from '@models/index' let driver const defaultOptions = { diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index 4183b8ce5..ae4e0ff01 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -1,8 +1,8 @@ /* eslint-disable n/no-process-exit */ import sample from 'lodash/sample' import { createTestClient } from 'apollo-server-testing' -import CONFIG from '../config' -import createServer from '../server' +import CONFIG from '@config/index' +import createServer from '@src/server' import { faker } from '@faker-js/faker' import Factory from './factories' import { getNeode, getDriver } from './neo4j' @@ -10,12 +10,12 @@ import { createGroupMutation, joinGroupMutation, changeGroupMemberRoleMutation, -} from '../graphql/groups' -import { createPostMutation } from '../graphql/posts' -import { createRoomMutation } from '../graphql/rooms' -import { createMessageMutation } from '../graphql/messages' -import { createCommentMutation } from '../graphql/comments' -import { categories } from '../constants/categories' +} from '@graphql/groups' +import { createPostMutation } from '@graphql/posts' +import { createRoomMutation } from '@graphql/rooms' +import { createMessageMutation } from '@graphql/messages' +import { createCommentMutation } from '@graphql/comments' +import { categories } from '@constants/categories' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot seed the database in a non-staging and real production environment!`) diff --git a/backend/src/jwt/decode.spec.ts b/backend/src/jwt/decode.spec.ts index ca27ef624..5dd1f3a20 100644 --- a/backend/src/jwt/decode.spec.ts +++ b/backend/src/jwt/decode.spec.ts @@ -1,5 +1,5 @@ -import Factory, { cleanDatabase } from '../db/factories' -import { getDriver, getNeode } from '../db/neo4j' +import Factory, { cleanDatabase } from '@db/factories' +import { getDriver, getNeode } from '@db/neo4j' import decode from './decode' import encode from './encode' diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts index 45888dead..7f614274e 100644 --- a/backend/src/jwt/decode.ts +++ b/backend/src/jwt/decode.ts @@ -1,5 +1,5 @@ import jwt from 'jsonwebtoken' -import CONFIG from '../config' +import CONFIG from '@config/index' export default async (driver, authorizationHeader) => { if (!authorizationHeader) return null diff --git a/backend/src/jwt/encode.spec.ts b/backend/src/jwt/encode.spec.ts index 37775eb55..b5a6884c9 100644 --- a/backend/src/jwt/encode.spec.ts +++ b/backend/src/jwt/encode.spec.ts @@ -1,6 +1,6 @@ import encode from './encode' import jwt from 'jsonwebtoken' -import CONFIG from '../config' +import CONFIG from '@config/index' describe('encode', () => { let payload diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts index 0df81fa02..e4600e695 100644 --- a/backend/src/jwt/encode.ts +++ b/backend/src/jwt/encode.ts @@ -1,5 +1,5 @@ import jwt from 'jsonwebtoken' -import CONFIG from '../config' +import CONFIG from '@config/index' // Generate an Access Token for the given User ID export default function encode(user) { diff --git a/backend/src/middleware/excerptMiddleware.ts b/backend/src/middleware/excerptMiddleware.ts index 28b30fb4f..f1ea1425b 100644 --- a/backend/src/middleware/excerptMiddleware.ts +++ b/backend/src/middleware/excerptMiddleware.ts @@ -1,5 +1,5 @@ import trunc from 'trunc-html' -import { DESCRIPTION_EXCERPT_HTML_LENGTH } from '../constants/groups' +import { DESCRIPTION_EXCERPT_HTML_LENGTH } from '@constants/groups' export default { Mutation: { diff --git a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts index 10d53ab7b..1ec96826b 100644 --- a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts +++ b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts @@ -1,8 +1,8 @@ import gql from 'graphql-tag' -import { cleanDatabase } from '../../db/factories' +import { cleanDatabase } from '@db/factories' import { createTestClient } from 'apollo-server-testing' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' let server let query diff --git a/backend/src/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts index 6c1e0d8ba..82f042ece 100644 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ b/backend/src/middleware/helpers/email/sendMail.ts @@ -1,5 +1,5 @@ -import CONFIG from '../../../config' -import { cleanHtml } from '../cleanHtml' +import CONFIG from '@config/index' +import { cleanHtml } from '@middleware/helpers/cleanHtml' import nodemailer from 'nodemailer' import { htmlToText } from 'nodemailer-html-to-text' diff --git a/backend/src/middleware/helpers/email/templateBuilder.spec.ts b/backend/src/middleware/helpers/email/templateBuilder.spec.ts index 437672a9a..7907fe56b 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.spec.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.spec.ts @@ -1,5 +1,5 @@ -import CONFIG from '../../../config' -import logosWebapp from '../../../config/logos' +import CONFIG from '@config/index' +import logosWebapp from '@config/logos' import { signupTemplate, emailVerificationTemplate, diff --git a/backend/src/middleware/helpers/email/templateBuilder.ts b/backend/src/middleware/helpers/email/templateBuilder.ts index 398cbabf9..4a3d26e89 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.ts @@ -1,8 +1,8 @@ /* eslint-disable import/no-namespace */ import mustache from 'mustache' -import CONFIG from '../../../config' -import metadata from '../../../config/metadata' -import logosWebapp from '../../../config/logos' +import CONFIG from '@config/index' +import metadata from '@config//metadata' +import logosWebapp from '@config//logos' import * as templates from './templates' import * as templatesEN from './templates/en' diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 8eca3c8e8..3f593920f 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-object-injection */ import { applyMiddleware } from 'graphql-middleware' -import CONFIG from '../config' +import CONFIG from '@config/index' import softDelete from './softDelete/softDeleteMiddleware' import sluggify from './sluggifyMiddleware' import excerpt from './excerptMiddleware' diff --git a/backend/src/middleware/languages/languages.spec.ts b/backend/src/middleware/languages/languages.spec.ts index 8daa311e1..ee41b6740 100644 --- a/backend/src/middleware/languages/languages.spec.ts +++ b/backend/src/middleware/languages/languages.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' let mutate diff --git a/backend/src/middleware/languages/languages.ts b/backend/src/middleware/languages/languages.ts index 3c043ceec..7a86fb2ef 100644 --- a/backend/src/middleware/languages/languages.ts +++ b/backend/src/middleware/languages/languages.ts @@ -1,5 +1,5 @@ import LanguageDetect from 'languagedetect' -import { removeHtmlTags } from '../helpers/cleanHtml' +import { removeHtmlTags } from '@middleware/helpers/cleanHtml' const setPostLanguage = (text, defaultLanguage) => { const lngDetector = new LanguageDetect() diff --git a/backend/src/middleware/login/loginMiddleware.ts b/backend/src/middleware/login/loginMiddleware.ts index abf0d0b18..04d189b4b 100644 --- a/backend/src/middleware/login/loginMiddleware.ts +++ b/backend/src/middleware/login/loginMiddleware.ts @@ -1,10 +1,10 @@ -import { sendMail } from '../helpers/email/sendMail' +import { sendMail } from '@middleware/helpers/email/sendMail' import { signupTemplate, resetPasswordTemplate, wrongAccountTemplate, emailVerificationTemplate, -} from '../helpers/email/templateBuilder' +} from '@middleware/helpers/email/templateBuilder' const sendSignupMail = async (resolve, root, args, context, resolveInfo) => { const { inviteCode } = args diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 50d655484..ad4d80e04 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -1,17 +1,17 @@ import gql from 'graphql-tag' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import { createTestClient } from 'apollo-server-testing' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer, { pubsub } from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer, { pubsub } from '@src/server' import { createGroupMutation, joinGroupMutation, leaveGroupMutation, changeGroupMemberRoleMutation, removeUserFromGroupMutation, -} from '../../graphql/groups' -import { createMessageMutation } from '../../graphql/messages' -import { createRoomMutation } from '../../graphql/rooms' +} from '@graphql/groups' +import { createMessageMutation } from '@graphql/messages' +import { createRoomMutation } from '@graphql/rooms' const sendMailMock = jest.fn() jest.mock('../helpers/email/sendMail', () => ({ diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 09212a29d..e08753ec2 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,11 +1,14 @@ /* eslint-disable security/detect-object-injection */ // eslint-disable-next-line import/no-cycle -import { pubsub, NOTIFICATION_ADDED } from '../../server' +import { pubsub, NOTIFICATION_ADDED } from '@src/server' import extractMentionedUsers from './mentions/extractMentionedUsers' -import { validateNotifyUsers } from '../validation/validationMiddleware' -import { sendMail } from '../helpers/email/sendMail' -import { chatMessageTemplate, notificationTemplate } from '../helpers/email/templateBuilder' -import { isUserOnline } from '../helpers/isUserOnline' +import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' +import { sendMail } from '@middleware/helpers/email/sendMail' +import { + chatMessageTemplate, + notificationTemplate, +} from '@middleware/helpers/email/templateBuilder' +import { isUserOnline } from '@middleware/helpers/isUserOnline' const queryNotificationEmails = async (context, notificationUserIds) => { if (!(notificationUserIds && notificationUserIds.length)) return [] diff --git a/backend/src/middleware/notifications/observing-posts.spec.ts b/backend/src/middleware/notifications/observing-posts.spec.ts index 13b971ed8..9e85f3733 100644 --- a/backend/src/middleware/notifications/observing-posts.spec.ts +++ b/backend/src/middleware/notifications/observing-posts.spec.ts @@ -1,10 +1,10 @@ import gql from 'graphql-tag' -import { cleanDatabase } from '../../db/factories' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' -import CONFIG from '../../config' +import CONFIG from '@config/index' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/middleware/orderByMiddleware.spec.ts b/backend/src/middleware/orderByMiddleware.spec.ts index 7453cf301..639e87c00 100644 --- a/backend/src/middleware/orderByMiddleware.spec.ts +++ b/backend/src/middleware/orderByMiddleware.spec.ts @@ -1,8 +1,8 @@ import gql from 'graphql-tag' -import { cleanDatabase } from '../db/factories' -import { getNeode, getDriver } from '../db/neo4j' +import { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '../server' +import createServer from '@src/server' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/middleware/permissionsMiddleware.spec.ts b/backend/src/middleware/permissionsMiddleware.spec.ts index 667e74164..54681b519 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.ts +++ b/backend/src/middleware/permissionsMiddleware.spec.ts @@ -1,9 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '../server' -import Factory, { cleanDatabase } from '../db/factories' +import createServer from '@src/server' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver, getNeode } from '../db/neo4j' -import CONFIG from '../config' +import { getDriver, getNeode } from '@db/neo4j' +import CONFIG from '@config/index' const instance = getNeode() const driver = getDriver() diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index a38610efd..2be73edd4 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -1,7 +1,7 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield' -import { getNeode } from '../db/neo4j' -import CONFIG from '../config' -import { validateInviteCode } from '../schema/resolvers/transactions/inviteCodes' +import { getNeode } from '@db/neo4j' +import CONFIG from '@config/index' +import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes' const debug = !!CONFIG.DEBUG const allowExternalErrors = true diff --git a/backend/src/middleware/sentryMiddleware.ts b/backend/src/middleware/sentryMiddleware.ts index ace2c4eeb..ac2d575ef 100644 --- a/backend/src/middleware/sentryMiddleware.ts +++ b/backend/src/middleware/sentryMiddleware.ts @@ -1,5 +1,5 @@ import { sentry } from 'graphql-middleware-sentry' -import CONFIG from '../config' +import CONFIG from '@config/index' // eslint-disable-next-line import/no-mutable-exports let sentryMiddleware: any = (resolve, root, args, context, resolveInfo) => diff --git a/backend/src/middleware/slugifyMiddleware.spec.ts b/backend/src/middleware/slugifyMiddleware.spec.ts index 26bb2cb96..b09b33a13 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.ts +++ b/backend/src/middleware/slugifyMiddleware.spec.ts @@ -1,10 +1,10 @@ -import { getNeode, getDriver } from '../db/neo4j' -import createServer from '../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../db/factories' -import { createGroupMutation, updateGroupMutation } from '../graphql/groups' -import { createPostMutation } from '../graphql/posts' -import { signupVerificationMutation } from '../graphql/authentications' +import Factory, { cleanDatabase } from '@db/factories' +import { createGroupMutation, updateGroupMutation } from '@graphql/groups' +import { createPostMutation } from '@graphql/posts' +import { signupVerificationMutation } from '@graphql/authentications' let authenticatedUser let variables diff --git a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts index 88d46a1c7..c4e6b4d6e 100644 --- a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts +++ b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' const neode = getNeode() diff --git a/backend/src/middleware/userInteractions.spec.ts b/backend/src/middleware/userInteractions.spec.ts index 94d1ff274..b2ea1c389 100644 --- a/backend/src/middleware/userInteractions.spec.ts +++ b/backend/src/middleware/userInteractions.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../db/neo4j' -import createServer from '../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' let query, aUser, bUser, post, authenticatedUser, variables diff --git a/backend/src/middleware/validation/validationMiddleware.spec.ts b/backend/src/middleware/validation/validationMiddleware.spec.ts index 2e1cd6fa7..af8dfab97 100644 --- a/backend/src/middleware/validation/validationMiddleware.spec.ts +++ b/backend/src/middleware/validation/validationMiddleware.spec.ts @@ -1,8 +1,8 @@ import gql from 'graphql-tag' -import Factory, { cleanDatabase } from '../../db/factories' -import { getNeode, getDriver } from '../../db/neo4j' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server' +import createServer from '@src/server' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index 7b1b66145..5b9114837 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -1,4 +1,4 @@ -import walkRecursive from '../helpers/walkRecursive' +import walkRecursive from '@helpers/walkRecursive' import { cleanHtml } from './helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized diff --git a/backend/src/models/User.spec.ts b/backend/src/models/User.spec.ts index 17f2fe0a9..3fde03462 100644 --- a/backend/src/models/User.spec.ts +++ b/backend/src/models/User.spec.ts @@ -1,5 +1,5 @@ -import { cleanDatabase } from '../db/factories' -import { getNeode, getDriver } from '../db/neo4j' +import { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/comments.spec.ts b/backend/src/schema/resolvers/comments.spec.ts index b2730dad4..91bf7494d 100644 --- a/backend/src/schema/resolvers/comments.spec.ts +++ b/backend/src/schema/resolvers/comments.spec.ts @@ -1,8 +1,8 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server' -import { getNeode, getDriver } from '../../db/neo4j' +import createServer from '@src/server' +import { getNeode, getDriver } from '@db/neo4j' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/donations.spec.ts b/backend/src/schema/resolvers/donations.spec.ts index 9fc010eca..fdc4aa976 100644 --- a/backend/src/schema/resolvers/donations.spec.ts +++ b/backend/src/schema/resolvers/donations.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' let mutate, query, authenticatedUser, variables const instance = getNeode() diff --git a/backend/src/schema/resolvers/emails.spec.ts b/backend/src/schema/resolvers/emails.spec.ts index 02a631495..db17ee93e 100644 --- a/backend/src/schema/resolvers/emails.spec.ts +++ b/backend/src/schema/resolvers/emails.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver, getNeode } from '../../db/neo4j' -import createServer from '../../server' +import { getDriver, getNeode } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' const neode = getNeode() diff --git a/backend/src/schema/resolvers/embeds.spec.ts b/backend/src/schema/resolvers/embeds.spec.ts index 51dc18bd8..095497142 100644 --- a/backend/src/schema/resolvers/embeds.spec.ts +++ b/backend/src/schema/resolvers/embeds.spec.ts @@ -2,7 +2,7 @@ import fetch from 'node-fetch' import fs from 'node:fs' import path from 'node:path' import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server' +import createServer from '@src/server' import gql from 'graphql-tag' jest.mock('node-fetch') diff --git a/backend/src/schema/resolvers/filter-posts.spec.ts b/backend/src/schema/resolvers/filter-posts.spec.ts index 95a072d8a..20b664dc8 100644 --- a/backend/src/schema/resolvers/filter-posts.spec.ts +++ b/backend/src/schema/resolvers/filter-posts.spec.ts @@ -1,9 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' -import CONFIG from '../../config' -import { filterPosts, createPostMutation } from '../../graphql/posts' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' +import CONFIG from '@config/index' +import { filterPosts, createPostMutation } from '@graphql/posts' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/schema/resolvers/follow.spec.ts b/backend/src/schema/resolvers/follow.spec.ts index c9d8dc1bf..25dd8ac1a 100644 --- a/backend/src/schema/resolvers/follow.spec.ts +++ b/backend/src/schema/resolvers/follow.spec.ts @@ -1,7 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' -import { getDriver, getNeode } from '../../db/neo4j' -import createServer from '../../server' +import Factory, { cleanDatabase } from '@db/factories' +import { getDriver, getNeode } from '@db/neo4j' +import createServer from '@src/server' import gql from 'graphql-tag' const driver = getDriver() diff --git a/backend/src/schema/resolvers/follow.ts b/backend/src/schema/resolvers/follow.ts index 6cf4938c7..11447974d 100644 --- a/backend/src/schema/resolvers/follow.ts +++ b/backend/src/schema/resolvers/follow.ts @@ -1,4 +1,4 @@ -import { getNeode } from '../../db/neo4j' +import { getNeode } from '@db/neo4j' const neode = getNeode() diff --git a/backend/src/schema/resolvers/groups.spec.ts b/backend/src/schema/resolvers/groups.spec.ts index 1d66b376c..9cea62491 100644 --- a/backend/src/schema/resolvers/groups.spec.ts +++ b/backend/src/schema/resolvers/groups.spec.ts @@ -1,5 +1,5 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import { createGroupMutation, updateGroupMutation, @@ -9,10 +9,10 @@ import { removeUserFromGroupMutation, groupMembersQuery, groupQuery, -} from '../../graphql/groups' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' -import CONFIG from '../../config' +} from '@graphql/groups' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' +import CONFIG from '@config/index' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/groups.ts b/backend/src/schema/resolvers/groups.ts index f5282a3bb..90fbe1ba0 100644 --- a/backend/src/schema/resolvers/groups.ts +++ b/backend/src/schema/resolvers/groups.ts @@ -1,9 +1,9 @@ import { v4 as uuid } from 'uuid' import { UserInputError } from 'apollo-server' -import CONFIG from '../../config' -import { CATEGORIES_MIN, CATEGORIES_MAX } from '../../constants/categories' -import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '../../constants/groups' -import { removeHtmlTags } from '../../middleware/helpers/cleanHtml' +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 Resolver, { removeUndefinedNullValuesFromObject, convertObjectToCypherMapLiteral, diff --git a/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts b/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts index 1d1369e0d..0495a5dfd 100644 --- a/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts +++ b/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts @@ -1,4 +1,4 @@ -import { getMutedUsers } from '../users' +import { getMutedUsers } from '@schema/resolvers/users' import { mergeWith, isArray } from 'lodash' export const filterForMutedUsers = async (params, context) => { diff --git a/backend/src/schema/resolvers/helpers/generateInviteCode.ts b/backend/src/schema/resolvers/helpers/generateInviteCode.ts index e3f555931..6e580fab9 100644 --- a/backend/src/schema/resolvers/helpers/generateInviteCode.ts +++ b/backend/src/schema/resolvers/helpers/generateInviteCode.ts @@ -1,4 +1,4 @@ -import CONSTANTS_REGISTRATION from '../../../constants/registration' +import CONSTANTS_REGISTRATION from '@constants/registration' export default function generateInviteCode() { // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) diff --git a/backend/src/schema/resolvers/helpers/generateNonce.ts b/backend/src/schema/resolvers/helpers/generateNonce.ts index de1294567..7e0f7542c 100644 --- a/backend/src/schema/resolvers/helpers/generateNonce.ts +++ b/backend/src/schema/resolvers/helpers/generateNonce.ts @@ -1,4 +1,4 @@ -import CONSTANTS_REGISTRATION from '../../../constants/registration' +import CONSTANTS_REGISTRATION from '@constants/registration' // TODO: why this is not used in resolver 'requestPasswordReset'? export default function generateNonce() { diff --git a/backend/src/schema/resolvers/images/images.spec.ts b/backend/src/schema/resolvers/images/images.spec.ts index 94602ccd8..02d46c61d 100644 --- a/backend/src/schema/resolvers/images/images.spec.ts +++ b/backend/src/schema/resolvers/images/images.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable promise/prefer-await-to-callbacks */ import { deleteImage, mergeImage } from './images' -import { getNeode, getDriver } from '../../../db/neo4j' -import Factory, { cleanDatabase } from '../../../db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import Factory, { cleanDatabase } from '@db/factories' import { UserInputError } from 'apollo-server' const driver = getDriver() diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index 5d19d96f7..f34c6c226 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -6,8 +6,8 @@ import { S3 } from 'aws-sdk' import slug from 'slug' import { existsSync, unlinkSync, createWriteStream } from 'node:fs' import { UserInputError } from 'apollo-server' -import { getDriver } from '../../../db/neo4j' -import CONFIG from '../../../config' +import { getDriver } from '@db/neo4j' +import CONFIG from '@config/index' // const widths = [34, 160, 320, 640, 1024] const { AWS_ENDPOINT: endpoint, AWS_REGION: region, AWS_BUCKET: Bucket, S3_CONFIGURED } = CONFIG diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/schema/resolvers/inviteCodes.spec.ts index bd6a55bc8..93124dbe2 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.ts +++ b/backend/src/schema/resolvers/inviteCodes.spec.ts @@ -1,10 +1,10 @@ /* eslint-disable security/detect-non-literal-regexp */ -import Factory, { cleanDatabase } from '../../db/factories' -import { getDriver } from '../../db/neo4j' +import Factory, { cleanDatabase } from '@db/factories' +import { getDriver } from '@db/neo4j' import gql from 'graphql-tag' -import createServer from '../../server' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' -import CONSTANTS_REGISTRATION from '../../constants/registration' +import CONSTANTS_REGISTRATION from '@constants/registration' let user let query diff --git a/backend/src/schema/resolvers/locations.spec.ts b/backend/src/schema/resolvers/locations.spec.ts index 82aebd441..8880ed830 100644 --- a/backend/src/schema/resolvers/locations.spec.ts +++ b/backend/src/schema/resolvers/locations.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' let mutate, authenticatedUser diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index 83d9fdc6b..d6bd752c0 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -1,9 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' -import { getNeode, getDriver } from '../../db/neo4j' -import { createRoomMutation, roomQuery } from '../../graphql/rooms' -import { createMessageMutation, messageQuery, markMessagesAsSeen } from '../../graphql/messages' -import createServer, { pubsub } from '../../server' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import { createRoomMutation, roomQuery } from '@graphql/rooms' +import { createMessageMutation, messageQuery, markMessagesAsSeen } from '@graphql/messages' +import createServer, { pubsub } from '@src/server' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index c1381045f..729059307 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -2,7 +2,7 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import Resolver from './helpers/Resolver' import { getUnreadRoomsCount } from './rooms' -import { pubsub, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '../../server' +import { pubsub, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' import { withFilter } from 'graphql-subscriptions' const setMessagesAsDistributed = async (undistributedMessagesIds, session) => { diff --git a/backend/src/schema/resolvers/moderation.spec.ts b/backend/src/schema/resolvers/moderation.spec.ts index 1665e9446..a8155d1a8 100644 --- a/backend/src/schema/resolvers/moderation.spec.ts +++ b/backend/src/schema/resolvers/moderation.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/notifications.spec.ts b/backend/src/schema/resolvers/notifications.spec.ts index e3bcb9489..6b3d08b8b 100644 --- a/backend/src/schema/resolvers/notifications.spec.ts +++ b/backend/src/schema/resolvers/notifications.spec.ts @@ -1,13 +1,13 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver } from '../../db/neo4j' +import { getDriver } from '@db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server' +import createServer from '@src/server' import { markAsReadMutation, markAllAsReadMutation, notificationQuery, -} from '../../graphql/notifications' +} from '@graphql/notifications' const driver = getDriver() let authenticatedUser diff --git a/backend/src/schema/resolvers/notifications.ts b/backend/src/schema/resolvers/notifications.ts index 6a3e232cc..63307d54c 100644 --- a/backend/src/schema/resolvers/notifications.ts +++ b/backend/src/schema/resolvers/notifications.ts @@ -1,6 +1,6 @@ import log from './helpers/databaseLogger' import { withFilter } from 'graphql-subscriptions' -import { pubsub, NOTIFICATION_ADDED } from '../../server' +import { pubsub, NOTIFICATION_ADDED } from '@src/server' export default { Subscription: { diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/schema/resolvers/observePosts.spec.ts index 2d98c33a7..ab94a1934 100644 --- a/backend/src/schema/resolvers/observePosts.spec.ts +++ b/backend/src/schema/resolvers/observePosts.spec.ts @@ -1,11 +1,11 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' -import { createPostMutation } from '../../graphql/posts' -import CONFIG from '../../config' +import { createPostMutation } from '@graphql/posts' +import CONFIG from '@config/index' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/schema/resolvers/passwordReset.spec.ts index d0ca3e4a8..f939b3b1c 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/schema/resolvers/passwordReset.spec.ts @@ -1,9 +1,9 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import CONSTANTS_REGISTRATION from '../../constants/registration' +import { getNeode, getDriver } from '@db/neo4j' +import CONSTANTS_REGISTRATION from '@constants/registration' import createPasswordReset from './helpers/createPasswordReset' -import createServer from '../../server' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' const neode = getNeode() diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index 4adca11d3..8ec26538c 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -1,6 +1,6 @@ import { v4 as uuid } from 'uuid' import bcrypt from 'bcryptjs' -import CONSTANTS_REGISTRATION from '../../constants/registration' +import CONSTANTS_REGISTRATION from '@constants/registration' import createPasswordReset from './helpers/createPasswordReset' export default { diff --git a/backend/src/schema/resolvers/posts.spec.ts b/backend/src/schema/resolvers/posts.spec.ts index d7eb063d2..3c52e78f8 100644 --- a/backend/src/schema/resolvers/posts.spec.ts +++ b/backend/src/schema/resolvers/posts.spec.ts @@ -1,10 +1,10 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' -import { createPostMutation } from '../../graphql/posts' -import CONFIG from '../../config' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' +import { createPostMutation } from '@graphql/posts' +import CONFIG from '@config/index' CONFIG.CATEGORIES_ACTIVE = true diff --git a/backend/src/schema/resolvers/posts.ts b/backend/src/schema/resolvers/posts.ts index ce342cea7..534782369 100644 --- a/backend/src/schema/resolvers/posts.ts +++ b/backend/src/schema/resolvers/posts.ts @@ -9,7 +9,7 @@ import { filterInvisiblePosts } from './helpers/filterInvisiblePosts' import { filterPostsOfMyGroups } from './helpers/filterPostsOfMyGroups' import { validateEventParams } from './helpers/events' import { createOrUpdateLocations } from './users/location' -import CONFIG from '../../config' +import CONFIG from '@config/index' const maintainPinnedPosts = (params) => { const pinnedPostFilter = { pinned: true } diff --git a/backend/src/schema/resolvers/postsInGroups.spec.ts b/backend/src/schema/resolvers/postsInGroups.spec.ts index c7fc34ec7..d6b19e6d2 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.ts +++ b/backend/src/schema/resolvers/postsInGroups.spec.ts @@ -1,28 +1,28 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createGroupMutation, changeGroupMemberRoleMutation, leaveGroupMutation, -} from '../../graphql/groups' +} from '@graphql/groups' import { createPostMutation, postQuery, filterPosts, profilePagePosts, searchPosts, -} from '../../graphql/posts' -import { createCommentMutation } from '../../graphql/comments' +} from '@graphql/posts' +import { createCommentMutation } from '@graphql/comments' // eslint-disable-next-line no-unused-vars -import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '../../constants/groups' -import CONFIG from '../../config' -import { signupVerificationMutation } from '../../graphql/authentications' +import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '@constants/groups' +import CONFIG from '@config/index' +import { signupVerificationMutation } from '@graphql/authentications' CONFIG.CATEGORIES_ACTIVE = false -jest.mock('../../constants/groups', () => { +jest.mock('@constants/groups', () => { return { __esModule: true, DESCRIPTION_WITHOUT_HTML_LENGTH_MIN: 5, diff --git a/backend/src/schema/resolvers/registration.spec.ts b/backend/src/schema/resolvers/registration.spec.ts index 54e7f1ba7..cb63fbe5c 100644 --- a/backend/src/schema/resolvers/registration.spec.ts +++ b/backend/src/schema/resolvers/registration.spec.ts @@ -1,9 +1,9 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver, getNeode } from '../../db/neo4j' -import createServer from '../../server' +import { getDriver, getNeode } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' -import CONFIG from '../../config' +import CONFIG from '@config/index' const neode = getNeode() diff --git a/backend/src/schema/resolvers/registration.ts b/backend/src/schema/resolvers/registration.ts index 8d5aac346..12f13fff3 100644 --- a/backend/src/schema/resolvers/registration.ts +++ b/backend/src/schema/resolvers/registration.ts @@ -1,6 +1,6 @@ import { UserInputError } from 'apollo-server' -import { getNeode } from '../../db/neo4j' -import encryptPassword from '../../helpers/encryptPassword' +import { getNeode } from '@db/neo4j' +import encryptPassword from '@helpers/encryptPassword' import generateNonce from './helpers/generateNonce' import existingEmailAddress from './helpers/existingEmailAddress' import normalizeEmail from './helpers/normalizeEmail' diff --git a/backend/src/schema/resolvers/reports.spec.ts b/backend/src/schema/resolvers/reports.spec.ts index 2e6b4d302..f1e405c37 100644 --- a/backend/src/schema/resolvers/reports.spec.ts +++ b/backend/src/schema/resolvers/reports.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server' -import Factory, { cleanDatabase } from '../../db/factories' +import createServer from '@src/server' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver, getNeode } from '../../db/neo4j' +import { getDriver, getNeode } from '@db/neo4j' const instance = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/rewards.spec.ts b/backend/src/schema/resolvers/rewards.spec.ts index 06fe87ec0..cbd8c58c7 100644 --- a/backend/src/schema/resolvers/rewards.spec.ts +++ b/backend/src/schema/resolvers/rewards.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' const driver = getDriver() const instance = getNeode() diff --git a/backend/src/schema/resolvers/rewards.ts b/backend/src/schema/resolvers/rewards.ts index c271ca8f8..4022f9180 100644 --- a/backend/src/schema/resolvers/rewards.ts +++ b/backend/src/schema/resolvers/rewards.ts @@ -1,4 +1,4 @@ -import { getNeode } from '../../db/neo4j' +import { getNeode } from '@db/neo4j' import { UserInputError } from 'apollo-server' const neode = getNeode() diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts index 2e26dc1e3..154f9eb43 100644 --- a/backend/src/schema/resolvers/rooms.spec.ts +++ b/backend/src/schema/resolvers/rooms.spec.ts @@ -1,9 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' -import { getNeode, getDriver } from '../../db/neo4j' -import { createRoomMutation, roomQuery, unreadRoomsQuery } from '../../graphql/rooms' -import { createMessageMutation } from '../../graphql/messages' -import createServer from '../../server' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import { createRoomMutation, roomQuery, unreadRoomsQuery } from '@graphql/rooms' +import { createMessageMutation } from '@graphql/messages' +import createServer from '@src/server' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/schema/resolvers/rooms.ts index 5382c5ee7..479e361ea 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/schema/resolvers/rooms.ts @@ -1,6 +1,6 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import Resolver from './helpers/Resolver' -import { pubsub, ROOM_COUNT_UPDATED } from '../../server' +import { pubsub, ROOM_COUNT_UPDATED } from '@src/server' import { withFilter } from 'graphql-subscriptions' export const getUnreadRoomsCount = async (userId, session) => { diff --git a/backend/src/schema/resolvers/searches.spec.ts b/backend/src/schema/resolvers/searches.spec.ts index f889c2ac8..385dc5a36 100644 --- a/backend/src/schema/resolvers/searches.spec.ts +++ b/backend/src/schema/resolvers/searches.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' let query, authenticatedUser, user diff --git a/backend/src/schema/resolvers/shout.spec.ts b/backend/src/schema/resolvers/shout.spec.ts index 294a28a76..02d52f46f 100644 --- a/backend/src/schema/resolvers/shout.spec.ts +++ b/backend/src/schema/resolvers/shout.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' let mutate, query, authenticatedUser, variables const instance = getNeode() diff --git a/backend/src/schema/resolvers/socialMedia.spec.ts b/backend/src/schema/resolvers/socialMedia.spec.ts index 8265e8376..51e888118 100644 --- a/backend/src/schema/resolvers/socialMedia.spec.ts +++ b/backend/src/schema/resolvers/socialMedia.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '../../server' -import Factory, { cleanDatabase } from '../../db/factories' +import createServer from '@src/server' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver } from '../../db/neo4j' +import { getDriver } from '@db/neo4j' const driver = getDriver() diff --git a/backend/src/schema/resolvers/socialMedia.ts b/backend/src/schema/resolvers/socialMedia.ts index c5b9dcd91..6503a5110 100644 --- a/backend/src/schema/resolvers/socialMedia.ts +++ b/backend/src/schema/resolvers/socialMedia.ts @@ -1,4 +1,4 @@ -import { getNeode } from '../../db/neo4j' +import { getNeode } from '@db/neo4j' import Resolver from './helpers/Resolver' const neode = getNeode() diff --git a/backend/src/schema/resolvers/statistics.spec.ts b/backend/src/schema/resolvers/statistics.spec.ts index 15aa2d449..f81c1ba0b 100644 --- a/backend/src/schema/resolvers/statistics.spec.ts +++ b/backend/src/schema/resolvers/statistics.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' let query, authenticatedUser const instance = getNeode() diff --git a/backend/src/schema/resolvers/userData.spec.ts b/backend/src/schema/resolvers/userData.spec.ts index 3c521a4f1..6e9c27c86 100644 --- a/backend/src/schema/resolvers/userData.spec.ts +++ b/backend/src/schema/resolvers/userData.spec.ts @@ -1,7 +1,7 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' let query, authenticatedUser diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/schema/resolvers/user_management.spec.ts index 797f08126..8b8dcc717 100644 --- a/backend/src/schema/resolvers/user_management.spec.ts +++ b/backend/src/schema/resolvers/user_management.spec.ts @@ -1,14 +1,14 @@ /* eslint-disable promise/prefer-await-to-callbacks */ import jwt from 'jsonwebtoken' -import CONFIG from '../../config' -import Factory, { cleanDatabase } from '../../db/factories' +import CONFIG from '@config/index' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { loginMutation } from '../../graphql/userManagement' +import { loginMutation } from '@graphql/userManagement' import { createTestClient } from 'apollo-server-testing' -import createServer, { context } from '../../server' -import encode from '../../jwt/encode' -import { getNeode, getDriver } from '../../db/neo4j' -import { categories } from '../../constants/categories' +import createServer, { context } from '@src/server' +import encode from '@jwt/encode' +import { getNeode, getDriver } from '@db/neo4j' +import { categories } from '@constants/categories' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/schema/resolvers/user_management.ts index d88eafdae..dceceab24 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/schema/resolvers/user_management.ts @@ -1,7 +1,7 @@ -import encode from '../../jwt/encode' +import encode from '@jwt/encode' import bcrypt from 'bcryptjs' import { AuthenticationError } from 'apollo-server' -import { getNeode } from '../../db/neo4j' +import { getNeode } from '@db/neo4j' import normalizeEmail from './helpers/normalizeEmail' import log from './helpers/databaseLogger' diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index 09f98ad53..d2a86623a 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -1,9 +1,9 @@ -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' -import { categories } from '../../constants/categories' +import { categories } from '@constants/categories' const categoryIds = ['cat9'] let user diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index cab0bc8a3..81306e13d 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -1,5 +1,5 @@ import { neo4jgraphql } from 'neo4j-graphql-js' -import { getNeode } from '../../db/neo4j' +import { getNeode } from '@db/neo4j' import { UserInputError, ForbiddenError } from 'apollo-server' import { mergeImage, deleteImage } from './images/images' import Resolver from './helpers/Resolver' diff --git a/backend/src/schema/resolvers/users/location.spec.ts b/backend/src/schema/resolvers/users/location.spec.ts index f77f8d7f0..0b4db63f3 100644 --- a/backend/src/schema/resolvers/users/location.spec.ts +++ b/backend/src/schema/resolvers/users/location.spec.ts @@ -1,8 +1,8 @@ import gql from 'graphql-tag' -import Factory, { cleanDatabase } from '../../../db/factories' -import { getNeode, getDriver } from '../../../db/neo4j' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '../../../server' +import createServer from '@src/server' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/schema/resolvers/users/location.ts index 49b7c6b1c..a05a4402b 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/schema/resolvers/users/location.ts @@ -5,8 +5,8 @@ import request from 'request' import { UserInputError } from 'apollo-server' // eslint-disable-next-line import/no-extraneous-dependencies import Debug from 'debug' -import asyncForEach from '../../../helpers/asyncForEach' -import CONFIG from '../../../config' +import asyncForEach from '@helpers/asyncForEach' +import CONFIG from '@config/index' const debug = Debug('human-connection:location') diff --git a/backend/src/schema/resolvers/users/mutedUsers.spec.ts b/backend/src/schema/resolvers/users/mutedUsers.spec.ts index 762893af0..4bfce2421 100644 --- a/backend/src/schema/resolvers/users/mutedUsers.spec.ts +++ b/backend/src/schema/resolvers/users/mutedUsers.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '../../../server' -import { cleanDatabase } from '../../../db/factories' +import createServer from '@src/server' +import { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../../db/neo4j' +import { getNeode, getDriver } from '@db/neo4j' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts b/backend/src/schema/resolvers/viewedTeaserCount.spec.ts index ee90d1a08..e73502324 100644 --- a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts +++ b/backend/src/schema/resolvers/viewedTeaserCount.spec.ts @@ -1,8 +1,8 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '../../db/factories' +import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '../../db/neo4j' -import createServer from '../../server' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' const driver = getDriver() const neode = getNeode() diff --git a/backend/tsconfig.json b/backend/tsconfig.json index b6f3526a3..7de3aad0c 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -29,7 +29,19 @@ // "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. */ + "paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */ + "@config/*": ["./src/config/*"], + "@constants/*": ["./src/constants/*"], + "@db/*": ["./src/db/*"], + "@graphql/*": ["./src/graphql/*"], + "@helpers/*": ["./src/helpers/*"], + "@jwt/*": ["./src/jwt/*"], + "@middleware/*": ["./src/middleware/*"], + "@models/*": ["./src/models/*"], + "@schema/*": ["./src/schema/*"], + "@src/*": ["./src/*"], + "@root/*": ["./*"] + }, // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ // "types": [], /* Specify type package names to be included without being referenced in a source file. */ diff --git a/backend/yarn.lock b/backend/yarn.lock index 204fe73bd..3588f5f26 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -3471,7 +3471,7 @@ cheerio@~1.0.0: undici "^6.19.5" whatwg-mimetype "^4.0.0" -chokidar@^3.5.2, chokidar@^3.6.0: +chokidar@^3.5.2, chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -3614,6 +3614,11 @@ commander@^6.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== +commander@^9.0.0: + version "9.5.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" @@ -5475,7 +5480,7 @@ globby@11.0.0: merge2 "^1.3.0" slash "^3.0.0" -globby@^11.1.0: +globby@^11.0.4, globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -6989,7 +6994,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.3: +json5@^2.2.2, json5@^2.2.3, json5@^2.x: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -7686,6 +7691,11 @@ mustache@^4.2.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== +mylas@^2.1.9: + version "2.1.13" + resolved "https://registry.yarnpkg.com/mylas/-/mylas-2.1.13.tgz#1e23b37d58fdcc76e15d8a5ed23f9ae9fc0cbdf4" + integrity sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg== + n-gram@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/n-gram/-/n-gram-1.1.1.tgz#a374dc176a9063a2388d1be18ed7c35828be2a97" @@ -8370,6 +8380,13 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +plimit-lit@^1.2.6: + version "1.6.1" + resolved "https://registry.yarnpkg.com/plimit-lit/-/plimit-lit-1.6.1.tgz#a34594671b31ee8e93c72d505dfb6852eb72374a" + integrity sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA== + dependencies: + queue-lit "^1.5.1" + possible-typed-array-names@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" @@ -8523,6 +8540,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= +queue-lit@^1.5.1: + version "1.5.2" + resolved "https://registry.yarnpkg.com/queue-lit/-/queue-lit-1.5.2.tgz#83c24d4f4764802377b05a6e5c73017caf3f8747" + integrity sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -8743,6 +8765,13 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-json5@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/require-json5/-/require-json5-1.3.0.tgz#b47d236194e458f144c615dd061bdca085628474" + integrity sha512-FkOrdR0kqHFwIqrlifaXNg6fdg2YcUL5lX9bYlaENKLlWp+g0GO/tRMAvoWIM2pYzAGp57oF/jgkVLwxGk7KyQ== + dependencies: + json5 "^2.x" + resolve-alpn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.0.tgz#058bb0888d1cd4d12474e9a4b6eb17bdd5addc44" @@ -9779,6 +9808,19 @@ ts-node@^10.9.2: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tsc-alias@^1.8.14: + version "1.8.14" + resolved "https://registry.yarnpkg.com/tsc-alias/-/tsc-alias-1.8.14.tgz#cbe9566bcb4b014c32d0704ac495ab3361436edc" + integrity sha512-abPL5KpLkZCR06QkgyOBaswNPgNL/Ub/am16tvQ0kTsmPx3FEhBOAsvIPUU8OkYrLv0JlzJEaJ1r6XkJBIQvYg== + dependencies: + chokidar "^3.5.3" + commander "^9.0.0" + get-tsconfig "^4.10.0" + globby "^11.0.4" + mylas "^2.1.9" + normalize-path "^3.0.0" + plimit-lit "^1.2.6" + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -9789,6 +9831,15 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" +tsconfig-paths@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@1.11.1: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" From f782032563bb98657dbeaeb8c2665d474c42662d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 9 Apr 2025 09:44:48 +0200 Subject: [PATCH 088/266] refactor(backend): lint - import/order (#8350) * lint - import/no-relative-parent-imports fix build error * fix md * lint import/order enabled rule import/order and fixed impot order in each file --- backend/.eslintrc.cjs | 48 +++++++++---------- backend/src/config/index.ts | 1 + backend/src/db/clean.ts | 1 + backend/src/db/factories.ts | 10 ++-- backend/src/db/migrate/store.ts | 5 +- ...123150105-merge_duplicate_user_accounts.ts | 1 + ...23150110-merge_duplicate_location_nodes.ts | 1 + .../20200312140328-bulk_upload_to_s3.ts | 6 ++- ...200326160326-remove_dangling_image_urls.ts | 3 +- .../20210506150512-add-donations-node.ts | 3 +- backend/src/db/neo4j.ts | 3 +- backend/src/db/seed.ts | 20 ++++---- backend/src/index.ts | 2 +- backend/src/jwt/decode.spec.ts | 1 + backend/src/jwt/decode.ts | 1 + backend/src/jwt/encode.spec.ts | 4 +- backend/src/jwt/encode.ts | 1 + backend/src/middleware/excerptMiddleware.ts | 1 + .../hashtags/hashtagsMiddleware.spec.ts | 5 +- backend/src/middleware/helpers/cleanHtml.ts | 2 +- .../src/middleware/helpers/email/sendMail.ts | 5 +- .../helpers/email/templateBuilder.spec.ts | 1 + .../helpers/email/templateBuilder.ts | 7 +-- backend/src/middleware/index.ts | 22 +++++---- .../middleware/languages/languages.spec.ts | 5 +- backend/src/middleware/languages/languages.ts | 1 + .../notificationsMiddleware.spec.ts | 7 +-- .../notifications/notificationsMiddleware.ts | 9 ++-- .../notifications/observing-posts.spec.ts | 6 +-- .../src/middleware/orderByMiddleware.spec.ts | 3 +- .../middleware/permissionsMiddleware.spec.ts | 7 +-- .../src/middleware/permissionsMiddleware.ts | 3 +- backend/src/middleware/sentryMiddleware.ts | 1 + .../src/middleware/slugifyMiddleware.spec.ts | 7 +-- .../softDelete/softDeleteMiddleware.spec.ts | 5 +- .../src/middleware/userInteractions.spec.ts | 5 +- .../validation/validationMiddleware.spec.ts | 3 +- backend/src/middleware/xssMiddleware.ts | 1 + backend/src/schema/index.ts | 3 +- backend/src/schema/resolvers/comments.spec.ts | 7 +-- backend/src/schema/resolvers/comments.ts | 1 + .../src/schema/resolvers/donations.spec.ts | 3 +- backend/src/schema/resolvers/emails.spec.ts | 5 +- backend/src/schema/resolvers/emails.ts | 7 +-- backend/src/schema/resolvers/embeds.spec.ts | 6 ++- .../schema/resolvers/embeds/findProvider.ts | 1 + .../src/schema/resolvers/embeds/scraper.ts | 9 ++-- .../src/schema/resolvers/filter-posts.spec.ts | 5 +- backend/src/schema/resolvers/follow.spec.ts | 3 +- backend/src/schema/resolvers/groups.spec.ts | 5 +- backend/src/schema/resolvers/groups.ts | 4 +- .../resolvers/helpers/filterForMutedUsers.ts | 3 +- .../schema/resolvers/images/images.spec.ts | 8 ++-- backend/src/schema/resolvers/images/images.ts | 10 ++-- backend/src/schema/resolvers/index.ts | 1 + .../src/schema/resolvers/inviteCodes.spec.ts | 7 +-- .../src/schema/resolvers/locations.spec.ts | 5 +- backend/src/schema/resolvers/locations.ts | 1 + backend/src/schema/resolvers/messages.spec.ts | 3 +- backend/src/schema/resolvers/messages.ts | 11 +++-- .../src/schema/resolvers/moderation.spec.ts | 3 +- .../schema/resolvers/notifications.spec.ts | 9 ++-- backend/src/schema/resolvers/notifications.ts | 4 +- .../src/schema/resolvers/observePosts.spec.ts | 8 ++-- .../schema/resolvers/passwordReset.spec.ts | 14 +++--- backend/src/schema/resolvers/passwordReset.ts | 4 +- backend/src/schema/resolvers/posts.spec.ts | 9 ++-- backend/src/schema/resolvers/posts.ts | 16 ++++--- .../schema/resolvers/postsInGroups.spec.ts | 11 ++--- .../src/schema/resolvers/registration.spec.ts | 7 +-- backend/src/schema/resolvers/registration.ts | 4 +- backend/src/schema/resolvers/reports.spec.ts | 5 +- backend/src/schema/resolvers/rewards.spec.ts | 3 +- backend/src/schema/resolvers/rewards.ts | 3 +- backend/src/schema/resolvers/rooms.spec.ts | 3 +- backend/src/schema/resolvers/rooms.ts | 8 ++-- backend/src/schema/resolvers/searches.spec.ts | 5 +- backend/src/schema/resolvers/shout.spec.ts | 3 +- .../src/schema/resolvers/socialMedia.spec.ts | 5 +- backend/src/schema/resolvers/socialMedia.ts | 1 + .../src/schema/resolvers/statistics.spec.ts | 3 +- backend/src/schema/resolvers/userData.spec.ts | 5 +- .../schema/resolvers/user_management.spec.ts | 17 +++---- .../src/schema/resolvers/user_management.ts | 8 ++-- backend/src/schema/resolvers/users.spec.ts | 7 +-- backend/src/schema/resolvers/users.ts | 10 ++-- .../schema/resolvers/users/location.spec.ts | 3 +- .../src/schema/resolvers/users/location.ts | 5 +- .../schema/resolvers/users/mutedUsers.spec.ts | 5 +- .../resolvers/viewedTeaserCount.spec.ts | 3 +- backend/src/schema/types/index.ts | 1 + backend/src/server.spec.ts | 1 + backend/src/server.ts | 22 +++++---- 93 files changed, 322 insertions(+), 218 deletions(-) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 5cdee6690..cff4c1de1 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -78,30 +78,30 @@ module.exports = { 'import/no-named-default': 'error', 'import/no-namespace': 'error', 'import/no-unassigned-import': 'error', - // 'import/order': [ - // 'error', - // { - // groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], - // 'newlines-between': 'always', - // pathGroups: [ - // { - // pattern: '@?*/**', - // group: 'external', - // position: 'after', - // }, - // { - // pattern: '@/**', - // group: 'external', - // position: 'after', - // }, - // ], - // alphabetize: { - // order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, - // caseInsensitive: true /* ignore case. Options: [true, false] */, - // }, - // distinctGroup: true, - // }, - // ], + 'import/order': [ + 'error', + { + groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], + 'newlines-between': 'always', + pathGroups: [ + { + pattern: '@?*/**', + group: 'external', + position: 'after', + }, + { + pattern: '@/**', + group: 'external', + position: 'after', + }, + ], + alphabetize: { + order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */, + caseInsensitive: true /* ignore case. Options: [true, false] */, + }, + distinctGroup: true, + }, + ], 'import/prefer-default-export': 'off', // n diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index e6a02a87d..5eb58fa89 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -2,6 +2,7 @@ /* eslint-disable n/no-unpublished-require */ /* eslint-disable n/no-missing-require */ import { config } from 'dotenv' + import emails from './emails' import metadata from './metadata' diff --git a/backend/src/db/clean.ts b/backend/src/db/clean.ts index 64ef91c31..0f316faf8 100644 --- a/backend/src/db/clean.ts +++ b/backend/src/db/clean.ts @@ -1,5 +1,6 @@ /* eslint-disable n/no-process-exit */ import CONFIG from '@config/index' + import { cleanDatabase } from './factories' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index 5cb573f12..459a8d186 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -1,12 +1,14 @@ -import { v4 as uuid } from 'uuid' -import slugify from 'slug' +import { faker } from '@faker-js/faker' import { hashSync } from 'bcryptjs' import { Factory } from 'rosie' -import { faker } from '@faker-js/faker' -import { getDriver, getNeode } from './neo4j' +import slugify from 'slug' +import { v4 as uuid } from 'uuid' + import CONFIG from '@config/index' import generateInviteCode from '@schema/resolvers/helpers/generateInviteCode' +import { getDriver, getNeode } from './neo4j' + const neode = getNeode() const uniqueImageUrl = (imageUrl) => { diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index 742c9f11e..1e32b49f5 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -1,8 +1,9 @@ -import { getDriver, getNeode } from '@db/neo4j' import { hashSync } from 'bcryptjs' import { v4 as uuid } from 'uuid' -import { categories } from '@constants/categories' + import CONFIG from '@config/index' +import { categories } from '@constants/categories' +import { getDriver, getNeode } from '@db/neo4j' const defaultAdmin = { email: 'admin@example.org', 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 d1be6542b..df4cec41e 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 @@ -2,6 +2,7 @@ /* eslint-disable promise/prefer-await-to-callbacks */ 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' diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index 083bfa420..89cef62fc 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -2,6 +2,7 @@ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError } from 'rxjs/operators' + import { getDriver } from '@db/neo4j' export const description = ` 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 index bf154e3d1..f0531b6c8 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -1,11 +1,13 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import { getDriver } from '@db/neo4j' +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 https from 'https' +import { getDriver } from '@db/neo4j' export const description = ` Upload all image files to a S3 compatible object storage in order to reduce diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index b79d74b9a..0190ead48 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -1,7 +1,8 @@ /* eslint-disable security/detect-non-literal-fs-filename */ -import { getDriver } from '@db/neo4j' import { existsSync } from 'node:fs' +import { getDriver } from '@db/neo4j' + export const description = ` In this review: https://github.com/Human-Connection/Human-Connection/pull/3262#discussion_r398634249 diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.ts b/backend/src/db/migrations/20210506150512-add-donations-node.ts index 95b7e9664..3d01f28bb 100644 --- a/backend/src/db/migrations/20210506150512-add-donations-node.ts +++ b/backend/src/db/migrations/20210506150512-add-donations-node.ts @@ -1,6 +1,7 @@ -import { getDriver } from '@db/neo4j' import { v4 as uuid } from 'uuid' +import { getDriver } from '@db/neo4j' + export const description = 'This migration adds a Donations node with default settings to the database.' diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index a83c9972d..c94c552f0 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,7 +1,8 @@ /* eslint-disable import/no-named-as-default-member */ import neo4j from 'neo4j-driver' -import CONFIG from '@config/index' import Neode from 'neode' + +import CONFIG from '@config/index' import models from '@models/index' let driver diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index ae4e0ff01..34a6ebb03 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -1,21 +1,23 @@ /* eslint-disable n/no-process-exit */ -import sample from 'lodash/sample' -import { createTestClient } from 'apollo-server-testing' -import CONFIG from '@config/index' -import createServer from '@src/server' import { faker } from '@faker-js/faker' -import Factory from './factories' -import { getNeode, getDriver } from './neo4j' +import { createTestClient } from 'apollo-server-testing' +import sample from 'lodash/sample' + +import CONFIG from '@config/index' +import { categories } from '@constants/categories' +import { createCommentMutation } from '@graphql/comments' import { createGroupMutation, joinGroupMutation, changeGroupMemberRoleMutation, } from '@graphql/groups' +import { createMessageMutation } from '@graphql/messages' import { createPostMutation } from '@graphql/posts' import { createRoomMutation } from '@graphql/rooms' -import { createMessageMutation } from '@graphql/messages' -import { createCommentMutation } from '@graphql/comments' -import { categories } from '@constants/categories' +import createServer from '@src/server' + +import Factory from './factories' +import { getNeode, getDriver } from './neo4j' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot seed the database in a non-staging and real production environment!`) diff --git a/backend/src/index.ts b/backend/src/index.ts index 59718dad1..35c215803 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,5 +1,5 @@ -import createServer from './server' import CONFIG from './config' +import createServer from './server' const { server, httpServer } = createServer() const url = new URL(CONFIG.GRAPHQL_URI) diff --git a/backend/src/jwt/decode.spec.ts b/backend/src/jwt/decode.spec.ts index 5dd1f3a20..29783bc6b 100644 --- a/backend/src/jwt/decode.spec.ts +++ b/backend/src/jwt/decode.spec.ts @@ -1,5 +1,6 @@ import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' + import decode from './decode' import encode from './encode' diff --git a/backend/src/jwt/decode.ts b/backend/src/jwt/decode.ts index 7f614274e..b4424e660 100644 --- a/backend/src/jwt/decode.ts +++ b/backend/src/jwt/decode.ts @@ -1,4 +1,5 @@ import jwt from 'jsonwebtoken' + import CONFIG from '@config/index' export default async (driver, authorizationHeader) => { diff --git a/backend/src/jwt/encode.spec.ts b/backend/src/jwt/encode.spec.ts index b5a6884c9..55c74bf8d 100644 --- a/backend/src/jwt/encode.spec.ts +++ b/backend/src/jwt/encode.spec.ts @@ -1,7 +1,9 @@ -import encode from './encode' import jwt from 'jsonwebtoken' + import CONFIG from '@config/index' +import encode from './encode' + describe('encode', () => { let payload beforeEach(() => { diff --git a/backend/src/jwt/encode.ts b/backend/src/jwt/encode.ts index e4600e695..110111faf 100644 --- a/backend/src/jwt/encode.ts +++ b/backend/src/jwt/encode.ts @@ -1,4 +1,5 @@ import jwt from 'jsonwebtoken' + import CONFIG from '@config/index' // Generate an Access Token for the given User ID diff --git a/backend/src/middleware/excerptMiddleware.ts b/backend/src/middleware/excerptMiddleware.ts index f1ea1425b..f903dd01c 100644 --- a/backend/src/middleware/excerptMiddleware.ts +++ b/backend/src/middleware/excerptMiddleware.ts @@ -1,4 +1,5 @@ import trunc from 'trunc-html' + import { DESCRIPTION_EXCERPT_HTML_LENGTH } from '@constants/groups' export default { diff --git a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts index 1ec96826b..2bb617a3d 100644 --- a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts +++ b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts @@ -1,6 +1,7 @@ -import gql from 'graphql-tag' -import { cleanDatabase } from '@db/factories' import { createTestClient } from 'apollo-server-testing' +import gql from 'graphql-tag' + +import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/middleware/helpers/cleanHtml.ts b/backend/src/middleware/helpers/cleanHtml.ts index 04d6deae4..e72746fcf 100644 --- a/backend/src/middleware/helpers/cleanHtml.ts +++ b/backend/src/middleware/helpers/cleanHtml.ts @@ -1,6 +1,6 @@ /* eslint-disable security/detect-unsafe-regex */ -import sanitizeHtml from 'sanitize-html' import linkifyHtml from 'linkify-html' +import sanitizeHtml from 'sanitize-html' export const removeHtmlTags = (input) => { return sanitizeHtml(input, { diff --git a/backend/src/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts index 82f042ece..02456f391 100644 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ b/backend/src/middleware/helpers/email/sendMail.ts @@ -1,8 +1,9 @@ -import CONFIG from '@config/index' -import { cleanHtml } from '@middleware/helpers/cleanHtml' 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 = diff --git a/backend/src/middleware/helpers/email/templateBuilder.spec.ts b/backend/src/middleware/helpers/email/templateBuilder.spec.ts index 7907fe56b..48e8b4c99 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.spec.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.spec.ts @@ -1,5 +1,6 @@ import CONFIG from '@config/index' import logosWebapp from '@config/logos' + import { signupTemplate, emailVerificationTemplate, diff --git a/backend/src/middleware/helpers/email/templateBuilder.ts b/backend/src/middleware/helpers/email/templateBuilder.ts index 4a3d26e89..c091bf1f8 100644 --- a/backend/src/middleware/helpers/email/templateBuilder.ts +++ b/backend/src/middleware/helpers/email/templateBuilder.ts @@ -1,12 +1,13 @@ /* eslint-disable import/no-namespace */ import mustache from 'mustache' -import CONFIG from '@config/index' -import metadata from '@config//metadata' + import logosWebapp from '@config//logos' +import metadata from '@config//metadata' +import CONFIG from '@config/index' import * as templates from './templates' -import * as templatesEN from './templates/en' 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) diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 3f593920f..225e02209 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -1,22 +1,24 @@ /* eslint-disable security/detect-object-injection */ import { applyMiddleware } from 'graphql-middleware' + import CONFIG from '@config/index' -import softDelete from './softDelete/softDeleteMiddleware' -import sluggify from './sluggifyMiddleware' + +import chatMiddleware from './chatMiddleware' import excerpt from './excerptMiddleware' -import xss from './xssMiddleware' -import permissions from './permissionsMiddleware' +import hashtags from './hashtags/hashtagsMiddleware' import includedFields from './includedFieldsMiddleware' -import orderBy from './orderByMiddleware' -import validation from './validation/validationMiddleware' +import languages from './languages/languages' +import login from './login/loginMiddleware' // eslint-disable-next-line import/no-cycle import notifications from './notifications/notificationsMiddleware' -import hashtags from './hashtags/hashtagsMiddleware' -import login from './login/loginMiddleware' +import orderBy from './orderByMiddleware' +import permissions from './permissionsMiddleware' import sentry from './sentryMiddleware' -import languages from './languages/languages' +import sluggify from './sluggifyMiddleware' +import softDelete from './softDelete/softDeleteMiddleware' import userInteractions from './userInteractions' -import chatMiddleware from './chatMiddleware' +import validation from './validation/validationMiddleware' +import xss from './xssMiddleware' export default (schema) => { const middlewares = { diff --git a/backend/src/middleware/languages/languages.spec.ts b/backend/src/middleware/languages/languages.spec.ts index ee41b6740..ca77acac8 100644 --- a/backend/src/middleware/languages/languages.spec.ts +++ b/backend/src/middleware/languages/languages.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +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' -import { createTestClient } from 'apollo-server-testing' let mutate let authenticatedUser diff --git a/backend/src/middleware/languages/languages.ts b/backend/src/middleware/languages/languages.ts index 7a86fb2ef..6149b90d5 100644 --- a/backend/src/middleware/languages/languages.ts +++ b/backend/src/middleware/languages/languages.ts @@ -1,4 +1,5 @@ import LanguageDetect from 'languagedetect' + import { removeHtmlTags } from '@middleware/helpers/cleanHtml' const setPostLanguage = (text, defaultLanguage) => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index ad4d80e04..75b84f720 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -1,8 +1,8 @@ -import gql from 'graphql-tag' -import Factory, { cleanDatabase } from '@db/factories' 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, { pubsub } from '@src/server' import { createGroupMutation, joinGroupMutation, @@ -12,6 +12,7 @@ import { } from '@graphql/groups' import { createMessageMutation } from '@graphql/messages' import { createRoomMutation } from '@graphql/rooms' +import createServer, { pubsub } from '@src/server' const sendMailMock = jest.fn() jest.mock('../helpers/email/sendMail', () => ({ diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index e08753ec2..237e294b4 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,14 +1,15 @@ /* eslint-disable security/detect-object-injection */ -// eslint-disable-next-line import/no-cycle -import { pubsub, NOTIFICATION_ADDED } from '@src/server' -import extractMentionedUsers from './mentions/extractMentionedUsers' -import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' import { sendMail } from '@middleware/helpers/email/sendMail' import { chatMessageTemplate, notificationTemplate, } from '@middleware/helpers/email/templateBuilder' import { isUserOnline } from '@middleware/helpers/isUserOnline' +import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' +// eslint-disable-next-line import/no-cycle +import { pubsub, NOTIFICATION_ADDED } from '@src/server' + +import extractMentionedUsers from './mentions/extractMentionedUsers' const queryNotificationEmails = async (context, notificationUserIds) => { if (!(notificationUserIds && notificationUserIds.length)) return [] diff --git a/backend/src/middleware/notifications/observing-posts.spec.ts b/backend/src/middleware/notifications/observing-posts.spec.ts index 9e85f3733..e10d61d9f 100644 --- a/backend/src/middleware/notifications/observing-posts.spec.ts +++ b/backend/src/middleware/notifications/observing-posts.spec.ts @@ -1,10 +1,10 @@ +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + +import CONFIG from '@config/index' import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' -import { createTestClient } from 'apollo-server-testing' - -import CONFIG from '@config/index' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/middleware/orderByMiddleware.spec.ts b/backend/src/middleware/orderByMiddleware.spec.ts index 639e87c00..9534af76d 100644 --- a/backend/src/middleware/orderByMiddleware.spec.ts +++ b/backend/src/middleware/orderByMiddleware.spec.ts @@ -1,7 +1,8 @@ +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import { createTestClient } from 'apollo-server-testing' import createServer from '@src/server' const neode = getNeode() diff --git a/backend/src/middleware/permissionsMiddleware.spec.ts b/backend/src/middleware/permissionsMiddleware.spec.ts index 54681b519..81d73bae8 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.ts +++ b/backend/src/middleware/permissionsMiddleware.spec.ts @@ -1,9 +1,10 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getDriver, getNeode } from '@db/neo4j' + import CONFIG from '@config/index' +import Factory, { cleanDatabase } from '@db/factories' +import { getDriver, getNeode } from '@db/neo4j' +import createServer from '@src/server' const instance = getNeode() const driver = getDriver() diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 2be73edd4..c1eaf4b75 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -1,6 +1,7 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield' -import { getNeode } from '@db/neo4j' + import CONFIG from '@config/index' +import { getNeode } from '@db/neo4j' import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes' const debug = !!CONFIG.DEBUG diff --git a/backend/src/middleware/sentryMiddleware.ts b/backend/src/middleware/sentryMiddleware.ts index ac2d575ef..b77f680d6 100644 --- a/backend/src/middleware/sentryMiddleware.ts +++ b/backend/src/middleware/sentryMiddleware.ts @@ -1,4 +1,5 @@ import { sentry } from 'graphql-middleware-sentry' + import CONFIG from '@config/index' // eslint-disable-next-line import/no-mutable-exports diff --git a/backend/src/middleware/slugifyMiddleware.spec.ts b/backend/src/middleware/slugifyMiddleware.spec.ts index b09b33a13..9e55d54b1 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.ts +++ b/backend/src/middleware/slugifyMiddleware.spec.ts @@ -1,10 +1,11 @@ -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' + import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import { signupVerificationMutation } from '@graphql/authentications' import { createGroupMutation, updateGroupMutation } from '@graphql/groups' import { createPostMutation } from '@graphql/posts' -import { signupVerificationMutation } from '@graphql/authentications' +import createServer from '@src/server' let authenticatedUser let variables diff --git a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts index c4e6b4d6e..fa62ed101 100644 --- a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts +++ b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +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' -import { createTestClient } from 'apollo-server-testing' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/middleware/userInteractions.spec.ts b/backend/src/middleware/userInteractions.spec.ts index b2ea1c389..37b5401e3 100644 --- a/backend/src/middleware/userInteractions.spec.ts +++ b/backend/src/middleware/userInteractions.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +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' -import { createTestClient } from 'apollo-server-testing' let query, aUser, bUser, post, authenticatedUser, variables diff --git a/backend/src/middleware/validation/validationMiddleware.spec.ts b/backend/src/middleware/validation/validationMiddleware.spec.ts index af8dfab97..8e4b4329f 100644 --- a/backend/src/middleware/validation/validationMiddleware.spec.ts +++ b/backend/src/middleware/validation/validationMiddleware.spec.ts @@ -1,7 +1,8 @@ +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import { createTestClient } from 'apollo-server-testing' import createServer from '@src/server' const neode = getNeode() diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index 5b9114837..3ed310b40 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -1,4 +1,5 @@ import walkRecursive from '@helpers/walkRecursive' + import { cleanHtml } from './helpers/cleanHtml' // exclamation mark separetes field names, that should not be sanitized diff --git a/backend/src/schema/index.ts b/backend/src/schema/index.ts index 07721bceb..9f83bc43b 100644 --- a/backend/src/schema/index.ts +++ b/backend/src/schema/index.ts @@ -1,6 +1,7 @@ import { makeAugmentedSchema } from 'neo4j-graphql-js' -import typeDefs from './types' + import resolvers from './resolvers' +import typeDefs from './types' export default makeAugmentedSchema({ typeDefs, diff --git a/backend/src/schema/resolvers/comments.spec.ts b/backend/src/schema/resolvers/comments.spec.ts index 91bf7494d..e92daf86e 100644 --- a/backend/src/schema/resolvers/comments.spec.ts +++ b/backend/src/schema/resolvers/comments.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' -import gql from 'graphql-tag' import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' +import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/comments.ts b/backend/src/schema/resolvers/comments.ts index b9c0271c1..897c71d6f 100644 --- a/backend/src/schema/resolvers/comments.ts +++ b/backend/src/schema/resolvers/comments.ts @@ -1,4 +1,5 @@ import { v4 as uuid } from 'uuid' + import Resolver from './helpers/Resolver' export default { diff --git a/backend/src/schema/resolvers/donations.spec.ts b/backend/src/schema/resolvers/donations.spec.ts index fdc4aa976..ef2070d4e 100644 --- a/backend/src/schema/resolvers/donations.spec.ts +++ b/backend/src/schema/resolvers/donations.spec.ts @@ -1,6 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/schema/resolvers/emails.spec.ts b/backend/src/schema/resolvers/emails.spec.ts index db17ee93e..c594f99f7 100644 --- a/backend/src/schema/resolvers/emails.spec.ts +++ b/backend/src/schema/resolvers/emails.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' import createServer from '@src/server' -import { createTestClient } from 'apollo-server-testing' const neode = getNeode() diff --git a/backend/src/schema/resolvers/emails.ts b/backend/src/schema/resolvers/emails.ts index ff37948f2..d4de9c87b 100644 --- a/backend/src/schema/resolvers/emails.ts +++ b/backend/src/schema/resolvers/emails.ts @@ -1,10 +1,11 @@ -import generateNonce from './helpers/generateNonce' -import Resolver from './helpers/Resolver' -import existingEmailAddress from './helpers/existingEmailAddress' import { UserInputError } from 'apollo-server' // eslint-disable-next-line import/extensions import Validator from 'neode/build/Services/Validator.js' + +import existingEmailAddress from './helpers/existingEmailAddress' +import generateNonce from './helpers/generateNonce' import normalizeEmail from './helpers/normalizeEmail' +import Resolver from './helpers/Resolver' export default { Query: { diff --git a/backend/src/schema/resolvers/embeds.spec.ts b/backend/src/schema/resolvers/embeds.spec.ts index 095497142..92dd224e3 100644 --- a/backend/src/schema/resolvers/embeds.spec.ts +++ b/backend/src/schema/resolvers/embeds.spec.ts @@ -1,9 +1,11 @@ -import fetch from 'node-fetch' import fs from 'node:fs' import path from 'node:path' + import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' import gql from 'graphql-tag' +import fetch from 'node-fetch' + +import createServer from '@src/server' jest.mock('node-fetch') const mockedFetch = jest.mocked(fetch) diff --git a/backend/src/schema/resolvers/embeds/findProvider.ts b/backend/src/schema/resolvers/embeds/findProvider.ts index 7bedf2a77..a9a30f2bf 100644 --- a/backend/src/schema/resolvers/embeds/findProvider.ts +++ b/backend/src/schema/resolvers/embeds/findProvider.ts @@ -1,5 +1,6 @@ import fs from 'node:fs' import path from 'node:path' + import { minimatch } from 'minimatch' let oEmbedProvidersFile = fs.readFileSync( diff --git a/backend/src/schema/resolvers/embeds/scraper.ts b/backend/src/schema/resolvers/embeds/scraper.ts index 4771ba160..e4e19e6b9 100644 --- a/backend/src/schema/resolvers/embeds/scraper.ts +++ b/backend/src/schema/resolvers/embeds/scraper.ts @@ -2,13 +2,14 @@ /* eslint-disable n/global-require */ /* eslint-disable import/no-commonjs */ /* eslint-disable import/no-named-as-default */ + +import { ApolloError } from 'apollo-server' +import isArray from 'lodash/isArray' +import isEmpty from 'lodash/isEmpty' +import mergeWith from 'lodash/mergeWith' import Metascraper from 'metascraper' import fetch from 'node-fetch' -import { ApolloError } from 'apollo-server' -import isEmpty from 'lodash/isEmpty' -import isArray from 'lodash/isArray' -import mergeWith from 'lodash/mergeWith' import findProvider from './findProvider' // eslint-disable-next-line import/no-extraneous-dependencies diff --git a/backend/src/schema/resolvers/filter-posts.spec.ts b/backend/src/schema/resolvers/filter-posts.spec.ts index 20b664dc8..d5d4485a3 100644 --- a/backend/src/schema/resolvers/filter-posts.spec.ts +++ b/backend/src/schema/resolvers/filter-posts.spec.ts @@ -1,9 +1,10 @@ import { createTestClient } from 'apollo-server-testing' + +import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' -import CONFIG from '@config/index' import { filterPosts, createPostMutation } from '@graphql/posts' +import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/schema/resolvers/follow.spec.ts b/backend/src/schema/resolvers/follow.spec.ts index 25dd8ac1a..1e05b2fea 100644 --- a/backend/src/schema/resolvers/follow.spec.ts +++ b/backend/src/schema/resolvers/follow.spec.ts @@ -1,8 +1,9 @@ import { createTestClient } from 'apollo-server-testing' +import gql from 'graphql-tag' + import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' import createServer from '@src/server' -import gql from 'graphql-tag' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/groups.spec.ts b/backend/src/schema/resolvers/groups.spec.ts index 9cea62491..624b09e39 100644 --- a/backend/src/schema/resolvers/groups.spec.ts +++ b/backend/src/schema/resolvers/groups.spec.ts @@ -1,5 +1,8 @@ import { createTestClient } from 'apollo-server-testing' + +import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' import { createGroupMutation, updateGroupMutation, @@ -10,9 +13,7 @@ import { groupMembersQuery, groupQuery, } from '@graphql/groups' -import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' -import CONFIG from '@config/index' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/groups.ts b/backend/src/schema/resolvers/groups.ts index 90fbe1ba0..8b383e702 100644 --- a/backend/src/schema/resolvers/groups.ts +++ b/backend/src/schema/resolvers/groups.ts @@ -1,9 +1,11 @@ -import { v4 as uuid } from 'uuid' import { UserInputError } from 'apollo-server' +import { v4 as uuid } from 'uuid' + 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 Resolver, { removeUndefinedNullValuesFromObject, convertObjectToCypherMapLiteral, diff --git a/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts b/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts index 0495a5dfd..5a53bf9cb 100644 --- a/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts +++ b/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts @@ -1,6 +1,7 @@ -import { getMutedUsers } from '@schema/resolvers/users' import { mergeWith, isArray } from 'lodash' +import { getMutedUsers } from '@schema/resolvers/users' + export const filterForMutedUsers = async (params, context) => { if (!context.user) return params const [mutedUsers] = await Promise.all([getMutedUsers(context)]) diff --git a/backend/src/schema/resolvers/images/images.spec.ts b/backend/src/schema/resolvers/images/images.spec.ts index 02d46c61d..a4eb5b1a5 100644 --- a/backend/src/schema/resolvers/images/images.spec.ts +++ b/backend/src/schema/resolvers/images/images.spec.ts @@ -1,9 +1,11 @@ /* eslint-disable promise/prefer-await-to-callbacks */ -import { deleteImage, mergeImage } from './images' -import { getNeode, getDriver } from '@db/neo4j' -import Factory, { cleanDatabase } from '@db/factories' import { UserInputError } from 'apollo-server' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' + +import { deleteImage, mergeImage } from './images' + const driver = getDriver() const neode = getNeode() const uuid = '[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}' diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index f34c6c226..46eb453c5 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -1,13 +1,15 @@ /* eslint-disable promise/avoid-new */ /* eslint-disable security/detect-non-literal-fs-filename */ +import { existsSync, unlinkSync, createWriteStream } from 'node:fs' import path from 'node:path' -import { v4 as uuid } from 'uuid' + +import { UserInputError } from 'apollo-server' import { S3 } from 'aws-sdk' import slug from 'slug' -import { existsSync, unlinkSync, createWriteStream } from 'node:fs' -import { UserInputError } from 'apollo-server' -import { getDriver } from '@db/neo4j' +import { v4 as uuid } from 'uuid' + import CONFIG from '@config/index' +import { getDriver } from '@db/neo4j' // const widths = [34, 160, 320, 640, 1024] const { AWS_ENDPOINT: endpoint, AWS_REGION: region, AWS_BUCKET: Bucket, S3_CONFIGURED } = CONFIG diff --git a/backend/src/schema/resolvers/index.ts b/backend/src/schema/resolvers/index.ts index bc028f0db..9a21f9a9d 100644 --- a/backend/src/schema/resolvers/index.ts +++ b/backend/src/schema/resolvers/index.ts @@ -1,4 +1,5 @@ import path from 'node:path' + import { fileLoader, mergeResolvers } from 'merge-graphql-schemas' // the files must be correctly evaluated in built and dev state - therefore accept both js & ts files diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/schema/resolvers/inviteCodes.spec.ts index 93124dbe2..e1a0dac17 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.ts +++ b/backend/src/schema/resolvers/inviteCodes.spec.ts @@ -1,10 +1,11 @@ /* eslint-disable security/detect-non-literal-regexp */ +import { createTestClient } from 'apollo-server-testing' +import gql from 'graphql-tag' + +import CONSTANTS_REGISTRATION from '@constants/registration' import Factory, { cleanDatabase } from '@db/factories' import { getDriver } from '@db/neo4j' -import gql from 'graphql-tag' import createServer from '@src/server' -import { createTestClient } from 'apollo-server-testing' -import CONSTANTS_REGISTRATION from '@constants/registration' let user let query diff --git a/backend/src/schema/resolvers/locations.spec.ts b/backend/src/schema/resolvers/locations.spec.ts index 8880ed830..824372d28 100644 --- a/backend/src/schema/resolvers/locations.spec.ts +++ b/backend/src/schema/resolvers/locations.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +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' -import { createTestClient } from 'apollo-server-testing' let mutate, authenticatedUser diff --git a/backend/src/schema/resolvers/locations.ts b/backend/src/schema/resolvers/locations.ts index fa0feafa1..fcc2fa0aa 100644 --- a/backend/src/schema/resolvers/locations.ts +++ b/backend/src/schema/resolvers/locations.ts @@ -1,4 +1,5 @@ import { UserInputError } from 'apollo-server' + import Resolver from './helpers/Resolver' import { queryLocations } from './users/location' diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index d6bd752c0..4384ddd0f 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -1,8 +1,9 @@ import { createTestClient } from 'apollo-server-testing' + import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import { createRoomMutation, roomQuery } from '@graphql/rooms' import { createMessageMutation, messageQuery, markMessagesAsSeen } from '@graphql/messages' +import { createRoomMutation, roomQuery } from '@graphql/rooms' import createServer, { pubsub } from '@src/server' const driver = getDriver() diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/schema/resolvers/messages.ts index 729059307..6879c4be9 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/schema/resolvers/messages.ts @@ -1,9 +1,10 @@ -import { neo4jgraphql } from 'neo4j-graphql-js' -import Resolver from './helpers/Resolver' - -import { getUnreadRoomsCount } from './rooms' -import { pubsub, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' import { withFilter } from 'graphql-subscriptions' +import { neo4jgraphql } from 'neo4j-graphql-js' + +import { pubsub, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' + +import Resolver from './helpers/Resolver' +import { getUnreadRoomsCount } from './rooms' const setMessagesAsDistributed = async (undistributedMessagesIds, session) => { return session.writeTransaction(async (transaction) => { diff --git a/backend/src/schema/resolvers/moderation.spec.ts b/backend/src/schema/resolvers/moderation.spec.ts index a8155d1a8..46befdf10 100644 --- a/backend/src/schema/resolvers/moderation.spec.ts +++ b/backend/src/schema/resolvers/moderation.spec.ts @@ -1,6 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/schema/resolvers/notifications.spec.ts b/backend/src/schema/resolvers/notifications.spec.ts index 6b3d08b8b..a10f97590 100644 --- a/backend/src/schema/resolvers/notifications.spec.ts +++ b/backend/src/schema/resolvers/notifications.spec.ts @@ -1,13 +1,14 @@ -import Factory, { cleanDatabase } from '@db/factories' -import gql from 'graphql-tag' -import { getDriver } from '@db/neo4j' import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' +import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' +import { getDriver } from '@db/neo4j' import { markAsReadMutation, markAllAsReadMutation, notificationQuery, } from '@graphql/notifications' +import createServer from '@src/server' const driver = getDriver() let authenticatedUser diff --git a/backend/src/schema/resolvers/notifications.ts b/backend/src/schema/resolvers/notifications.ts index 63307d54c..5dbbe3d40 100644 --- a/backend/src/schema/resolvers/notifications.ts +++ b/backend/src/schema/resolvers/notifications.ts @@ -1,7 +1,9 @@ -import log from './helpers/databaseLogger' import { withFilter } from 'graphql-subscriptions' + import { pubsub, NOTIFICATION_ADDED } from '@src/server' +import log from './helpers/databaseLogger' + export default { Subscription: { notificationAdded: { diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/schema/resolvers/observePosts.spec.ts index ab94a1934..13fd5ccfc 100644 --- a/backend/src/schema/resolvers/observePosts.spec.ts +++ b/backend/src/schema/resolvers/observePosts.spec.ts @@ -1,11 +1,11 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' -import { createPostMutation } from '@graphql/posts' import CONFIG from '@config/index' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import { createPostMutation } from '@graphql/posts' +import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/schema/resolvers/passwordReset.spec.ts index f939b3b1c..b5c7e10dd 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/schema/resolvers/passwordReset.spec.ts @@ -1,10 +1,12 @@ -import Factory, { cleanDatabase } from '@db/factories' -import gql from 'graphql-tag' -import { getNeode, getDriver } from '@db/neo4j' -import CONSTANTS_REGISTRATION from '@constants/registration' -import createPasswordReset from './helpers/createPasswordReset' -import createServer from '@src/server' import { createTestClient } from 'apollo-server-testing' +import gql from 'graphql-tag' + +import CONSTANTS_REGISTRATION from '@constants/registration' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' + +import createPasswordReset from './helpers/createPasswordReset' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index 8ec26538c..b9d4d7f51 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -1,6 +1,8 @@ -import { v4 as uuid } from 'uuid' import bcrypt from 'bcryptjs' +import { v4 as uuid } from 'uuid' + import CONSTANTS_REGISTRATION from '@constants/registration' + import createPasswordReset from './helpers/createPasswordReset' export default { diff --git a/backend/src/schema/resolvers/posts.spec.ts b/backend/src/schema/resolvers/posts.spec.ts index 3c52e78f8..103ba98c0 100644 --- a/backend/src/schema/resolvers/posts.spec.ts +++ b/backend/src/schema/resolvers/posts.spec.ts @@ -1,10 +1,11 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' -import { createPostMutation } from '@graphql/posts' + import CONFIG from '@config/index' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import { createPostMutation } from '@graphql/posts' +import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = true diff --git a/backend/src/schema/resolvers/posts.ts b/backend/src/schema/resolvers/posts.ts index 534782369..cb48d78ea 100644 --- a/backend/src/schema/resolvers/posts.ts +++ b/backend/src/schema/resolvers/posts.ts @@ -1,15 +1,17 @@ -import { v4 as uuid } from 'uuid' -import { neo4jgraphql } from 'neo4j-graphql-js' -import { isEmpty } from 'lodash' import { UserInputError } from 'apollo-server' -import { mergeImage, deleteImage } from './images/images' -import Resolver from './helpers/Resolver' +import { isEmpty } from 'lodash' +import { neo4jgraphql } from 'neo4j-graphql-js' +import { v4 as uuid } from 'uuid' + +import CONFIG from '@config/index' + +import { validateEventParams } from './helpers/events' import { filterForMutedUsers } from './helpers/filterForMutedUsers' import { filterInvisiblePosts } from './helpers/filterInvisiblePosts' import { filterPostsOfMyGroups } from './helpers/filterPostsOfMyGroups' -import { validateEventParams } from './helpers/events' +import Resolver from './helpers/Resolver' +import { mergeImage, deleteImage } from './images/images' import { createOrUpdateLocations } from './users/location' -import CONFIG from '@config/index' const maintainPinnedPosts = (params) => { const pinnedPostFilter = { pinned: true } diff --git a/backend/src/schema/resolvers/postsInGroups.spec.ts b/backend/src/schema/resolvers/postsInGroups.spec.ts index d6b19e6d2..17d4f274e 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.ts +++ b/backend/src/schema/resolvers/postsInGroups.spec.ts @@ -1,7 +1,10 @@ import { createTestClient } from 'apollo-server-testing' + +import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +import { signupVerificationMutation } from '@graphql/authentications' +import { createCommentMutation } from '@graphql/comments' import { createGroupMutation, changeGroupMemberRoleMutation, @@ -14,11 +17,7 @@ import { profilePagePosts, searchPosts, } from '@graphql/posts' -import { createCommentMutation } from '@graphql/comments' -// eslint-disable-next-line no-unused-vars -import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '@constants/groups' -import CONFIG from '@config/index' -import { signupVerificationMutation } from '@graphql/authentications' +import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false diff --git a/backend/src/schema/resolvers/registration.spec.ts b/backend/src/schema/resolvers/registration.spec.ts index cb63fbe5c..e61460786 100644 --- a/backend/src/schema/resolvers/registration.spec.ts +++ b/backend/src/schema/resolvers/registration.spec.ts @@ -1,9 +1,10 @@ -import Factory, { cleanDatabase } from '@db/factories' +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + +import CONFIG from '@config/index' +import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' import createServer from '@src/server' -import { createTestClient } from 'apollo-server-testing' -import CONFIG from '@config/index' const neode = getNeode() diff --git a/backend/src/schema/resolvers/registration.ts b/backend/src/schema/resolvers/registration.ts index 12f13fff3..3d5dfd6b3 100644 --- a/backend/src/schema/resolvers/registration.ts +++ b/backend/src/schema/resolvers/registration.ts @@ -1,8 +1,10 @@ import { UserInputError } from 'apollo-server' + import { getNeode } from '@db/neo4j' import encryptPassword from '@helpers/encryptPassword' -import generateNonce from './helpers/generateNonce' + import existingEmailAddress from './helpers/existingEmailAddress' +import generateNonce from './helpers/generateNonce' import normalizeEmail from './helpers/normalizeEmail' const neode = getNeode() diff --git a/backend/src/schema/resolvers/reports.spec.ts b/backend/src/schema/resolvers/reports.spec.ts index f1e405c37..a57efc011 100644 --- a/backend/src/schema/resolvers/reports.spec.ts +++ b/backend/src/schema/resolvers/reports.spec.ts @@ -1,8 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' +import createServer from '@src/server' const instance = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/rewards.spec.ts b/backend/src/schema/resolvers/rewards.spec.ts index cbd8c58c7..2cfe122a0 100644 --- a/backend/src/schema/resolvers/rewards.spec.ts +++ b/backend/src/schema/resolvers/rewards.spec.ts @@ -1,6 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/schema/resolvers/rewards.ts b/backend/src/schema/resolvers/rewards.ts index 4022f9180..bbb889c41 100644 --- a/backend/src/schema/resolvers/rewards.ts +++ b/backend/src/schema/resolvers/rewards.ts @@ -1,6 +1,7 @@ -import { getNeode } from '@db/neo4j' import { UserInputError } from 'apollo-server' +import { getNeode } from '@db/neo4j' + const neode = getNeode() const getUserAndBadge = async ({ badgeKey, userId }) => { diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts index 154f9eb43..87ebb4557 100644 --- a/backend/src/schema/resolvers/rooms.spec.ts +++ b/backend/src/schema/resolvers/rooms.spec.ts @@ -1,8 +1,9 @@ import { createTestClient } from 'apollo-server-testing' + import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import { createRoomMutation, roomQuery, unreadRoomsQuery } from '@graphql/rooms' import { createMessageMutation } from '@graphql/messages' +import { createRoomMutation, roomQuery, unreadRoomsQuery } from '@graphql/rooms' import createServer from '@src/server' const driver = getDriver() diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/schema/resolvers/rooms.ts index 479e361ea..0ff37b594 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/schema/resolvers/rooms.ts @@ -1,7 +1,9 @@ -import { neo4jgraphql } from 'neo4j-graphql-js' -import Resolver from './helpers/Resolver' -import { pubsub, ROOM_COUNT_UPDATED } from '@src/server' import { withFilter } from 'graphql-subscriptions' +import { neo4jgraphql } from 'neo4j-graphql-js' + +import { pubsub, ROOM_COUNT_UPDATED } from '@src/server' + +import Resolver from './helpers/Resolver' export const getUnreadRoomsCount = async (userId, session) => { return session.readTransaction(async (transaction) => { diff --git a/backend/src/schema/resolvers/searches.spec.ts b/backend/src/schema/resolvers/searches.spec.ts index 385dc5a36..5902f2746 100644 --- a/backend/src/schema/resolvers/searches.spec.ts +++ b/backend/src/schema/resolvers/searches.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +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' -import { createTestClient } from 'apollo-server-testing' let query, authenticatedUser, user diff --git a/backend/src/schema/resolvers/shout.spec.ts b/backend/src/schema/resolvers/shout.spec.ts index 02d52f46f..7fe7176ab 100644 --- a/backend/src/schema/resolvers/shout.spec.ts +++ b/backend/src/schema/resolvers/shout.spec.ts @@ -1,6 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/schema/resolvers/socialMedia.spec.ts b/backend/src/schema/resolvers/socialMedia.spec.ts index 51e888118..3a36e791e 100644 --- a/backend/src/schema/resolvers/socialMedia.spec.ts +++ b/backend/src/schema/resolvers/socialMedia.spec.ts @@ -1,8 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getDriver } from '@db/neo4j' +import createServer from '@src/server' const driver = getDriver() diff --git a/backend/src/schema/resolvers/socialMedia.ts b/backend/src/schema/resolvers/socialMedia.ts index 6503a5110..ac27eb1f9 100644 --- a/backend/src/schema/resolvers/socialMedia.ts +++ b/backend/src/schema/resolvers/socialMedia.ts @@ -1,4 +1,5 @@ import { getNeode } from '@db/neo4j' + import Resolver from './helpers/Resolver' const neode = getNeode() diff --git a/backend/src/schema/resolvers/statistics.spec.ts b/backend/src/schema/resolvers/statistics.spec.ts index f81c1ba0b..4c8c8aa01 100644 --- a/backend/src/schema/resolvers/statistics.spec.ts +++ b/backend/src/schema/resolvers/statistics.spec.ts @@ -1,6 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/schema/resolvers/userData.spec.ts b/backend/src/schema/resolvers/userData.spec.ts index 6e9c27c86..1165ec33c 100644 --- a/backend/src/schema/resolvers/userData.spec.ts +++ b/backend/src/schema/resolvers/userData.spec.ts @@ -1,8 +1,9 @@ -import Factory, { cleanDatabase } from '@db/factories' +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' -import { createTestClient } from 'apollo-server-testing' let query, authenticatedUser diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/schema/resolvers/user_management.spec.ts index 8b8dcc717..d9905fd71 100644 --- a/backend/src/schema/resolvers/user_management.spec.ts +++ b/backend/src/schema/resolvers/user_management.spec.ts @@ -1,14 +1,15 @@ /* eslint-disable promise/prefer-await-to-callbacks */ -import jwt from 'jsonwebtoken' -import CONFIG from '@config/index' -import Factory, { cleanDatabase } from '@db/factories' -import gql from 'graphql-tag' -import { loginMutation } from '@graphql/userManagement' import { createTestClient } from 'apollo-server-testing' -import createServer, { context } from '@src/server' -import encode from '@jwt/encode' -import { getNeode, getDriver } from '@db/neo4j' +import gql from 'graphql-tag' +import jwt from 'jsonwebtoken' + +import CONFIG from '@config/index' import { categories } from '@constants/categories' +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import { loginMutation } from '@graphql/userManagement' +import encode from '@jwt/encode' +import createServer, { context } from '@src/server' const neode = getNeode() const driver = getDriver() diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/schema/resolvers/user_management.ts index dceceab24..dfc33b6ae 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/schema/resolvers/user_management.ts @@ -1,9 +1,11 @@ -import encode from '@jwt/encode' -import bcrypt from 'bcryptjs' import { AuthenticationError } from 'apollo-server' +import bcrypt from 'bcryptjs' + import { getNeode } from '@db/neo4j' -import normalizeEmail from './helpers/normalizeEmail' +import encode from '@jwt/encode' + import log from './helpers/databaseLogger' +import normalizeEmail from './helpers/normalizeEmail' const neode = getNeode() diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index d2a86623a..88dfa653d 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -1,9 +1,10 @@ -import Factory, { cleanDatabase } from '@db/factories' +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + +import { categories } from '@constants/categories' +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' -import { createTestClient } from 'apollo-server-testing' -import { categories } from '@constants/categories' const categoryIds = ['cat9'] let user diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index 81306e13d..fe5e3d2de 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -1,9 +1,11 @@ -import { neo4jgraphql } from 'neo4j-graphql-js' -import { getNeode } from '@db/neo4j' import { UserInputError, ForbiddenError } from 'apollo-server' -import { mergeImage, deleteImage } from './images/images' -import Resolver from './helpers/Resolver' +import { neo4jgraphql } from 'neo4j-graphql-js' + +import { getNeode } from '@db/neo4j' + import log from './helpers/databaseLogger' +import Resolver from './helpers/Resolver' +import { mergeImage, deleteImage } from './images/images' import { createOrUpdateLocations } from './users/location' const neode = getNeode() diff --git a/backend/src/schema/resolvers/users/location.spec.ts b/backend/src/schema/resolvers/users/location.spec.ts index 0b4db63f3..4f54dcc06 100644 --- a/backend/src/schema/resolvers/users/location.spec.ts +++ b/backend/src/schema/resolvers/users/location.spec.ts @@ -1,7 +1,8 @@ +import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' + import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' -import { createTestClient } from 'apollo-server-testing' import createServer from '@src/server' const neode = getNeode() diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/schema/resolvers/users/location.ts index a05a4402b..b663eebdf 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/schema/resolvers/users/location.ts @@ -1,12 +1,13 @@ /* eslint-disable promise/avoid-new */ /* eslint-disable promise/prefer-await-to-callbacks */ /* eslint-disable import/no-named-as-default */ -import request from 'request' import { UserInputError } from 'apollo-server' // eslint-disable-next-line import/no-extraneous-dependencies import Debug from 'debug' -import asyncForEach from '@helpers/asyncForEach' +import request from 'request' + import CONFIG from '@config/index' +import asyncForEach from '@helpers/asyncForEach' const debug = Debug('human-connection:location') diff --git a/backend/src/schema/resolvers/users/mutedUsers.spec.ts b/backend/src/schema/resolvers/users/mutedUsers.spec.ts index 4bfce2421..1fda2b392 100644 --- a/backend/src/schema/resolvers/users/mutedUsers.spec.ts +++ b/backend/src/schema/resolvers/users/mutedUsers.spec.ts @@ -1,8 +1,9 @@ import { createTestClient } from 'apollo-server-testing' -import createServer from '@src/server' -import { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' const driver = getDriver() const neode = getNeode() diff --git a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts b/backend/src/schema/resolvers/viewedTeaserCount.spec.ts index e73502324..ebcb19c4e 100644 --- a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts +++ b/backend/src/schema/resolvers/viewedTeaserCount.spec.ts @@ -1,6 +1,7 @@ import { createTestClient } from 'apollo-server-testing' -import Factory, { cleanDatabase } from '@db/factories' import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' diff --git a/backend/src/schema/types/index.ts b/backend/src/schema/types/index.ts index fe8a6315e..42d813ae4 100644 --- a/backend/src/schema/types/index.ts +++ b/backend/src/schema/types/index.ts @@ -1,4 +1,5 @@ import path from 'node:path' + import { mergeTypes, fileLoader } from 'merge-graphql-schemas' const typeDefs = fileLoader(path.join(__dirname, './**/*.gql')) diff --git a/backend/src/server.spec.ts b/backend/src/server.spec.ts index 6d4ef546d..1d5c5aca8 100644 --- a/backend/src/server.spec.ts +++ b/backend/src/server.spec.ts @@ -1,4 +1,5 @@ import { createTestClient } from 'apollo-server-testing' + import createServer from './server' /** diff --git a/backend/src/server.ts b/backend/src/server.ts index 86c0c3658..117e0c3b6 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -1,19 +1,21 @@ /* eslint-disable import/no-named-as-default-member */ -import express from 'express' import http from 'node:http' -import helmet from 'helmet' + import { ApolloServer } from 'apollo-server-express' -import CONFIG from './config' -// eslint-disable-next-line import/no-cycle -import middleware from './middleware' -import { getNeode, getDriver } from './db/neo4j' -import decode from './jwt/decode' -import schema from './schema' +import bodyParser from 'body-parser' +import express from 'express' import { RedisPubSub } from 'graphql-redis-subscriptions' import { PubSub } from 'graphql-subscriptions' -import Redis from 'ioredis' -import bodyParser from 'body-parser' import { graphqlUploadExpress } from 'graphql-upload' +import helmet from 'helmet' +import Redis from 'ioredis' + +import CONFIG from './config' +import { getNeode, getDriver } from './db/neo4j' +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' From fe7bab46753991da7cb918ab19a75bc3337b67b0 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 9 Apr 2025 10:36:56 +0200 Subject: [PATCH 089/266] posts and comments created by the factory set the observe relation (#8344) --- backend/src/db/factories.ts | 7 ++++++- backend/src/models/Post.ts | 11 +++++++++++ backend/src/models/User.ts | 11 +++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index 459a8d186..86f51a259 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -175,6 +175,7 @@ Factory.define('post') ]) await Promise.all([ post.relateTo(author, 'author'), + post.relateTo(author, 'observes'), // Promise.all(categories.map((c) => c.relateTo(post, 'post'))), Promise.all(tags.map((t) => t.relateTo(post, 'post'))), ]) @@ -210,7 +211,11 @@ Factory.define('comment') options.author, options.post, ]) - await Promise.all([comment.relateTo(author, 'author'), comment.relateTo(post, 'post')]) + await Promise.all([ + comment.relateTo(author, 'author'), + comment.relateTo(post, 'post'), + post.relateTo(author, 'observes'), + ]) return comment }) diff --git a/backend/src/models/Post.ts b/backend/src/models/Post.ts index e206ea1f5..75081b728 100644 --- a/backend/src/models/Post.ts +++ b/backend/src/models/Post.ts @@ -58,4 +58,15 @@ export default { }, pinned: { type: 'boolean', default: null, valid: [null, true] }, postType: { type: 'string', default: 'Article', valid: ['Article', 'Event'] }, + observes: { + type: 'relationship', + relationship: 'OBSERVES', + target: 'User', + direction: 'in', + properties: { + createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, + updatedAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, + active: { type: 'boolean', default: true }, + }, + }, } diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 9b828e27e..fa357775a 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -163,4 +163,15 @@ export default { type: 'string', allow: [null], }, + observes: { + type: 'relationship', + relationship: 'OBSERVES', + target: 'Post', + direction: 'out', + properties: { + createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, + updatedAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() }, + active: { type: 'boolean', default: true }, + }, + }, } From 1b07b06ca79d2cfe4eab8815b66bcf50fc761f37 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 9 Apr 2025 15:21:38 +0200 Subject: [PATCH 090/266] feat(webapp): notification settings frontend (#8320) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements detailed settings for email notifications. Issues relates 🚀 [Feature] Drei neue Interaktions-Benachrichtigungen #8280 --- backend/src/db/factories.ts | 1 - backend/src/db/migrate/store.ts | 25 +- ...50405030454-email-notification-settings.ts | 68 +++ .../notificationsMiddleware.spec.ts | 426 +++++++++++++++--- .../notifications/notificationsMiddleware.ts | 76 ++-- .../src/middleware/permissionsMiddleware.ts | 1 + backend/src/models/User.ts | 29 +- backend/src/schema/index.ts | 2 + backend/src/schema/resolvers/registration.ts | 1 - backend/src/schema/resolvers/users.spec.ts | 214 +++++++++ backend/src/schema/resolvers/users.ts | 61 ++- .../enum/EmailNotificationSettingsName.gql | 9 + .../enum/EmailNotificationSettingsType.gql | 5 + backend/src/schema/types/type/User.gql | 19 +- ... User.SettingNotifications.feature.broken} | 0 webapp/graphql/User.js | 21 +- webapp/locales/de.json | 18 +- webapp/locales/en.json | 18 +- webapp/locales/es.json | 20 +- webapp/locales/fr.json | 20 +- webapp/locales/it.json | 20 +- webapp/locales/nl.json | 20 +- webapp/locales/pl.json | 20 +- webapp/locales/pt.json | 20 +- webapp/locales/ru.json | 20 +- webapp/package.json | 2 + .../__snapshots__/notifications.spec.js.snap | 197 ++++++++ webapp/pages/settings/notifications.spec.js | 153 ++++++- webapp/pages/settings/notifications.vue | 102 ++++- webapp/yarn.lock | 316 +++++++++++-- 30 files changed, 1687 insertions(+), 217 deletions(-) create mode 100644 backend/src/db/migrations/20250405030454-email-notification-settings.ts create mode 100644 backend/src/schema/types/enum/EmailNotificationSettingsName.gql create mode 100644 backend/src/schema/types/enum/EmailNotificationSettingsType.gql rename cypress/e2e/{User.SettingNotifications.feature => User.SettingNotifications.feature.broken} (100%) create mode 100644 webapp/pages/settings/__snapshots__/notifications.spec.js.snap diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index 86f51a259..e09a4f921 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -72,7 +72,6 @@ Factory.define('basicUser') termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z', allowEmbedIframes: false, showShoutsPublicly: false, - sendNotificationEmails: true, locale: 'en', }) .attr('slug', ['slug', 'name'], (slug, name) => { diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index 1e32b49f5..e373c41c0 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -56,19 +56,18 @@ const createDefaultAdminUser = async (session) => { `MERGE (e:EmailAddress { email: "${defaultAdmin.email}", createdAt: toString(datetime()) - })-[:BELONGS_TO]->(u:User { - name: "${defaultAdmin.name}", - encryptedPassword: "${defaultAdmin.password}", - role: "admin", - id: "${defaultAdmin.id}", - slug: "${defaultAdmin.slug}", - createdAt: toString(datetime()), - allowEmbedIframes: false, - showShoutsPublicly: false, - sendNotificationEmails: true, - deleted: false, - disabled: false - })-[:PRIMARY_EMAIL]->(e)`, + })-[:BELONGS_TO]->(u:User { + name: "${defaultAdmin.name}", + encryptedPassword: "${defaultAdmin.password}", + role: "admin", + id: "${defaultAdmin.id}", + slug: "${defaultAdmin.slug}", + createdAt: toString(datetime()), + allowEmbedIframes: false, + showShoutsPublicly: false, + deleted: false, + disabled: false + })-[:PRIMARY_EMAIL]->(e)`, ) }) try { diff --git a/backend/src/db/migrations/20250405030454-email-notification-settings.ts b/backend/src/db/migrations/20250405030454-email-notification-settings.ts new file mode 100644 index 000000000..07ce9ab79 --- /dev/null +++ b/backend/src/db/migrations/20250405030454-email-notification-settings.ts @@ -0,0 +1,68 @@ +import { getDriver } from '@db/neo4j' + +export const description = + 'Transforms the `sendNotificationEmails` property on User to a multi value system' + +export async function up(next) { + const driver = getDriver() + const session = driver.session() + const transaction = session.beginTransaction() + + try { + // Implement your migration here. + await transaction.run(` + MATCH (user:User) + SET user.emailNotificationsCommentOnObservedPost = user.sendNotificationEmails + SET user.emailNotificationsMention = user.sendNotificationEmails + SET user.emailNotificationsChatMessage = user.sendNotificationEmails + SET user.emailNotificationsGroupMemberJoined = user.sendNotificationEmails + SET user.emailNotificationsGroupMemberLeft = user.sendNotificationEmails + SET user.emailNotificationsGroupMemberRemoved = user.sendNotificationEmails + SET user.emailNotificationsGroupMemberRoleChanged = user.sendNotificationEmails + REMOVE user.sendNotificationEmails + `) + 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 { + 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(` + MATCH (user:User) + SET user.sendNotificationEmails = true + REMOVE user.emailNotificationsCommentOnObservedPost + REMOVE user.emailNotificationsMention + REMOVE user.emailNotificationsChatMessage + REMOVE user.emailNotificationsGroupMemberJoined + REMOVE user.emailNotificationsGroupMemberLeft + REMOVE user.emailNotificationsGroupMemberRemoved + REMOVE user.emailNotificationsGroupMemberRoleChanged + `) + 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 { + session.close() + } +} diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 75b84f720..c636a7c87 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -20,8 +20,10 @@ jest.mock('../helpers/email/sendMail', () => ({ })) const chatMessageTemplateMock = jest.fn() +const notificationTemplateMock = jest.fn() jest.mock('../helpers/email/templateBuilder', () => ({ chatMessageTemplate: () => chatMessageTemplateMock(), + notificationTemplate: () => notificationTemplateMock(), })) let isUserOnlineMock = jest.fn() @@ -86,8 +88,8 @@ afterAll(async () => { beforeEach(async () => { publishSpy.mockClear() - notifiedUser = await neode.create( - 'User', + notifiedUser = await Factory.build( + 'user', { id: 'you', name: 'Al Capone', @@ -187,6 +189,7 @@ describe('notifications', () => { describe('commenter is not me', () => { beforeEach(async () => { + jest.clearAllMocks() commentContent = 'Commenters comment.' commentAuthor = await neode.create( 'User', @@ -202,25 +205,8 @@ describe('notifications', () => { ) }) - it('sends me a notification', async () => { + it('sends me a notification and email', async () => { await createCommentOnPostAction() - const expected = expect.objectContaining({ - data: { - notifications: [ - { - read: false, - createdAt: expect.any(String), - reason: 'commented_on_post', - from: { - __typename: 'Comment', - id: 'c47', - content: commentContent, - }, - relatedUser: null, - }, - ], - }, - }) await expect( query({ query: notificationQuery, @@ -228,24 +214,85 @@ describe('notifications', () => { read: false, }, }), - ).resolves.toEqual(expected) + ).resolves.toMatchObject( + expect.objectContaining({ + data: { + notifications: [ + { + read: false, + createdAt: expect.any(String), + reason: 'commented_on_post', + from: { + __typename: 'Comment', + id: 'c47', + content: commentContent, + }, + relatedUser: null, + }, + ], + }, + }), + ) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(notificationTemplateMock).toHaveBeenCalledTimes(1) }) - it('sends me no notification if I have blocked the comment author', async () => { - await notifiedUser.relateTo(commentAuthor, 'blocked') - await createCommentOnPostAction() - const expected = expect.objectContaining({ - data: { notifications: [] }, - }) + describe('if I have disabled `emailNotificationsCommentOnObservedPost`', () => { + it('sends me a notification but no email', async () => { + await notifiedUser.update({ emailNotificationsCommentOnObservedPost: false }) + await createCommentOnPostAction() + await expect( + query({ + query: notificationQuery, + variables: { + read: false, + }, + }), + ).resolves.toMatchObject( + expect.objectContaining({ + data: { + notifications: [ + { + read: false, + createdAt: expect.any(String), + reason: 'commented_on_post', + from: { + __typename: 'Comment', + id: 'c47', + content: commentContent, + }, + relatedUser: null, + }, + ], + }, + }), + ) - await expect( - query({ - query: notificationQuery, - variables: { - read: false, - }, - }), - ).resolves.toEqual(expected) + // No Mail + expect(sendMailMock).not.toHaveBeenCalled() + expect(notificationTemplateMock).not.toHaveBeenCalled() + }) + }) + + describe('if I have blocked the comment author', () => { + it('sends me no notification', async () => { + await notifiedUser.relateTo(commentAuthor, 'blocked') + await createCommentOnPostAction() + const expected = expect.objectContaining({ + data: { notifications: [] }, + }) + + await expect( + query({ + query: notificationQuery, + variables: { + read: false, + }, + }), + ).resolves.toEqual(expected) + }) }) }) @@ -274,6 +321,7 @@ describe('notifications', () => { }) beforeEach(async () => { + jest.clearAllMocks() postAuthor = await neode.create( 'User', { @@ -296,7 +344,7 @@ describe('notifications', () => { 'Hey
@al-capone how do you do?' }) - it('sends me a notification', async () => { + it('sends me a notification and email', async () => { await createPostAction() const expectedContent = 'Hey @al-capone how do you do?' @@ -324,6 +372,47 @@ describe('notifications', () => { ], }, }) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + }) + + describe('if I have disabled `emailNotificationsMention`', () => { + it('sends me a notification but no email', async () => { + await notifiedUser.update({ emailNotificationsMention: false }) + await createPostAction() + const expectedContent = + 'Hey @al-capone how do you do?' + await expect( + query({ + query: notificationQuery, + variables: { + read: false, + }, + }), + ).resolves.toMatchObject({ + errors: undefined, + data: { + notifications: [ + { + read: false, + createdAt: expect.any(String), + reason: 'mentioned_in_post', + from: { + __typename: 'Post', + id: 'p47', + content: expectedContent, + }, + }, + ], + }, + }) + + // Mail + expect(sendMailMock).not.toHaveBeenCalled() + expect(notificationTemplateMock).not.toHaveBeenCalled() + }) }) it('publishes `NOTIFICATION_ADDED` to me', async () => { @@ -689,7 +778,7 @@ describe('notifications', () => { roomId = room.data.CreateRoom.id }) - describe('chatReceiver is online', () => { + describe('if the chatReceiver is online', () => { it('sends no email', async () => { isUserOnlineMock = jest.fn().mockReturnValue(true) @@ -706,7 +795,7 @@ describe('notifications', () => { }) }) - describe('chatReceiver is offline', () => { + describe('if the chatReceiver is offline', () => { it('sends an email', async () => { isUserOnlineMock = jest.fn().mockReturnValue(false) @@ -723,7 +812,7 @@ describe('notifications', () => { }) }) - describe('chatReceiver has blocked chatSender', () => { + describe('if the chatReceiver has blocked chatSender', () => { it('sends no email', async () => { isUserOnlineMock = jest.fn().mockReturnValue(false) await chatReceiver.relateTo(chatSender, 'blocked') @@ -741,10 +830,10 @@ describe('notifications', () => { }) }) - describe('chatReceiver has disabled email notifications', () => { + describe('if the chatReceiver has disabled `emailNotificationsChatMessage`', () => { it('sends no email', async () => { isUserOnlineMock = jest.fn().mockReturnValue(false) - await chatReceiver.update({ sendNotificationEmails: false }) + await chatReceiver.update({ emailNotificationsChatMessage: false }) await mutate({ mutation: createMessageMutation(), @@ -764,8 +853,8 @@ describe('notifications', () => { let groupOwner beforeEach(async () => { - groupOwner = await neode.create( - 'User', + groupOwner = await Factory.build( + 'user', { id: 'group-owner', name: 'Group Owner', @@ -792,7 +881,7 @@ describe('notifications', () => { }) describe('user joins group', () => { - beforeEach(async () => { + const joinGroupAction = async () => { authenticatedUser = await notifiedUser.toJson() await mutate({ mutation: joinGroupMutation(), @@ -802,9 +891,14 @@ describe('notifications', () => { }, }) authenticatedUser = await groupOwner.toJson() + } + + beforeEach(async () => { + jest.clearAllMocks() }) - it('has the notification in database', async () => { + it('sends the group owner a notification and email', async () => { + await joinGroupAction() await expect( query({ query: notificationQuery, @@ -828,19 +922,50 @@ describe('notifications', () => { }, errors: undefined, }) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + }) + + describe('if the group owner has disabled `emailNotificationsGroupMemberJoined`', () => { + it('sends the group owner a notification but no email', async () => { + await groupOwner.update({ emailNotificationsGroupMemberJoined: false }) + await joinGroupAction() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + read: false, + reason: 'user_joined_group', + createdAt: expect.any(String), + from: { + __typename: 'Group', + id: 'closed-group', + }, + relatedUser: { + id: 'you', + }, + }, + ], + }, + errors: undefined, + }) + + // Mail + expect(sendMailMock).not.toHaveBeenCalled() + expect(notificationTemplateMock).not.toHaveBeenCalled() + }) }) }) - describe('user leaves group', () => { - beforeEach(async () => { + describe('user joins and leaves group', () => { + const leaveGroupAction = async () => { authenticatedUser = await notifiedUser.toJson() - await mutate({ - mutation: joinGroupMutation(), - variables: { - groupId: 'closed-group', - userId: authenticatedUser.id, - }, - }) await mutate({ mutation: leaveGroupMutation(), variables: { @@ -849,9 +974,22 @@ describe('notifications', () => { }, }) authenticatedUser = await groupOwner.toJson() + } + + beforeEach(async () => { + jest.clearAllMocks() + authenticatedUser = await notifiedUser.toJson() + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'closed-group', + userId: authenticatedUser.id, + }, + }) }) - it('has two the notification in database', async () => { + it('sends the group owner two notifications and emails', async () => { + await leaveGroupAction() await expect( query({ query: notificationQuery, @@ -887,19 +1025,61 @@ describe('notifications', () => { }, errors: undefined, }) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(2) + expect(notificationTemplateMock).toHaveBeenCalledTimes(2) + }) + + describe('if the group owner has disabled `emailNotificationsGroupMemberLeft`', () => { + it('sends the group owner two notification but only only one email', async () => { + await groupOwner.update({ emailNotificationsGroupMemberLeft: false }) + await leaveGroupAction() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + read: false, + reason: 'user_left_group', + createdAt: expect.any(String), + from: { + __typename: 'Group', + id: 'closed-group', + }, + relatedUser: { + id: 'you', + }, + }, + { + read: false, + reason: 'user_joined_group', + createdAt: expect.any(String), + from: { + __typename: 'Group', + id: 'closed-group', + }, + relatedUser: { + id: 'you', + }, + }, + ], + }, + errors: undefined, + }) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + }) }) }) describe('user role in group changes', () => { - beforeEach(async () => { - authenticatedUser = await notifiedUser.toJson() - await mutate({ - mutation: joinGroupMutation(), - variables: { - groupId: 'closed-group', - userId: authenticatedUser.id, - }, - }) + const changeGroupMemberRoleAction = async () => { authenticatedUser = await groupOwner.toJson() await mutate({ mutation: changeGroupMemberRoleMutation(), @@ -910,9 +1090,23 @@ describe('notifications', () => { }, }) authenticatedUser = await notifiedUser.toJson() + } + + beforeEach(async () => { + authenticatedUser = await notifiedUser.toJson() + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'closed-group', + userId: authenticatedUser.id, + }, + }) + // Clear after because the above generates a notification not related + jest.clearAllMocks() }) - it('has notification in database', async () => { + it('sends the group member a notification and email', async () => { + await changeGroupMemberRoleAction() await expect( query({ query: notificationQuery, @@ -936,19 +1130,49 @@ describe('notifications', () => { }, errors: undefined, }) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + }) + + describe('if the group member has disabled `emailNotificationsGroupMemberRoleChanged`', () => { + it('sends the group member a notification but no email', async () => { + notifiedUser.update({ emailNotificationsGroupMemberRoleChanged: false }) + await changeGroupMemberRoleAction() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + read: false, + reason: 'changed_group_member_role', + createdAt: expect.any(String), + from: { + __typename: 'Group', + id: 'closed-group', + }, + relatedUser: { + id: 'group-owner', + }, + }, + ], + }, + errors: undefined, + }) + + // Mail + expect(sendMailMock).not.toHaveBeenCalled() + expect(notificationTemplateMock).not.toHaveBeenCalled() + }) }) }) describe('user is removed from group', () => { - beforeEach(async () => { - authenticatedUser = await notifiedUser.toJson() - await mutate({ - mutation: joinGroupMutation(), - variables: { - groupId: 'closed-group', - userId: authenticatedUser.id, - }, - }) + const removeUserFromGroupAction = async () => { authenticatedUser = await groupOwner.toJson() await mutate({ mutation: removeUserFromGroupMutation(), @@ -958,9 +1182,23 @@ describe('notifications', () => { }, }) authenticatedUser = await notifiedUser.toJson() + } + + beforeEach(async () => { + authenticatedUser = await notifiedUser.toJson() + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'closed-group', + userId: authenticatedUser.id, + }, + }) + // Clear after because the above generates a notification not related + jest.clearAllMocks() }) - it('has notification in database', async () => { + it('sends the previous group member a notification and email', async () => { + await removeUserFromGroupAction() await expect( query({ query: notificationQuery, @@ -984,6 +1222,44 @@ describe('notifications', () => { }, errors: undefined, }) + + // Mail + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + }) + + describe('if the previous group member has disabled `emailNotificationsGroupMemberRemoved`', () => { + it('sends the previous group member a notification but no email', async () => { + notifiedUser.update({ emailNotificationsGroupMemberRemoved: false }) + await removeUserFromGroupAction() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + read: false, + reason: 'removed_user_from_group', + createdAt: expect.any(String), + from: { + __typename: 'Group', + id: 'closed-group', + }, + relatedUser: { + id: 'group-owner', + }, + }, + ], + }, + errors: undefined, + }) + + // Mail + expect(sendMailMock).not.toHaveBeenCalled() + expect(notificationTemplateMock).not.toHaveBeenCalled() + }) }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 237e294b4..faf4fd994 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -38,7 +38,7 @@ const queryNotificationEmails = async (context, notificationUserIds) => { } } -const publishNotifications = async (context, promises) => { +const publishNotifications = async (context, promises, emailNotificationSetting: string) => { let notifications = await Promise.all(promises) notifications = notifications.flat() const notificationsEmailAddresses = await queryNotificationEmails( @@ -47,7 +47,7 @@ const publishNotifications = async (context, promises) => { ) notifications.forEach((notificationAdded, index) => { pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) - if (notificationAdded.to.sendNotificationEmails) { + if (notificationAdded.to[emailNotificationSetting] ?? true) { sendMail( notificationTemplate({ email: notificationsEmailAddresses[index].email, @@ -62,9 +62,11 @@ const handleJoinGroup = async (resolve, root, args, context, resolveInfo) => { const { groupId, userId } = args const user = await resolve(root, args, context, resolveInfo) if (user) { - await publishNotifications(context, [ - notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context), - ]) + await publishNotifications( + context, + [notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context)], + 'emailNotificationsGroupMemberJoined', + ) } return user } @@ -73,9 +75,11 @@ const handleLeaveGroup = async (resolve, root, args, context, resolveInfo) => { const { groupId, userId } = args const user = await resolve(root, args, context, resolveInfo) if (user) { - await publishNotifications(context, [ - notifyOwnersOfGroup(groupId, userId, 'user_left_group', context), - ]) + await publishNotifications( + context, + [notifyOwnersOfGroup(groupId, userId, 'user_left_group', context)], + 'emailNotificationsGroupMemberLeft', + ) } return user } @@ -84,9 +88,11 @@ const handleChangeGroupMemberRole = async (resolve, root, args, context, resolve const { groupId, userId } = args const user = await resolve(root, args, context, resolveInfo) if (user) { - await publishNotifications(context, [ - notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context), - ]) + await publishNotifications( + context, + [notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context)], + 'emailNotificationsGroupMemberRoleChanged', + ) } return user } @@ -95,9 +101,11 @@ const handleRemoveUserFromGroup = async (resolve, root, args, context, resolveIn const { groupId, userId } = args const user = await resolve(root, args, context, resolveInfo) if (user) { - await publishNotifications(context, [ - notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context), - ]) + await publishNotifications( + context, + [notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context)], + 'emailNotificationsGroupMemberRemoved', + ) } return user } @@ -106,9 +114,11 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo const idsOfUsers = extractMentionedUsers(args.content) const post = await resolve(root, args, context, resolveInfo) if (post) { - await publishNotifications(context, [ - notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context), - ]) + await publishNotifications( + context, + [notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context)], + 'emailNotificationsMention', + ) } return post } @@ -119,16 +129,26 @@ const handleContentDataOfComment = async (resolve, root, args, context, resolveI const comment = await resolve(root, args, context, resolveInfo) const [postAuthor] = await postAuthorOfComment(comment.id, { context }) idsOfMentionedUsers = idsOfMentionedUsers.filter((id) => id !== postAuthor.id) - await publishNotifications(context, [ - notifyUsersOfMention( - 'Comment', - comment.id, - idsOfMentionedUsers, - 'mentioned_in_comment', - context, - ), - notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context), - ]) + await publishNotifications( + context, + [ + notifyUsersOfMention( + 'Comment', + comment.id, + idsOfMentionedUsers, + 'mentioned_in_comment', + context, + ), + ], + 'emailNotificationsMention', + ) + + await publishNotifications( + context, + [notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context)], + 'emailNotificationsCommentOnObservedPost', + ) + return comment } @@ -339,7 +359,7 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => MATCH (room)<-[:CHATS_IN]-(recipientUser:User)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) WHERE NOT recipientUser.id = $currentUserId AND NOT (recipientUser)-[:BLOCKED]-(currentUser) - AND recipientUser.sendNotificationEmails = true + AND NOT recipientUser.emailNotificationsChatMessage = false RETURN recipientUser, emailAddress {.email} ` const txResponse = await transaction.run(messageRecipientCypher, { diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index c1eaf4b75..fcda6d218 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -471,6 +471,7 @@ export default shield( }, User: { email: or(isMyOwn, isAdmin), + emailNotificationSettings: isMyOwn, }, Report: isModerator, }, diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index fa357775a..e9fbfb6ce 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -155,10 +155,37 @@ export default { type: 'boolean', default: false, }, - sendNotificationEmails: { + + // emailNotifications + emailNotificationsCommentOnObservedPost: { type: 'boolean', default: true, }, + emailNotificationsMention: { + type: 'boolean', + default: true, + }, + emailNotificationsChatMessage: { + type: 'boolean', + default: true, + }, + emailNotificationsGroupMemberJoined: { + type: 'boolean', + default: true, + }, + emailNotificationsGroupMemberLeft: { + type: 'boolean', + default: true, + }, + emailNotificationsGroupMemberRemoved: { + type: 'boolean', + default: true, + }, + emailNotificationsGroupMemberRoleChanged: { + type: 'boolean', + default: true, + }, + locale: { type: 'string', allow: [null], diff --git a/backend/src/schema/index.ts b/backend/src/schema/index.ts index 9f83bc43b..e043bc243 100644 --- a/backend/src/schema/index.ts +++ b/backend/src/schema/index.ts @@ -11,6 +11,8 @@ export default makeAugmentedSchema({ exclude: [ 'Badge', 'Embed', + 'EmailNotificationSettings', + 'EmailNotificationSettingsOption', 'EmailAddress', 'Notification', 'Statistics', diff --git a/backend/src/schema/resolvers/registration.ts b/backend/src/schema/resolvers/registration.ts index 3d5dfd6b3..fc3fc37bb 100644 --- a/backend/src/schema/resolvers/registration.ts +++ b/backend/src/schema/resolvers/registration.ts @@ -100,7 +100,6 @@ const signupCypher = (inviteCode) => { SET user.updatedAt = toString(datetime()) SET user.allowEmbedIframes = false SET user.showShoutsPublicly = false - SET user.sendNotificationEmails = true SET email.verifiedAt = toString(datetime()) WITH user OPTIONAL MATCH (post:Post)-[:IN]->(group:Group) diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index 88dfa653d..df5a7f785 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -593,6 +593,220 @@ describe('switch user role', () => { }) }) +let anotherUser +const emailNotificationSettingsQuery = gql` + query ($id: ID!) { + User(id: $id) { + emailNotificationSettings { + type + settings { + name + value + } + } + } + } +` + +const emailNotificationSettingsMutation = gql` + mutation ($id: ID!, $emailNotificationSettings: [EmailNotificationSettingsInput]!) { + UpdateUser(id: $id, emailNotificationSettings: $emailNotificationSettings) { + emailNotificationSettings { + type + settings { + name + value + } + } + } + } +` + +describe('emailNotificationSettings', () => { + beforeEach(async () => { + user = await Factory.build('user', { + id: 'user', + role: 'user', + }) + anotherUser = await Factory.build('user', { + id: 'anotherUser', + role: 'anotherUser', + }) + }) + + describe('query the field', () => { + describe('as another user', () => { + it('throws an error', async () => { + authenticatedUser = await anotherUser.toJson() + const targetUser = await user.toJson() + await expect( + query({ query: emailNotificationSettingsQuery, variables: { id: targetUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + expect.objectContaining({ + message: 'Not Authorized!', + }), + ], + }), + ) + }) + }) + + describe('as self', () => { + it('returns the emailNotificationSettings', async () => { + authenticatedUser = await user.toJson() + await expect( + query({ query: emailNotificationSettingsQuery, variables: { id: authenticatedUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + emailNotificationSettings: [ + { + type: 'post', + settings: [ + { + name: 'commentOnObservedPost', + value: true, + }, + { + name: 'mention', + value: true, + }, + ], + }, + { + type: 'chat', + settings: [ + { + name: 'chatMessage', + value: true, + }, + ], + }, + { + type: 'group', + settings: [ + { + name: 'groupMemberJoined', + value: true, + }, + { + name: 'groupMemberLeft', + value: true, + }, + { + name: 'groupMemberRemoved', + value: true, + }, + { + name: 'groupMemberRoleChanged', + value: true, + }, + ], + }, + ], + }, + ], + }, + }), + ) + }) + }) + }) + + describe('mutate the field', () => { + const emailNotificationSettings = [{ name: 'mention', value: false }] + + describe('as another user', () => { + it('throws an error', async () => { + authenticatedUser = await anotherUser.toJson() + const targetUser = await user.toJson() + await expect( + mutate({ + mutation: emailNotificationSettingsMutation, + variables: { id: targetUser.id, emailNotificationSettings }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + expect.objectContaining({ + message: 'Not Authorized!', + }), + ], + }), + ) + }) + }) + + describe('as self', () => { + it('updates the emailNotificationSettings', async () => { + authenticatedUser = await user.toJson() + await expect( + mutate({ + mutation: emailNotificationSettingsMutation, + variables: { id: authenticatedUser.id, emailNotificationSettings }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + UpdateUser: { + emailNotificationSettings: [ + { + type: 'post', + settings: [ + { + name: 'commentOnObservedPost', + value: true, + }, + { + name: 'mention', + value: false, + }, + ], + }, + { + type: 'chat', + settings: [ + { + name: 'chatMessage', + value: true, + }, + ], + }, + { + type: 'group', + settings: [ + { + name: 'groupMemberJoined', + value: true, + }, + { + name: 'groupMemberLeft', + value: true, + }, + { + name: 'groupMemberRemoved', + value: true, + }, + { + name: 'groupMemberRoleChanged', + value: true, + }, + ], + }, + ], + }, + }, + }), + ) + }) + }) + }) +}) + describe('save category settings', () => { beforeEach(async () => { await Promise.all( diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index fe5e3d2de..cca8e1278 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -152,6 +152,19 @@ export default { } params.termsAndConditionsAgreedAt = new Date().toISOString() } + + const { + emailNotificationSettings, + }: { emailNotificationSettings: { name: string; value: boolean }[] | undefined } = params + delete params.emailNotificationSettings + if (emailNotificationSettings) { + emailNotificationSettings.forEach((setting) => { + params[ + 'emailNotifications' + setting.name.charAt(0).toUpperCase() + setting.name.slice(1) + ] = setting.value + }) + } + const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { @@ -357,6 +370,53 @@ export default { const [{ email }] = result.records.map((r) => r.get('e').properties) return email }, + emailNotificationSettings: async (parent, params, context, resolveInfo) => { + return [ + { + type: 'post', + settings: [ + { + name: 'commentOnObservedPost', + value: parent.emailNotificationsCommentOnObservedPost ?? true, + }, + { + name: 'mention', + value: parent.emailNotificationsMention ?? true, + }, + ], + }, + { + type: 'chat', + settings: [ + { + name: 'chatMessage', + value: parent.emailNotificationsChatMessage ?? true, + }, + ], + }, + { + type: 'group', + settings: [ + { + name: 'groupMemberJoined', + value: parent.emailNotificationsGroupMemberJoined ?? true, + }, + { + name: 'groupMemberLeft', + value: parent.emailNotificationsGroupMemberLeft ?? true, + }, + { + name: 'groupMemberRemoved', + value: parent.emailNotificationsGroupMemberRemoved ?? true, + }, + { + name: 'groupMemberRoleChanged', + value: parent.emailNotificationsGroupMemberRoleChanged ?? true, + }, + ], + }, + ] + }, ...Resolver('User', { undefinedToNull: [ 'actorId', @@ -368,7 +428,6 @@ export default { 'termsAndConditionsAgreedAt', 'allowEmbedIframes', 'showShoutsPublicly', - 'sendNotificationEmails', 'locale', ], boolean: { diff --git a/backend/src/schema/types/enum/EmailNotificationSettingsName.gql b/backend/src/schema/types/enum/EmailNotificationSettingsName.gql new file mode 100644 index 000000000..fa1d5846e --- /dev/null +++ b/backend/src/schema/types/enum/EmailNotificationSettingsName.gql @@ -0,0 +1,9 @@ +enum EmailNotificationSettingsName { + commentOnObservedPost + mention + chatMessage + groupMemberJoined + groupMemberLeft + groupMemberRemoved + groupMemberRoleChanged +} \ No newline at end of file diff --git a/backend/src/schema/types/enum/EmailNotificationSettingsType.gql b/backend/src/schema/types/enum/EmailNotificationSettingsType.gql new file mode 100644 index 000000000..70128a6b2 --- /dev/null +++ b/backend/src/schema/types/enum/EmailNotificationSettingsType.gql @@ -0,0 +1,5 @@ +enum EmailNotificationSettingsType { + post + chat + group +} \ No newline at end of file diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 70b10aa42..37281d6bb 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -19,6 +19,21 @@ enum _UserOrdering { locale_desc } +input EmailNotificationSettingsInput { + name: EmailNotificationSettingsName + value: Boolean +} + +type EmailNotificationSettings { + type: EmailNotificationSettingsType + settings: [EmailNotificationSettingsOption] @neo4j_ignore +} + +type EmailNotificationSettingsOption { + name: EmailNotificationSettingsName + value: Boolean +} + type User { id: ID! actorId: String @@ -46,7 +61,7 @@ type User { allowEmbedIframes: Boolean showShoutsPublicly: Boolean - sendNotificationEmails: Boolean + emailNotificationSettings: [EmailNotificationSettings]! @neo4j_ignore locale: String friends: [User]! @relation(name: "FRIENDS", direction: "BOTH") friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)") @@ -206,7 +221,7 @@ type Mutation { termsAndConditionsAgreedAt: String allowEmbedIframes: Boolean showShoutsPublicly: Boolean - sendNotificationEmails: Boolean + emailNotificationSettings: [EmailNotificationSettingsInput] locale: String ): User diff --git a/cypress/e2e/User.SettingNotifications.feature b/cypress/e2e/User.SettingNotifications.feature.broken similarity index 100% rename from cypress/e2e/User.SettingNotifications.feature rename to cypress/e2e/User.SettingNotifications.feature.broken diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 4b743a0e3..8ad247ad1 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -46,7 +46,6 @@ export const profileUserQuery = (i18n) => { url } showShoutsPublicly - sendNotificationEmails } } ` @@ -335,7 +334,7 @@ export const updateUserMutation = () => { $about: String $allowEmbedIframes: Boolean $showShoutsPublicly: Boolean - $sendNotificationEmails: Boolean + $emailNotificationSettings: [EmailNotificationSettingsInput] $termsAndConditionsAgreedVersion: String $avatar: ImageInput $locationName: String # empty string '' sets it to null @@ -347,7 +346,7 @@ export const updateUserMutation = () => { about: $about allowEmbedIframes: $allowEmbedIframes showShoutsPublicly: $showShoutsPublicly - sendNotificationEmails: $sendNotificationEmails + emailNotificationSettings: $emailNotificationSettings termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion avatar: $avatar locationName: $locationName @@ -359,7 +358,13 @@ export const updateUserMutation = () => { about allowEmbedIframes showShoutsPublicly - sendNotificationEmails + emailNotificationSettings { + type + settings { + name + value + } + } locale termsAndConditionsAgreedVersion avatar { @@ -390,7 +395,13 @@ export const currentUserQuery = gql` locale allowEmbedIframes showShoutsPublicly - sendNotificationEmails + emailNotificationSettings { + type + settings { + name + value + } + } termsAndConditionsAgreedVersion socialMedia { id diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 518ba99a9..42f6ab74f 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -1039,9 +1039,23 @@ }, "name": "Einstellungen", "notifications": { - "name": "Benachrichtigungen", + "chat": "Chat", + "chatMessage": "Nachricht erhalten während Abwesenheit", + "checkAll": "Alle auswählen", + "commentOnObservedPost": "Kommentare zu beobachteten Beiträgen", + "group": "Gruppen", + "groupMemberJoined": "Ein Mitglied ist deiner Gruppe beigetreten", + "groupMemberLeft": "Ein Mitglied hat deine Gruppe verlassen", + "groupMemberRemoved": "Du wurdest aus einer Gruppe entfernt", + "groupMemberRoleChanged": "Deine Rolle in einer Gruppe wurde geändert", + "mention": "Ich wurde erwähnt", + "name": "Benachrichtigungen per Email", + "post": "Beiträge und Kommentare", + "postByFollowedUser": "Beitrag von einem Nutzer, dem ich folge", + "postInGroup": "Beitrag in einer Gruppe, die ich beobachte", "send-email-notifications": "Sende E-Mail-Benachrichtigungen", - "success-update": "Benachrichtigungs-Einstellungen gespeichert!" + "success-update": "Benachrichtigungs-Einstellungen gespeichert!", + "uncheckAll": "Alle abwählen" }, "organizations": { "name": "Meine Organisationen" diff --git a/webapp/locales/en.json b/webapp/locales/en.json index f78728c4f..714c3f3c0 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -1039,9 +1039,23 @@ }, "name": "Settings", "notifications": { - "name": "Notifications", + "chat": "Chat", + "chatMessage": "Message received while absent", + "checkAll": "Check all", + "commentOnObservedPost": "Comments on observed posts", + "group": "Groups", + "groupMemberJoined": "Member joined a group I own", + "groupMemberLeft": "Member left a group I own", + "groupMemberRemoved": "I was removed from a group", + "groupMemberRoleChanged": "My role in a group was changed", + "mention": "I was mentioned", + "name": "Email Notifications", + "post": "Posts and comments", + "postByFollowedUser": "Posts by users I follow", + "postInGroup": "Post in a group I am a member of", "send-email-notifications": "Send e-mail notifications", - "success-update": "Notifications settings saved!" + "success-update": "Notifications settings saved!", + "uncheckAll": "Uncheck all" }, "organizations": { "name": "My Organizations" diff --git a/webapp/locales/es.json b/webapp/locales/es.json index a085a53e0..f0a1a866b 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -1039,9 +1039,23 @@ }, "name": "Configuración", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Chat", + "chatMessage": "Mensaje recibido mientras estaba ausente", + "checkAll": "Seleccionar todo", + "commentOnObservedPost": "Comentario en una contribución que estoy observando", + "group": "Grupos", + "groupMemberJoined": "Un nuevo miembro se unió a un grupo mio", + "groupMemberLeft": "Un miembro dejó un grupo mio", + "groupMemberRemoved": "Fui eliminado de un grupo", + "groupMemberRoleChanged": "Mi rol en un grupo ha cambiado", + "mention": "Mencionado en una contribución", + "name": "Notificaciones por correo electrónico", + "post": "Entradas y comentarios", + "postByFollowedUser": "Posts by users I follow", + "postInGroup": "Post en un grupo del que soy miembro", + "send-email-notifications": "Enviar notificaciones por correo electrónico", + "success-update": "¡Configuración de notificaciones guardada!", + "uncheckAll": "Deseleccionar todo" }, "organizations": { "name": "Mis organizaciones" diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index f1b5642de..a31e197a1 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -1039,9 +1039,23 @@ }, "name": "Paramètres", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Chat", + "chatMessage": "Message reçu pendant l'absence", + "checkAll": "Tout cocher", + "commentOnObservedPost": "Commentez une contribution que je suis", + "group": "Groups", + "groupMemberJoined": "Un nouveau membre a rejoint un de mes groupes", + "groupMemberLeft": "Un membre a quitté un de mes groupes", + "groupMemberRemoved": "J'ai été retiré d'un groupe", + "groupMemberRoleChanged": "Mon rôle au sein d'un groupe a changé", + "mention": "Mentionné dans une contribution", + "name": "Notifications par mail", + "post": "Messages et commentaires", + "postByFollowedUser": "Messages des utilisateurs que je suis", + "postInGroup": "Message dans un groupe dont je suis membre", + "send-email-notifications": "Envoyer des notifications par courrier électronique", + "success-update": "Paramètres de notification sauvegardés ! ", + "uncheckAll": "Tout décocher" }, "organizations": { "name": "Mes organisations" diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 54248e6ee..8f14dfda2 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -1039,9 +1039,23 @@ }, "name": "Impostazioni", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Chat", + "chatMessage": "Messaggio ricevuto durante l'assenza", + "checkAll": "Seleziona tutto", + "commentOnObservedPost": "Commenta un contributo che sto guardando", + "group": "Gruppi", + "groupMemberJoined": "Un nuovo membro si è unito a un mio gruppo", + "groupMemberLeft": "Un membro ha lasciato un mio gruppo", + "groupMemberRemoved": "Sono stato rimosso da un gruppo", + "groupMemberRoleChanged": "Il mio ruolo in un gruppo è cambiato", + "mention": "Menzionato in un contributo", + "name": "Notifiche via e-mail", + "post": "Messaggi e commenti", + "postByFollowedUser": "Messaggi di utenti che seguo", + "postInGroup": "Post in un gruppo di cui sono membro", + "send-email-notifications": "Invia notifiche via e-mail", + "success-update": "Impostazioni di notifica salvate! ", + "uncheckAll": "Deseleziona tutto" }, "organizations": { "name": "Mie organizzazioni" diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 7907ce052..e332d38dc 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -1039,9 +1039,23 @@ }, "name": "Instellingen", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Chat", + "chatMessage": "Bericht ontvangen tijdens afwezigheid", + "checkAll": "Vink alles aan", + "commentOnObservedPost": "Geef commentaar op een bijdrage die ik volg", + "group": "Groepen", + "groupMemberJoined": "Een nieuw lid is lid geworden van een groep van mij", + "groupMemberLeft": "Een lid heeft een groep van mij verlaten", + "groupMemberRemoved": "Ik ben verwijderd uit een groep", + "groupMemberRoleChanged": "Mijn rol in een groep is veranderd", + "mention": "Genoemd in een bijdrage", + "name": "Email Meldingen", + "post": "Berichten en reacties", + "postByFollowedUser": "Berichten van gebruikers die ik volg", + "postInGroup": "Bericht in een groep waar ik lid van ben", + "send-email-notifications": "E-mailmeldingen verzenden", + "success-update": "Meldingsinstellingen opgeslagen! ", + "uncheckAll": "Vink alles uit" }, "organizations": { "name": "Mijn Organisaties" diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index 7a800b3d0..5c636dfab 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -1039,9 +1039,23 @@ }, "name": "Ustawienia", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Chat", + "chatMessage": "Wiadomość otrzymana podczas nieobecności", + "checkAll": "Wybierz wszystko", + "commentOnObservedPost": "Skomentuj wpis, który obserwuję", + "group": "Grupy", + "groupMemberJoined": "Nowy członek dołączył do mojej grupy", + "groupMemberLeft": "Członek opuścił moją grupę", + "groupMemberRemoved": "Zostałem usunięty z grupy", + "groupMemberRoleChanged": "Moja rola w grupie uległa zmianie", + "mention": "Mentioned in a contribution", + "name": "Powiadomienia e-mail", + "post": "Posty", + "postByFollowedUser": "Posty użytkowników, których obserwuję", + "postInGroup": "Posty w grupie, której jestem członkiem", + "send-email-notifications": "Wyślij powiadomienia e-mail", + "success-update": "Ustawienia powiadomień zapisane! ", + "uncheckAll": "Odznacz wszystko" }, "organizations": { "name": "My Organizations" diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index c0bb8a500..c00acbf0a 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -1039,9 +1039,23 @@ }, "name": "Configurações", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Chat", + "chatMessage": "Mensagem recebida durante a ausência", + "checkAll": "Marcar tudo", + "commentOnObservedPost": "Comentários sobre as mensagens observadas", + "group": "Grupos", + "groupMemberJoined": "Member joined a group I own", + "groupMemberLeft": "Membro saiu de um grupo de que sou proprietário", + "groupMemberRemoved": "Fui removido de um grupo", + "groupMemberRoleChanged": "O meu papel num grupo foi alterado", + "mention": "Fui mencionado", + "name": "Notificações por correio eletrónico", + "post": "Posts e comentários", + "postByFollowedUser": "Publicações de utilizadores que sigo", + "postInGroup": "Postar num grupo de que sou membro", + "send-email-notifications": "Enviar notificações por correio eletrónico", + "success-update": "Definições de notificações guardadas!", + "uncheckAll": "Desmarcar tudo" }, "organizations": { "name": "Minhas Organizações" diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index bebae1012..5775264fa 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -1039,9 +1039,23 @@ }, "name": "Настройки", "notifications": { - "name": null, - "send-email-notifications": null, - "success-update": null + "chat": "Чат", + "chatMessage": "Сообщение, полученное во время отсутствия", + "checkAll": "Отметить все", + "commentOnObservedPost": "Комментарии по поводу замеченных сообщений", + "group": "Группы", + "groupMemberJoined": "Участник присоединился к группе, которой я владею", + "groupMemberLeft": "Участник вышел из группы, которой владею", + "groupMemberRemoved": "Был удален из группы", + "groupMemberRoleChanged": "Моя роль в группе была изменена", + "mention": "Упоминание в вкладе", + "name": "Уведомления", + "post": "Сообщения и комментарии", + "postByFollowedUser": "Сообщения пользователей, за которыми я слежу", + "postInGroup": "Сообщение в группе, членом которой я являюсь", + "send-email-notifications": "Отправлять уведомления по электронной почте", + "success-update": "Настройки уведомлений сохранены! ", + "uncheckAll": "Снимите все флажки" }, "organizations": { "name": "Мои организации" diff --git a/webapp/package.json b/webapp/package.json index 8ae97ec3e..f1c3778d0 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -18,6 +18,7 @@ "locales:normalize": "../scripts/translations/normalize.sh", "precommit": "yarn lint", "test": "cross-env NODE_ENV=test jest --coverage --forceExit --detectOpenHandles", + "test:unit:update": "yarn test -- --updateSnapshot", "test:unit:debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --no-cache --runInBand" }, "dependencies": { @@ -78,6 +79,7 @@ "@storybook/addon-actions": "^5.3.21", "@storybook/addon-notes": "^5.3.18", "@storybook/vue": "~7.4.0", + "@testing-library/vue": "5", "@vue/cli-shared-utils": "~4.3.1", "@vue/eslint-config-prettier": "~6.0.0", "@vue/server-test-utils": "~1.0.0-beta.31", diff --git a/webapp/pages/settings/__snapshots__/notifications.spec.js.snap b/webapp/pages/settings/__snapshots__/notifications.spec.js.snap new file mode 100644 index 000000000..0b70393ee --- /dev/null +++ b/webapp/pages/settings/__snapshots__/notifications.spec.js.snap @@ -0,0 +1,197 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`notifications.vue mount renders 1`] = ` +
+

+ settings.notifications.name +

+ +
+
+

+ settings.notifications.post +

+
+ +
+
+ + + +
+
+ + + +
+
+
+
+
+

+ settings.notifications.group +

+
+ +
+
+ + + +
+
+ + + +
+
+ + + +
+
+ + + +
+
+
+ + + + + + + + +
+`; diff --git a/webapp/pages/settings/notifications.spec.js b/webapp/pages/settings/notifications.spec.js index 855505fe2..a16e99ed4 100644 --- a/webapp/pages/settings/notifications.spec.js +++ b/webapp/pages/settings/notifications.spec.js @@ -1,5 +1,6 @@ import Vuex from 'vuex' import { mount } from '@vue/test-utils' +import { render, fireEvent, screen } from '@testing-library/vue' import Notifications from './notifications.vue' const localVue = global.localVue @@ -11,7 +12,7 @@ describe('notifications.vue', () => { beforeEach(() => { mocks = { - $t: jest.fn(), + $t: jest.fn((v) => v), $apollo: { mutate: jest.fn(), }, @@ -26,7 +27,42 @@ describe('notifications.vue', () => { return { id: 'u343', name: 'MyAccount', - sendNotificationEmails: true, + emailNotificationSettings: [ + { + type: 'post', + settings: [ + { + name: 'commentOnObservedPost', + value: true, + }, + { + name: 'mention', + value: false, + }, + ], + }, + { + type: 'group', + settings: [ + { + name: 'groupMemberJoined', + value: true, + }, + { + name: 'groupMemberLeft', + value: true, + }, + { + name: 'groupMemberRemoved', + value: false, + }, + { + name: 'groupMemberRoleChanged', + value: true, + }, + ], + }, + ], } }, }, @@ -47,21 +83,116 @@ describe('notifications.vue', () => { }) it('renders', () => { - expect(wrapper.classes('base-card')).toBe(true) + expect(wrapper.element).toMatchSnapshot() + }) + }) + + describe('Notifications', () => { + beforeEach(() => { + render(Notifications, { + store, + mocks, + localVue, + }) }) - it('clicking on submit changes notifyByEmail to false', async () => { - await wrapper.find('#send-email').setChecked(false) - await wrapper.find('.base-button').trigger('click') - expect(wrapper.vm.notifyByEmail).toBe(false) + it('check all button works', async () => { + const button = screen.getByText('settings.notifications.checkAll') + await fireEvent.click(button) + + const checkboxes = screen.getAllByRole('checkbox') + for (const checkbox of checkboxes) { + expect(checkbox.checked).toEqual(true) + } + + // Check that the button is disabled + expect(button.disabled).toBe(true) }) - it('clicking on submit with a server error shows a toast and notifyByEmail is still true', async () => { + it('uncheck all button works', async () => { + const button = screen.getByText('settings.notifications.uncheckAll') + await fireEvent.click(button) + + const checkboxes = screen.getAllByRole('checkbox') + for (const checkbox of checkboxes) { + expect(checkbox.checked).toEqual(false) + } + + // Check that the button is disabled + expect(button.disabled).toBe(true) + }) + + it('clicking on submit keeps set values and shows success message', async () => { + mocks.$apollo.mutate = jest.fn().mockResolvedValue({ + data: { + UpdateUser: { + emailNotificationSettings: [ + { + type: 'post', + settings: [ + { + name: 'commentOnObservedPost', + value: false, + }, + { + name: 'mention', + value: false, + }, + ], + }, + { + type: 'group', + settings: [ + { + name: 'groupMemberJoined', + value: true, + }, + { + name: 'groupMemberLeft', + value: true, + }, + { + name: 'groupMemberRemoved', + value: false, + }, + { + name: 'groupMemberRoleChanged', + value: true, + }, + ], + }, + ], + }, + }, + }) + + // Change some value to enable save button + const checkbox = screen.getAllByRole('checkbox')[0] + await fireEvent.click(checkbox) + + const newValue = checkbox.checked + + // Click save button + const button = screen.getByText('actions.save') + await fireEvent.click(button) + + expect(checkbox.checked).toEqual(newValue) + + expect(mocks.$toast.success).toHaveBeenCalledWith('settings.notifications.success-update') + }) + + it('clicking on submit with a server error shows a toast', async () => { mocks.$apollo.mutate = jest.fn().mockRejectedValue({ message: 'Ouch!' }) - await wrapper.find('#send-email').setChecked(false) - await wrapper.find('.base-button').trigger('click') + + // Change some value to enable save button + const checkbox = screen.getAllByRole('checkbox')[0] + await fireEvent.click(checkbox) + + // Click save button + const button = screen.getByText('actions.save') + await fireEvent.click(button) + expect(mocks.$toast.error).toHaveBeenCalledWith('Ouch!') - expect(wrapper.vm.notifyByEmail).toBe(true) }) }) }) diff --git a/webapp/pages/settings/notifications.vue b/webapp/pages/settings/notifications.vue index a2828a1a9..35249a37d 100644 --- a/webapp/pages/settings/notifications.vue +++ b/webapp/pages/settings/notifications.vue @@ -1,11 +1,26 @@ @@ -87,3 +87,24 @@ export default { }, } + + diff --git a/webapp/pages/settings/blocked-users.vue b/webapp/pages/settings/blocked-users.vue index 90519452f..0eed6d370 100644 --- a/webapp/pages/settings/blocked-users.vue +++ b/webapp/pages/settings/blocked-users.vue @@ -75,8 +75,10 @@ + + diff --git a/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap b/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap new file mode 100644 index 000000000..c09a50725 --- /dev/null +++ b/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap @@ -0,0 +1,36 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Admin/BadgesSection renders 1`] = ` + +
+
+

+ +

+ +
+ + +
+
+
+ +`; diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 8ad247ad1..147e93c6f 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -90,6 +90,23 @@ export const adminUserQuery = () => { ` } +export const adminUserBadgesQuery = () => { + return gql` + query User($id: ID!) { + User(id: $id) { + id + name + badgeTrophies { + id + } + badgeVerification { + id + } + } + } + ` +} + export const mapUserQuery = (i18n) => { const lang = i18n.locale().toUpperCase() return gql` diff --git a/webapp/graphql/admin/Badges.js b/webapp/graphql/admin/Badges.js new file mode 100644 index 000000000..2c037f2f3 --- /dev/null +++ b/webapp/graphql/admin/Badges.js @@ -0,0 +1,54 @@ +import gql from 'graphql-tag' + +export const queryBadges = () => gql` + query { + Badge { + id + type + icon + description + } + } +` + +export const setVerificationBadge = () => gql` + mutation ($badgeId: ID!, $userId: ID!) { + setVerificationBadge(badgeId: $badgeId, userId: $userId) { + id + badgeVerification { + id + } + badgeTrophies { + id + } + } + } +` + +export const rewardTrophyBadge = () => gql` + mutation ($badgeId: ID!, $userId: ID!) { + rewardTrophyBadge(badgeId: $badgeId, userId: $userId) { + id + badgeVerification { + id + } + badgeTrophies { + id + } + } + } +` + +export const revokeBadge = () => gql` + mutation ($badgeId: ID!, $userId: ID!) { + revokeBadge(badgeId: $badgeId, userId: $userId) { + id + badgeVerification { + id + } + badgeTrophies { + id + } + } + } +` diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 19d0896a9..ce122672d 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -10,6 +10,28 @@ "saveCategories": "Themen speichern" }, "admin": { + "badges": { + "description": "Stelle die verfügbaren Auszeichnungen für diesen Nutzer ein.", + "revokeTrophy": { + "error": "Trophäe konnte nicht widerrufen werden!", + "success": "Trophäe erfolgreich widerrufen" + }, + "revokeVerification": { + "error": "Verifizierung konnte nicht gesetzt werden!", + "success": "Verifizierung erfolgreich widerrufen" + }, + "rewardTrophy": { + "error": "Trophäe konnte nicht vergeben werden!", + "success": "Trophäe erfolgreich vergeben!" + }, + "setVerification": { + "error": "Verifizierung konnte nicht gesetzt werden!", + "success": "Verifizierung erfolgreich gesetzt" + }, + "title": "Auszeichnungen", + "trophyBadges": "Trophäen", + "verificationBadges": "Verifizierungen" + }, "categories": { "categoryName": "Name", "name": "Themen", @@ -68,13 +90,15 @@ "roleChanged": "Rolle erfolgreich geändert!", "table": { "columns": { + "badges": "Auszeichnungen", "createdAt": "Erstellt am", "email": "E-Mail", "name": "Name", "number": "Nr.", "role": "Rolle", "slug": "Alias" - } + }, + "edit": "Bearbeiten" } } }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index b4c1125f3..f178da549 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -10,6 +10,28 @@ "saveCategories": "Save topics" }, "admin": { + "badges": { + "description": "Configure the available badges for this user", + "revokeTrophy": { + "error": "Trophy could not be revoked!", + "success": "Trophy successfully revoked!" + }, + "revokeVerification": { + "error": "Verification could not be revoked!", + "success": "Verification succesfully revoked" + }, + "rewardTrophy": { + "error": "Trophy could not be rewarded!", + "success": "Trophy successfully rewarded!" + }, + "setVerification": { + "error": "Verification could not be set!", + "success": "Verification successfully set!" + }, + "title": "Badges", + "trophyBadges": "Trophies", + "verificationBadges": "Verifications" + }, "categories": { "categoryName": "Name", "name": "Topics", @@ -68,13 +90,15 @@ "roleChanged": "Role changed successfully!", "table": { "columns": { + "badges": "Badges", "createdAt": "Created at", "email": "E-mail", "name": "Name", "number": "No.", "role": "Role", "slug": "Slug" - } + }, + "edit": "Edit" } } }, diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 7184a327a..31f2cc5f4 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Nombre", "name": "Categorías", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": "Creado el", "email": "Correo electrónico", "name": "Nombre", "number": "No.", "role": "Rol", "slug": "Alias" - } + }, + "edit": null } } }, diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index 851743e63..4bbca2b82 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Nom", "name": "Catégories", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": "Créé à", "email": "Mail", "name": "Nom", "number": "Num.", "role": "Rôle", "slug": "Slug" - } + }, + "edit": null } } }, diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 0c693ca43..21bfaa859 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Nome", "name": "Categorie", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": null, "email": null, "name": null, "number": null, "role": null, "slug": null - } + }, + "edit": null } } }, diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 433adf8e8..f67518c21 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Naam", "name": "Categorieën", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": null, "email": null, "name": null, "number": null, "role": null, "slug": null - } + }, + "edit": null } } }, diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index c0ab9d09c..4c6a96a5f 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Nazwa", "name": "Kategorie", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": null, "email": null, "name": null, "number": null, "role": null, "slug": null - } + }, + "edit": null } } }, diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 02f8fb2cc..7d5ad52c1 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Nome", "name": "Categorias", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": "Criado em", "email": "E-mail", "name": "Nome", "number": "N.º", "role": "Função", "slug": "Slug" - } + }, + "edit": null } } }, diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index ea0279450..3a394d6ff 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -10,6 +10,28 @@ "saveCategories": null }, "admin": { + "badges": { + "description": null, + "revokeTrophy": { + "error": null, + "success": null + }, + "revokeVerification": { + "error": null, + "success": null + }, + "rewardTrophy": { + "error": null, + "success": null + }, + "setVerification": { + "error": null, + "success": null + }, + "title": null, + "trophyBadges": null, + "verificationBadges": null + }, "categories": { "categoryName": "Имя", "name": "Категории", @@ -68,13 +90,15 @@ "roleChanged": null, "table": { "columns": { + "badges": null, "createdAt": "Дата создания", "email": "Эл. почта", "name": "Имя", "number": "№", "role": "Роль", "slug": "Алиас" - } + }, + "edit": null } } }, diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index b3bbdfc2d..1c963615a 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -207,6 +207,15 @@ export default { 'X-API-TOKEN': CONFIG.BACKEND_TOKEN, }, }, + '/img': { + // make this configurable (nuxt-dotenv) + target: CONFIG.GRAPHQL_URI, + toProxy: true, // cloudflare needs that + headers: { + 'X-UI-Request': true, + 'X-API-TOKEN': CONFIG.BACKEND_TOKEN, + }, + }, }, // Give apollo module options diff --git a/webapp/pages/admin/users/__snapshots__/_id.spec.js.snap b/webapp/pages/admin/users/__snapshots__/_id.spec.js.snap new file mode 100644 index 000000000..2c5ddc686 --- /dev/null +++ b/webapp/pages/admin/users/__snapshots__/_id.spec.js.snap @@ -0,0 +1,104 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`.vue renders 1`] = ` + +
+
+
+
+
+

+ + User1 + - + admin.badges.title + +

+ +

+ admin.badges.description +

+
+ +
+
+

+ admin.badges.verificationBadges +

+ +
+ + +
+
+ +
+

+ admin.badges.trophyBadges +

+ +
+ + +
+
+ + +
+
+
+
+
+ +`; diff --git a/webapp/pages/admin/users/_id.spec.js b/webapp/pages/admin/users/_id.spec.js new file mode 100644 index 000000000..933de58de --- /dev/null +++ b/webapp/pages/admin/users/_id.spec.js @@ -0,0 +1,326 @@ +import { render, fireEvent, screen } from '@testing-library/vue' +import BadgesPage from './_id.vue' + +const localVue = global.localVue + +const availableBadges = [ + { + id: 'verification-badge-1', + icon: 'icon1', + type: 'verification', + description: 'description-v-1', + }, + { + id: 'verification-badge-2', + icon: 'icon2', + type: 'verification', + description: 'description-v-2', + }, + { + id: 'trophy-badge-1', + icon: 'icon3', + type: 'trophy', + description: 'description-t-1', + }, + { + id: 'trophy-badge-2', + icon: 'icon4', + type: 'trophy', + description: 'description-t-2', + }, +] + +const user = { + id: 'user1', + name: 'User1', + badgeVerification: { + id: 'verification-badge-1', + }, + badgeTrophies: [ + { + id: 'trophy-badge-2', + }, + ], +} + +describe('.vue', () => { + let wrapper + let mocks + + beforeEach(() => { + mocks = { + $t: jest.fn((v) => v), + $apollo: { + User: { + query: jest.fn(), + }, + badges: { + query: jest.fn(), + }, + mutate: jest.fn(), + }, + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + } + }) + const Wrapper = () => { + return render(BadgesPage, { + mocks, + localVue, + data: () => ({ + user, + badges: availableBadges, + }), + }) + } + + beforeEach(() => { + wrapper = Wrapper() + }) + + it('renders', () => { + expect(wrapper.baseElement).toMatchSnapshot() + }) + + describe('after clicking an inactive verification badge', () => { + let button + beforeEach(() => { + button = screen.getByAltText(availableBadges[1].description) + }) + + describe('and successful server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockResolvedValue({ + data: { + setVerificationBadge: { + id: 'user1', + badgeVerification: { + id: availableBadges[1].id, + }, + badgeTrophies: [], + }, + }, + }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[1].id, + userId: 'user1', + }, + }) + }) + + it('shows success message', async () => { + expect(mocks.$toast.success).toHaveBeenCalledWith('admin.badges.setVerification.success') + }) + }) + + describe('and failed server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[1].id, + userId: 'user1', + }, + }) + }) + + it('shows error message', async () => { + expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.setVerification.error') + }) + }) + + describe('after clicking an inactive trophy badge', () => { + let button + beforeEach(() => { + button = screen.getByAltText(availableBadges[2].description) + }) + + describe('and successful server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockResolvedValue({ + data: { + setTrophyBadge: { + id: 'user1', + badgeVerification: null, + badgeTrophies: [ + { + id: availableBadges[2].id, + }, + ], + }, + }, + }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[2].id, + userId: 'user1', + }, + }) + }) + + it('shows success message', async () => { + expect(mocks.$toast.success).toHaveBeenCalledWith('admin.badges.rewardTrophy.success') + }) + }) + + describe('and failed server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[2].id, + userId: 'user1', + }, + }) + }) + + it('shows error message', async () => { + expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.rewardTrophy.error') + }) + }) + }) + + describe('after clicking an active verification badge', () => { + let button + beforeEach(() => { + button = screen.getByAltText(availableBadges[0].description) + }) + + describe('and successful server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockResolvedValue({ + data: { + setVerificationBadge: { + id: 'user1', + badgeVerification: null, + badgeTrophies: [], + }, + }, + }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[0].id, + userId: 'user1', + }, + }) + }) + + it('shows success message', async () => { + expect(mocks.$toast.success).toHaveBeenCalledWith( + 'admin.badges.revokeVerification.success', + ) + }) + }) + + describe('and failed server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[0].id, + userId: 'user1', + }, + }) + }) + + it('shows error message', async () => { + expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.revokeVerification.error') + }) + }) + }) + }) + + describe('after clicking an active trophy badge', () => { + let button + beforeEach(() => { + button = screen.getByAltText(availableBadges[3].description) + }) + + describe('and successful server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockResolvedValue({ + data: { + setTrophyBadge: { + id: 'user1', + badgeVerification: null, + badgeTrophies: [ + { + id: availableBadges[3].id, + }, + ], + }, + }, + }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[3].id, + userId: 'user1', + }, + }) + }) + + it('shows success message', async () => { + expect(mocks.$toast.success).toHaveBeenCalledWith('admin.badges.revokeTrophy.success') + }) + }) + + describe('and failed server response', () => { + beforeEach(async () => { + mocks.$apollo.mutate.mockRejectedValue({ message: 'Ouch!' }) + await fireEvent.click(button) + }) + + it('calls the mutation', async () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + variables: { + badgeId: availableBadges[3].id, + userId: 'user1', + }, + }) + }) + + it('shows error message', async () => { + expect(mocks.$toast.error).toHaveBeenCalledWith('admin.badges.revokeTrophy.error') + }) + }) + }) +}) diff --git a/webapp/pages/admin/users/_id.vue b/webapp/pages/admin/users/_id.vue new file mode 100644 index 000000000..808e1653a --- /dev/null +++ b/webapp/pages/admin/users/_id.vue @@ -0,0 +1,163 @@ + + + diff --git a/webapp/pages/admin/users.spec.js b/webapp/pages/admin/users/index.spec.js similarity index 99% rename from webapp/pages/admin/users.spec.js rename to webapp/pages/admin/users/index.spec.js index 43c51fb52..8d6b923c5 100644 --- a/webapp/pages/admin/users.spec.js +++ b/webapp/pages/admin/users/index.spec.js @@ -1,6 +1,6 @@ import { mount } from '@vue/test-utils' import Vuex from 'vuex' -import Users from './users.vue' +import Users from './index.vue' const localVue = global.localVue diff --git a/webapp/pages/admin/users.vue b/webapp/pages/admin/users/index.vue similarity index 93% rename from webapp/pages/admin/users.vue rename to webapp/pages/admin/users/index.vue index 44f162c77..24258a57f 100644 --- a/webapp/pages/admin/users.vue +++ b/webapp/pages/admin/users/index.vue @@ -63,6 +63,16 @@ {{ scope.row.role }} + @@ -132,6 +142,10 @@ export default { label: this.$t('admin.users.table.columns.role'), align: 'right', }, + badges: { + label: this.$t('admin.users.table.columns.badges'), + align: 'right', + }, } }, }, From e58efb1ce0b8c88e06dccded409f91112dd7494a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 23 Apr 2025 13:56:17 +0200 Subject: [PATCH 148/266] refactor(webapp): refactor branding diverse v2 (#8427) * Set new 'config.resolve.alias' * Set new alias 'compilerOptions.paths' --- webapp/jsconfig.json | 13 +++++++++++-- webapp/nuxt.config.js | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/webapp/jsconfig.json b/webapp/jsconfig.json index 7e3695e4e..98874805a 100644 --- a/webapp/jsconfig.json +++ b/webapp/jsconfig.json @@ -3,11 +3,20 @@ "baseUrl": ".", "paths": { "~/*": [ - "./*" + "*" ], "~*": [ + "*" + ], + "~@": [ + "*" + ], + "@": [ + "*" + ], + "@@/*": [ "./*" ], } } -} \ No newline at end of file +} diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 1c963615a..07cfa6bc4 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -270,6 +270,9 @@ export default { }, } + config.resolve.alias['~@'] = path.resolve(__dirname, '/') + config.resolve.alias['@@'] = path.resolve(__dirname, '/') + if (CONFIG.STYLEGUIDE_DEV) { config.resolve.alias['@@'] = path.resolve(__dirname, `${styleguidePath}/src/system`) config.module.rules.push({ From 6b40a0dc590e3063306f4e821bc4907c110aa355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 23 Apr 2025 23:24:41 +0800 Subject: [PATCH 149/266] chore(frontend): run npm install (#8432) Just running `npm install` leads to local changes. Why are they not checked in? I'm using the specified node version from `/.tool-versions` in the root directory. --- frontend/package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 63b54a127..c912f54a9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social-frontend", - "version": "3.2.1", + "version": "3.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.2.1", + "version": "3.3.0", "license": "Apache-2.0", "dependencies": { "@intlify/unplugin-vue-i18n": "^2.0.0", From 873cd6cd3409345708c7cc2455cb07e81fdf94bb Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Apr 2025 18:21:06 +0200 Subject: [PATCH 150/266] refactor(backend): allow to set selected badge-slot to null (#8421) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * allow to set selected badgeslot to null Free a specific badge slot by setting it to null * Update backend/src/schema/resolvers/users.ts Co-authored-by: Wolfgang Huß --------- Co-authored-by: Wolfgang Huß Co-authored-by: Max --- backend/src/graphql/types/type/User.gql | 2 +- backend/src/schema/resolvers/users.spec.ts | 49 +++++++++++++++++++++- backend/src/schema/resolvers/users.ts | 18 +++++--- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/types/type/User.gql b/backend/src/graphql/types/type/User.gql index f1a2bcc15..751931d5b 100644 --- a/backend/src/graphql/types/type/User.gql +++ b/backend/src/graphql/types/type/User.gql @@ -252,6 +252,6 @@ type Mutation { # Get a JWT Token for the given Email and password login(email: String!, password: String!): String! - setTrophyBadgeSelected(slot: Int!, badgeId: ID!): User + setTrophyBadgeSelected(slot: Int!, badgeId: ID): User resetTrophyBadgesSelected: User } diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index 0a74d46d3..8d082b682 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -76,7 +76,7 @@ const updateOnlineStatus = gql` ` const setTrophyBadgeSelected = gql` - mutation ($slot: Int!, $badgeId: ID!) { + mutation ($slot: Int!, $badgeId: ID) { setTrophyBadgeSelected(slot: $slot, badgeId: $badgeId) { badgeTrophiesCount badgeTrophiesSelected { @@ -1294,6 +1294,53 @@ describe('setTrophyBadgeSelected', () => { }), ) }) + + describe('set badge to null', () => { + it('returns the user with no badge set on the selected slot', async () => { + await mutate({ + mutation: setTrophyBadgeSelected, + variables: { slot: 0, badgeId: 'trophy_bear' }, + }) + await mutate({ + mutation: setTrophyBadgeSelected, + variables: { slot: 5, badgeId: 'trophy_panda' }, + }) + + await expect( + mutate({ + mutation: setTrophyBadgeSelected, + variables: { slot: 5, badgeId: null }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + setTrophyBadgeSelected: { + badgeTrophiesCount: 2, + badgeTrophiesSelected: [ + { + id: 'trophy_bear', + }, + null, + null, + null, + null, + null, + null, + null, + null, + ], + badgeTrophiesUnused: [ + { + id: 'trophy_panda', + }, + ], + badgeTrophiesUnusedCount: 1, + }, + }, + }), + ) + }) + }) }) }) diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index 4f1fb6d5b..913427085 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -404,17 +404,25 @@ export default { const session = context.driver.session() const query = session.writeTransaction(async (transaction) => { - const result = await transaction.run( - ` + const queryBadge = ` MATCH (user:User {id: $userId})<-[:REWARDED]-(badge:Badge {id: $badgeId}) OPTIONAL MATCH (user)-[badgeRelation:SELECTED]->(badge) OPTIONAL MATCH (user)-[slotRelation:SELECTED{slot: $slot}]->(:Badge) DELETE badgeRelation, slotRelation MERGE (user)-[:SELECTED{slot: toInteger($slot)}]->(badge) RETURN user {.*} - `, - { userId, badgeId, slot }, - ) + ` + const queryNull = ` + MATCH (user:User {id: $userId}) + OPTIONAL MATCH (user)-[slotRelation:SELECTED {slot: $slot}]->(:Badge) + DELETE slotRelation + RETURN user {.*} + ` + const result = await transaction.run(badgeId ? queryBadge : queryNull, { + userId, + badgeId, + slot, + }) return result.records.map((record) => record.get('user'))[0] }) try { From 5883818b9116926b11c70fc2ee2bbac95fc0f25c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 23 Apr 2025 19:12:24 +0200 Subject: [PATCH 151/266] refactor(backend): default badges, always return a badge (#8430) * default badges, always return a badge - default badges for trophy and verification - always return a badge instead of null - isDefault field on Badge lint fixes * default_verification svg * add default-trophy Co-authored-by: Sebastian Stein --------- Co-authored-by: Sebastian Stein --- backend/public/img/badges/default_trophy.svg | 12 + .../img/badges/default_verification.svg | 28 +++ backend/src/graphql/types/type/Badge.gql | 1 + backend/src/graphql/types/type/User.gql | 4 +- backend/src/schema/resolvers/badges.spec.ts | 19 +- backend/src/schema/resolvers/badges.ts | 20 ++ backend/src/schema/resolvers/users.spec.ts | 227 ++++++++++++++++-- backend/src/schema/resolvers/users.ts | 56 +++-- 8 files changed, 311 insertions(+), 56 deletions(-) create mode 100644 backend/public/img/badges/default_trophy.svg create mode 100644 backend/public/img/badges/default_verification.svg diff --git a/backend/public/img/badges/default_trophy.svg b/backend/public/img/badges/default_trophy.svg new file mode 100644 index 000000000..b203cdfc6 --- /dev/null +++ b/backend/public/img/badges/default_trophy.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/backend/public/img/badges/default_verification.svg b/backend/public/img/badges/default_verification.svg new file mode 100644 index 000000000..7bde29f35 --- /dev/null +++ b/backend/public/img/badges/default_verification.svg @@ -0,0 +1,28 @@ + + + + + + + + + + diff --git a/backend/src/graphql/types/type/Badge.gql b/backend/src/graphql/types/type/Badge.gql index cbfe0193d..8cdad2ee7 100644 --- a/backend/src/graphql/types/type/Badge.gql +++ b/backend/src/graphql/types/type/Badge.gql @@ -4,6 +4,7 @@ type Badge { icon: String! createdAt: String description: String! + isDefault: Boolean! rewarded: [User]! @relation(name: "REWARDED", direction: "OUT") verifies: [User]! @relation(name: "VERIFIES", direction: "OUT") diff --git a/backend/src/graphql/types/type/User.gql b/backend/src/graphql/types/type/User.gql index 751931d5b..81dd9cf5b 100644 --- a/backend/src/graphql/types/type/User.gql +++ b/backend/src/graphql/types/type/User.gql @@ -125,10 +125,10 @@ type User { categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT") - badgeVerification: Badge @relation(name: "VERIFIES", direction: "IN") + badgeVerification: Badge! @neo4j_ignore badgeTrophies: [Badge]! @relation(name: "REWARDED", direction: "IN") badgeTrophiesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)") - badgeTrophiesSelected: [Badge]! @neo4j_ignore + badgeTrophiesSelected: [Badge!]! @neo4j_ignore badgeTrophiesUnused: [Badge]! @neo4j_ignore badgeTrophiesUnusedCount: Int! @neo4j_ignore diff --git a/backend/src/schema/resolvers/badges.spec.ts b/backend/src/schema/resolvers/badges.spec.ts index 17fe46c99..2588c9b5c 100644 --- a/backend/src/schema/resolvers/badges.spec.ts +++ b/backend/src/schema/resolvers/badges.spec.ts @@ -100,6 +100,7 @@ describe('Badges', () => { id badgeVerification { id + isDefault } badgeTrophies { id @@ -204,7 +205,7 @@ describe('Badges', () => { data: { setVerificationBadge: { id: 'regular-user-id', - badgeVerification: { id: 'verification_moderator' }, + badgeVerification: { id: 'verification_moderator', isDefault: false }, badgeTrophies: [], }, }, @@ -226,7 +227,7 @@ describe('Badges', () => { data: { setVerificationBadge: { id: 'regular-user-id', - badgeVerification: { id: 'verification_admin' }, + badgeVerification: { id: 'verification_admin', isDefault: false }, badgeTrophies: [], }, }, @@ -255,7 +256,7 @@ describe('Badges', () => { data: { setVerificationBadge: { id: 'regular-user-2-id', - badgeVerification: { id: 'verification_moderator' }, + badgeVerification: { id: 'verification_moderator', isDefault: false }, badgeTrophies: [], }, }, @@ -299,6 +300,7 @@ describe('Badges', () => { id badgeVerification { id + isDefault } badgeTrophies { id @@ -403,7 +405,7 @@ describe('Badges', () => { data: { rewardTrophyBadge: { id: 'regular-user-id', - badgeVerification: null, + badgeVerification: { id: 'default_verification', isDefault: true }, badgeTrophies: [{ id: 'trophy_rhino' }], }, }, @@ -530,6 +532,7 @@ describe('Badges', () => { id badgeVerification { id + isDefault } badgeTrophies { id @@ -596,7 +599,7 @@ describe('Badges', () => { data: { revokeBadge: { id: 'regular-user-id', - badgeVerification: { id: 'verification_moderator' }, + badgeVerification: { id: 'verification_moderator', isDefault: false }, badgeTrophies: [], }, }, @@ -610,7 +613,7 @@ describe('Badges', () => { data: { revokeBadge: { id: 'regular-user-id', - badgeVerification: { id: 'verification_moderator' }, + badgeVerification: { id: 'verification_moderator', isDefault: false }, badgeTrophies: [], }, }, @@ -631,7 +634,7 @@ describe('Badges', () => { data: { revokeBadge: { id: 'regular-user-id', - badgeVerification: null, + badgeVerification: { id: 'default_verification', isDefault: true }, badgeTrophies: [{ id: 'trophy_rhino' }], }, }, @@ -659,7 +662,7 @@ describe('Badges', () => { data: { revokeBadge: { id: 'regular-user-id', - badgeVerification: null, + badgeVerification: { id: 'default_verification', isDefault: true }, badgeTrophies: [{ id: 'trophy_rhino' }], }, }, diff --git a/backend/src/schema/resolvers/badges.ts b/backend/src/schema/resolvers/badges.ts index 430e3bf75..587204b54 100644 --- a/backend/src/schema/resolvers/badges.ts +++ b/backend/src/schema/resolvers/badges.ts @@ -6,6 +6,22 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { neo4jgraphql } from 'neo4j-graphql-js' +export const defaultTrophyBadge = { + id: 'default_trophy', + type: 'trophy', + icon: '/img/badges/default_trophy.svg', + description: '', + createdAt: '', +} + +export const defaultVerificationBadge = { + id: 'default_verification', + type: 'verification', + icon: '/img/badges/default_verification.svg', + description: '', + createdAt: '', +} + export default { Query: { Badge: async (object, args, context, resolveInfo) => @@ -123,4 +139,8 @@ export default { } }, }, + Badge: { + isDefault: async (parent, _params, _context, _resolveInfo) => + [defaultTrophyBadge.id, defaultVerificationBadge.id].includes(parent.id), + }, } diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index 8d082b682..d4f5e00eb 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -81,6 +81,7 @@ const setTrophyBadgeSelected = gql` badgeTrophiesCount badgeTrophiesSelected { id + isDefault } badgeTrophiesUnused { id @@ -96,6 +97,7 @@ const resetTrophyBadgesSelected = gql` badgeTrophiesCount badgeTrophiesSelected { id + isDefault } badgeTrophiesUnused { id @@ -1242,15 +1244,40 @@ describe('setTrophyBadgeSelected', () => { badgeTrophiesSelected: [ { id: 'trophy_bear', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, }, - null, - null, - null, - null, - null, - null, - null, - null, ], badgeTrophiesUnused: [ { @@ -1275,17 +1302,40 @@ describe('setTrophyBadgeSelected', () => { badgeTrophiesSelected: [ { id: 'trophy_bear', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, }, - null, - null, - null, - null, { id: 'trophy_panda', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, }, - null, - null, - null, ], badgeTrophiesUnused: [], badgeTrophiesUnusedCount: 0, @@ -1295,8 +1345,8 @@ describe('setTrophyBadgeSelected', () => { ) }) - describe('set badge to null', () => { - it('returns the user with no badge set on the selected slot', async () => { + describe('set badge to null or default', () => { + beforeEach(async () => { await mutate({ mutation: setTrophyBadgeSelected, variables: { slot: 0, badgeId: 'trophy_bear' }, @@ -1305,7 +1355,9 @@ describe('setTrophyBadgeSelected', () => { mutation: setTrophyBadgeSelected, variables: { slot: 5, badgeId: 'trophy_panda' }, }) + }) + it('returns the user with no badge set on the selected slot when sending null', async () => { await expect( mutate({ mutation: setTrophyBadgeSelected, @@ -1319,15 +1371,101 @@ describe('setTrophyBadgeSelected', () => { badgeTrophiesSelected: [ { id: 'trophy_bear', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], + badgeTrophiesUnused: [ + { + id: 'trophy_panda', + }, + ], + badgeTrophiesUnusedCount: 1, + }, + }, + }), + ) + }) + + it('returns the user with no badge set on the selected slot when sending default_trophy', async () => { + await expect( + mutate({ + mutation: setTrophyBadgeSelected, + variables: { slot: 5, badgeId: 'default_trophy' }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + setTrophyBadgeSelected: { + badgeTrophiesCount: 2, + badgeTrophiesSelected: [ + { + id: 'trophy_bear', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, }, - null, - null, - null, - null, - null, - null, - null, - null, ], badgeTrophiesUnused: [ { @@ -1411,7 +1549,44 @@ describe('resetTrophyBadgesSelected', () => { data: { resetTrophyBadgesSelected: { badgeTrophiesCount: 2, - badgeTrophiesSelected: [null, null, null, null, null, null, null, null, null], + badgeTrophiesSelected: [ + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], badgeTrophiesUnused: [ { id: 'trophy_panda', diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index 913427085..c165e8e44 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -11,6 +11,7 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges' import { getNeode } from '@db/neo4j' +import { defaultTrophyBadge, defaultVerificationBadge } from './badges' import log from './helpers/databaseLogger' import Resolver from './helpers/Resolver' import { mergeImage, deleteImage } from './images/images' @@ -412,13 +413,15 @@ export default { MERGE (user)-[:SELECTED{slot: toInteger($slot)}]->(badge) RETURN user {.*} ` - const queryNull = ` + const queryEmpty = ` MATCH (user:User {id: $userId}) OPTIONAL MATCH (user)-[slotRelation:SELECTED {slot: $slot}]->(:Badge) DELETE slotRelation RETURN user {.*} ` - const result = await transaction.run(badgeId ? queryBadge : queryNull, { + const isDefault = !badgeId || badgeId === defaultTrophyBadge.id + + const result = await transaction.run(isDefault ? queryEmpty : queryBadge, { userId, badgeId, slot, @@ -538,7 +541,7 @@ export default { }) try { const badgesSelected = await query - const result = Array(TROPHY_BADGES_SELECTED_MAX).fill(null) + const result = Array(TROPHY_BADGES_SELECTED_MAX).fill(defaultTrophyBadge) badgesSelected.map((record) => { result[record.get('slot')] = record.get('badge') return true @@ -550,21 +553,17 @@ export default { session.close() } }, - badgeTrophiesUnused: async (_parent, _params, context, _resolveInfo) => { - const { - user: { id: userId }, - } = context - + badgeTrophiesUnused: async (parent, _params, context, _resolveInfo) => { const session = context.driver.session() - const query = session.writeTransaction(async (transaction) => { + const query = session.readTransaction(async (transaction) => { const result = await transaction.run( ` - MATCH (user:User {id: $userId})<-[:REWARDED]-(badge:Badge) + MATCH (user:User {id: $parent.id})<-[:REWARDED]-(badge:Badge) WHERE NOT (user)-[:SELECTED]-(badge) RETURN badge {.*} `, - { userId }, + { parent }, ) return result.records.map((record) => record.get('badge')) }) @@ -576,21 +575,17 @@ export default { session.close() } }, - badgeTrophiesUnusedCount: async (_parent, _params, context, _resolveInfo) => { - const { - user: { id: userId }, - } = context - + badgeTrophiesUnusedCount: async (parent, _params, context, _resolveInfo) => { const session = context.driver.session() - const query = session.writeTransaction(async (transaction) => { + const query = session.readTransaction(async (transaction) => { const result = await transaction.run( ` - MATCH (user:User {id: $userId})<-[:REWARDED]-(badge:Badge) + MATCH (user:User {id: $parent.id})<-[:REWARDED]-(badge:Badge) WHERE NOT (user)-[:SELECTED]-(badge) RETURN toString(COUNT(badge)) as count `, - { userId }, + { parent }, ) return result.records.map((record) => record.get('count'))[0] }) @@ -602,6 +597,28 @@ export default { session.close() } }, + badgeVerification: async (parent, _params, context, _resolveInfo) => { + const session = context.driver.session() + + const query = session.writeTransaction(async (transaction) => { + const result = await transaction.run( + ` + MATCH (user:User {id: $parent.id})<-[:VERIFIES]-(verification:Badge) + RETURN verification {.*} + `, + { parent }, + ) + return result.records.map((record) => record.get('verification'))[0] + }) + try { + const result = await query + return result ?? defaultVerificationBadge + } catch (error) { + throw new Error(error) + } finally { + session.close() + } + }, ...Resolver('User', { undefinedToNull: [ 'actorId', @@ -642,7 +659,6 @@ export default { invitedBy: '<-[:INVITED]-(related:User)', location: '-[:IS_IN]->(related:Location)', redeemedInviteCode: '-[:REDEEMED]->(related:InviteCode)', - badgeVerification: '<-[:VERIFIES]-(related:Badge)', }, hasMany: { followedBy: '<-[:FOLLOWS]-(related:User)', From 649491f7cbe312fb4101bc32e6a4d81af94c38e0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Apr 2025 00:58:53 +0200 Subject: [PATCH 152/266] fix(backend): fix notification emails with different name (#8419) * fix diffent name notifications We had emails sent with incorrect names. This PR combines the query for the email with the user the notification is sent to since the notification in database was correct. The underlying problem is the unstable order in which the database can return values. The results of the two queries were matched by id since it was assumed that they always return the same order of elements. lint fixes fix typo fix factory fix tests * fix tests accoridng to review also test for the right amount of emails in every test --- .../notificationsMiddleware.emails.spec.ts | 86 +++++++---- ...ficationsMiddleware.followed-users.spec.ts | 80 ++++++++-- ...tionsMiddleware.mentions-in-groups.spec.ts | 71 +++++++-- ...icationsMiddleware.observing-posts.spec.ts | 74 +++++++-- ...ificationsMiddleware.online-status.spec.ts | 6 +- ...icationsMiddleware.posts-in-groups.spec.ts | 30 +++- .../notificationsMiddleware.spec.ts | 42 ++--- .../notifications/notificationsMiddleware.ts | 146 +++++++----------- 8 files changed, 355 insertions(+), 180 deletions(-) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 78c95b454..55edef940 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -16,20 +16,21 @@ import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock = jest.fn() -jest.mock('../helpers/email/sendMail', () => ({ - sendMail: () => sendMailMock(), +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), })) -let server, query, mutate, authenticatedUser +let server, query, mutate, authenticatedUser, emaillessMember let postAuthor, groupMember const driver = getDriver() const neode = getNeode() -const mentionString = - '@group-member' +const mentionString = ` + @group-member + @email-less-member` const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { @@ -148,6 +149,11 @@ describe('emails sent for notifications', () => { password: '1234', }, ) + emaillessMember = await neode.create('User', { + id: 'email-less-member', + name: 'Email-less Member', + slug: 'email-less-member', + }) authenticatedUser = await postAuthor.toJson() await mutate({ mutation: createGroupMutation(), @@ -171,6 +177,18 @@ describe('emails sent for notifications', () => { mutation: followUserMutation, variables: { id: 'post-author' }, }) + authenticatedUser = await emaillessMember.toJson() + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'public-group', + userId: 'group-member', + }, + }) + await mutate({ + mutation: followUserMutation, + variables: { id: 'post-author' }, + }) }) afterEach(async () => { @@ -188,7 +206,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -213,7 +231,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'post_in_group', @@ -225,7 +243,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'followed_user_posted', @@ -237,7 +255,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_post', @@ -260,7 +278,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -285,7 +303,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'post_in_group', @@ -297,7 +315,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'followed_user_posted', @@ -309,7 +327,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_post', @@ -333,7 +351,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -358,7 +376,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'post_in_group', @@ -370,7 +388,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'followed_user_posted', @@ -382,7 +400,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_post', @@ -407,7 +425,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -432,7 +450,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'post_in_group', @@ -444,7 +462,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'followed_user_posted', @@ -456,7 +474,7 @@ describe('emails sent for notifications', () => { __typename: 'Post', id: 'post', content: - 'Hello, @group-member, my trusty follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_post', @@ -481,7 +499,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -501,7 +519,7 @@ describe('emails sent for notifications', () => { mutation: createCommentMutation, variables: { id: 'comment-2', - content: `Hello, ${mentionString}, my beloved follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, postId: 'post', }, }) @@ -529,7 +547,7 @@ describe('emails sent for notifications', () => { __typename: 'Comment', id: 'comment-2', content: - 'Hello, @group-member, my beloved follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'commented_on_post', @@ -541,7 +559,7 @@ describe('emails sent for notifications', () => { __typename: 'Comment', id: 'comment-2', content: - 'Hello, @group-member, my beloved follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_comment', @@ -563,7 +581,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -583,7 +601,7 @@ describe('emails sent for notifications', () => { mutation: createCommentMutation, variables: { id: 'comment-2', - content: `Hello, ${mentionString}, my beloved follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, postId: 'post', }, }) @@ -611,7 +629,7 @@ describe('emails sent for notifications', () => { __typename: 'Comment', id: 'comment-2', content: - 'Hello, @group-member, my beloved follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'commented_on_post', @@ -623,7 +641,7 @@ describe('emails sent for notifications', () => { __typename: 'Comment', id: 'comment-2', content: - 'Hello, @group-member, my beloved follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_comment', @@ -646,7 +664,7 @@ describe('emails sent for notifications', () => { variables: { id: 'post', title: 'This is the post', - content: `Hello, ${mentionString}, my trusty follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, groupId: 'public-group', }, }) @@ -666,7 +684,7 @@ describe('emails sent for notifications', () => { mutation: createCommentMutation, variables: { id: 'comment-2', - content: `Hello, ${mentionString}, my beloved follower.`, + content: `Hello, ${mentionString}, my trusty followers.`, postId: 'post', }, }) @@ -694,7 +712,7 @@ describe('emails sent for notifications', () => { __typename: 'Comment', id: 'comment-2', content: - 'Hello, @group-member, my beloved follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'commented_on_post', @@ -706,7 +724,7 @@ describe('emails sent for notifications', () => { __typename: 'Comment', id: 'comment-2', content: - 'Hello, @group-member, my beloved follower.', + 'Hello,
@group-member
@email-less-member, my trusty followers.', }, read: false, reason: 'mentioned_in_comment', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 5be4ea5b5..18da6bff8 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -14,14 +14,14 @@ import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock = jest.fn() -jest.mock('../helpers/email/sendMail', () => ({ - sendMail: () => sendMailMock(), +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), })) let server, query, mutate, authenticatedUser -let postAuthor, firstFollower, secondFollower +let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower const driver = getDriver() const neode = getNeode() @@ -107,7 +107,7 @@ describe('following users notifications', () => { slug: 'post-author', }, { - email: 'test@example.org', + email: 'post-author@example.org', password: '1234', }, ) @@ -119,7 +119,7 @@ describe('following users notifications', () => { slug: 'first-follower', }, { - email: 'test2@example.org', + email: 'first-follower@example.org', password: '1234', }, ) @@ -131,10 +131,27 @@ describe('following users notifications', () => { slug: 'second-follower', }, { - email: 'test3@example.org', + email: 'second-follower@example.org', password: '1234', }, ) + thirdFollower = await Factory.build( + 'user', + { + id: 'third-follower', + name: 'Third Follower', + slug: 'third-follower', + }, + { + email: 'third-follower@example.org', + password: '1234', + }, + ) + emaillessFollower = await neode.create('User', { + id: 'email-less-follower', + name: 'Email-less Follower', + slug: 'email-less-follower', + }) await secondFollower.update({ emailNotificationsFollowingUsers: false }) authenticatedUser = await firstFollower.toJson() await mutate({ @@ -146,6 +163,16 @@ describe('following users notifications', () => { mutation: followUserMutation, variables: { id: 'post-author' }, }) + authenticatedUser = await thirdFollower.toJson() + await mutate({ + mutation: followUserMutation, + variables: { id: 'post-author' }, + }) + authenticatedUser = await emaillessFollower.toJson() + await mutate({ + mutation: followUserMutation, + variables: { id: 'post-author' }, + }) jest.clearAllMocks() }) @@ -221,8 +248,43 @@ describe('following users notifications', () => { }) }) - it('sends only one email, as second follower has emails disabled', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + it('sends notification to the email-less follower', async () => { + authenticatedUser = await emaillessFollower.toJson() + await expect( + query({ + query: notificationQuery, + }), + ).resolves.toMatchObject({ + data: { + notifications: [ + { + from: { + __typename: 'Post', + id: 'post', + }, + read: false, + reason: 'followed_user_posted', + }, + ], + }, + errors: undefined, + }) + }) + + 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.objectContaining({ + html: expect.stringContaining('Hello First Follower'), + to: 'first-follower@example.org', + }), + ) + expect(sendMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + html: expect.stringContaining('Hello Third Follower'), + to: 'third-follower@example.org', + }), + ) }) }) 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 7058efd25..1c7ca4c71 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -17,22 +17,23 @@ import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock = jest.fn() -jest.mock('../helpers/email/sendMail', () => ({ - sendMail: () => sendMailMock(), +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), })) let server, query, mutate, authenticatedUser -let postAuthor, groupMember, pendingMember, noMember +let postAuthor, groupMember, pendingMember, noMember, emaillessMember const driver = getDriver() const neode = getNeode() const mentionString = ` - @no-meber + @no-member @pending-member @group-member. + @email-less-member. ` const createPostMutation = gql` @@ -168,6 +169,12 @@ describe('mentions in groups', () => { password: '1234', }, ) + emaillessMember = await neode.create('User', { + id: 'email-less-member', + name: 'Email-less Member', + slug: 'email-less-member', + }) + authenticatedUser = await postAuthor.toJson() await mutate({ mutation: createGroupMutation(), @@ -243,6 +250,28 @@ describe('mentions in groups', () => { userId: 'pending-member', }, }) + authenticatedUser = await emaillessMember.toJson() + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'public-group', + userId: 'group-member', + }, + }) + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'closed-group', + userId: 'group-member', + }, + }) + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'hidden-group', + userId: 'group-member', + }, + }) authenticatedUser = await postAuthor.toJson() await mutate({ mutation: changeGroupMemberRoleMutation(), @@ -260,8 +289,26 @@ describe('mentions in groups', () => { roleInGroup: 'usual', }, }) + await mutate({ + mutation: changeGroupMemberRoleMutation(), + variables: { + groupId: 'closed-group', + userId: 'email-less-member', + roleInGroup: 'usual', + }, + }) + await mutate({ + mutation: changeGroupMemberRoleMutation(), + variables: { + groupId: 'hidden-group', + userId: 'email-less-member', + roleInGroup: 'usual', + }, + }) authenticatedUser = await groupMember.toJson() await markAllAsRead() + authenticatedUser = await emaillessMember.toJson() + await markAllAsRead() }) afterEach(async () => { @@ -327,7 +374,7 @@ describe('mentions in groups', () => { __typename: 'Post', id: 'public-post', content: - 'Hey
@no-meber
@pending-member
@group-member.
! Please read this', + 'Hey
@no-member
@pending-member
@group-member.
@email-less-member.
! Please read this', }, read: false, reason: 'post_in_group', @@ -339,7 +386,7 @@ describe('mentions in groups', () => { __typename: 'Post', id: 'public-post', content: - 'Hey
@no-meber
@pending-member
@group-member.
! Please read this', + 'Hey
@no-member
@pending-member
@group-member.
@email-less-member.
! Please read this', }, read: false, reason: 'mentioned_in_post', @@ -351,7 +398,7 @@ describe('mentions in groups', () => { }) }) - it('sends 3 emails, one for each user', () => { + it('sends only 3 emails, one for each user with an email', () => { expect(sendMailMock).toHaveBeenCalledTimes(3) }) }) @@ -423,7 +470,7 @@ describe('mentions in groups', () => { __typename: 'Post', id: 'closed-post', content: - 'Hey members
@no-meber
@pending-member
@group-member.
! Please read this', + 'Hey members
@no-member
@pending-member
@group-member.
@email-less-member.
! Please read this', }, read: false, reason: 'post_in_group', @@ -435,7 +482,7 @@ describe('mentions in groups', () => { __typename: 'Post', id: 'closed-post', content: - 'Hey members
@no-meber
@pending-member
@group-member.
! Please read this', + 'Hey members
@no-member
@pending-member
@group-member.
@email-less-member.
! Please read this', }, read: false, reason: 'mentioned_in_post', @@ -519,7 +566,7 @@ describe('mentions in groups', () => { __typename: 'Post', id: 'hidden-post', content: - 'Hey hiders
@no-meber
@pending-member
@group-member.
! Please read this', + 'Hey hiders
@no-member
@pending-member
@group-member.
@email-less-member.
! Please read this', }, read: false, reason: 'post_in_group', @@ -531,7 +578,7 @@ describe('mentions in groups', () => { __typename: 'Post', id: 'hidden-post', content: - 'Hey hiders
@no-meber
@pending-member
@group-member.
! Please read this', + 'Hey hiders
@no-member
@pending-member
@group-member.
@email-less-member.
! Please read this', }, read: false, reason: 'mentioned_in_post', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index 2c73d2beb..9f193eaeb 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -6,15 +6,20 @@ import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import CONFIG from '@config/index' -import { cleanDatabase } from '@db/factories' +import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), +})) + let server, query, mutate, authenticatedUser -let postAuthor, firstCommenter, secondCommenter +let postAuthor, firstCommenter, secondCommenter, emaillessObserver const driver = getDriver() const neode = getNeode() @@ -102,42 +107,47 @@ afterAll(async () => { describe('notifications for users that observe a post', () => { beforeAll(async () => { - postAuthor = await neode.create( - 'User', + postAuthor = await Factory.build( + 'user', { id: 'post-author', name: 'Post Author', slug: 'post-author', }, { - email: 'test@example.org', + email: 'post-author@example.org', password: '1234', }, ) - firstCommenter = await neode.create( - 'User', + firstCommenter = await Factory.build( + 'user', { id: 'first-commenter', name: 'First Commenter', slug: 'first-commenter', }, { - email: 'test2@example.org', + email: 'first-commenter@example.org', password: '1234', }, ) - secondCommenter = await neode.create( - 'User', + secondCommenter = await Factory.build( + 'user', { id: 'second-commenter', name: 'Second Commenter', slug: 'second-commenter', }, { - email: 'test3@example.org', + email: 'second-commenter@example.org', password: '1234', }, ) + emaillessObserver = await neode.create('User', { + id: 'email-less-observer', + name: 'Email-less Observer', + slug: 'email-less-observer', + }) authenticatedUser = await postAuthor.toJson() await mutate({ mutation: createPostMutation, @@ -147,6 +157,14 @@ describe('notifications for users that observe a post', () => { content: 'This is the content of the post', }, }) + authenticatedUser = await emaillessObserver.toJson() + await mutate({ + mutation: toggleObservePostMutation, + variables: { + id: 'post', + value: true, + }, + }) }) describe('first comment on the post', () => { @@ -198,8 +216,18 @@ describe('notifications for users that observe a post', () => { }) }) + it('sends one email', () => { + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + to: 'post-author@example.org', + }), + ) + }) + describe('second comment on post', () => { beforeAll(async () => { + jest.clearAllMocks() authenticatedUser = await secondCommenter.toJson() await mutate({ mutation: createCommentMutation, @@ -277,10 +305,25 @@ describe('notifications for users that observe a post', () => { errors: undefined, }) }) + + it('sends two emails', () => { + expect(sendMailMock).toHaveBeenCalledTimes(2) + expect(sendMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + to: 'post-author@example.org', + }), + ) + expect(sendMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + to: 'first-commenter@example.org', + }), + ) + }) }) describe('first commenter unfollows the post and post author comments post', () => { beforeAll(async () => { + jest.clearAllMocks() authenticatedUser = await firstCommenter.toJson() await mutate({ mutation: toggleObservePostMutation, @@ -376,6 +419,15 @@ describe('notifications for users that observe a post', () => { errors: undefined, }) }) + + it('sends one email', () => { + expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + to: 'second-commenter@example.org', + }), + ) + }) }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts index da9a6b250..47842029c 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -13,9 +13,9 @@ import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock = jest.fn() -jest.mock('../helpers/email/sendMail', () => ({ - sendMail: () => sendMailMock(), +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), })) let isUserOnlineMock = jest.fn().mockReturnValue(false) 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 9ca4ae7ab..6bde0aee2 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -17,14 +17,14 @@ import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock = jest.fn() -jest.mock('../helpers/email/sendMail', () => ({ - sendMail: () => sendMailMock(), +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), })) let server, query, mutate, authenticatedUser -let postAuthor, groupMember, pendingMember +let postAuthor, groupMember, pendingMember, emaillessMember const driver = getDriver() const neode = getNeode() @@ -159,6 +159,12 @@ describe('notify group members of new posts in group', () => { password: '1234', }, ) + emaillessMember = await neode.create('User', { + id: 'email-less-member', + name: 'Email-less Member', + slug: 'email-less-member', + }) + authenticatedUser = await postAuthor.toJson() await mutate({ mutation: createGroupMutation(), @@ -186,6 +192,14 @@ describe('notify group members of new posts in group', () => { userId: 'pending-member', }, }) + authenticatedUser = await emaillessMember.toJson() + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'g-1', + userId: 'group-member', + }, + }) authenticatedUser = await postAuthor.toJson() await mutate({ mutation: changeGroupMemberRoleMutation(), @@ -195,6 +209,14 @@ describe('notify group members of new posts in group', () => { roleInGroup: 'usual', }, }) + await mutate({ + mutation: changeGroupMemberRoleMutation(), + variables: { + groupId: 'g-1', + userId: 'email-less-member', + roleInGroup: 'usual', + }, + }) }) afterEach(async () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 908ccac22..31e458e2a 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -18,9 +18,9 @@ import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' import createServer, { pubsub } from '@src/server' -const sendMailMock = jest.fn() -jest.mock('../helpers/email/sendMail', () => ({ - sendMail: () => sendMailMock(), +const sendMailMock: (notification) => void = jest.fn() +jest.mock('@middleware/helpers/email/sendMail', () => ({ + sendMail: (notification) => sendMailMock(notification), })) const chatMessageTemplateMock = jest.fn() @@ -195,8 +195,8 @@ describe('notifications', () => { beforeEach(async () => { jest.clearAllMocks() commentContent = 'Commenters comment.' - commentAuthor = await neode.create( - 'User', + commentAuthor = await Factory.build( + 'user', { id: 'commentAuthor', name: 'Mrs Comment', @@ -345,8 +345,8 @@ describe('notifications', () => { beforeEach(async () => { jest.clearAllMocks() - postAuthor = await neode.create( - 'User', + postAuthor = await Factory.build( + 'user', { id: 'postAuthor', name: 'Mrs Post', @@ -658,8 +658,8 @@ describe('notifications', () => { beforeEach(async () => { commentContent = 'One mention about me with @al-capone.' - commentAuthor = await neode.create( - 'User', + commentAuthor = await Factory.build( + 'user', { id: 'commentAuthor', name: 'Mrs Comment', @@ -673,15 +673,15 @@ describe('notifications', () => { }) it('sends only one notification with reason mentioned_in_comment', async () => { - postAuthor = await neode.create( - 'User', + postAuthor = await Factory.build( + 'user', { id: 'MrPostAuthor', name: 'Mr Author', slug: 'mr-author', }, { - email: 'post-author@example.org', + email: 'post-author2@example.org', password: '1234', }, ) @@ -756,8 +756,8 @@ describe('notifications', () => { await postAuthor.relateTo(notifiedUser, 'blocked') commentContent = 'One mention about me with @al-capone.' - commentAuthor = await neode.create( - 'User', + commentAuthor = await Factory.build( + 'user', { id: 'commentAuthor', name: 'Mrs Comment', @@ -807,8 +807,8 @@ describe('notifications', () => { await postAuthor.relateTo(notifiedUser, 'muted') commentContent = 'One mention about me with @al-capone.' - commentAuthor = await neode.create( - 'User', + commentAuthor = await Factory.build( + 'user', { id: 'commentAuthor', name: 'Mrs Comment', @@ -879,8 +879,8 @@ describe('notifications', () => { beforeEach(async () => { jest.clearAllMocks() - chatSender = await neode.create( - 'User', + chatSender = await Factory.build( + 'user', { id: 'chatSender', name: 'chatSender', @@ -931,7 +931,7 @@ describe('notifications', () => { content: 'Some nice message to chatReceiver', senderId: 'chatSender', username: 'chatSender', - avatar: null, + avatar: expect.any(String), date: expect.any(String), saved: true, distributed: false, @@ -967,7 +967,7 @@ describe('notifications', () => { content: 'Some nice message to chatReceiver', senderId: 'chatSender', username: 'chatSender', - avatar: null, + avatar: expect.any(String), date: expect.any(String), saved: true, distributed: false, @@ -1046,7 +1046,7 @@ describe('notifications', () => { content: 'Some nice message to chatReceiver', senderId: 'chatSender', username: 'chatSender', - avatar: null, + avatar: expect.any(String), date: expect.any(String), saved: true, distributed: false, diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index f7be031c8..b9f5c4284 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -18,59 +18,28 @@ import { pubsub, NOTIFICATION_ADDED, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } fr import extractMentionedUsers from './mentions/extractMentionedUsers' -const queryNotificationEmails = async (context, notificationUserIds) => { - if (!notificationUserIds?.length) return [] - const userEmailCypher = ` - MATCH (user: User) - // blocked users are filtered out from notifications already - WHERE user.id in $notificationUserIds - WITH user - MATCH (user)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) - RETURN emailAddress {.email} - ` - const session = context.driver.session() - const writeTxResultPromise = session.readTransaction(async (transaction) => { - const emailAddressTransactionResponse = await transaction.run(userEmailCypher, { - notificationUserIds, - }) - return emailAddressTransactionResponse.records.map((record) => record.get('emailAddress')) - }) - try { - const emailAddresses = await writeTxResultPromise - return emailAddresses - } catch (error) { - throw new Error(error) - } finally { - session.close() - } -} - const publishNotifications = async ( context, - promises, + notificationsPromise, emailNotificationSetting: string, emailsSent: string[] = [], ): Promise => { - let notifications = await Promise.all(promises) - notifications = notifications.flat() - const notificationsEmailAddresses = await queryNotificationEmails( - context, - notifications.map((notification) => notification.to.id), - ) - notifications.forEach((notificationAdded, index) => { + const notifications = await notificationsPromise + notifications.forEach((notificationAdded) => { pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) if ( + notificationAdded.email && // no primary email was found (notificationAdded.to[emailNotificationSetting] ?? true) && !isUserOnline(notificationAdded.to) && - !emailsSent.includes(notificationsEmailAddresses[index].email) + !emailsSent.includes(notificationAdded.email) ) { sendMail( notificationTemplate({ - email: notificationsEmailAddresses[index].email, + email: notificationAdded.email, variables: { notification: notificationAdded }, }), ) - emailsSent.push(notificationsEmailAddresses[index].email) + emailsSent.push(notificationAdded.email) } }) return emailsSent @@ -82,7 +51,7 @@ const handleJoinGroup = async (resolve, root, args, context, resolveInfo) => { if (user) { await publishNotifications( context, - [notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context)], + notifyOwnersOfGroup(groupId, userId, 'user_joined_group', context), 'emailNotificationsGroupMemberJoined', ) } @@ -95,7 +64,7 @@ const handleLeaveGroup = async (resolve, root, args, context, resolveInfo) => { if (user) { await publishNotifications( context, - [notifyOwnersOfGroup(groupId, userId, 'user_left_group', context)], + notifyOwnersOfGroup(groupId, userId, 'user_left_group', context), 'emailNotificationsGroupMemberLeft', ) } @@ -108,7 +77,7 @@ const handleChangeGroupMemberRole = async (resolve, root, args, context, resolve if (user) { await publishNotifications( context, - [notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context)], + notifyMemberOfGroup(groupId, userId, 'changed_group_member_role', context), 'emailNotificationsGroupMemberRoleChanged', ) } @@ -121,7 +90,7 @@ const handleRemoveUserFromGroup = async (resolve, root, args, context, resolveIn if (user) { await publishNotifications( context, - [notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context)], + notifyMemberOfGroup(groupId, userId, 'removed_user_from_group', context), 'emailNotificationsGroupMemberRemoved', ) } @@ -135,20 +104,20 @@ const handleContentDataOfPost = async (resolve, root, args, context, resolveInfo if (post) { const sentEmails: string[] = await publishNotifications( context, - [notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context)], + notifyUsersOfMention('Post', post.id, idsOfUsers, 'mentioned_in_post', context), 'emailNotificationsMention', ) sentEmails.concat( await publishNotifications( context, - [notifyFollowingUsers(post.id, groupId, context)], + notifyFollowingUsers(post.id, groupId, context), 'emailNotificationsFollowingUsers', sentEmails, ), ) await publishNotifications( context, - [notifyGroupMembersOfNewPost(post.id, groupId, context)], + notifyGroupMembersOfNewPost(post.id, groupId, context), 'emailNotificationsPostInGroup', sentEmails, ) @@ -164,20 +133,18 @@ const handleContentDataOfComment = async (resolve, root, args, context, resolveI idsOfMentionedUsers = idsOfMentionedUsers.filter((id) => id !== postAuthor.id) const sentEmails: string[] = await publishNotifications( context, - [ - notifyUsersOfMention( - 'Comment', - comment.id, - idsOfMentionedUsers, - 'mentioned_in_comment', - context, - ), - ], + notifyUsersOfMention( + 'Comment', + comment.id, + idsOfMentionedUsers, + 'mentioned_in_comment', + context, + ), 'emailNotificationsMention', ) await publishNotifications( context, - [notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context)], + notifyUsersOfComment('Comment', comment.id, 'commented_on_post', context), 'emailNotificationsCommentOnObservedPost', sentEmails, ) @@ -208,17 +175,20 @@ const notifyFollowingUsers = async (postId, groupId, context) => { const cypher = ` MATCH (post:Post { id: $postId })<-[:WROTE]-(author:User { id: $userId })<-[:FOLLOWS]-(user:User) OPTIONAL MATCH (post)-[:IN]->(group:Group { id: $groupId }) - WITH post, author, user, group WHERE group IS NULL OR group.groupType = 'public' + OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) + WITH post, author, user, emailAddress, group + WHERE group IS NULL OR group.groupType = 'public' MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user) SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) - WITH notification, author, user, + WITH notification, author, user, emailAddress.email as email, post {.*, author: properties(author) } AS finalResource RETURN notification { .*, from: finalResource, to: properties(user), + email: email, relatedUser: properties(author) } ` @@ -233,8 +203,7 @@ const notifyFollowingUsers = async (postId, groupId, context) => { return notificationTransactionResponse.records.map((record) => record.get('notification')) }) try { - const notifications = await writeTxResultPromise - return notifications + return await writeTxResultPromise } catch (error) { throw new Error(error) } finally { @@ -247,23 +216,25 @@ const notifyGroupMembersOfNewPost = async (postId, groupId, context) => { const reason = 'post_in_group' const cypher = ` MATCH (post:Post { id: $postId })<-[:WROTE]-(author:User { id: $userId }) + OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) MATCH (post)-[:IN]->(group:Group { id: $groupId })<-[membership:MEMBER_OF]-(user:User) WHERE NOT membership.role = 'pending' AND NOT (user)-[:MUTED]->(group) AND NOT (user)-[:MUTED]->(author) AND NOT (user)-[:BLOCKED]-(author) AND NOT user.id = $userId - WITH post, author, user + WITH post, author, user, emailAddress MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user) SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) - WITH notification, author, user, + WITH notification, author, user, emailAddress.email as email, post {.*, author: properties(author) } AS finalResource RETURN notification { .*, from: finalResource, to: properties(user), + email: email, relatedUser: properties(author) } ` @@ -278,8 +249,7 @@ const notifyGroupMembersOfNewPost = async (postId, groupId, context) => { return notificationTransactionResponse.records.map((record) => record.get('notification')) }) try { - const notifications = await writeTxResultPromise - return notifications + return await writeTxResultPromise } catch (error) { throw new Error(error) } finally { @@ -295,12 +265,13 @@ const notifyOwnersOfGroup = async (groupId, userId, reason, context) => { WITH owner, group, user, membership MERGE (group)-[notification:NOTIFIED {reason: $reason}]->(owner) WITH group, owner, notification, user, membership + OPTIONAL MATCH (owner)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) SET notification.relatedUserId = $userId - WITH owner, group { __typename: 'Group', .*, myRole: membership.roleInGroup } AS finalGroup, user, notification - RETURN notification {.*, from: finalGroup, to: properties(owner), relatedUser: properties(user) } + WITH owner, emailAddress.email as email, group { __typename: 'Group', .*, myRole: membership.roleInGroup } AS finalGroup, user, notification + RETURN notification {.*, from: finalGroup, to: properties(owner), email: email, relatedUser: properties(user) } ` const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { @@ -312,8 +283,7 @@ const notifyOwnersOfGroup = async (groupId, userId, reason, context) => { return notificationTransactionResponse.records.map((record) => record.get('notification')) }) try { - const notifications = await writeTxResultPromise - return notifications + return await writeTxResultPromise } catch (error) { throw new Error(error) } finally { @@ -327,17 +297,18 @@ const notifyMemberOfGroup = async (groupId, userId, reason, context) => { MATCH (owner:User { id: $ownerId }) MATCH (user:User { id: $userId }) MATCH (group:Group { id: $groupId }) + OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) OPTIONAL MATCH (user)-[membership:MEMBER_OF]->(group) - WITH user, group, owner, membership + WITH user, group, owner, membership, emailAddress MERGE (group)-[notification:NOTIFIED {reason: $reason}]->(user) - WITH group, user, notification, owner, membership + WITH group, user, notification, owner, membership, emailAddress SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) SET notification.relatedUserId = $ownerId WITH group { __typename: 'Group', .*, myRole: membership.roleInGroup } AS finalGroup, - notification, user, owner - RETURN notification {.*, from: finalGroup, to: properties(user), relatedUser: properties(owner) } + notification, user, emailAddress.email as email, owner + RETURN notification {.*, from: finalGroup, to: properties(user), email: email, relatedUser: properties(owner) } ` const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { @@ -350,8 +321,7 @@ const notifyMemberOfGroup = async (groupId, userId, reason, context) => { return notificationTransactionResponse.records.map((record) => record.get('notification')) }) try { - const notifications = await writeTxResultPromise - return notifications + return await writeTxResultPromise } catch (error) { throw new Error(error) } finally { @@ -371,11 +341,13 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => { WHERE user.id in $idsOfUsers AND NOT (user)-[:BLOCKED]-(author) AND NOT (user)-[:MUTED]->(author) + OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) OPTIONAL MATCH (post)-[:IN]->(group:Group) OPTIONAL MATCH (group)<-[membership:MEMBER_OF]-(user) - WITH post, author, user, group WHERE group IS NULL OR group.groupType = 'public' OR membership.role IN ['usual', 'admin', 'owner'] + WITH post, author, user, group, emailAddress + WHERE group IS NULL OR group.groupType = 'public' OR membership.role IN ['usual', 'admin', 'owner'] MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user) - WITH post AS resource, notification, user + WITH post AS resource, notification, user, emailAddress ` break } @@ -388,25 +360,27 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => { AND NOT (user)-[:BLOCKED]-(postAuthor) AND NOT (user)-[:MUTED]->(commenter) AND NOT (user)-[:MUTED]->(postAuthor) + OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) OPTIONAL MATCH (post)-[:IN]->(group:Group) OPTIONAL MATCH (group)<-[membership:MEMBER_OF]-(user) - WITH comment, user, group WHERE group IS NULL OR group.groupType = 'public' OR membership.role IN ['usual', 'admin', 'owner'] + WITH comment, user, group, emailAddress + WHERE group IS NULL OR group.groupType = 'public' OR membership.role IN ['usual', 'admin', 'owner'] MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user) - WITH comment AS resource, notification, user + WITH comment AS resource, notification, user, emailAddress ` break } } mentionedCypher += ` - WITH notification, user, resource, + WITH notification, user, resource, emailAddress, [(resource)<-[:WROTE]-(author:User) | author {.*}] AS authors, [(resource)-[:COMMENTS]->(post:Post)<-[:WROTE]-(author:User) | post{.*, author: properties(author)} ] AS posts - WITH resource, user, notification, authors, posts, + WITH resource, user, emailAddress.email as email, notification, authors, posts, resource {.*, __typename: [l IN labels(resource) WHERE l IN ['Post', 'Comment', 'Group']][0], author: authors[0], post: posts[0]} AS finalResource SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) - RETURN notification {.*, from: finalResource, to: properties(user), relatedUser: properties(user) } + RETURN notification {.*, from: finalResource, to: properties(user), email: email, relatedUser: properties(user) } ` const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { @@ -418,8 +392,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => { return notificationTransactionResponse.records.map((record) => record.get('notification')) }) try { - const notifications = await writeTxResultPromise - return notifications + return await writeTxResultPromise } catch (error) { throw new Error(error) } finally { @@ -437,18 +410,20 @@ const notifyUsersOfComment = async (label, commentId, reason, context) => { WHERE NOT (observingUser)-[:BLOCKED]-(commenter) AND NOT (observingUser)-[:MUTED]->(commenter) AND NOT observingUser.id = $userId - WITH observingUser, post, comment, commenter + OPTIONAL MATCH (observingUser)-[:PRIMARY_EMAIL]->(emailAddress:EmailAddress) + WITH observingUser, emailAddress, post, comment, commenter MATCH (postAuthor:User)-[:WROTE]->(post) MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(observingUser) SET notification.read = FALSE SET notification.createdAt = COALESCE(notification.createdAt, toString(datetime())) SET notification.updatedAt = toString(datetime()) - WITH notification, observingUser, post, commenter, postAuthor, + WITH notification, observingUser, emailAddress.email as email, post, commenter, postAuthor, comment {.*, __typename: labels(comment)[0], author: properties(commenter), post: post {.*, author: properties(postAuthor) } } AS finalResource RETURN notification { .*, from: finalResource, to: properties(observingUser), + email: email, relatedUser: properties(commenter) } `, @@ -461,8 +436,7 @@ const notifyUsersOfComment = async (label, commentId, reason, context) => { return notificationTransactionResponse.records.map((record) => record.get('notification')) }) try { - const notifications = await writeTxResultPromise - return notifications + return await writeTxResultPromise } finally { session.close() } From d4cc843662eb0b0ac356f01793073142487764d5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Apr 2025 21:50:13 +0200 Subject: [PATCH 153/266] lint n/no-sync (#8405) --- backend/.eslintrc.cjs | 4 ++-- backend/src/db/admin.ts | 1 + backend/src/db/factories.ts | 1 + .../20200312140328-bulk_upload_to_s3.ts | 1 + .../20200326160326-remove_dangling_image_urls.ts | 1 + backend/src/helpers/encryptPassword.ts | 1 + .../src/middleware/helpers/email/templates/de/index.ts | 1 + .../src/middleware/helpers/email/templates/en/index.ts | 1 + backend/src/middleware/helpers/email/templates/index.ts | 1 + backend/src/schema/resolvers/embeds.spec.ts | 3 +++ backend/src/schema/resolvers/embeds/findProvider.ts | 1 + backend/src/schema/resolvers/images/images.ts | 1 + backend/src/schema/resolvers/passwordReset.ts | 2 +- backend/src/schema/resolvers/user_management.ts | 8 ++++---- 14 files changed, 20 insertions(+), 7 deletions(-) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 51adb6831..1fe6b8779 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -115,7 +115,7 @@ module.exports = { 'n/no-callback-literal': 'error', // 'n/no-deprecated-api': 'error', // part of n/recommended // 'n/no-exports-assign': 'error', // part of n/recommended - 'n/no-extraneous-import': 'off', // TODO // part of n/recommended + 'n/no-extraneous-import': 'off', // duplicate of import/no-extraneous-dependencies // part of n/recommended // 'n/no-extraneous-require': 'error', // part of n/recommended 'n/no-hide-core-modules': 'error', 'n/no-missing-import': 'off', // not compatible with typescript // part of n/recommended @@ -127,7 +127,7 @@ module.exports = { // 'n/no-process-exit': 'error', // part of n/recommended 'n/no-restricted-import': 'error', 'n/no-restricted-require': 'error', - // 'n/no-sync': 'error', + 'n/no-sync': 'error', // 'n/no-unpublished-bin': 'error', // part of n/recommended 'n/no-unpublished-import': [ 'error', diff --git a/backend/src/db/admin.ts b/backend/src/db/admin.ts index ae6bca49e..b60eeb38a 100644 --- a/backend/src/db/admin.ts +++ b/backend/src/db/admin.ts @@ -11,6 +11,7 @@ import { getDriver } from './neo4j' const defaultAdmin = { email: 'admin@example.org', + // eslint-disable-next-line n/no-sync password: hashSync('1234', 10), name: 'admin', id: uuid(), diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index a0230a467..83f4cab75 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -95,6 +95,7 @@ Factory.define('basicUser') return slug || slugify(name, { lower: true }) }) .attr('encryptedPassword', ['password'], (password) => { + // eslint-disable-next-line n/no-sync return hashSync(password, 10) }) 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 index 9d97aab7e..bdbd459b3 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -55,6 +55,7 @@ export async function up(next) { 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 = { diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index f5fbd24de..6f2d26c37 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -33,6 +33,7 @@ export async function up(next) { const urls = records.map((record) => record.get('url')) const danglingUrls = urls.filter((url) => { const fileLocation = `public${url}` + // eslint-disable-next-line n/no-sync return !existsSync(fileLocation) }) await transaction.run( diff --git a/backend/src/helpers/encryptPassword.ts b/backend/src/helpers/encryptPassword.ts index d8d08c23c..1d12556ea 100644 --- a/backend/src/helpers/encryptPassword.ts +++ b/backend/src/helpers/encryptPassword.ts @@ -4,6 +4,7 @@ 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/middleware/helpers/email/templates/de/index.ts b/backend/src/middleware/helpers/email/templates/de/index.ts index 408bbb34d..4aa323b9f 100644 --- a/backend/src/middleware/helpers/email/templates/de/index.ts +++ b/backend/src/middleware/helpers/email/templates/de/index.ts @@ -3,6 +3,7 @@ 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/index.ts b/backend/src/middleware/helpers/email/templates/en/index.ts index 408bbb34d..4aa323b9f 100644 --- a/backend/src/middleware/helpers/email/templates/en/index.ts +++ b/backend/src/middleware/helpers/email/templates/en/index.ts @@ -3,6 +3,7 @@ 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/index.ts b/backend/src/middleware/helpers/email/templates/index.ts index f481516db..9a64192ce 100644 --- a/backend/src/middleware/helpers/email/templates/index.ts +++ b/backend/src/middleware/helpers/email/templates/index.ts @@ -3,6 +3,7 @@ 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') diff --git a/backend/src/schema/resolvers/embeds.spec.ts b/backend/src/schema/resolvers/embeds.spec.ts index a3b33f81f..f6de4d13e 100644 --- a/backend/src/schema/resolvers/embeds.spec.ts +++ b/backend/src/schema/resolvers/embeds.spec.ts @@ -20,14 +20,17 @@ afterEach(() => { let variables = {} +// eslint-disable-next-line n/no-sync const HumanConnectionOrg = fs.readFileSync( path.join(__dirname, '../../../snapshots/embeds/HumanConnectionOrg.html'), 'utf8', ) +// eslint-disable-next-line n/no-sync const pr3934 = fs.readFileSync( path.join(__dirname, '../../../snapshots/embeds/pr3934.html'), 'utf8', ) +// eslint-disable-next-line n/no-sync const babyLovesCat = fs.readFileSync( path.join(__dirname, '../../../snapshots/embeds/babyLovesCat.html'), 'utf8', diff --git a/backend/src/schema/resolvers/embeds/findProvider.ts b/backend/src/schema/resolvers/embeds/findProvider.ts index 9be9732bf..6f5e0df90 100644 --- a/backend/src/schema/resolvers/embeds/findProvider.ts +++ b/backend/src/schema/resolvers/embeds/findProvider.ts @@ -8,6 +8,7 @@ import path from 'node:path' import { minimatch } from 'minimatch' +// eslint-disable-next-line n/no-sync let oEmbedProvidersFile = fs.readFileSync( path.join(__dirname, '../../../../public/providers.json'), 'utf8', diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index d8ce03758..11532347d 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -152,6 +152,7 @@ const s3Upload = async ({ createReadStream, uniqueFilename, mimetype }) => { const localFileDelete = async (url) => { const location = `public${url}` + // eslint-disable-next-line n/no-sync if (existsSync(location)) unlinkSync(location) } diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index ab53d65fa..6e74ac710 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -21,7 +21,7 @@ export default { resetPassword: async (_parent, { email, nonce, newPassword }, { driver }) => { const stillValid = new Date() stillValid.setDate(stillValid.getDate() - 1) - const encryptedNewPassword = await bcrypt.hashSync(newPassword, 10) + const encryptedNewPassword = await bcrypt.hash(newPassword, 10) const session = driver.session() try { const passwordResetTxPromise = session.writeTransaction(async (transaction) => { diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/schema/resolvers/user_management.ts index 13437e815..072755850 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/schema/resolvers/user_management.ts @@ -44,7 +44,7 @@ export default { const [currentUser] = await loginReadTxResultPromise if ( currentUser && - (await bcrypt.compareSync(password, currentUser.encryptedPassword)) && + (await bcrypt.compare(password, currentUser.encryptedPassword)) && !currentUser.disabled ) { delete currentUser.encryptedPassword @@ -62,15 +62,15 @@ export default { const currentUser = await neode.find('User', user.id) const encryptedPassword = currentUser.get('encryptedPassword') - if (!(await bcrypt.compareSync(oldPassword, encryptedPassword))) { + if (!(await bcrypt.compare(oldPassword, encryptedPassword))) { throw new AuthenticationError('Old password is not correct') } - if (await bcrypt.compareSync(newPassword, encryptedPassword)) { + if (await bcrypt.compare(newPassword, encryptedPassword)) { throw new AuthenticationError('Old password and new password should be different') } - const newEncryptedPassword = await bcrypt.hashSync(newPassword, 10) + const newEncryptedPassword = await bcrypt.hash(newPassword, 10) await currentUser.update({ encryptedPassword: newEncryptedPassword, updatedAt: new Date().toISOString(), From 2369d13ca2c702a2786d028466401cba6d285a83 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 24 Apr 2025 22:39:32 +0200 Subject: [PATCH 154/266] lint everything, disable some setup steps for jest (#8423) --- backend/.eslintignore | 3 +++ backend/package.json | 2 +- backend/test/setup.ts | 10 ++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 backend/.eslintignore diff --git a/backend/.eslintignore b/backend/.eslintignore new file mode 100644 index 000000000..e19e2338d --- /dev/null +++ b/backend/.eslintignore @@ -0,0 +1,3 @@ +node_modules/ +build/ +coverage/ \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 60ecba12e..7137d5c25 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "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 --ext .js,.ts ./src", + "lint": "eslint --max-warnings=0 --ext .js,.ts .", "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", diff --git a/backend/test/setup.ts b/backend/test/setup.ts index d2f24bd40..594b9763d 100644 --- a/backend/test/setup.ts +++ b/backend/test/setup.ts @@ -1,8 +1,10 @@ // Polyfill missing encoders in jsdom // https://stackoverflow.com/questions/68468203/why-am-i-getting-textencoder-is-not-defined-in-jest -import { TextEncoder, TextDecoder } from 'util' -global.TextEncoder = TextEncoder -global.TextDecoder = TextDecoder as any +// import { TextEncoder, TextDecoder } from 'util' + +// global.TextEncoder = TextEncoder +// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment +// global.TextDecoder = TextDecoder as any // Metascraper takes longer nowadays, double time -jest.setTimeout(10000) \ No newline at end of file +// jest.setTimeout(10000) From 507179738a5a86cefd82114086333c1eb4201984 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Apr 2025 10:04:58 +0200 Subject: [PATCH 155/266] refactor(backend): types for neo4j & neode (#8409) * type for neo4j and neode * fix build * remove flakyness * wait for neode to install schema * remove flakyness * explain why we wait for a non-promise --- backend/src/db/badges.ts | 2 +- backend/src/db/factories.ts | 2 +- backend/src/db/migrate/store.ts | 13 ++++--- backend/src/db/migrate/template.ts | 4 +-- ...123150105-merge_duplicate_user_accounts.ts | 3 +- ...23150110-merge_duplicate_location_nodes.ts | 3 +- ..._between_existing_blocked_relationships.ts | 8 ++--- .../20200207080200-fulltext_index_for_tags.ts | 4 +-- ...213230248-add_unique_index_to_image_url.ts | 4 +-- .../20200312140328-bulk_upload_to_s3.ts | 4 +-- ...15-refactor_all_images_to_separate_type.ts | 4 +-- ...emove_deleted_users_obsolete_attributes.ts | 4 +-- ...emove_deleted_posts_obsolete_attributes.ts | 4 +-- ...200326160326-remove_dangling_image_urls.ts | 2 +- ...1614023644903-add-clickedCount-to-posts.ts | 4 +-- ...77130817-add-viewedTeaserCount-to-posts.ts | 4 +-- .../20210506150512-add-donations-node.ts | 4 +-- ...otificationEmails-property-to-all-users.ts | 4 +-- ...text_indices_and_unique_keys_for_groups.ts | 4 +-- .../20230320130345-fulltext-search-indexes.ts | 4 +-- .../20230329150329-article-label-for-posts.ts | 4 +-- .../20230608130637-add-postType-property.ts | 4 +-- .../20231017141022-fix-event-dates.ts | 9 ++--- ...20250331130323-author-observes-own-post.ts | 4 +-- .../20250331140313-commenter-observes-post.ts | 4 +-- ...50405030454-email-notification-settings.ts | 4 +-- .../20250414220436-delete-old-badges.ts | 4 +-- backend/src/db/neo4j.ts | 6 ++-- backend/src/db/seed.ts | 2 +- backend/src/jwt/decode.spec.ts | 17 +++++---- .../hashtags/hashtagsMiddleware.spec.ts | 19 ++++------ .../middleware/languages/languages.spec.ts | 2 +- .../notificationsMiddleware.emails.spec.ts | 2 +- ...ficationsMiddleware.followed-users.spec.ts | 2 +- ...tionsMiddleware.mentions-in-groups.spec.ts | 2 +- ...icationsMiddleware.observing-posts.spec.ts | 2 +- ...ificationsMiddleware.online-status.spec.ts | 2 +- ...icationsMiddleware.posts-in-groups.spec.ts | 2 +- .../notificationsMiddleware.spec.ts | 2 +- .../src/middleware/orderByMiddleware.spec.ts | 2 +- .../middleware/permissionsMiddleware.spec.ts | 2 +- .../src/middleware/permissionsMiddleware.ts | 8 +++-- .../src/middleware/slugifyMiddleware.spec.ts | 2 +- .../softDelete/softDeleteMiddleware.spec.ts | 2 +- .../src/middleware/userInteractions.spec.ts | 2 +- .../validation/validationMiddleware.spec.ts | 2 +- backend/src/models/User.spec.ts | 3 +- backend/src/schema/resolvers/badges.spec.ts | 2 +- backend/src/schema/resolvers/comments.spec.ts | 2 +- .../src/schema/resolvers/donations.spec.ts | 2 +- backend/src/schema/resolvers/emails.spec.ts | 31 ++++++++++------ .../src/schema/resolvers/filter-posts.spec.ts | 2 +- backend/src/schema/resolvers/follow.spec.ts | 2 +- backend/src/schema/resolvers/follow.ts | 1 + backend/src/schema/resolvers/groups.spec.ts | 2 +- .../schema/resolvers/images/images.spec.ts | 27 ++++++++------ backend/src/schema/resolvers/images/images.ts | 2 +- .../src/schema/resolvers/inviteCodes.spec.ts | 2 +- .../src/schema/resolvers/locations.spec.ts | 2 +- backend/src/schema/resolvers/messages.spec.ts | 2 +- .../src/schema/resolvers/moderation.spec.ts | 4 +-- .../schema/resolvers/notifications.spec.ts | 2 +- .../src/schema/resolvers/observePosts.spec.ts | 2 +- .../schema/resolvers/passwordReset.spec.ts | 3 +- backend/src/schema/resolvers/posts.spec.ts | 24 +++++++++---- .../schema/resolvers/postsInGroups.spec.ts | 2 +- .../src/schema/resolvers/registration.spec.ts | 36 +++++++++++++------ backend/src/schema/resolvers/reports.spec.ts | 2 +- backend/src/schema/resolvers/rooms.spec.ts | 2 +- backend/src/schema/resolvers/searches.spec.ts | 2 +- backend/src/schema/resolvers/shout.spec.ts | 2 +- .../src/schema/resolvers/socialMedia.spec.ts | 2 +- backend/src/schema/resolvers/socialMedia.ts | 1 + .../src/schema/resolvers/statistics.spec.ts | 2 +- backend/src/schema/resolvers/userData.spec.ts | 2 +- .../schema/resolvers/user_management.spec.ts | 30 +++++++++------- .../src/schema/resolvers/user_management.ts | 2 +- backend/src/schema/resolvers/users.spec.ts | 9 ++--- .../schema/resolvers/users/location.spec.ts | 3 +- .../schema/resolvers/users/mutedUsers.spec.ts | 2 +- .../resolvers/viewedTeaserCount.spec.ts | 2 +- 81 files changed, 236 insertions(+), 180 deletions(-) diff --git a/backend/src/db/badges.ts b/backend/src/db/badges.ts index b4e879357..3fbbd4d7f 100644 --- a/backend/src/db/badges.ts +++ b/backend/src/db/badges.ts @@ -12,6 +12,6 @@ import { trophies, verification } from './seed/badges' await trophies() await verification() } finally { - await neode.close() + neode.close() } })() diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index 83f4cab75..e951c3839 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -39,7 +39,7 @@ export const cleanDatabase = async ({ withMigrations } = { withMigrations: false return transaction.run(clean) }) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index 947364e4d..bc70fe361 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -11,7 +11,7 @@ import { getDriver, getNeode } from '@db/neo4j' class Store { async init(errFn) { const neode = getNeode() - const session = neode.driver.session() + const session = neode.session() const txFreshIndicesConstrains = session.writeTransaction(async (txc) => { // drop all indices and constraints await txc.run('CALL apoc.schema.assert({},{},true)') @@ -38,6 +38,9 @@ class Store { // we need to have all constraints and indexes defined here. They can not be properly migrated await txFreshIndicesConstrains + // You have to wait for the schema to install, else the constraints will not be present. + // This is a type error of the library + // eslint-disable-next-line @typescript-eslint/await-thenable await getNeode().schema.install() // eslint-disable-next-line no-console console.log('Successfully created database indices and constraints!') @@ -46,8 +49,8 @@ class Store { console.log(error) // eslint-disable-line no-console errFn(error) } finally { - session.close() - neode.driver.close() + await session.close() + neode.close() } } @@ -76,7 +79,7 @@ class Store { console.log(error) // eslint-disable-line no-console next(error) } finally { - session.close() + await session.close() } } @@ -112,7 +115,7 @@ class Store { console.log(error) // eslint-disable-line no-console next(error) } finally { - session.close() + await session.close() } } } diff --git a/backend/src/db/migrate/template.ts b/backend/src/db/migrate/template.ts index ce538f260..9306ec27c 100644 --- a/backend/src/db/migrate/template.ts +++ b/backend/src/db/migrate/template.ts @@ -27,7 +27,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -48,6 +48,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } 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 d13eeecf9..7d4195131 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 @@ -25,7 +25,8 @@ export const description = ` ` export function up(next) { const driver = getDriver() - const rxSession = driver.rxSession() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const rxSession = driver.rxSession() as any rxSession .beginTransaction() .pipe( diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index 249464257..1b180616b 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -19,7 +19,8 @@ export const description = ` ` export function up(next) { const driver = getDriver() - const rxSession = driver.rxSession() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const rxSession = driver.rxSession() as any rxSession .beginTransaction() .pipe( diff --git a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts index cfc00fcfe..a8b6f8179 100644 --- a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts +++ b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts @@ -37,20 +37,20 @@ export async function up(_next) { // eslint-disable-next-line no-console console.log('rolled back') } finally { - session.close() + await session.close() } } -export function down(next) { +export async function down(next) { const driver = getDriver() const session = driver.session() try { // Rollback your migration here. - next() + // next() // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { next(err) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts index f7bcb0810..3fb44c77a 100644 --- a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts +++ b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts @@ -39,7 +39,7 @@ export async function up(next) { throw new Error(error) } } finally { - session.close() + await session.close() } } @@ -66,6 +66,6 @@ export async function down(next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts index a22a38127..0af7626a1 100644 --- a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts +++ b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts @@ -40,7 +40,7 @@ export async function up(next) { throw new Error(error) } } finally { - session.close() + await session.close() } } @@ -64,6 +64,6 @@ export async function down(next) { // eslint-disable-next-line no-console console.log('rolled back') } finally { - session.close() + await session.close() } } 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 index bdbd459b3..606986d68 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -92,7 +92,7 @@ export async function up(next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -114,6 +114,6 @@ export async function down(next) { // eslint-disable-next-line no-console console.log('rolled back') } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts index 61be45099..3028b9837 100644 --- a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts +++ b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts @@ -66,7 +66,7 @@ export async function up() { console.log('Created image nodes from all user avatars and post images.') printSummaries(stats) } finally { - session.close() + await session.close() } } @@ -104,6 +104,6 @@ export async function down() { console.log('UNDO: Split images from users and posts.') printSummaries(stats) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts index 36b29f477..d0e0ab5e6 100644 --- a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts @@ -28,7 +28,7 @@ export async function up(next) { `) try { // Implement your migration here. - const users = await updateDeletedUserAttributes.records.map((record) => record.get('user')) + const users = updateDeletedUserAttributes.records.map((record) => record.get('user')) // eslint-disable-next-line no-console console.log(users) await transaction.commit() @@ -41,7 +41,7 @@ export async function up(next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts index 1a3e97dba..0bda73770 100644 --- a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts @@ -30,7 +30,7 @@ export async function up(next) { `) try { // Implement your migration here. - const posts = await updateDeletedPostsAttributes.records.map((record) => record.get('post')) + const posts = updateDeletedPostsAttributes.records.map((record) => record.get('post')) // eslint-disable-next-line no-console console.log(posts) await transaction.commit() @@ -43,7 +43,7 @@ export async function up(next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index 6f2d26c37..740b1a85e 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -62,7 +62,7 @@ export async function up(next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts index 9bb7ab996..e1b884de0 100644 --- a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts +++ b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts @@ -27,7 +27,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -50,6 +50,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts index 9ebfee85c..5ed831b61 100644 --- a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts +++ b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts @@ -27,7 +27,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -50,6 +50,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.ts b/backend/src/db/migrations/20210506150512-add-donations-node.ts index 78919d46e..2cba177b7 100644 --- a/backend/src/db/migrations/20210506150512-add-donations-node.ts +++ b/backend/src/db/migrations/20210506150512-add-donations-node.ts @@ -43,7 +43,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -68,6 +68,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts index e4ed26095..c7c774c70 100644 --- a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts +++ b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts @@ -33,7 +33,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -60,6 +60,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts index 9fa0ffcd2..106aec0dc 100644 --- a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts +++ b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts @@ -42,7 +42,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -74,6 +74,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts index bf3541e7c..b12db9964 100644 --- a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts +++ b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts @@ -50,7 +50,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -75,6 +75,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts index 1c1259ca0..06f05dbd1 100644 --- a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts +++ b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts @@ -30,7 +30,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -54,6 +54,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20230608130637-add-postType-property.ts b/backend/src/db/migrations/20230608130637-add-postType-property.ts index e4f1033b5..f58e4de97 100644 --- a/backend/src/db/migrations/20230608130637-add-postType-property.ts +++ b/backend/src/db/migrations/20230608130637-add-postType-property.ts @@ -30,7 +30,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -54,6 +54,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20231017141022-fix-event-dates.ts b/backend/src/db/migrations/20231017141022-fix-event-dates.ts index b3d7d14bd..1b92537ba 100644 --- a/backend/src/db/migrations/20231017141022-fix-event-dates.ts +++ b/backend/src/db/migrations/20231017141022-fix-event-dates.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-base-to-string */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ @@ -26,11 +27,11 @@ export async function up(_next) { `) for (const event of events.records) { let [id, eventStart, eventEnd] = event - let date = new Date(eventStart) + let date = new Date(eventStart as string) date.setHours(date.getHours() - 1) eventStart = date.toISOString() if (eventEnd) { - date = new Date(eventEnd) + date = new Date(eventEnd as string) date.setHours(date.getHours() - 1) eventEnd = date.toISOString() } @@ -50,7 +51,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -69,6 +70,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts index e088e12c1..4411107e7 100644 --- a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts +++ b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts @@ -37,7 +37,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -62,6 +62,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20250331140313-commenter-observes-post.ts b/backend/src/db/migrations/20250331140313-commenter-observes-post.ts index f3f358f20..664c64047 100644 --- a/backend/src/db/migrations/20250331140313-commenter-observes-post.ts +++ b/backend/src/db/migrations/20250331140313-commenter-observes-post.ts @@ -37,7 +37,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -63,6 +63,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20250405030454-email-notification-settings.ts b/backend/src/db/migrations/20250405030454-email-notification-settings.ts index f17c88fa9..afe3a5ed3 100644 --- a/backend/src/db/migrations/20250405030454-email-notification-settings.ts +++ b/backend/src/db/migrations/20250405030454-email-notification-settings.ts @@ -38,7 +38,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -69,6 +69,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/migrations/20250414220436-delete-old-badges.ts b/backend/src/db/migrations/20250414220436-delete-old-badges.ts index 9c1d2b8e1..6a932543e 100644 --- a/backend/src/db/migrations/20250414220436-delete-old-badges.ts +++ b/backend/src/db/migrations/20250414220436-delete-old-badges.ts @@ -30,7 +30,7 @@ export async function up(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } @@ -52,6 +52,6 @@ export async function down(_next) { console.log('rolled back') throw new Error(error) } finally { - session.close() + await session.close() } } diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index b7c0eec56..62594ee2d 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -4,13 +4,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable import/no-named-as-default-member */ -import neo4j from 'neo4j-driver' +import neo4j, { Driver } from 'neo4j-driver' import Neode from 'neode' import CONFIG from '@config/index' import models from '@models/index' -let driver +let driver: Driver const defaultOptions = { uri: CONFIG.NEO4J_URI, username: CONFIG.NEO4J_USERNAME, @@ -25,7 +25,7 @@ export function getDriver(options = {}) { return driver } -let neodeInstance +let neodeInstance: Neode export function getNeode(options = {}) { if (!neodeInstance) { const { uri, username, password } = { ...defaultOptions, ...options } diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index 08594d1b4..6e09df986 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -1585,7 +1585,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] /* eslint-disable-next-line no-console */ console.log('Seeded Data...') await driver.close() - await neode.close() + neode.close() process.exit(0) // eslint-disable-next-line no-catch-all/no-catch-all } catch (err) { diff --git a/backend/src/jwt/decode.spec.ts b/backend/src/jwt/decode.spec.ts index 34dd86d68..0cd52a5d5 100644 --- a/backend/src/jwt/decode.spec.ts +++ b/backend/src/jwt/decode.spec.ts @@ -3,6 +3,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' +import User from '@models/User' import decode from './decode' import encode from './encode' @@ -16,7 +17,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 @@ -86,26 +87,28 @@ describe('decode', () => { }) it('sets `lastActiveAt`', async () => { - let user = await neode.first('User', { id: 'u3' }) + let user = await neode.first('User', { id: 'u3' }, undefined) await expect(user.toJson()).resolves.not.toHaveProperty('lastActiveAt') await decode(driver, validAuthorizationHeader) - user = await neode.first('User', { id: 'u3' }) + user = await neode.first('User', { id: 'u3' }, undefined) await expect(user.toJson()).resolves.toMatchObject({ lastActiveAt: expect.any(String), }) }) it('updates `lastActiveAt` for every authenticated request', async () => { - let user = await neode.first('User', { id: 'u3' }) + let user = await neode.first('User', { id: 'u3' }, undefined) await user.update({ - updatedAt: new Date().toISOString(), - lastActiveAt: '2019-10-03T23:33:08.598Z', + // eslint-disable-next-line @typescript-eslint/no-explicit-any + updatedAt: new Date().toISOString() as any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + lastActiveAt: '2019-10-03T23:33:08.598Z' as any, }) await expect(user.toJson()).resolves.toMatchObject({ lastActiveAt: '2019-10-03T23:33:08.598Z', }) await decode(driver, validAuthorizationHeader) - user = await neode.first('User', { id: 'u3' }) + user = await neode.first('User', { id: 'u3' }, undefined) await expect(user.toJson()).resolves.toMatchObject({ // should be a different time by now ;) lastActiveAt: expect.not.stringContaining('2019-10-03T23:33'), diff --git a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts index 4f0d57303..bc3b96594 100644 --- a/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts +++ b/backend/src/middleware/hashtags/hashtagsMiddleware.spec.ts @@ -55,22 +55,15 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { - hashtagingUser = await neode.create( - 'User', - { - id: 'you', - name: 'Al Capone', - slug: 'al-capone', - }, - { - password: '1234', - email: 'test@example.org', - }, - ) + hashtagingUser = await neode.create('User', { + id: 'you', + name: 'Al Capone', + slug: 'al-capone', + }) await neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', diff --git a/backend/src/middleware/languages/languages.spec.ts b/backend/src/middleware/languages/languages.spec.ts index 11ebf3a41..50e3a028f 100644 --- a/backend/src/middleware/languages/languages.spec.ts +++ b/backend/src/middleware/languages/languages.spec.ts @@ -32,7 +32,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) const createPostMutation = gql` diff --git a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 55edef940..79d95e43e 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -120,7 +120,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('emails sent for notifications', () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 18da6bff8..7d311ce95 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -94,7 +94,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('following users notifications', () => { 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 1c7ca4c71..96c7e9d18 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -116,7 +116,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('mentions in groups', () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index 9f193eaeb..a0864fe07 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -102,7 +102,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('notifications for users that observe a post', () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts index 47842029c..3a47d376d 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -62,7 +62,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) afterEach(async () => { 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 6bde0aee2..25aef2e2b 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -118,7 +118,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('notify group members of new posts in group', () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index 31e458e2a..ab0a6a5b2 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -88,7 +88,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/middleware/orderByMiddleware.spec.ts b/backend/src/middleware/orderByMiddleware.spec.ts index 1ae15b26a..e46ee1c1f 100644 --- a/backend/src/middleware/orderByMiddleware.spec.ts +++ b/backend/src/middleware/orderByMiddleware.spec.ts @@ -28,7 +28,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/middleware/permissionsMiddleware.spec.ts b/backend/src/middleware/permissionsMiddleware.spec.ts index 834e9888a..ca45005fe 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.ts +++ b/backend/src/middleware/permissionsMiddleware.spec.ts @@ -33,7 +33,7 @@ describe('authorization', () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 20063de11..3897a61e9 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -8,6 +8,7 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield' import CONFIG from '@config/index' import { getNeode } from '@db/neo4j' +import SocialMedia from '@models/SocialMedia' import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes' const debug = !!CONFIG.DEBUG @@ -48,15 +49,16 @@ const isMySocialMedia = rule({ if (!user) { return false } - let socialMedia = await neode.find('SocialMedia', args.id) + const socialMedia = await neode.find('SocialMedia', args.id) // Did we find a social media node? if (!socialMedia) { return false } - socialMedia = await socialMedia.toJson() // whats this for? + const socialMediaJson = await socialMedia.toJson() // whats this for? // Is it my social media entry? - return socialMedia.ownedBy.node.id === user.id + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (socialMediaJson.ownedBy as any).node.id === user.id }) const isAllowedToChangeGroupSettings = rule({ diff --git a/backend/src/middleware/slugifyMiddleware.spec.ts b/backend/src/middleware/slugifyMiddleware.spec.ts index 35247471c..75a52e4cf 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.ts +++ b/backend/src/middleware/slugifyMiddleware.spec.ts @@ -42,7 +42,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts index 0264dedb9..ed9dcbf37 100644 --- a/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts +++ b/backend/src/middleware/softDelete/softDeleteMiddleware.spec.ts @@ -202,7 +202,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('softDeleteMiddleware', () => { diff --git a/backend/src/middleware/userInteractions.spec.ts b/backend/src/middleware/userInteractions.spec.ts index fc096c2b7..61d92ff83 100644 --- a/backend/src/middleware/userInteractions.spec.ts +++ b/backend/src/middleware/userInteractions.spec.ts @@ -46,7 +46,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('middleware/userInteractions', () => { diff --git a/backend/src/middleware/validation/validationMiddleware.spec.ts b/backend/src/middleware/validation/validationMiddleware.spec.ts index 3d3cd9bda..ea4f6ec54 100644 --- a/backend/src/middleware/validation/validationMiddleware.spec.ts +++ b/backend/src/middleware/validation/validationMiddleware.spec.ts @@ -79,7 +79,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/models/User.spec.ts b/backend/src/models/User.spec.ts index 7d2c584b5..272ad2df3 100644 --- a/backend/src/models/User.spec.ts +++ b/backend/src/models/User.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ @@ -15,7 +16,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 diff --git a/backend/src/schema/resolvers/badges.spec.ts b/backend/src/schema/resolvers/badges.spec.ts index 2588c9b5c..ae2fe0b0d 100644 --- a/backend/src/schema/resolvers/badges.spec.ts +++ b/backend/src/schema/resolvers/badges.spec.ts @@ -33,7 +33,7 @@ describe('Badges', () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/schema/resolvers/comments.spec.ts b/backend/src/schema/resolvers/comments.spec.ts index 432723bae..a7177d754 100644 --- a/backend/src/schema/resolvers/comments.spec.ts +++ b/backend/src/schema/resolvers/comments.spec.ts @@ -30,7 +30,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/schema/resolvers/donations.spec.ts b/backend/src/schema/resolvers/donations.spec.ts index 8e58153c1..8fc23d4e9 100644 --- a/backend/src/schema/resolvers/donations.spec.ts +++ b/backend/src/schema/resolvers/donations.spec.ts @@ -42,7 +42,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('donations', () => { diff --git a/backend/src/schema/resolvers/emails.spec.ts b/backend/src/schema/resolvers/emails.spec.ts index d16adbf5d..f77602463 100644 --- a/backend/src/schema/resolvers/emails.spec.ts +++ b/backend/src/schema/resolvers/emails.spec.ts @@ -36,7 +36,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { @@ -110,11 +110,14 @@ describe('AddEmailAddress', () => { it('connects `UnverifiedEmailAddress` to the authenticated user', async () => { await mutate({ mutation, variables }) - const result = await neode.cypher(` + const result = await neode.cypher( + ` MATCH(u:User)-[:PRIMARY_EMAIL]->(:EmailAddress {email: "user@example.org"}) MATCH(u:User)<-[:BELONGS_TO]-(e:UnverifiedEmailAddress {email: "new-email@example.org"}) RETURN e - `) + `, + {}, + ) const email = neode.hydrateFirst(result, 'e', neode.model('UnverifiedEmailAddress')) await expect(email.toJson()).resolves.toMatchObject({ email: 'new-email@example.org', @@ -257,10 +260,13 @@ describe('VerifyEmailAddress', () => { it('connects the new `EmailAddress` as PRIMARY', async () => { await mutate({ mutation, variables }) - const result = await neode.cypher(` + const result = await neode.cypher( + ` MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"}) RETURN e - `) + `, + {}, + ) const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) await expect(email.toJson()).resolves.toMatchObject({ email: 'to-be-verified@example.org', @@ -272,13 +278,13 @@ describe('VerifyEmailAddress', () => { MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "user@example.org"}) RETURN e ` - let result = await neode.cypher(cypherStatement) + let result = await neode.cypher(cypherStatement, {}) let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) await expect(email.toJson()).resolves.toMatchObject({ email: 'user@example.org', }) await mutate({ mutation, variables }) - result = await neode.cypher(cypherStatement) + result = await neode.cypher(cypherStatement, {}) email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) await expect(email).toBe(false) }) @@ -288,13 +294,13 @@ describe('VerifyEmailAddress', () => { MATCH(u:User {id: "567"})<-[:BELONGS_TO]-(e:EmailAddress {email: "user@example.org"}) RETURN e ` - let result = await neode.cypher(cypherStatement) + let result = await neode.cypher(cypherStatement, {}) let email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) await expect(email.toJson()).resolves.toMatchObject({ email: 'user@example.org', }) await mutate({ mutation, variables }) - result = await neode.cypher(cypherStatement) + result = await neode.cypher(cypherStatement, {}) email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) await expect(email).toBe(false) }) @@ -319,10 +325,13 @@ describe('VerifyEmailAddress', () => { it('connects the new `EmailAddress` as PRIMARY', async () => { await mutate({ mutation, variables }) - const result = await neode.cypher(` + const result = await neode.cypher( + ` MATCH(u:User {id: "567"})-[:PRIMARY_EMAIL]->(e:EmailAddress {email: "to-be-verified@example.org"}) RETURN e - `) + `, + {}, + ) const email = neode.hydrateFirst(result, 'e', neode.model('EmailAddress')) await expect(email.toJson()).resolves.toMatchObject({ email: 'to-be-verified@example.org', diff --git a/backend/src/schema/resolvers/filter-posts.spec.ts b/backend/src/schema/resolvers/filter-posts.spec.ts index 87ba2a8e5..c29b98365 100644 --- a/backend/src/schema/resolvers/filter-posts.spec.ts +++ b/backend/src/schema/resolvers/filter-posts.spec.ts @@ -38,7 +38,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('Filter Posts', () => { diff --git a/backend/src/schema/resolvers/follow.spec.ts b/backend/src/schema/resolvers/follow.spec.ts index 437b4e160..e846eb56f 100644 --- a/backend/src/schema/resolvers/follow.spec.ts +++ b/backend/src/schema/resolvers/follow.spec.ts @@ -76,7 +76,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/schema/resolvers/follow.ts b/backend/src/schema/resolvers/follow.ts index d3c1a9081..809d77760 100644 --- a/backend/src/schema/resolvers/follow.ts +++ b/backend/src/schema/resolvers/follow.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-call */ diff --git a/backend/src/schema/resolvers/groups.spec.ts b/backend/src/schema/resolvers/groups.spec.ts index 39ab87dd4..664f57397 100644 --- a/backend/src/schema/resolvers/groups.spec.ts +++ b/backend/src/schema/resolvers/groups.spec.ts @@ -241,7 +241,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('in mode', () => { diff --git a/backend/src/schema/resolvers/images/images.spec.ts b/backend/src/schema/resolvers/images/images.spec.ts index 0a2cbadbd..938571126 100644 --- a/backend/src/schema/resolvers/images/images.spec.ts +++ b/backend/src/schema/resolvers/images/images.spec.ts @@ -25,7 +25,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { @@ -83,7 +83,7 @@ describe('deleteImage', () => { return result }) } finally { - session.close() + await session.close() } await expect(neode.all('Image')).resolves.toHaveLength(0) await expect(someString).toEqual('Hello') @@ -106,7 +106,7 @@ describe('deleteImage', () => { await expect(neode.all('Image')).resolves.toHaveLength(1) // all good } finally { - session.close() + await session.close() } }) }) @@ -198,9 +198,10 @@ describe('mergeImage', () => { it('connects resource with image via given image type', async () => { await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback }) - const result = await neode.cypher(` - MATCH(p:Post {id: "p99"})-[:HERO_IMAGE]->(i:Image) RETURN i,p - `) + const result = await neode.cypher( + `MATCH(p:Post {id: "p99"})-[:HERO_IMAGE]->(i:Image) RETURN i,p`, + {}, + ) post = neode.hydrateFirst(result, 'p', neode.model('Post')) const image = neode.hydrateFirst(result, 'i', neode.model('Image')) expect(post).toBeTruthy() @@ -215,7 +216,7 @@ describe('mergeImage', () => { it('sets metadata', async () => { await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback }) - const image = await neode.first('Image', {}) + const image = await neode.first('Image', {}, undefined) await expect(image.toJson()).resolves.toMatchObject({ alt: 'A description of the new image', createdAt: expect.any(String), @@ -243,9 +244,13 @@ describe('mergeImage', () => { ) }) } finally { - session.close() + await session.close() } - const image = await neode.first('Image', { alt: 'This alt text gets overwritten' }) + const image = await neode.first( + 'Image', + { alt: 'This alt text gets overwritten' }, + undefined, + ) await expect(image.toJson()).resolves.toMatchObject({ alt: 'This alt text gets overwritten', }) @@ -268,7 +273,7 @@ describe('mergeImage', () => { await expect(neode.all('Image')).resolves.toHaveLength(0) // all good } finally { - session.close() + await session.close() } }) }) @@ -296,7 +301,7 @@ describe('mergeImage', () => { await expect(neode.all('Image')).resolves.toHaveLength(1) await mergeImage(post, 'HERO_IMAGE', imageInput, { uploadCallback, deleteCallback }) await expect(neode.all('Image')).resolves.toHaveLength(1) - const image = await neode.first('Image', {}) + const image = await neode.first('Image', {}, undefined) await expect(image.toJson()).resolves.toMatchObject({ alt: 'A description of the new image', createdAt: expect.any(String), diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/schema/resolvers/images/images.ts index 11532347d..2e76a7889 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/schema/resolvers/images/images.ts @@ -91,7 +91,7 @@ const wrapTransaction = async (wrappedCallback, args, opts) => { }) return result } finally { - session.close() + await session.close() } } diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/schema/resolvers/inviteCodes.spec.ts index aac79210f..7d335077a 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.ts +++ b/backend/src/schema/resolvers/inviteCodes.spec.ts @@ -57,7 +57,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('inviteCodes', () => { diff --git a/backend/src/schema/resolvers/locations.spec.ts b/backend/src/schema/resolvers/locations.spec.ts index d4510284c..aed85da54 100644 --- a/backend/src/schema/resolvers/locations.spec.ts +++ b/backend/src/schema/resolvers/locations.spec.ts @@ -30,7 +30,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/schema/resolvers/messages.spec.ts index 7b46e0205..8061cf460 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/schema/resolvers/messages.spec.ts @@ -45,7 +45,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('Message', () => { diff --git a/backend/src/schema/resolvers/moderation.spec.ts b/backend/src/schema/resolvers/moderation.spec.ts index 67d070893..f3224421e 100644 --- a/backend/src/schema/resolvers/moderation.spec.ts +++ b/backend/src/schema/resolvers/moderation.spec.ts @@ -75,7 +75,7 @@ describe('moderate resources', () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { @@ -194,7 +194,7 @@ describe('moderate resources', () => { ]) const cypher = 'MATCH (:Report)<-[review:REVIEWED]-(moderator:User {id: "moderator-id"}) RETURN review' - const reviews = await neode.cypher(cypher) + const reviews = await neode.cypher(cypher, {}) expect(reviews.records).toHaveLength(1) }) diff --git a/backend/src/schema/resolvers/notifications.spec.ts b/backend/src/schema/resolvers/notifications.spec.ts index 2aebe4c24..d6d22e459 100644 --- a/backend/src/schema/resolvers/notifications.spec.ts +++ b/backend/src/schema/resolvers/notifications.spec.ts @@ -38,7 +38,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/schema/resolvers/observePosts.spec.ts index 9176d424e..76ad5b058 100644 --- a/backend/src/schema/resolvers/observePosts.spec.ts +++ b/backend/src/schema/resolvers/observePosts.spec.ts @@ -61,7 +61,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('observing posts', () => { diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/schema/resolvers/passwordReset.spec.ts index 7a260d345..66823cc5d 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/schema/resolvers/passwordReset.spec.ts @@ -22,6 +22,7 @@ let variables const getAllPasswordResets = async () => { const passwordResetQuery = await neode.cypher( 'MATCH (passwordReset:PasswordReset) RETURN passwordReset', + {}, ) const resets = passwordResetQuery.records.map((record) => record.get('passwordReset')) return resets @@ -44,7 +45,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(() => { diff --git a/backend/src/schema/resolvers/posts.spec.ts b/backend/src/schema/resolvers/posts.spec.ts index 403db3950..0a05200fd 100644 --- a/backend/src/schema/resolvers/posts.spec.ts +++ b/backend/src/schema/resolvers/posts.spec.ts @@ -10,6 +10,7 @@ import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' 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 @@ -46,7 +47,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { @@ -974,9 +975,13 @@ describe('UpdatePost', () => { variables = { ...variables, image: { sensitive: true } } }) it('updates the image', async () => { - await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy() + await expect( + neode.first('Image', { sensitive: true }, undefined), + ).resolves.toBeFalsy() await mutate({ mutation: updatePostMutation, variables }) - await expect(neode.first('Image', { sensitive: true })).resolves.toBeTruthy() + await expect( + neode.first('Image', { sensitive: true }, undefined), + ).resolves.toBeTruthy() }) }) @@ -996,9 +1001,13 @@ describe('UpdatePost', () => { delete variables.image }) it('keeps the image unchanged', async () => { - await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy() + await expect( + neode.first('Image', { sensitive: true }, undefined), + ).resolves.toBeFalsy() await mutate({ mutation: updatePostMutation, variables }) - await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy() + await expect( + neode.first('Image', { sensitive: true }, undefined), + ).resolves.toBeFalsy() }) }) }) @@ -1244,11 +1253,11 @@ describe('pin posts', () => { it('removes previous `pinned` attribute', async () => { const cypher = 'MATCH (post:Post) WHERE post.pinned IS NOT NULL RETURN post' - pinnedPost = await neode.cypher(cypher) + pinnedPost = await neode.cypher(cypher, {}) expect(pinnedPost.records).toHaveLength(1) variables = { ...variables, id: 'only-pinned-post' } await mutate({ mutation: pinPostMutation, variables }) - pinnedPost = await neode.cypher(cypher) + pinnedPost = await neode.cypher(cypher, {}) expect(pinnedPost.records).toHaveLength(1) }) @@ -1257,6 +1266,7 @@ describe('pin posts', () => { await mutate({ mutation: pinPostMutation, variables }) pinnedPost = await neode.cypher( `MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`, + {}, ) expect(pinnedPost.records).toHaveLength(1) }) diff --git a/backend/src/schema/resolvers/postsInGroups.spec.ts b/backend/src/schema/resolvers/postsInGroups.spec.ts index 664a64b9f..7cb0bdc76 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.ts +++ b/backend/src/schema/resolvers/postsInGroups.spec.ts @@ -63,7 +63,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('Posts in Groups', () => { diff --git a/backend/src/schema/resolvers/registration.spec.ts b/backend/src/schema/resolvers/registration.spec.ts index 9a0e578cd..deaee0d08 100644 --- a/backend/src/schema/resolvers/registration.spec.ts +++ b/backend/src/schema/resolvers/registration.spec.ts @@ -9,6 +9,8 @@ import gql from 'graphql-tag' import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' import { getDriver, getNeode } from '@db/neo4j' +import EmailAddress from '@models/EmailAddress' +import User from '@models/User' import createServer from '@src/server' const neode = getNeode() @@ -35,7 +37,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { @@ -97,17 +99,27 @@ describe('Signup', () => { describe('creates a EmailAddress node', () => { it('with `createdAt` attribute', async () => { await mutate({ mutation, variables }) - let emailAddress = await neode.first('EmailAddress', { email: 'someuser@example.org' }) - emailAddress = await emailAddress.toJson() - expect(emailAddress.createdAt).toBeTruthy() - expect(Date.parse(emailAddress.createdAt)).toEqual(expect.any(Number)) + const emailAddress = await neode.first( + 'EmailAddress', + { email: 'someuser@example.org' }, + undefined, + ) + const emailAddressJson = await emailAddress.toJson() + expect(emailAddressJson.createdAt).toBeTruthy() + expect(Date.parse(emailAddressJson.createdAt as unknown as string)).toEqual( + expect.any(Number), + ) }) it('with a cryptographic `nonce`', async () => { await mutate({ mutation, variables }) - let emailAddress = await neode.first('EmailAddress', { email: 'someuser@example.org' }) - emailAddress = await emailAddress.toJson() - expect(emailAddress.nonce).toEqual(expect.any(String)) + const emailAddress = await neode.first( + 'EmailAddress', + { email: 'someuser@example.org' }, + undefined, + ) + const emailAddressJson = await emailAddress.toJson() + expect(emailAddressJson.nonce).toEqual(expect.any(String)) }) describe('if the email already exists', () => { @@ -247,7 +259,11 @@ describe('SignupVerification', () => { it('sets `verifiedAt` attribute of EmailAddress', async () => { await mutate({ mutation, variables }) - const email = await neode.first('EmailAddress', { email: 'john@example.org' }) + const email = await neode.first( + 'EmailAddress', + { email: 'john@example.org' }, + undefined, + ) await expect(email.toJson()).resolves.toEqual( expect.objectContaining({ verifiedAt: expect.any(String), @@ -268,7 +284,7 @@ describe('SignupVerification', () => { it('sets `about` attribute of User', async () => { variables = { ...variables, about: 'Find this description in the user profile' } await mutate({ mutation, variables }) - const user = await neode.first('User', { name: 'John Doe' }) + const user = await neode.first('User', { name: 'John Doe' }, undefined) await expect(user.toJson()).resolves.toMatchObject({ about: 'Find this description in the user profile', }) diff --git a/backend/src/schema/resolvers/reports.spec.ts b/backend/src/schema/resolvers/reports.spec.ts index 4401329cb..bcbe1df4e 100644 --- a/backend/src/schema/resolvers/reports.spec.ts +++ b/backend/src/schema/resolvers/reports.spec.ts @@ -122,7 +122,7 @@ describe('file a report on a resource', () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/schema/resolvers/rooms.spec.ts index f374285e1..9a226a2f8 100644 --- a/backend/src/schema/resolvers/rooms.spec.ts +++ b/backend/src/schema/resolvers/rooms.spec.ts @@ -40,7 +40,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('Room', () => { diff --git a/backend/src/schema/resolvers/searches.spec.ts b/backend/src/schema/resolvers/searches.spec.ts index cb774cad5..8a94fbf21 100644 --- a/backend/src/schema/resolvers/searches.spec.ts +++ b/backend/src/schema/resolvers/searches.spec.ts @@ -30,7 +30,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() neode.close() }) diff --git a/backend/src/schema/resolvers/shout.spec.ts b/backend/src/schema/resolvers/shout.spec.ts index 4ec6189bd..9023284c6 100644 --- a/backend/src/schema/resolvers/shout.spec.ts +++ b/backend/src/schema/resolvers/shout.spec.ts @@ -56,7 +56,7 @@ describe('shout and unshout posts', () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(async () => { diff --git a/backend/src/schema/resolvers/socialMedia.spec.ts b/backend/src/schema/resolvers/socialMedia.spec.ts index 584e64cfb..168360a3b 100644 --- a/backend/src/schema/resolvers/socialMedia.spec.ts +++ b/backend/src/schema/resolvers/socialMedia.spec.ts @@ -18,7 +18,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('SocialMedia', () => { diff --git a/backend/src/schema/resolvers/socialMedia.ts b/backend/src/schema/resolvers/socialMedia.ts index d3a563d2c..ad2bdabe5 100644 --- a/backend/src/schema/resolvers/socialMedia.ts +++ b/backend/src/schema/resolvers/socialMedia.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ diff --git a/backend/src/schema/resolvers/statistics.spec.ts b/backend/src/schema/resolvers/statistics.spec.ts index 9d68b611f..50f124ac9 100644 --- a/backend/src/schema/resolvers/statistics.spec.ts +++ b/backend/src/schema/resolvers/statistics.spec.ts @@ -44,7 +44,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 diff --git a/backend/src/schema/resolvers/userData.spec.ts b/backend/src/schema/resolvers/userData.spec.ts index b3cd75694..17f1f4446 100644 --- a/backend/src/schema/resolvers/userData.spec.ts +++ b/backend/src/schema/resolvers/userData.spec.ts @@ -64,7 +64,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('resolvers/userData', () => { diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/schema/resolvers/user_management.spec.ts index 92832baa2..1029ab2b1 100644 --- a/backend/src/schema/resolvers/user_management.spec.ts +++ b/backend/src/schema/resolvers/user_management.spec.ts @@ -4,6 +4,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable promise/prefer-await-to-callbacks */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable jest/unbound-method */ import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import jwt from 'jsonwebtoken' @@ -56,7 +58,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(() => { @@ -160,8 +162,16 @@ describe('currentUser', () => { await respondsWith({ data: { currentUser: expect.objectContaining({ - activeCategories: [ + activeCategories: expect.arrayContaining([ 'cat1', + 'cat2', + 'cat3', + 'cat4', + 'cat5', + 'cat6', + 'cat7', + 'cat8', + 'cat9', 'cat10', 'cat11', 'cat12', @@ -172,15 +182,7 @@ describe('currentUser', () => { 'cat17', 'cat18', 'cat19', - 'cat2', - 'cat3', - 'cat4', - 'cat5', - 'cat6', - 'cat7', - 'cat8', - 'cat9', - ], + ]), }), }, }) @@ -272,7 +274,11 @@ describe('login', () => { describe('normalization', () => { describe('email address is a gmail address ', () => { beforeEach(async () => { - const email = await neode.first('EmailAddress', { email: 'test@example.org' }) + const email = await neode.first( + 'EmailAddress', + { email: 'test@example.org' }, + undefined, + ) await email.update({ email: 'someuser@gmail.com' }) }) diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/schema/resolvers/user_management.ts index 072755850..6b84f7256 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/schema/resolvers/user_management.ts @@ -61,7 +61,7 @@ export default { changePassword: async (_, { oldPassword, newPassword }, { user }) => { const currentUser = await neode.find('User', user.id) - const encryptedPassword = currentUser.get('encryptedPassword') + const encryptedPassword = currentUser.get('encryptedPassword') if (!(await bcrypt.compare(oldPassword, encryptedPassword))) { throw new AuthenticationError('Old password is not correct') } diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index d4f5e00eb..ad37e2024 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -9,6 +9,7 @@ import gql from 'graphql-tag' import { categories } from '@constants/categories' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' +import User from '@models/User' import createServer from '@src/server' const categoryIds = ['cat9'] @@ -125,7 +126,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + 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 @@ -1083,7 +1084,7 @@ 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 dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince: expect.any(String), @@ -1587,14 +1588,14 @@ describe('resetTrophyBadgesSelected', () => { isDefault: true, }, ], - badgeTrophiesUnused: [ + badgeTrophiesUnused: expect.arrayContaining([ { id: 'trophy_panda', }, { id: 'trophy_bear', }, - ], + ]), badgeTrophiesUnusedCount: 2, }, }, diff --git a/backend/src/schema/resolvers/users/location.spec.ts b/backend/src/schema/resolvers/users/location.spec.ts index 9c3791e35..659c126dd 100644 --- a/backend/src/schema/resolvers/users/location.spec.ts +++ b/backend/src/schema/resolvers/users/location.spec.ts @@ -94,7 +94,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(() => { @@ -213,6 +213,7 @@ describe('userMiddleware', () => { await mutate({ mutation: updateUserMutation, variables }) const locations = await neode.cypher( `MATCH (city:Location)-[:IS_IN]->(district:Location)-[:IS_IN]->(state:Location)-[:IS_IN]->(country:Location) return city {.*}, state {.*}, country {.*}`, + {}, ) expect( locations.records.map((record) => { diff --git a/backend/src/schema/resolvers/users/mutedUsers.spec.ts b/backend/src/schema/resolvers/users/mutedUsers.spec.ts index 455672199..ccb6d2a87 100644 --- a/backend/src/schema/resolvers/users/mutedUsers.spec.ts +++ b/backend/src/schema/resolvers/users/mutedUsers.spec.ts @@ -23,7 +23,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) beforeEach(() => { diff --git a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts b/backend/src/schema/resolvers/viewedTeaserCount.spec.ts index 9fb8a7eb9..f4fba31f8 100644 --- a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts +++ b/backend/src/schema/resolvers/viewedTeaserCount.spec.ts @@ -32,7 +32,7 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - driver.close() + await driver.close() }) describe('count post teaser views', () => { From 4e9dbf4c98ac94a2e06502dd345bdbcb67ffc64c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Fri, 25 Apr 2025 21:38:49 +0800 Subject: [PATCH 156/266] fix(docu): remove required but missing `frontend/.env` (#8431) When you follow the documentation, your `docker compose up` will fail because a `.env` file is referenced that doesn't exist yet. The documentation mentions a `.env.template` file, so I guess that one was deleted but the documentation not updated. --- README.md | 4 ---- docker-compose.yml | 2 -- 2 files changed, 6 deletions(-) diff --git a/README.md b/README.md index b1fe0ea14..910eae5a4 100644 --- a/README.md +++ b/README.md @@ -187,10 +187,6 @@ $ cp .env.template .env # in folder backend/ $ cp .env.template .env -# in folder frontend/ -$ cp .env.template .env -``` - For Development: ```bash diff --git a/docker-compose.yml b/docker-compose.yml index d46b5cd29..8397c4e47 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,8 +43,6 @@ services: # Application only envs #- HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp #- GRAPHQL_URI=http://backend:4000 - env_file: - - ./frontend/.env backend: image: ghcr.io/ocelot-social-community/ocelot-social/backend:${OCELOT_VERSION:-latest} From 0873fc748c365f774ef08c85d8257208c4f36c14 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Apr 2025 16:31:49 +0200 Subject: [PATCH 157/266] feat(backend): lint - detect unused typescript disables (#8425) * detect unused typescript disables * fix lint errors uuid-types remove debug fix config * lint fixes --- backend/package.json | 3 ++- backend/src/config/index.ts | 20 ++---------------- backend/src/db/admin.ts | 5 +---- backend/src/db/badges.ts | 3 --- backend/src/db/categories.ts | 4 +--- backend/src/db/compiler.ts | 2 +- backend/src/db/factories.ts | 2 -- backend/src/db/migrate/store.ts | 4 ---- backend/src/db/migrate/template.ts | 8 +------ ...123150105-merge_duplicate_user_accounts.ts | 2 -- ...23150110-merge_duplicate_location_nodes.ts | 2 -- ..._between_existing_blocked_relationships.ts | 8 +------ ...0206190233-swap_latitude_with_longitude.ts | 6 +----- .../20200207080200-fulltext_index_for_tags.ts | 4 ---- ...213230248-add_unique_index_to_image_url.ts | 4 ---- .../20200312140328-bulk_upload_to_s3.ts | 2 -- ...15-refactor_all_images_to_separate_type.ts | 7 +------ ...emove_deleted_users_obsolete_attributes.ts | 5 +---- ...emove_deleted_posts_obsolete_attributes.ts | 5 +---- ...200326160326-remove_dangling_image_urls.ts | 3 +-- ...1614023644903-add-clickedCount-to-posts.ts | 4 +--- ...77130817-add-viewedTeaserCount-to-posts.ts | 4 +--- .../20210506150512-add-donations-node.ts | 8 +------ ...otificationEmails-property-to-all-users.ts | 8 +------ ...text_indices_and_unique_keys_for_groups.ts | 8 +------ .../20230320130345-fulltext-search-indexes.ts | 8 +------ .../20230329150329-article-label-for-posts.ts | 8 +------ .../20230608130637-add-postType-property.ts | 8 +------ .../20231017141022-fix-event-dates.ts | 7 +------ ...20250331130323-author-observes-own-post.ts | 8 +------ .../20250331140313-commenter-observes-post.ts | 8 +------ ...50405030454-email-notification-settings.ts | 8 +------ .../20250414220436-delete-old-badges.ts | 8 +------ backend/src/db/neo4j.ts | 4 +--- backend/src/db/seed.ts | 2 -- ...ficationsMiddleware.followed-users.spec.ts | 2 +- .../notifications/notificationsMiddleware.ts | 1 - .../src/middleware/orderByMiddleware.spec.ts | 2 -- backend/src/models/Category.ts | 1 - backend/src/models/Comment.ts | 1 - backend/src/models/Donations.ts | 1 - backend/src/models/Group.ts | 1 - backend/src/models/Post.ts | 1 - backend/src/models/Report.ts | 1 - backend/src/models/SocialMedia.ts | 1 - backend/src/models/User.spec.ts | 5 +---- backend/src/models/User.ts | 1 - backend/src/schema/resolvers/follow.ts | 4 ++-- .../src/schema/resolvers/helpers/Resolver.ts | 5 ----- .../resolvers/helpers/databaseLogger.ts | 21 ------------------- backend/src/schema/resolvers/moderation.ts | 3 --- backend/src/schema/resolvers/notifications.ts | 5 ----- backend/src/schema/resolvers/passwordReset.ts | 2 +- .../src/schema/resolvers/registration.spec.ts | 1 - backend/src/schema/resolvers/reports.ts | 6 ------ backend/src/schema/resolvers/searches.ts | 2 -- backend/src/schema/resolvers/shout.ts | 4 ---- backend/src/schema/resolvers/socialMedia.ts | 4 ++-- backend/src/schema/resolvers/statistics.ts | 3 --- .../src/schema/resolvers/user_management.ts | 4 +--- backend/src/schema/resolvers/users.ts | 2 -- .../src/schema/resolvers/users/location.ts | 7 ------- backend/test/setup.ts | 8 ------- backend/yarn.lock | 5 +++++ 64 files changed, 41 insertions(+), 263 deletions(-) delete mode 100644 backend/src/schema/resolvers/helpers/databaseLogger.ts diff --git a/backend/package.json b/backend/package.json index 7137d5c25..d904bbd20 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "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 --ext .js,.ts .", + "lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts .", "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", @@ -105,6 +105,7 @@ "@faker-js/faker": "9.7.0", "@types/jest": "^29.5.14", "@types/node": "^22.14.1", + "@types/uuid": "~9.0.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "apollo-server-testing": "~2.11.0", diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index cf07297df..35e800c64 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,35 +1,19 @@ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ /* eslint-disable n/no-process-env */ -/* eslint-disable n/no-unpublished-require */ -/* eslint-disable n/no-missing-require */ import { config } from 'dotenv' import emails from './emails' import metadata from './metadata' // Load env file -if (require.resolve) { - try { - config({ path: require.resolve('../../.env') }) - } catch (error) { - // This error is thrown when the .env is not found - if (error.code !== 'MODULE_NOT_FOUND') { - throw error - } - } -} +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 // eslint-disable-line no-undef +const env = typeof Cypress !== 'undefined' ? Cypress.env() : process.env const environment = { NODE_ENV: env.NODE_ENV || process.env.NODE_ENV, diff --git a/backend/src/db/admin.ts b/backend/src/db/admin.ts index b60eeb38a..1f62c8733 100644 --- a/backend/src/db/admin.ts +++ b/backend/src/db/admin.ts @@ -1,9 +1,6 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* 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/restrict-template-expressions */ + import { hashSync } from 'bcryptjs' import { v4 as uuid } from 'uuid' diff --git a/backend/src/db/badges.ts b/backend/src/db/badges.ts index 3fbbd4d7f..cbad0b004 100644 --- a/backend/src/db/badges.ts +++ b/backend/src/db/badges.ts @@ -1,6 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { getNeode } from './neo4j' import { trophies, verification } from './seed/badges' diff --git a/backend/src/db/categories.ts b/backend/src/db/categories.ts index b3774e7b9..a007b25ae 100644 --- a/backend/src/db/categories.ts +++ b/backend/src/db/categories.ts @@ -1,9 +1,7 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* 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 */ + import { categories } from '@constants/categories' import { getDriver } from './neo4j' diff --git a/backend/src/db/compiler.ts b/backend/src/db/compiler.ts index 9c2140f2a..1b364f919 100644 --- a/backend/src/db/compiler.ts +++ b/backend/src/db/compiler.ts @@ -3,7 +3,7 @@ /* eslint-disable import/no-commonjs */ // eslint-disable-next-line n/no-unpublished-require, @typescript-eslint/no-var-requires const tsNode = require('ts-node') -// eslint-disable-next-line import/no-unassigned-import, import/no-extraneous-dependencies, n/no-unpublished-require +// eslint-disable-next-line import/no-unassigned-import, n/no-unpublished-require require('tsconfig-paths/register') module.exports = tsNode.register diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index e951c3839..90a666205 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -1,11 +1,9 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ import { faker } from '@faker-js/faker' import { hashSync } from 'bcryptjs' import { Factory } from 'rosie' diff --git a/backend/src/db/migrate/store.ts b/backend/src/db/migrate/store.ts index bc70fe361..9976be8b4 100644 --- a/backend/src/db/migrate/store.ts +++ b/backend/src/db/migrate/store.ts @@ -1,11 +1,7 @@ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ import { getDriver, getNeode } from '@db/neo4j' class Store { diff --git a/backend/src/db/migrate/template.ts b/backend/src/db/migrate/template.ts index 9306ec27c..a7287dd42 100644 --- a/backend/src/db/migrate/template.ts +++ b/backend/src/db/migrate/template.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = '' 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 7d4195131..eda3206b4 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 @@ -1,11 +1,9 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* 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-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ /* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' diff --git a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts index 1b180616b..de73bdaae 100644 --- a/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts +++ b/backend/src/db/migrations-examples/20200123150110-merge_duplicate_location_nodes.ts @@ -1,11 +1,9 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* 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-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ /* eslint-disable import/no-extraneous-dependencies */ /* eslint-disable promise/prefer-await-to-callbacks */ import { throwError, concat } from 'rxjs' diff --git a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts index a8b6f8179..8be7bad07 100644 --- a/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts +++ b/backend/src/db/migrations-examples/20200127110135-create_muted_relationship_between_existing_blocked_relationships.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts index a1eab53fa..f63be216d 100644 --- a/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts +++ b/backend/src/db/migrations-examples/20200206190233-swap_latitude_with_longitude.ts @@ -1,12 +1,8 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts index 3fb44c77a..f2e32d6f8 100644 --- a/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts +++ b/backend/src/db/migrations-examples/20200207080200-fulltext_index_for_tags.ts @@ -1,11 +1,7 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ import { getDriver } from '@db/neo4j' export const description = diff --git a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts index 0af7626a1..81d96f68c 100644 --- a/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts +++ b/backend/src/db/migrations-examples/20200213230248-add_unique_index_to_image_url.ts @@ -1,11 +1,7 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ import { getDriver } from '@db/neo4j' export const description = ` 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 index 606986d68..0307a2e6e 100644 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ diff --git a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts index 3028b9837..1d24bd141 100644 --- a/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts +++ b/backend/src/db/migrations-examples/20200320200315-refactor_all_images_to_separate_type.ts @@ -1,11 +1,6 @@ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + /* eslint-disable no-console */ import { getDriver } from '@db/neo4j' diff --git a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts index d0e0ab5e6..b75324a78 100644 --- a/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323140300-remove_deleted_users_obsolete_attributes.ts @@ -1,11 +1,8 @@ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = diff --git a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts index 0bda73770..597eb1d83 100644 --- a/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts +++ b/backend/src/db/migrations-examples/20200323160336-remove_deleted_posts_obsolete_attributes.ts @@ -1,11 +1,8 @@ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = diff --git a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts index 740b1a85e..1109ac623 100644 --- a/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts +++ b/backend/src/db/migrations-examples/20200326160326-remove_dangling_image_urls.ts @@ -3,8 +3,7 @@ /* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + /* eslint-disable security/detect-non-literal-fs-filename */ import { existsSync } from 'node:fs' diff --git a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts index e1b884de0..7443b4749 100644 --- a/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts +++ b/backend/src/db/migrations/1614023644903-add-clickedCount-to-posts.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts index 5ed831b61..b23bf96bf 100644 --- a/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts +++ b/backend/src/db/migrations/1614177130817-add-viewedTeaserCount-to-posts.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations/20210506150512-add-donations-node.ts b/backend/src/db/migrations/20210506150512-add-donations-node.ts index 2cba177b7..90f00e26f 100644 --- a/backend/src/db/migrations/20210506150512-add-donations-node.ts +++ b/backend/src/db/migrations/20210506150512-add-donations-node.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { v4 as uuid } from 'uuid' import { getDriver } from '@db/neo4j' diff --git a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts index c7c774c70..5bc6aed17 100644 --- a/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts +++ b/backend/src/db/migrations/20210923140939-add-sendNotificationEmails-property-to-all-users.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = '' diff --git a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts index 106aec0dc..f06c10984 100644 --- a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts +++ b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts index b12db9964..686d221de 100644 --- a/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts +++ b/backend/src/db/migrations/20230320130345-fulltext-search-indexes.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = '' diff --git a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts index 06f05dbd1..44433e56b 100644 --- a/backend/src/db/migrations/20230329150329-article-label-for-posts.ts +++ b/backend/src/db/migrations/20230329150329-article-label-for-posts.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = 'Add to all existing posts the Article label' diff --git a/backend/src/db/migrations/20230608130637-add-postType-property.ts b/backend/src/db/migrations/20230608130637-add-postType-property.ts index f58e4de97..1e5474064 100644 --- a/backend/src/db/migrations/20230608130637-add-postType-property.ts +++ b/backend/src/db/migrations/20230608130637-add-postType-property.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = 'Add postType property Article to all posts' diff --git a/backend/src/db/migrations/20231017141022-fix-event-dates.ts b/backend/src/db/migrations/20231017141022-fix-event-dates.ts index 1b92537ba..259e3ff65 100644 --- a/backend/src/db/migrations/20231017141022-fix-event-dates.ts +++ b/backend/src/db/migrations/20231017141022-fix-event-dates.ts @@ -1,12 +1,7 @@ /* eslint-disable @typescript-eslint/no-base-to-string */ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts index 4411107e7..df6eebf23 100644 --- a/backend/src/db/migrations/20250331130323-author-observes-own-post.ts +++ b/backend/src/db/migrations/20250331130323-author-observes-own-post.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations/20250331140313-commenter-observes-post.ts b/backend/src/db/migrations/20250331140313-commenter-observes-post.ts index 664c64047..ce1d32bc0 100644 --- a/backend/src/db/migrations/20250331140313-commenter-observes-post.ts +++ b/backend/src/db/migrations/20250331140313-commenter-observes-post.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = ` diff --git a/backend/src/db/migrations/20250405030454-email-notification-settings.ts b/backend/src/db/migrations/20250405030454-email-notification-settings.ts index afe3a5ed3..eaa9a7a6e 100644 --- a/backend/src/db/migrations/20250405030454-email-notification-settings.ts +++ b/backend/src/db/migrations/20250405030454-email-notification-settings.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = diff --git a/backend/src/db/migrations/20250414220436-delete-old-badges.ts b/backend/src/db/migrations/20250414220436-delete-old-badges.ts index 6a932543e..d03e14619 100644 --- a/backend/src/db/migrations/20250414220436-delete-old-badges.ts +++ b/backend/src/db/migrations/20250414220436-delete-old-badges.ts @@ -1,11 +1,5 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ + import { getDriver } from '@db/neo4j' export const description = '' diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index 62594ee2d..dcd19a0ea 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,7 +1,5 @@ -/* 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-return */ + /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable import/no-named-as-default-member */ import neo4j, { Driver } from 'neo4j-driver' diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index 6e09df986..0e2c2c61d 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -28,7 +28,6 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] -/* eslint-disable no-multi-spaces */ ;(async function () { let authenticatedUser = null const driver = getDriver() @@ -1594,4 +1593,3 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] process.exit(1) } })() -/* eslint-enable no-multi-spaces */ diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 7d311ce95..21d4a14a0 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -2,7 +2,7 @@ /* 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' diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index b9f5c4284..4926dc94e 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -13,7 +13,6 @@ 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' -// eslint-disable-next-line import/no-cycle import { pubsub, NOTIFICATION_ADDED, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' import extractMentionedUsers from './mentions/extractMentionedUsers' diff --git a/backend/src/middleware/orderByMiddleware.spec.ts b/backend/src/middleware/orderByMiddleware.spec.ts index e46ee1c1f..b98c21c0b 100644 --- a/backend/src/middleware/orderByMiddleware.spec.ts +++ b/backend/src/middleware/orderByMiddleware.spec.ts @@ -1,5 +1,3 @@ -/* 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' diff --git a/backend/src/models/Category.ts b/backend/src/models/Category.ts index f61d5aaab..9a3f47fd0 100644 --- a/backend/src/models/Category.ts +++ b/backend/src/models/Category.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/Comment.ts b/backend/src/models/Comment.ts index f05cb7ccc..f4548f0c2 100644 --- a/backend/src/models/Comment.ts +++ b/backend/src/models/Comment.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/Donations.ts b/backend/src/models/Donations.ts index 61113702d..742bfb569 100644 --- a/backend/src/models/Donations.ts +++ b/backend/src/models/Donations.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/Group.ts b/backend/src/models/Group.ts index cff388a0a..a75ad518f 100644 --- a/backend/src/models/Group.ts +++ b/backend/src/models/Group.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/Post.ts b/backend/src/models/Post.ts index 466e8a21d..75081b728 100644 --- a/backend/src/models/Post.ts +++ b/backend/src/models/Post.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/Report.ts b/backend/src/models/Report.ts index 07e8a79c1..3e001746b 100644 --- a/backend/src/models/Report.ts +++ b/backend/src/models/Report.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/SocialMedia.ts b/backend/src/models/SocialMedia.ts index 86f2f90be..6010c97bb 100644 --- a/backend/src/models/SocialMedia.ts +++ b/backend/src/models/SocialMedia.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/models/User.spec.ts b/backend/src/models/User.spec.ts index 272ad2df3..cea2d4db0 100644 --- a/backend/src/models/User.spec.ts +++ b/backend/src/models/User.spec.ts @@ -1,9 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* 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-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + import { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 611b6a984..77a37c3c1 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { v4 as uuid } from 'uuid' export default { diff --git a/backend/src/schema/resolvers/follow.ts b/backend/src/schema/resolvers/follow.ts index 809d77760..d08f243b1 100644 --- a/backend/src/schema/resolvers/follow.ts +++ b/backend/src/schema/resolvers/follow.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ + /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ + /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getNeode } from '@db/neo4j' diff --git a/backend/src/schema/resolvers/helpers/Resolver.ts b/backend/src/schema/resolvers/helpers/Resolver.ts index 2d7faa7b7..71d7602a4 100644 --- a/backend/src/schema/resolvers/helpers/Resolver.ts +++ b/backend/src/schema/resolvers/helpers/Resolver.ts @@ -7,8 +7,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */ -import log from './databaseLogger' - export const undefinedToNullResolver = (list) => { const resolvers = {} list.forEach((key) => { @@ -41,7 +39,6 @@ export default function Resolver(type, options: any = {}) { RETURN related {.*} as related ` const result = await txc.run(cypher, { id, cypherParams }) - log(result) return result.records.map((r) => r.get('related')) }) try { @@ -66,7 +63,6 @@ export default function Resolver(type, options: any = {}) { const nodeCondition = condition.replace('this', 'this {id: $id}') const cypher = `${nodeCondition} as ${key}` const result = await txc.run(cypher, { id, cypherParams }) - log(result) const [response] = result.records.map((r) => r.get(key)) return response }) @@ -93,7 +89,6 @@ export default function Resolver(type, options: any = {}) { RETURN COUNT(DISTINCT(related)) as count ` const result = await txc.run(cypher, { id, cypherParams }) - log(result) const [response] = result.records.map((r) => r.get('count').toNumber()) return response }) diff --git a/backend/src/schema/resolvers/helpers/databaseLogger.ts b/backend/src/schema/resolvers/helpers/databaseLogger.ts deleted file mode 100644 index ad1de5828..000000000 --- a/backend/src/schema/resolvers/helpers/databaseLogger.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable import/no-named-as-default */ -// eslint-disable-next-line import/no-extraneous-dependencies -import Debug from 'debug' - -const debugCypher = Debug('human-connection:neo4j:cypher') -const debugStats = Debug('human-connection:neo4j:stats') - -export default function log(response) { - const { counters, resultConsumedAfter, resultAvailableAfter, query } = response.summary - const { text, parameters } = query - debugCypher('%s', text) - debugCypher('%o', parameters) - debugStats('%o', counters) - debugStats('%o', { - resultConsumedAfter: resultConsumedAfter.toNumber(), - resultAvailableAfter: resultAvailableAfter.toNumber(), - }) -} diff --git a/backend/src/schema/resolvers/moderation.ts b/backend/src/schema/resolvers/moderation.ts index 6fe8637c6..bcdb3992a 100644 --- a/backend/src/schema/resolvers/moderation.ts +++ b/backend/src/schema/resolvers/moderation.ts @@ -2,8 +2,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import log from './helpers/databaseLogger' - export default { Mutation: { review: async (_object, params, context, _resolveInfo) => { @@ -31,7 +29,6 @@ export default { moderatorId: moderator.id, dateTime: new Date().toISOString(), }) - log(reviewTransactionResponse) return reviewTransactionResponse.records.map((record) => record.get('review')) }) const [reviewed] = await reviewWriteTxResultPromise diff --git a/backend/src/schema/resolvers/notifications.ts b/backend/src/schema/resolvers/notifications.ts index 6151d305e..0168558c3 100644 --- a/backend/src/schema/resolvers/notifications.ts +++ b/backend/src/schema/resolvers/notifications.ts @@ -8,8 +8,6 @@ import { withFilter } from 'graphql-subscriptions' import { pubsub, NOTIFICATION_ADDED } from '@src/server' -import log from './helpers/databaseLogger' - export default { Subscription: { notificationAdded: { @@ -76,7 +74,6 @@ export default { `, { id: currentUser.id }, ) - log(notificationsTransactionResponse) return notificationsTransactionResponse.records.map((record) => record.get('notification')) }) try { @@ -106,7 +103,6 @@ export default { `, { resourceId: args.id, id: currentUser.id }, ) - log(markNotificationAsReadTransactionResponse) return markNotificationAsReadTransactionResponse.records.map((record) => record.get('notification'), ) @@ -136,7 +132,6 @@ export default { `, { id: currentUser.id }, ) - log(markAllNotificationAsReadTransactionResponse) return markAllNotificationAsReadTransactionResponse.records.map((record) => record.get('notification'), ) diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index 6e74ac710..3159d7006 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/await-thenable */ + /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ diff --git a/backend/src/schema/resolvers/registration.spec.ts b/backend/src/schema/resolvers/registration.spec.ts index deaee0d08..f19c6bf01 100644 --- a/backend/src/schema/resolvers/registration.spec.ts +++ b/backend/src/schema/resolvers/registration.spec.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/backend/src/schema/resolvers/reports.ts b/backend/src/schema/resolvers/reports.ts index 35e250f48..b8886c48f 100644 --- a/backend/src/schema/resolvers/reports.ts +++ b/backend/src/schema/resolvers/reports.ts @@ -3,8 +3,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import log from './helpers/databaseLogger' - export default { Mutation: { fileReport: async (_parent, params, context, _resolveInfo) => { @@ -33,7 +31,6 @@ export default { reasonDescription, }, ) - log(fileReportTransactionResponse) return fileReportTransactionResponse.records.map((record) => record.get('filedReport')) }) try { @@ -106,7 +103,6 @@ export default { ${offset} ${limit} `, ) - log(reportsTransactionResponse) return reportsTransactionResponse.records.map((record) => record.get('report')) }) try { @@ -131,7 +127,6 @@ export default { `, { id }, ) - log(filedReportsTransactionResponse) return filedReportsTransactionResponse.records.map((record) => ({ submitter: record.get('submitter').properties, filed: record.get('filed').properties, @@ -166,7 +161,6 @@ export default { `, { id }, ) - log(reviewedReportsTransactionResponse) return reviewedReportsTransactionResponse.records.map((record) => ({ review: record.get('review').properties, moderator: record.get('moderator').properties, diff --git a/backend/src/schema/resolvers/searches.ts b/backend/src/schema/resolvers/searches.ts index 845a070a5..34fc11709 100644 --- a/backend/src/schema/resolvers/searches.ts +++ b/backend/src/schema/resolvers/searches.ts @@ -4,7 +4,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import log from './helpers/databaseLogger' import { queryString } from './searches/queryString' // see http://lucene.apache.org/core/8_3_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description @@ -133,7 +132,6 @@ const getSearchResults = async (context, setup, params, resultCallback = searchR const session = context.driver.session() try { const results = await searchResultPromise(session, setup, params) - log(results) return resultCallback(results) } finally { session.close() diff --git a/backend/src/schema/resolvers/shout.ts b/backend/src/schema/resolvers/shout.ts index 0a7ec6a39..f0b5885eb 100644 --- a/backend/src/schema/resolvers/shout.ts +++ b/backend/src/schema/resolvers/shout.ts @@ -2,8 +2,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import log from './helpers/databaseLogger' - export default { Mutation: { shout: async (_object, params, context, _resolveInfo) => { @@ -25,7 +23,6 @@ export default { userId: context.user.id, }, ) - log(shoutTransactionResponse) return shoutTransactionResponse.records.map((record) => record.get('isShouted')) }) const [isShouted] = await shoutWriteTxResultPromise @@ -53,7 +50,6 @@ export default { userId: context.user.id, }, ) - log(unshoutTransactionResponse) return unshoutTransactionResponse.records.map((record) => record.get('isShouted')) }) const [isShouted] = await unshoutWriteTxResultPromise diff --git a/backend/src/schema/resolvers/socialMedia.ts b/backend/src/schema/resolvers/socialMedia.ts index ad2bdabe5..952e4a27e 100644 --- a/backend/src/schema/resolvers/socialMedia.ts +++ b/backend/src/schema/resolvers/socialMedia.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ + /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ + /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { getNeode } from '@db/neo4j' diff --git a/backend/src/schema/resolvers/statistics.ts b/backend/src/schema/resolvers/statistics.ts index e2b93bbea..f7af390bf 100644 --- a/backend/src/schema/resolvers/statistics.ts +++ b/backend/src/schema/resolvers/statistics.ts @@ -3,8 +3,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */ -import log from './helpers/databaseLogger' - export default { Query: { statistics: async (_parent, _args, { driver }) => { @@ -28,7 +26,6 @@ export default { RETURN labels, relTypesCount `, ) - log(statisticsTransactionResponse) return statisticsTransactionResponse.records.map((record) => { return { ...record.get('labels'), diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/schema/resolvers/user_management.ts index 6b84f7256..7bea1f53c 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/schema/resolvers/user_management.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/await-thenable */ + /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ @@ -12,7 +12,6 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import { getNeode } from '@db/neo4j' import encode from '@jwt/encode' -import log from './helpers/databaseLogger' import normalizeEmail from './helpers/normalizeEmail' const neode = getNeode() @@ -38,7 +37,6 @@ export default { `, { userEmail: email }, ) - log(loginTransactionResponse) return loginTransactionResponse.records.map((record) => record.get('user')) }) const [currentUser] = await loginReadTxResultPromise diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index c165e8e44..f549e79a3 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -12,7 +12,6 @@ import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges' import { getNeode } from '@db/neo4j' import { defaultTrophyBadge, defaultVerificationBadge } from './badges' -import log from './helpers/databaseLogger' import Resolver from './helpers/Resolver' import { mergeImage, deleteImage } from './images/images' import { createOrUpdateLocations } from './users/location' @@ -279,7 +278,6 @@ export default { `, { userId }, ) - log(deleteUserTransactionResponse) const [user] = deleteUserTransactionResponse.records.map((record) => record.get('user')) await deleteImage(user, 'AVATAR_IMAGE', { transaction }) return user diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/schema/resolvers/users/location.ts index d32c03cd2..6dfaede4e 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/schema/resolvers/users/location.ts @@ -8,17 +8,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable promise/avoid-new */ /* eslint-disable promise/prefer-await-to-callbacks */ -/* eslint-disable import/no-named-as-default */ import { UserInputError } from 'apollo-server' -// eslint-disable-next-line import/no-extraneous-dependencies -import Debug from 'debug' import request from 'request' import CONFIG from '@config/index' import asyncForEach from '@helpers/asyncForEach' -const debug = Debug('human-connection:location') - const fetch = (url) => { return new Promise((resolve, reject) => { request(url, function (error, response, body) { @@ -93,8 +88,6 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s }&types=region,place,country,address&language=${locales.join(',')}`, ) - debug(res) - if (!res?.features?.[0]) { throw new UserInputError('locationName is invalid') } diff --git a/backend/test/setup.ts b/backend/test/setup.ts index 594b9763d..128830f13 100644 --- a/backend/test/setup.ts +++ b/backend/test/setup.ts @@ -1,10 +1,2 @@ -// Polyfill missing encoders in jsdom -// https://stackoverflow.com/questions/68468203/why-am-i-getting-textencoder-is-not-defined-in-jest -// import { TextEncoder, TextDecoder } from 'util' - -// global.TextEncoder = TextEncoder -// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -// global.TextDecoder = TextDecoder as any - // Metascraper takes longer nowadays, double time // jest.setTimeout(10000) diff --git a/backend/yarn.lock b/backend/yarn.lock index 3f0aa3990..c972f9925 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2126,6 +2126,11 @@ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== +"@types/uuid@~9.0.1": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + "@types/ws@^7.0.0": version "7.2.5" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.2.5.tgz#513f28b04a1ea1aa9dc2cad3f26e8e37c88aae49" From 2fd138697f8065cd3ef86d96272a129607244449 Mon Sep 17 00:00:00 2001 From: sebastian2357 <80636200+sebastian2357@users.noreply.github.com> Date: Fri, 25 Apr 2025 18:55:46 +0200 Subject: [PATCH 158/266] feat(webapp): badges UI (#8426) - New badge UI, including editor. - Adds config to enable/disable badges. --------- Co-authored-by: Sebastian Stein Co-authored-by: Maximilian Harz --- webapp/components/BadgeSelection.spec.js | 76 + webapp/components/BadgeSelection.vue | 102 + webapp/components/Badges.spec.js | 119 +- webapp/components/Badges.vue | 172 +- .../__snapshots__/BadgeSelection.spec.js.snap | 84 + .../__snapshots__/Badges.spec.js.snap | 165 ++ webapp/config/index.js | 1 + webapp/graphql/Fragments.js | 8 +- webapp/graphql/User.js | 56 + webapp/locales/de.json | 10 + webapp/locales/en.json | 10 + webapp/locales/es.json | 10 + webapp/locales/fr.json | 10 + webapp/locales/it.json | 10 + webapp/locales/nl.json | 10 + webapp/locales/pl.json | 10 + webapp/locales/pt.json | 10 + webapp/locales/ru.json | 10 + webapp/package.json | 1 + .../pages/__snapshots__/settings.spec.js.snap | 427 +++ .../users/__snapshots__/index.spec.js.snap | 847 ++++++ webapp/pages/admin/users/index.spec.js | 236 +- webapp/pages/admin/users/index.vue | 11 +- .../_id/__snapshots__/_slug.spec.js.snap | 2442 +++++++++++++++++ webapp/pages/profile/_id/_slug.spec.js | 176 +- webapp/pages/profile/_id/_slug.vue | 17 +- webapp/pages/settings.spec.js | 36 +- webapp/pages/settings.vue | 11 +- .../__snapshots__/badges.spec.js.snap | 429 +++ webapp/pages/settings/badges.spec.js | 302 ++ webapp/pages/settings/badges.vue | 176 ++ webapp/static/img/badges/stars.svg | 11 + webapp/yarn.lock | 33 + 33 files changed, 5820 insertions(+), 208 deletions(-) create mode 100644 webapp/components/BadgeSelection.spec.js create mode 100644 webapp/components/BadgeSelection.vue create mode 100644 webapp/components/__snapshots__/BadgeSelection.spec.js.snap create mode 100644 webapp/components/__snapshots__/Badges.spec.js.snap create mode 100644 webapp/pages/__snapshots__/settings.spec.js.snap create mode 100644 webapp/pages/admin/users/__snapshots__/index.spec.js.snap create mode 100644 webapp/pages/profile/_id/__snapshots__/_slug.spec.js.snap create mode 100644 webapp/pages/settings/__snapshots__/badges.spec.js.snap create mode 100644 webapp/pages/settings/badges.spec.js create mode 100644 webapp/pages/settings/badges.vue create mode 100644 webapp/static/img/badges/stars.svg diff --git a/webapp/components/BadgeSelection.spec.js b/webapp/components/BadgeSelection.spec.js new file mode 100644 index 000000000..78f00b87a --- /dev/null +++ b/webapp/components/BadgeSelection.spec.js @@ -0,0 +1,76 @@ +import { render, screen, fireEvent } from '@testing-library/vue' +import BadgeSelection from './BadgeSelection.vue' + +const localVue = global.localVue + +describe('Badges.vue', () => { + const Wrapper = (propsData) => { + return render(BadgeSelection, { + propsData, + localVue, + }) + } + + describe('without badges', () => { + it('renders', () => { + const wrapper = Wrapper({ badges: [] }) + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('with badges', () => { + const badges = [ + { + id: '1', + icon: '/path/to/some/icon', + isDefault: false, + description: 'Some description', + }, + { + id: '2', + icon: '/path/to/another/icon', + isDefault: true, + description: 'Another description', + }, + { + id: '3', + icon: '/path/to/third/icon', + isDefault: false, + description: 'Third description', + }, + ] + + let wrapper + + beforeEach(() => { + wrapper = Wrapper({ badges }) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + describe('clicking on a badge', () => { + beforeEach(async () => { + const badge = screen.getByText(badges[1].description) + await fireEvent.click(badge) + }) + + it('emits badge-selected with badge', async () => { + expect(wrapper.emitted()['badge-selected']).toEqual([[badges[1]]]) + }) + }) + + describe('clicking twice on a badge', () => { + beforeEach(async () => { + const badge = screen.getByText(badges[1].description) + await fireEvent.click(badge) + await fireEvent.click(badge) + }) + + it('emits badge-selected with null', async () => { + expect(wrapper.emitted()['badge-selected']).toEqual([[badges[1]], [null]]) + }) + }) + }) +}) diff --git a/webapp/components/BadgeSelection.vue b/webapp/components/BadgeSelection.vue new file mode 100644 index 000000000..a6554d779 --- /dev/null +++ b/webapp/components/BadgeSelection.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/webapp/components/Badges.spec.js b/webapp/components/Badges.spec.js index d19c2beb2..ae15e0b0a 100644 --- a/webapp/components/Badges.spec.js +++ b/webapp/components/Badges.spec.js @@ -1,29 +1,114 @@ -import { shallowMount } from '@vue/test-utils' +import { render, screen, fireEvent } from '@testing-library/vue' import Badges from './Badges.vue' +const localVue = global.localVue + describe('Badges.vue', () => { - let propsData + const Wrapper = (propsData) => { + return render(Badges, { + propsData, + localVue, + }) + } - beforeEach(() => { - propsData = {} - }) - - describe('shallowMount', () => { - const Wrapper = () => { - return shallowMount(Badges, { propsData }) - } - - it('has class "hc-badges"', () => { - expect(Wrapper().find('.hc-badges').exists()).toBe(true) + describe('without badges', () => { + it('renders in presentation mode', () => { + const wrapper = Wrapper({ badges: [], selectionMode: false }) + expect(wrapper.container).toMatchSnapshot() }) - describe('given a badge', () => { + it('renders in selection mode', () => { + const wrapper = Wrapper({ badges: [], selectionMode: true }) + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('with badges', () => { + const badges = [ + { + id: '1', + icon: '/path/to/some/icon', + isDefault: false, + description: 'Some description', + }, + { + id: '2', + icon: '/path/to/another/icon', + isDefault: true, + description: 'Another description', + }, + { + id: '3', + icon: '/path/to/third/icon', + isDefault: false, + description: 'Third description', + }, + ] + + describe('in presentation mode', () => { + let wrapper + beforeEach(() => { - propsData.badges = [{ id: '1', icon: '/path/to/some/icon' }] + wrapper = Wrapper({ badges, scale: 1.2, selectionMode: false }) }) - it('proxies badge icon, which is just a URL without metadata', () => { - expect(Wrapper().find('img[src="/api/path/to/some/icon"]').exists()).toBe(true) + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('clicking on second badge does nothing', async () => { + const badge = screen.getByTitle(badges[1].description) + await fireEvent.click(badge) + expect(wrapper.emitted()).toEqual({}) + }) + }) + + describe('in selection mode', () => { + let wrapper + + beforeEach(() => { + wrapper = Wrapper({ badges, scale: 1.2, selectionMode: true }) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('clicking on first badge does nothing', async () => { + const badge = screen.getByTitle(badges[0].description) + await fireEvent.click(badge) + expect(wrapper.emitted()).toEqual({}) + }) + + describe('clicking on second badge', () => { + beforeEach(async () => { + const badge = screen.getByTitle(badges[1].description) + await fireEvent.click(badge) + }) + + it('selects badge', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('emits badge-selected with index', async () => { + expect(wrapper.emitted()['badge-selected']).toEqual([[1]]) + }) + }) + + describe('clicking twice on second badge', () => { + beforeEach(async () => { + const badge = screen.getByTitle(badges[1].description) + await fireEvent.click(badge) + await fireEvent.click(badge) + }) + + it('deselects badge', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('emits badge-selected with null', async () => { + expect(wrapper.emitted()['badge-selected']).toEqual([[1], [null]]) + }) }) }) }) diff --git a/webapp/components/Badges.vue b/webapp/components/Badges.vue index d569452c7..ca5c4f0ef 100644 --- a/webapp/components/Badges.vue +++ b/webapp/components/Badges.vue @@ -1,69 +1,171 @@ diff --git a/webapp/components/__snapshots__/BadgeSelection.spec.js.snap b/webapp/components/__snapshots__/BadgeSelection.spec.js.snap new file mode 100644 index 000000000..a31d547c9 --- /dev/null +++ b/webapp/components/__snapshots__/BadgeSelection.spec.js.snap @@ -0,0 +1,84 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Badges.vue with badges renders 1`] = ` +
+
+ + + +
+
+`; + +exports[`Badges.vue without badges renders 1`] = ` +
+
+
+`; diff --git a/webapp/components/__snapshots__/Badges.spec.js.snap b/webapp/components/__snapshots__/Badges.spec.js.snap new file mode 100644 index 000000000..6ea612a76 --- /dev/null +++ b/webapp/components/__snapshots__/Badges.spec.js.snap @@ -0,0 +1,165 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Badges.vue with badges in presentation mode renders 1`] = ` +
+
+
+ +
+
+ +
+
+ +
+
+
+`; + +exports[`Badges.vue with badges in selection mode clicking on second badge selects badge 1`] = ` +
+
+ + + +
+
+`; + +exports[`Badges.vue with badges in selection mode clicking twice on second badge deselects badge 1`] = ` +
+
+ + + +
+
+`; + +exports[`Badges.vue with badges in selection mode renders 1`] = ` +
+
+ + + +
+
+`; + +exports[`Badges.vue without badges renders in presentation mode 1`] = ` +
+
+
+`; + +exports[`Badges.vue without badges renders in selection mode 1`] = ` +
+
+
+`; diff --git a/webapp/config/index.js b/webapp/config/index.js index 5da17010b..fb275a8ec 100644 --- a/webapp/config/index.js +++ b/webapp/config/index.js @@ -35,6 +35,7 @@ const options = { COOKIE_EXPIRE_TIME: process.env.COOKIE_EXPIRE_TIME || 730, // Two years by default COOKIE_HTTPS_ONLY: process.env.COOKIE_HTTPS_ONLY || process.env.NODE_ENV === 'production', // ensure true in production if not set explicitly CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false, + BADGES_ENABLED: process.env.BADGES_ENABLED === 'true' || false, } const CONFIG = { diff --git a/webapp/graphql/Fragments.js b/webapp/graphql/Fragments.js index d0ad8a0fe..77af830e8 100644 --- a/webapp/graphql/Fragments.js +++ b/webapp/graphql/Fragments.js @@ -26,9 +26,15 @@ export const locationFragment = (lang) => gql` export const badgesFragment = gql` fragment badges on User { - badgeTrophies { + badgeTrophiesSelected { id icon + description + } + badgeVerification { + id + icon + description } } ` diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 147e93c6f..75342ef2a 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -405,6 +405,22 @@ export const currentUserQuery = gql` query { currentUser { ...user + badgeTrophiesSelected { + id + icon + description + isDefault + } + badgeTrophiesUnused { + id + icon + description + } + badgeVerification { + id + icon + description + } email role about @@ -466,3 +482,43 @@ export const userDataQuery = (i18n) => { } ` } + +export const setTrophyBadgeSelected = gql` + mutation ($slot: Int!, $badgeId: ID) { + setTrophyBadgeSelected(slot: $slot, badgeId: $badgeId) { + badgeTrophiesCount + badgeTrophiesSelected { + id + icon + description + isDefault + } + badgeTrophiesUnused { + id + icon + description + } + badgeTrophiesUnusedCount + } + } +` + +export const resetTrophyBadgesSelected = gql` + mutation { + resetTrophyBadgesSelected { + badgeTrophiesCount + badgeTrophiesSelected { + id + icon + description + isDefault + } + badgeTrophiesUnused { + id + icon + description + } + badgeTrophiesUnusedCount + } + } +` diff --git a/webapp/locales/de.json b/webapp/locales/de.json index ce122672d..4fe39ce24 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -957,6 +957,16 @@ "title": "Suchergebnisse" }, "settings": { + "badges": { + "click-to-select": "Klicke auf einen freien Platz, um eine Badge hinzufügen.", + "click-to-use": "Klicke auf eine Badge, um sie zu platzieren.", + "description": "Hier hast du die Möglichkeit zu entscheiden, wie deine bereits erworbenen Badges in deinem Profil gezeigt werden sollen.", + "name": "Badges", + "no-badges-available": "Im Moment stehen dir keine Badges zur Verfügung, die du hinzufügen könntest.", + "remove": "Badge entfernen", + "success-update": "Deine Badges wurden erfolgreich gespeichert.", + "verification": "Dies ist deine Verifikations-Badge und kann nicht geändert werden." + }, "blocked-users": { "block": "Nutzer blockieren", "columns": { diff --git a/webapp/locales/en.json b/webapp/locales/en.json index f178da549..bdd9cdefb 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -957,6 +957,16 @@ "title": "Search Results" }, "settings": { + "badges": { + "click-to-select": "Click on an empty space to add a badge.", + "click-to-use": "Click on a badge to use it in the selected slot.", + "description": "Here you can choose how to display your earned badges on your profile.", + "name": "Badges", + "no-badges-available": "You currently don't have any badges available to add.", + "remove": "Remove Badge", + "success-update": "Your badges have been updated successfully.", + "verification": "This is your verification badge and cannot be changed." + }, "blocked-users": { "block": "Block user", "columns": { diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 31f2cc5f4..60e65ca20 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": "Bloquear usuario", "columns": { diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index 4bbca2b82..e7b4fcd4a 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": "Bloquer l'utilisateur", "columns": { diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 21bfaa859..703b27fff 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": null, "columns": { diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index f67518c21..f6a53d0c8 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": null, "columns": { diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index 4c6a96a5f..4e928f417 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": null, "columns": { diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 7d5ad52c1..005eb77f4 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": "Bloquear usuário", "columns": { diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index 3a394d6ff..bc862bcc2 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -957,6 +957,16 @@ "title": null }, "settings": { + "badges": { + "click-to-select": null, + "click-to-use": null, + "description": null, + "name": null, + "no-badges-available": null, + "remove": null, + "success-update": null, + "verification": null + }, "blocked-users": { "block": "Блокировать", "columns": { diff --git a/webapp/package.json b/webapp/package.json index d18a88408..d11ffd4ab 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -79,6 +79,7 @@ "@storybook/addon-actions": "^5.3.21", "@storybook/addon-notes": "^5.3.18", "@storybook/vue": "~7.4.0", + "@testing-library/jest-dom": "^6.6.3", "@testing-library/vue": "5", "@vue/cli-shared-utils": "~4.3.1", "@vue/eslint-config-prettier": "~6.0.0", diff --git a/webapp/pages/__snapshots__/settings.spec.js.snap b/webapp/pages/__snapshots__/settings.spec.js.snap new file mode 100644 index 000000000..6672e41af --- /dev/null +++ b/webapp/pages/__snapshots__/settings.spec.js.snap @@ -0,0 +1,427 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`settings.vue given badges are disabled renders 1`] = ` +
+
+
+

+ +

+
+ +
+ +
+ + +
+ + + +
+
+
+
+`; + +exports[`settings.vue given badges are enabled renders 1`] = ` +
+
+
+

+ +

+
+ +
+ +
+ + +
+ + + +
+
+
+
+`; diff --git a/webapp/pages/admin/users/__snapshots__/index.spec.js.snap b/webapp/pages/admin/users/__snapshots__/index.spec.js.snap new file mode 100644 index 000000000..b72a2617f --- /dev/null +++ b/webapp/pages/admin/users/__snapshots__/index.spec.js.snap @@ -0,0 +1,847 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Users given badges are disabled renders 1`] = ` +
+
+

+ admin.users.name +

+ +
+
+
+
+ +
+
+ + + + + +
+ + +
+ + + +
+
+ +
+ +
+
+
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + admin.users.table.columns.number + + + + admin.users.table.columns.name + + + + admin.users.table.columns.email + + + + admin.users.table.columns.slug + + + + admin.users.table.columns.createdAt + + + + 🖉 + + + + 🗨 + + + + ❤ + + + + admin.users.table.columns.role + +
+ NaN. + + + + User + + + + + + user@example.org + + + + + + user + + + + + + + + + + + + + + + + + + + +
+ NaN. + + + + User + + + + + + user2@example.org + + + + + + user + + + + + + + + + + + + + + + + + + + +
+
+ +
+ + + + + +
+ + +
+
+`; + +exports[`Users given badges are enabled renders 1`] = ` +
+
+

+ admin.users.name +

+ +
+
+
+
+ +
+
+ + + + + +
+ + +
+ + + +
+
+ +
+ +
+
+
+ + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + admin.users.table.columns.number + + + + admin.users.table.columns.name + + + + admin.users.table.columns.email + + + + admin.users.table.columns.slug + + + + admin.users.table.columns.createdAt + + + + 🖉 + + + + 🗨 + + + + ❤ + + + + admin.users.table.columns.role + + + + admin.users.table.columns.badges + +
+ NaN. + + + + User + + + + + + user@example.org + + + + + + user + + + + + + + + + + + + + + + + + + + + + + + admin.users.table.edit + + +
+ NaN. + + + + User + + + + + + user2@example.org + + + + + + user + + + + + + + + + + + + + + + + + + + + + + + admin.users.table.edit + + +
+
+ +
+ + + + + +
+ + +
+
+`; diff --git a/webapp/pages/admin/users/index.spec.js b/webapp/pages/admin/users/index.spec.js index 8d6b923c5..85e8789b8 100644 --- a/webapp/pages/admin/users/index.spec.js +++ b/webapp/pages/admin/users/index.spec.js @@ -10,11 +10,9 @@ const stubs = { describe('Users', () => { let wrapper - let Wrapper - let getters const mocks = { - $t: jest.fn(), + $t: jest.fn((t) => t), $apollo: { loading: false, mutate: jest @@ -38,116 +36,154 @@ describe('Users', () => { }, } - describe('mount', () => { - getters = { - 'auth/isAdmin': () => true, - 'auth/user': () => { - return { id: 'admin' } - }, - } + const getters = { + 'auth/isAdmin': () => true, + 'auth/user': () => { + return { id: 'admin' } + }, + } - Wrapper = () => { - const store = new Vuex.Store({ getters }) - return mount(Users, { - mocks, - localVue, - store, - stubs, - }) - } + const Wrapper = () => { + const store = new Vuex.Store({ getters }) + return mount(Users, { + mocks, + localVue, + store, + stubs, + data: () => ({ + User: [ + { + id: 'user', + email: 'user@example.org', + name: 'User', + role: 'moderator', + slug: 'user', + }, + { + id: 'user2', + email: 'user2@example.org', + name: 'User', + role: 'moderator', + slug: 'user', + }, + ], + }), + }) + } + + describe('given badges are enabled', () => { + beforeEach(() => { + mocks.$env = { + BADGES_ENABLED: true, + } + wrapper = Wrapper() + }) it('renders', () => { + expect(wrapper.element).toMatchSnapshot() + }) + }) + + describe('given badges are disabled', () => { + beforeEach(() => { + mocks.$env = { + BADGES_ENABLED: false, + } wrapper = Wrapper() - expect(wrapper.element.tagName).toBe('DIV') }) - describe('search', () => { - let searchAction - beforeEach(() => { - searchAction = (wrapper, { query }) => { - wrapper.find('input').setValue(query) - wrapper.find('form').trigger('submit') - return wrapper - } + it('renders', () => { + expect(wrapper.element).toMatchSnapshot() + }) + }) + + describe('search', () => { + let searchAction + beforeEach(() => { + wrapper = Wrapper() + searchAction = (wrapper, { query }) => { + wrapper.find('input').setValue(query) + wrapper.find('form').trigger('submit') + return wrapper + } + }) + + describe('query looks like an email address', () => { + it('searches users for exact email address', async () => { + const wrapper = await searchAction(Wrapper(), { query: 'email@example.org' }) + expect(wrapper.vm.email).toEqual('email@example.org') + expect(wrapper.vm.filter).toBe(null) }) - describe('query looks like an email address', () => { - it('searches users for exact email address', async () => { - const wrapper = await searchAction(Wrapper(), { query: 'email@example.org' }) - expect(wrapper.vm.email).toEqual('email@example.org') - expect(wrapper.vm.filter).toBe(null) - }) - - it('email address is case-insensitive', async () => { - const wrapper = await searchAction(Wrapper(), { query: 'eMaiL@example.org' }) - expect(wrapper.vm.email).toEqual('email@example.org') - expect(wrapper.vm.filter).toBe(null) - }) - }) - - describe('query is just text', () => { - it('tries to find matching users by `name`, `slug` or `about`', async () => { - const wrapper = await searchAction(await Wrapper(), { query: 'Find me' }) - const expected = { - OR: [ - { name_contains: 'Find me' }, - { slug_contains: 'Find me' }, - { about_contains: 'Find me' }, - ], - } - expect(wrapper.vm.email).toBe(null) - expect(wrapper.vm.filter).toEqual(expected) - }) + it('email address is case-insensitive', async () => { + const wrapper = await searchAction(Wrapper(), { query: 'eMaiL@example.org' }) + expect(wrapper.vm.email).toEqual('email@example.org') + expect(wrapper.vm.filter).toBe(null) }) }) - describe('change roles', () => { - beforeAll(() => { - wrapper = Wrapper() - wrapper.setData({ - User: [ - { - id: 'admin', - email: 'admin@example.org', - name: 'Admin', - role: 'admin', - slug: 'admin', - }, - { - id: 'user', - email: 'user@example.org', - name: 'User', - role: 'user', - slug: 'user', - }, + describe('query is just text', () => { + it('tries to find matching users by `name`, `slug` or `about`', async () => { + const wrapper = await searchAction(await Wrapper(), { query: 'Find me' }) + const expected = { + OR: [ + { name_contains: 'Find me' }, + { slug_contains: 'Find me' }, + { about_contains: 'Find me' }, ], - userRoles: ['user', 'moderator', 'admin'], - }) - }) - - it('cannot change own role', () => { - const adminRow = wrapper.findAll('tr').at(1) - expect(adminRow.find('select').exists()).toBe(false) - }) - - it('changes the role of another user', () => { - const userRow = wrapper.findAll('tr').at(2) - userRow.findAll('option').at(1).setSelected() - expect(mocks.$apollo.mutate).toHaveBeenCalledWith( - expect.objectContaining({ - variables: { - id: 'user', - role: 'moderator', - }, - }), - ) - }) - - it('toasts a success message after role has changed', () => { - const userRow = wrapper.findAll('tr').at(2) - userRow.findAll('option').at(1).setSelected() - expect(mocks.$toast.success).toHaveBeenCalled() + } + expect(wrapper.vm.email).toBe(null) + expect(wrapper.vm.filter).toEqual(expected) }) }) }) + + describe('change roles', () => { + beforeAll(() => { + wrapper = Wrapper() + wrapper.setData({ + User: [ + { + id: 'admin', + email: 'admin@example.org', + name: 'Admin', + role: 'admin', + slug: 'admin', + }, + { + id: 'user', + email: 'user@example.org', + name: 'User', + role: 'user', + slug: 'user', + }, + ], + userRoles: ['user', 'moderator', 'admin'], + }) + }) + + it('cannot change own role', () => { + const adminRow = wrapper.findAll('tr').at(1) + expect(adminRow.find('select').exists()).toBe(false) + }) + + it('changes the role of another user', () => { + const userRow = wrapper.findAll('tr').at(2) + userRow.findAll('option').at(1).setSelected() + expect(mocks.$apollo.mutate).toHaveBeenCalledWith( + expect.objectContaining({ + variables: { + id: 'user', + role: 'moderator', + }, + }), + ) + }) + + it('toasts a success message after role has changed', () => { + const userRow = wrapper.findAll('tr').at(2) + userRow.findAll('option').at(1).setSelected() + expect(mocks.$toast.success).toHaveBeenCalled() + }) + }) }) diff --git a/webapp/pages/admin/users/index.vue b/webapp/pages/admin/users/index.vue index 24258a57f..fd08f1e0c 100644 --- a/webapp/pages/admin/users/index.vue +++ b/webapp/pages/admin/users/index.vue @@ -120,7 +120,7 @@ export default { currentUser: 'auth/user', }), fields() { - return { + const fields = { index: this.$t('admin.users.table.columns.number'), name: this.$t('admin.users.table.columns.name'), email: this.$t('admin.users.table.columns.email'), @@ -142,11 +142,16 @@ export default { label: this.$t('admin.users.table.columns.role'), align: 'right', }, - badges: { + } + + if (this.$env.BADGES_ENABLED) { + fields.badges = { label: this.$t('admin.users.table.columns.badges'), align: 'right', - }, + } } + + return fields }, }, apollo: { diff --git a/webapp/pages/profile/_id/__snapshots__/_slug.spec.js.snap b/webapp/pages/profile/_id/__snapshots__/_slug.spec.js.snap new file mode 100644 index 000000000..9eec4e96a --- /dev/null +++ b/webapp/pages/profile/_id/__snapshots__/_slug.spec.js.snap @@ -0,0 +1,2442 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ProfileSlug given an authenticated user given another profile user and badges are disabled renders 1`] = ` +
+
+
+ +
+
+
+
+ + BTB + + + + + +
+ + + + + +
+
+ +
+
+
+
+ +
+

+ + Bob the builder + +

+ +

+ + @undefined + +

+ + + +

+ + profile.memberSince + +

+
+ + + +
+
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.followers + +

+
+
+
+ +
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.following + +

+
+
+
+
+ +
+ + + + + + + +
+ + + + +
+ +
+ +

+ + profile.network.title + +

+ + + +
+ + + + +
+ +
+ + + +
+ + + + + + + +
+
+
+
+
+
+`; + +exports[`ProfileSlug given an authenticated user given another profile user and badges are enabled renders 1`] = ` +
+
+
+ +
+
+
+
+ + BTB + + + + + +
+ + + + + +
+
+ +
+
+
+
+ +
+

+ + Bob the builder + +

+ +

+ + @undefined + +

+ + + +

+ + profile.memberSince + +

+
+ +
+ + +
+
+ +
+
+ +
+
+ +
+
+
+ +
+
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.followers + +

+
+
+
+ +
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.following + +

+
+
+
+
+ +
+ + + + + + + +
+ + + + +
+ +
+ +

+ + profile.network.title + +

+ + + +
+ + + + +
+ +
+ + + +
+ + + + + + + +
+
+
+
+
+
+`; + +exports[`ProfileSlug given an authenticated user given the logged in user as profile user and badges are disabled renders 1`] = ` +
+
+
+ +
+
+
+
+
+
+
+
+ + BTB + + + + + +
+ +
+
+ +
+
+
+
+
+
+ + + + + +
+
+ +
+
+
+
+ +
+

+ + Bob the builder + +

+ +

+ + @undefined + +

+ + + +

+ + profile.memberSince + +

+
+ + + +
+
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.followers + +

+
+
+
+ +
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.following + +

+
+
+
+
+ + + + + + +
+ +
+ +

+ + profile.network.title + +

+ + + +
+ + + + +
+ +
+ + + +
+ + + + + + + +
+
+
+
+
+
+`; + +exports[`ProfileSlug given an authenticated user given the logged in user as profile user and badges are enabled renders 1`] = ` +
+
+
+ +
+
+
+
+
+
+
+
+ + BTB + + + + + +
+ +
+
+ +
+
+
+
+
+
+ + + + + +
+
+ +
+
+
+
+ +
+

+ + Bob the builder + +

+ +

+ + @undefined + +

+ + + +

+ + profile.memberSince + +

+
+ + + +
+
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.followers + +

+
+
+
+ +
+ +
+

+ + + + + 0 + + + + +

+

+ + profile.following + +

+
+
+
+
+ + + + + + +
+ +
+ +

+ + profile.network.title + +

+ + + +
+ + + + +
+ +
+ + + +
+ + + + + + + +
+
+
+
+
+
+`; diff --git a/webapp/pages/profile/_id/_slug.spec.js b/webapp/pages/profile/_id/_slug.spec.js index 5ab87ad3a..a4cc473c3 100644 --- a/webapp/pages/profile/_id/_slug.spec.js +++ b/webapp/pages/profile/_id/_slug.spec.js @@ -1,22 +1,25 @@ -import { mount } from '@vue/test-utils' +import { render } from '@testing-library/vue' import ProfileSlug from './_slug.vue' const localVue = global.localVue localVue.filter('date', (d) => d) +// Mock Math.random, used in Dropdown +Object.assign(Math, { + random: () => 0, +}) + const stubs = { 'client-only': true, 'v-popover': true, 'nuxt-link': true, - 'infinite-loading': true, 'follow-list': true, 'router-link': true, } describe('ProfileSlug', () => { let wrapper - let Wrapper let mocks beforeEach(() => { @@ -25,7 +28,7 @@ describe('ProfileSlug', () => { id: 'p23', name: 'It is a post', }, - $t: jest.fn(), + $t: jest.fn((t) => t), // If you're mocking router, then don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html $route: { params: { @@ -49,49 +52,144 @@ describe('ProfileSlug', () => { } }) - describe('mount', () => { - Wrapper = () => { - return mount(ProfileSlug, { - mocks, - localVue, - stubs, - }) - } + const Wrapper = (badgesEnabled, data) => { + return render(ProfileSlug, { + localVue, + stubs, + data: () => data, + mocks: { + ...mocks, + $env: { + BADGES_ENABLED: badgesEnabled, + }, + }, + }) + } - describe('given an authenticated user', () => { - beforeEach(() => { - mocks.$filters = { - removeLinks: (c) => c, - truncate: (a) => a, - } - mocks.$store = { - getters: { - 'auth/isModerator': () => false, - 'auth/user': { - id: 'u23', - }, + describe('given an authenticated user', () => { + beforeEach(() => { + mocks.$filters = { + removeLinks: (c) => c, + truncate: (a) => a, + } + mocks.$store = { + getters: { + 'auth/isModerator': () => false, + 'auth/user': { + id: 'u23', }, - } - }) + }, + } + }) - describe('given a user for the profile', () => { - beforeEach(() => { - wrapper = Wrapper() - wrapper.setData({ - User: [ + describe('given another profile user', () => { + const user = { + User: [ + { + id: 'u3', + name: 'Bob the builder', + contributionsCount: 6, + shoutedCount: 7, + commentedCount: 8, + badgeVerification: { + id: 'bv1', + icon: '/path/to/icon-bv1', + description: 'verified', + isDefault: false, + }, + badgeTrophiesSelected: [ { - id: 'u3', - name: 'Bob the builder', - contributionsCount: 6, - shoutedCount: 7, - commentedCount: 8, + id: 'bt1', + icon: '/path/to/icon-bt1', + description: 'a trophy', + isDefault: false, + }, + { + id: 'bt2', + icon: '/path/to/icon-bt2', + description: 'no trophy', + isDefault: true, }, ], - }) + }, + ], + } + + describe('and badges are enabled', () => { + beforeEach(() => { + wrapper = Wrapper(true, user) }) - it('displays name of the user', () => { - expect(wrapper.text()).toContain('Bob the builder') + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('and badges are disabled', () => { + beforeEach(() => { + wrapper = Wrapper(false, user) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + }) + + describe('given the logged in user as profile user', () => { + beforeEach(() => { + mocks.$route.params.id = 'u23' + }) + + const user = { + User: [ + { + id: 'u23', + name: 'Bob the builder', + contributionsCount: 6, + shoutedCount: 7, + commentedCount: 8, + badgeVerification: { + id: 'bv1', + icon: '/path/to/icon-bv1', + description: 'verified', + isDefault: false, + }, + badgeTrophiesSelected: [ + { + id: 'bt1', + icon: '/path/to/icon-bt1', + description: 'a trophy', + isDefault: false, + }, + { + id: 'bt2', + icon: '/path/to/icon-bt2', + description: 'no trophy', + isDefault: true, + }, + ], + }, + ], + } + + describe('and badges are enabled', () => { + beforeEach(() => { + wrapper = Wrapper(true, user) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('and badges are disabled', () => { + beforeEach(() => { + wrapper = Wrapper(false, user) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() }) }) }) diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue index 382350faf..38035e217 100644 --- a/webapp/pages/profile/_id/_slug.vue +++ b/webapp/pages/profile/_id/_slug.vue @@ -42,8 +42,11 @@ {{ $t('profile.memberSince') }} {{ user.createdAt | date('MMMM yyyy') }} - - + + + + + @@ -266,6 +269,10 @@ export default { user() { return this.User ? this.User[0] : {} }, + userBadges() { + if (!this.$env.BADGES_ENABLED) return null + return [this.user.badgeVerification, ...(this.user.badgeTrophiesSelected || [])] + }, userName() { const { name } = this.user || {} return name || this.$t('profile.userAnonym') @@ -456,6 +463,12 @@ export default { margin: auto; margin-top: -60px; } +.badge-edit-link { + transition: all 0.2s ease-out; + &:hover { + opacity: 0.7; + } +} .page-name-profile-id-slug { .ds-flex-item:first-child .content-menu { position: absolute; diff --git a/webapp/pages/settings.spec.js b/webapp/pages/settings.spec.js index 0f3c6e22c..8c2917c90 100644 --- a/webapp/pages/settings.spec.js +++ b/webapp/pages/settings.spec.js @@ -1,4 +1,4 @@ -import { mount } from '@vue/test-utils' +import { render } from '@testing-library/vue' import settings from './settings.vue' const localVue = global.localVue @@ -17,21 +17,37 @@ describe('settings.vue', () => { } }) - describe('mount', () => { - const Wrapper = () => { - return mount(settings, { - mocks, - localVue, - stubs, - }) - } + const Wrapper = () => { + return render(settings, { + mocks, + localVue, + stubs, + }) + } + describe('given badges are enabled', () => { beforeEach(() => { + mocks.$env = { + BADGES_ENABLED: true, + } wrapper = Wrapper() }) it('renders', () => { - expect(wrapper.element.tagName).toBe('DIV') + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('given badges are disabled', () => { + beforeEach(() => { + mocks.$env = { + BADGES_ENABLED: false, + } + wrapper = Wrapper() + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() }) }) }) diff --git a/webapp/pages/settings.vue b/webapp/pages/settings.vue index 5d526c3cc..e1181650e 100644 --- a/webapp/pages/settings.vue +++ b/webapp/pages/settings.vue @@ -21,7 +21,7 @@ export default { computed: { routes() { - return [ + const routes = [ { name: this.$t('settings.data.name'), path: `/settings`, @@ -83,6 +83,15 @@ export default { }, } */ ] + + if (this.$env.BADGES_ENABLED) { + routes.splice(2, 0, { + name: this.$t('settings.badges.name'), + path: `/settings/badges`, + }) + } + + return routes }, }, } diff --git a/webapp/pages/settings/__snapshots__/badges.spec.js.snap b/webapp/pages/settings/__snapshots__/badges.spec.js.snap new file mode 100644 index 000000000..358327202 --- /dev/null +++ b/webapp/pages/settings/__snapshots__/badges.spec.js.snap @@ -0,0 +1,429 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`badge settings with badges more badges available selecting an empty slot shows list with available badges 1`] = ` +
+
+

+ settings.badges.name +

+ +

+ settings.badges.description +

+ +
+
+
+ + + + +
+
+ + + +
+ + + settings.badges.click-to-use + + +
+ + + +
+
+ + +
+
+
+ + +
+
+`; + +exports[`badge settings with badges no more badges available selecting an empty slot shows no more badges available message 1`] = ` +
+
+

+ settings.badges.name +

+ +

+ settings.badges.description +

+ +
+
+
+ + + + +
+
+ +

+ + settings.badges.no-badges-available + +

+ + + + + + +
+ + +
+
+`; + +exports[`badge settings with badges renders 1`] = ` +
+
+

+ settings.badges.name +

+ +

+ settings.badges.description +

+ +
+
+
+ + + + +
+
+ + + +
+ + + settings.badges.click-to-select + + +
+ + + + +
+ + +
+
+`; + +exports[`badge settings with badges selecting a used badge clicking remove badge button with successful server request removes the badge 1`] = ` +
+
+

+ settings.badges.name +

+ +

+ settings.badges.description +

+ +
+
+
+ + + + +
+
+ + + + + + +
+ + +
+
+`; + +exports[`badge settings without badges renders 1`] = ` +
+
+

+ settings.badges.name +

+ +

+ settings.badges.description +

+ +
+
+
+ +
+
+ + + + + + + + +
+ + +
+
+`; diff --git a/webapp/pages/settings/badges.spec.js b/webapp/pages/settings/badges.spec.js new file mode 100644 index 000000000..291fd75d6 --- /dev/null +++ b/webapp/pages/settings/badges.spec.js @@ -0,0 +1,302 @@ +import { render, screen, fireEvent } from '@testing-library/vue' +import '@testing-library/jest-dom' +import badges from './badges.vue' + +const localVue = global.localVue + +describe('badge settings', () => { + let mocks + + const apolloMutateMock = jest.fn() + + const Wrapper = () => { + return render(badges, { + localVue, + mocks, + }) + } + + beforeEach(() => { + mocks = { + $t: jest.fn((t) => t), + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + $apollo: { + mutate: apolloMutateMock, + }, + } + }) + + describe('without badges', () => { + beforeEach(() => { + mocks.$store = { + getters: { + 'auth/isModerator': () => false, + 'auth/user': { + id: 'u23', + badgeVerification: { + id: 'bv1', + icon: '/verification/icon', + description: 'Verification description', + isDefault: true, + }, + badgeTrophiesSelected: [], + badgeTrophiesUnused: [], + }, + }, + } + }) + + it('renders', () => { + const wrapper = Wrapper() + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('with badges', () => { + const badgeTrophiesSelected = [ + { + id: '1', + icon: '/path/to/some/icon', + isDefault: false, + description: 'Some description', + }, + { + id: '2', + icon: '/path/to/empty/icon', + isDefault: true, + description: 'Empty', + }, + { + id: '3', + icon: '/path/to/third/icon', + isDefault: false, + description: 'Third description', + }, + ] + + const badgeTrophiesUnused = [ + { + id: '4', + icon: '/path/to/fourth/icon', + description: 'Fourth description', + }, + { + id: '5', + icon: '/path/to/fifth/icon', + description: 'Fifth description', + }, + ] + + let wrapper + + beforeEach(() => { + mocks.$store = { + getters: { + 'auth/isModerator': () => false, + 'auth/user': { + id: 'u23', + badgeVerification: { + id: 'bv1', + icon: '/verification/icon', + description: 'Verification description', + isDefault: false, + }, + badgeTrophiesSelected, + badgeTrophiesUnused, + }, + }, + } + wrapper = Wrapper() + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + describe('selecting a used badge', () => { + beforeEach(async () => { + const badge = screen.getByTitle(badgeTrophiesSelected[0].description) + await fireEvent.click(badge) + }) + + it('shows remove badge button', () => { + expect(screen.getByText('settings.badges.remove')).toBeInTheDocument() + }) + + describe('clicking remove badge button', () => { + const clickButton = async () => { + const removeButton = screen.getByText('settings.badges.remove') + await fireEvent.click(removeButton) + } + + describe('with successful server request', () => { + beforeEach(() => { + apolloMutateMock.mockResolvedValue({ + data: { + setTrophyBadgeSelected: { + id: 'u23', + badgeTrophiesSelected: [ + { + id: '2', + icon: '/path/to/empty/icon', + isDefault: true, + description: 'Empty', + }, + { + id: '2', + icon: '/path/to/empty/icon', + isDefault: true, + description: 'Empty', + }, + { + id: '3', + icon: '/path/to/third/icon', + isDefault: false, + description: 'Third description', + }, + ], + }, + }, + }) + clickButton() + }) + + it('calls the server', () => { + expect(apolloMutateMock).toHaveBeenCalledWith({ + mutation: expect.anything(), + update: expect.anything(), + variables: { + badgeId: null, + slot: 0, + }, + }) + }) + + /* To test this, we would need a better apollo mock */ + it.skip('removes the badge', async () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('shows a success message', () => { + expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update') + }) + }) + + describe('with failed server request', () => { + beforeEach(() => { + apolloMutateMock.mockRejectedValue({ message: 'Ouch!' }) + clickButton() + }) + + it('shows an error message', () => { + expect(mocks.$toast.error).toHaveBeenCalledWith('settings.badges.error-update') + }) + }) + }) + }) + + describe('no more badges available', () => { + beforeEach(async () => { + mocks.$store.getters['auth/user'].badgeTrophiesUnused = [] + }) + + describe('selecting an empty slot', () => { + beforeEach(async () => { + const emptySlot = screen.getAllByTitle('Empty')[0] + await fireEvent.click(emptySlot) + }) + + it('shows no more badges available message', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + }) + + describe('more badges available', () => { + describe('selecting an empty slot', () => { + beforeEach(async () => { + const emptySlot = screen.getAllByTitle('Empty')[0] + await fireEvent.click(emptySlot) + }) + + it('shows list with available badges', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + describe('clicking on an available badge', () => { + const clickBadge = async () => { + const badge = screen.getByText(badgeTrophiesUnused[0].description) + await fireEvent.click(badge) + } + + describe('with successful server request', () => { + beforeEach(() => { + apolloMutateMock.mockResolvedValue({ + data: { + setTrophyBadgeSelected: { + id: 'u23', + badgeTrophiesSelected: [ + { + id: '4', + icon: '/path/to/fourth/icon', + description: 'Fourth description', + isDefault: false, + }, + { + id: '2', + icon: '/path/to/empty/icon', + isDefault: true, + description: 'Empty', + }, + { + id: '3', + icon: '/path/to/third/icon', + isDefault: false, + description: 'Third description', + }, + ], + }, + }, + }) + clickBadge() + }) + + it('calls the server', () => { + expect(apolloMutateMock).toHaveBeenCalledWith({ + mutation: expect.anything(), + update: expect.anything(), + variables: { + badgeId: '4', + slot: 1, + }, + }) + }) + + /* To test this, we would need a better apollo mock */ + it.skip('adds the badge', async () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('shows a success message', () => { + expect(mocks.$toast.success).toHaveBeenCalledWith('settings.badges.success-update') + }) + }) + + describe('with failed server request', () => { + beforeEach(() => { + apolloMutateMock.mockRejectedValue({ message: 'Ouch!' }) + clickBadge() + }) + + it('shows an error message', () => { + expect(mocks.$toast.error).toHaveBeenCalledWith('settings.badges.error-update') + }) + }) + }) + }) + }) + }) +}) diff --git a/webapp/pages/settings/badges.vue b/webapp/pages/settings/badges.vue new file mode 100644 index 000000000..3f0e7c7e7 --- /dev/null +++ b/webapp/pages/settings/badges.vue @@ -0,0 +1,176 @@ + + + + + diff --git a/webapp/static/img/badges/stars.svg b/webapp/static/img/badges/stars.svg new file mode 100644 index 000000000..44d64a5f4 --- /dev/null +++ b/webapp/static/img/badges/stars.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 1ef19363e..e17834008 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -7,6 +7,11 @@ resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== +"@adobe/css-tools@^4.4.0": + version "4.4.2" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.4.2.tgz#c836b1bd81e6d62cd6cdf3ee4948bcdce8ea79c8" + integrity sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A== + "@ampproject/remapping@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" @@ -4253,6 +4258,19 @@ lz-string "^1.5.0" pretty-format "^27.0.2" +"@testing-library/jest-dom@^6.6.3": + version "6.6.3" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz#26ba906cf928c0f8172e182c6fe214eb4f9f2bd2" + integrity sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA== + dependencies: + "@adobe/css-tools" "^4.4.0" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.6.3" + lodash "^4.17.21" + redent "^3.0.0" + "@testing-library/vue@5": version "5.9.0" resolved "https://registry.yarnpkg.com/@testing-library/vue/-/vue-5.9.0.tgz#d33c52ae89e076808abe622f70dcbccb1b5d080c" @@ -6058,6 +6076,11 @@ aria-query@5.1.3: dependencies: deep-equal "^2.0.5" +aria-query@^5.0.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.2.tgz#93f81a43480e33a338f19163a3d10a50c01dcd59" + integrity sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw== + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -8230,6 +8253,11 @@ css-what@2.1, css-what@^2.1.2: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + csscolorparser@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/csscolorparser/-/csscolorparser-1.0.3.tgz#b34f391eea4da8f3e98231e2ccd8df9c041f171b" @@ -8793,6 +8821,11 @@ dom-accessibility-api@^0.5.9: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== +dom-accessibility-api@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz#993e925cc1d73f2c662e7d75dd5a5445259a8fd8" + integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w== + dom-converter@^0.2: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" From 2f808f8fcc01ef2e547d20e54cc37951734660c0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 25 Apr 2025 19:45:56 +0200 Subject: [PATCH 159/266] revokeBadge also removes selection (#8437) --- backend/src/schema/resolvers/badges.spec.ts | 220 +++++++++++++++++++- backend/src/schema/resolvers/badges.ts | 6 +- 2 files changed, 221 insertions(+), 5 deletions(-) diff --git a/backend/src/schema/resolvers/badges.spec.ts b/backend/src/schema/resolvers/badges.spec.ts index ae2fe0b0d..e6b5173a9 100644 --- a/backend/src/schema/resolvers/badges.spec.ts +++ b/backend/src/schema/resolvers/badges.spec.ts @@ -524,25 +524,30 @@ describe('Badges', () => { beforeEach(async () => { await regularUser.relateTo(badge, 'rewarded') await regularUser.relateTo(verification, 'verifies') + await regularUser.relateTo(badge, 'selected', { slot: 6 }) }) const revokeBadgeMutation = gql` mutation ($badgeId: ID!, $userId: ID!) { revokeBadge(badgeId: $badgeId, userId: $userId) { id + badgeTrophies { + id + } badgeVerification { id isDefault } - badgeTrophies { + badgeTrophiesSelected { id + isDefault } } } ` describe('check test setup', () => { - it('user has one badge', async () => { + it('user has one badge and has it selected', async () => { authenticatedUser = regularUser.toJson() const userQuery = gql` { @@ -551,11 +556,68 @@ describe('Badges', () => { badgeTrophies { id } + badgeVerification { + id + isDefault + } + badgeTrophiesSelected { + id + isDefault + } } } ` const expected = { - data: { User: [{ badgeTrophiesCount: 1, badgeTrophies: [{ id: 'trophy_rhino' }] }] }, + data: { + User: [ + { + badgeTrophiesCount: 1, + badgeTrophies: [{ id: 'trophy_rhino' }], + badgeVerification: { + id: 'verification_moderator', + isDefault: false, + }, + badgeTrophiesSelected: [ + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'trophy_rhino', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], + }, + ], + }, errors: undefined, } await expect(query({ query: userQuery })).resolves.toMatchObject(expected) @@ -601,6 +663,44 @@ describe('Badges', () => { id: 'regular-user-id', badgeVerification: { id: 'verification_moderator', isDefault: false }, badgeTrophies: [], + badgeTrophiesSelected: [ + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], }, }, errors: undefined, @@ -615,6 +715,44 @@ describe('Badges', () => { id: 'regular-user-id', badgeVerification: { id: 'verification_moderator', isDefault: false }, badgeTrophies: [], + badgeTrophiesSelected: [ + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], }, }, errors: undefined, @@ -636,6 +774,44 @@ describe('Badges', () => { id: 'regular-user-id', badgeVerification: { id: 'default_verification', isDefault: true }, badgeTrophies: [{ id: 'trophy_rhino' }], + badgeTrophiesSelected: [ + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'trophy_rhino', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], }, }, errors: undefined, @@ -664,6 +840,44 @@ describe('Badges', () => { id: 'regular-user-id', badgeVerification: { id: 'default_verification', isDefault: true }, badgeTrophies: [{ id: 'trophy_rhino' }], + badgeTrophiesSelected: [ + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'trophy_rhino', + isDefault: false, + }, + { + id: 'default_trophy', + isDefault: true, + }, + { + id: 'default_trophy', + isDefault: true, + }, + ], }, }, errors: undefined, diff --git a/backend/src/schema/resolvers/badges.ts b/backend/src/schema/resolvers/badges.ts index 587204b54..7c107e42c 100644 --- a/backend/src/schema/resolvers/badges.ts +++ b/backend/src/schema/resolvers/badges.ts @@ -119,8 +119,10 @@ export default { const response = await transaction.run( ` MATCH (user:User {id: $userId}) - OPTIONAL MATCH (badge:Badge {id: $badgeId})-[relation:REWARDED|VERIFIES]->(user) - DELETE relation + OPTIONAL MATCH (badge:Badge {id: $badgeId})-[rewarded:REWARDED|VERIFIES]->(user) + OPTIONAL MATCH (user)-[selected:SELECTED]->(badge) + DELETE rewarded + DELETE selected RETURN user {.*} `, { From 7482f8665e7da76a68ec0d34c7f39209a9179ca6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 01:35:02 +0200 Subject: [PATCH 160/266] build(deps-dev): bump eslint-config-prettier in /backend (#8370) Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 10.1.1 to 10.1.2. - [Release notes](https://github.com/prettier/eslint-config-prettier/releases) - [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-config-prettier/compare/v10.1.1...v10.1.2) --- updated-dependencies: - dependency-name: eslint-config-prettier dependency-version: 10.1.2 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index d904bbd20..baf0e13ce 100644 --- a/backend/package.json +++ b/backend/package.json @@ -110,7 +110,7 @@ "@typescript-eslint/parser": "^5.62.0", "apollo-server-testing": "~2.11.0", "eslint": "^8.57.1", - "eslint-config-prettier": "^10.1.1", + "eslint-config-prettier": "^10.1.2", "eslint-config-standard": "^17.1.0", "eslint-import-resolver-typescript": "^4.3.2", "eslint-plugin-import": "^2.31.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index c972f9925..46c6bb98d 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4586,10 +4586,10 @@ eslint-compat-utils@^0.5.1: dependencies: semver "^7.5.4" -eslint-config-prettier@^10.1.1: - version "10.1.1" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.1.tgz#cf0ff6e5c4e7e15f129f1f1ce2a5ecba92dec132" - integrity sha512-4EQQr6wXwS+ZJSzaR5ZCrYgLxqvUjdXctaEtBqHcbkW944B1NQyO4qpdHQbXBONfwxXdkAY81HH4+LUfrg+zPw== +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" + integrity sha512-Epgp/EofAUeEpIdZkW60MHKvPyru1ruQJxPL+WIycnaPApuseK0Zpkrh/FwL9oIpQvIhJwV7ptOy0DWUjTlCiA== eslint-config-standard@^17.1.0: version "17.1.0" From 91bdf3ca1ec044108ec78105bf71cac3a8c915a8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:52:14 +0200 Subject: [PATCH 161/266] build(deps-dev): bump eslint-import-resolver-typescript in /backend (#8445) Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 4.3.2 to 4.3.4. - [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases) - [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md) - [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v4.3.2...v4.3.4) --- updated-dependencies: - dependency-name: eslint-import-resolver-typescript dependency-version: 4.3.4 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 221 +++++++++++++++++++++++-------------------- 2 files changed, 118 insertions(+), 105 deletions(-) diff --git a/backend/package.json b/backend/package.json index baf0e13ce..39a0016bc 100644 --- a/backend/package.json +++ b/backend/package.json @@ -112,7 +112,7 @@ "eslint": "^8.57.1", "eslint-config-prettier": "^10.1.2", "eslint-config-standard": "^17.1.0", - "eslint-import-resolver-typescript": "^4.3.2", + "eslint-import-resolver-typescript": "^4.3.4", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", "eslint-plugin-n": "^17.17.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 46c6bb98d..f37877286 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1579,10 +1579,10 @@ url-regex "~4.1.1" video-extensions "~1.1.0" -"@napi-rs/wasm-runtime@^0.2.8": - version "0.2.8" - resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.8.tgz#642e8390ee78ed21d6b79c467aa610e249224ed6" - integrity sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg== +"@napi-rs/wasm-runtime@^0.2.9": + version "0.2.9" + resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz#7278122cf94f3b36d8170a8eee7d85356dfa6a96" + integrity sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg== dependencies: "@emnapi/core" "^1.4.0" "@emnapi/runtime" "^1.4.0" @@ -2294,87 +2294,92 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@unrs/resolver-binding-darwin-arm64@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.5.0.tgz#0c64ebe422a3d05ada91d8ba84e037383742c955" - integrity sha512-YmocNlEcX/AgJv8gI41bhjMOTcKcea4D2nRIbZj+MhRtSH5+vEU8r/pFuTuoF+JjVplLsBueU+CILfBPVISyGQ== +"@unrs/resolver-binding-darwin-arm64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.0.tgz#04fe2253f2b6366ae993b1565c6495e563ad8a4c" + integrity sha512-vIWAU56r2lZAmUsljp6m9+hrTlwNkZH6pqnSPff2WxzofV+jWRSHLmZRUS+g+VE+LlyPByifmGGHpJmhWetatg== -"@unrs/resolver-binding-darwin-x64@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.5.0.tgz#57210874eca22ec3a07039c97c028fb19c0c6d57" - integrity sha512-qpUrXgH4e/0xu1LOhPEdfgSY3vIXOxDQv370NEL8npN8h40HcQDA+Pl2r4HBW6tTXezWIjxUFcP7tj529RZtDw== +"@unrs/resolver-binding-darwin-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.0.tgz#8d74ee589f1c379b9b75880ea85871bdaf89766e" + integrity sha512-+bShFLgtdwuNteQbKq3X230754AouNMXSLDZ56EssgDyckDt6Ld7wRaJjZF0pY671HnY2pk9/amO4amAFzfN1A== -"@unrs/resolver-binding-freebsd-x64@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.5.0.tgz#4519371d0ad8e557a86623d8497e3abcdcb5ae43" - integrity sha512-3tX8r8vgjvZzaJZB4jvxUaaFCDCb3aWDCpZN3EjhGnnwhztslI05KSG5NY/jNjlcZ5QWZ7dEZZ/rNBFsmTaSPw== +"@unrs/resolver-binding-freebsd-x64@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.0.tgz#d0bcea8e240d54d048aa45a6c7bd7e4d4824abfb" + integrity sha512-HJjXb3aIptDZQ0saSmk2S4W1pWNVZ2iNpAbNGZOfsUXbi8xwCmHdVjErNS92hRp7djuDLup1OLrzOMtTdw5BmA== -"@unrs/resolver-binding-linux-arm-gnueabihf@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.5.0.tgz#4fc05aec9e65a6478003a0b9034a06ac0da886ab" - integrity sha512-FH+ixzBKaUU9fWOj3TYO+Yn/eO6kYvMLV9eNJlJlkU7OgrxkCmiMS6wUbyT0KA3FOZGxnEQ2z3/BHgYm2jqeLA== +"@unrs/resolver-binding-linux-arm-gnueabihf@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.0.tgz#ae56292948a47a876d894da740b8001a14c88bc3" + integrity sha512-NF3lk7KHulLD97UE+MHjH0mrOjeZG8Hz10h48YcFz2V0rlxBdRSRcMbGer8iH/1mIlLqxtvXJfGLUr4SMj0XZg== -"@unrs/resolver-binding-linux-arm-musleabihf@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.5.0.tgz#c24b35dd5818fcd25569425b1dc1a98a883e248b" - integrity sha512-pxCgXMgwB/4PfqFQg73lMhmWwcC0j5L+dNXhZoz/0ek0iS/oAWl65fxZeT/OnU7fVs52MgdP2q02EipqJJXHSg== +"@unrs/resolver-binding-linux-arm-musleabihf@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.0.tgz#4a32424660d2f0ed328297b24f46e64f4c2990d8" + integrity sha512-Gn1c/t24irDgU8yYj4vVG6qHplwUM42ti9/zYWgfmFjoXCH6L4Ab9hh6HuO7bfDSvGDRGWQt1IVaBpgbKHdh3Q== -"@unrs/resolver-binding-linux-arm64-gnu@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.5.0.tgz#07dc8478a0a356d343790208dc557d6d053689af" - integrity sha512-FX2FV7vpLE/+Z0NZX9/1pwWud5Wocm/2PgpUXbT5aSV3QEB10kBPJAzssOQylvdj8mOHoKl5pVkXpbCwww/T2g== +"@unrs/resolver-binding-linux-arm64-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.0.tgz#7b9d73558a2d85911c82314784edb89dcd0b274d" + integrity sha512-XRrVXRIUP++qyqAqgiXUpOv0GP3cHx7aA7NrzVFf6Cc8FoYuwtnmT+vctfSo4wRZN71MNU4xq2BEFxI4qvSerg== -"@unrs/resolver-binding-linux-arm64-musl@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.5.0.tgz#169e531731f7e462dffa410034a1d06a7a921aa8" - integrity sha512-+gF97xst1BZb28T3nwwzEtq2ewCoMDGKsenYsZuvpmNrW0019G1iUAunZN+FG55L21y+uP7zsGX06OXDQ/viKw== +"@unrs/resolver-binding-linux-arm64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.0.tgz#7d7305c5f5610744ef7a373d2a9022c922113568" + integrity sha512-Sligg+vTDAYTXkUtgviPjGEFIh57pkvlfdyRw21i9gkjp/eCNOAi2o5e7qLGTkoYdJHZJs5wVMViPEmAbw2/Tg== -"@unrs/resolver-binding-linux-ppc64-gnu@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.5.0.tgz#f6ad2ff47d74c8158b28a18536a71a8ecf84a17f" - integrity sha512-5bEmVcQw9js8JYM2LkUBw5SeELSIxX+qKf9bFrfFINKAp4noZ//hUxLpbF7u/3gTBN1GsER6xOzIZlw/VTdXtA== +"@unrs/resolver-binding-linux-ppc64-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.0.tgz#280e4846c3bd9b81fdda25ac3cdda203da9bfd20" + integrity sha512-Apek8/x+7Rg33zUJlQV44Bvq8/t1brfulk0veNJrk9wprF89bCYFMUHF7zQYcpf2u+m1+qs3mYQrBd43fGXhMA== -"@unrs/resolver-binding-linux-riscv64-gnu@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.5.0.tgz#2f3986cb44f285f90d27e87cee8b4059de3ffbdd" - integrity sha512-GGk/8TPUsf1Q99F+lzMdjE6sGL26uJCwQ9TlvBs8zR3cLQNw/MIumPN7zrs3GFGySjnwXc8gA6J3HKbejywmqA== +"@unrs/resolver-binding-linux-riscv64-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.0.tgz#63301787af18d158ab4e99ec5041f507da228721" + integrity sha512-kBale8CFX5clfV9VmI9EwKw2ZACMEx1ecjV92F9SeWTUoxl9d+LGzS6zMSX3kGYqcfJB3NXMwLCTwIDBLG1y4g== -"@unrs/resolver-binding-linux-s390x-gnu@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.5.0.tgz#813ea07833012bc34ecc59f023e422b421138761" - integrity sha512-5uRkFYYVNAeVaA4W/CwugjFN3iDOHCPqsBLCCOoJiMfFMMz4evBRsg+498OFa9w6VcTn2bD5aI+RRayaIgk2Sw== +"@unrs/resolver-binding-linux-riscv64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.0.tgz#b85d66b2c4d73fe335d448322c708448c4487c44" + integrity sha512-s/Q33xQjeFHSCvGl1sZztFZF6xhv7coMvFz6wa/x/ZlEArjiQoMMwGa/Aieq1Kp/6+S13iU3/IJF0ga6/451ow== -"@unrs/resolver-binding-linux-x64-gnu@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.5.0.tgz#18b0d7553268fa490db92be578ac4b0fd8cae049" - integrity sha512-j905CZH3nehYy6NimNqC2B14pxn4Ltd7guKMyPTzKehbFXTUgihQS/ZfHQTdojkMzbSwBOSgq1dOrY+IpgxDsA== +"@unrs/resolver-binding-linux-s390x-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.0.tgz#f4202f7bebd823e0744a785ac1426f07129a2f81" + integrity sha512-7PuNXAo97ydaxVNrIYJzPipvINJafDpB8pt5CoZHfu8BmqcU6d7kl6/SABTnqNffNkd6Cfhuo70jvGB2P7oJ/Q== -"@unrs/resolver-binding-linux-x64-musl@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.5.0.tgz#04541e98d16e358c695393251e365bc3d802dfa4" - integrity sha512-dmLevQTuzQRwu5A+mvj54R5aye5I4PVKiWqGxg8tTaYP2k2oTs/3Mo8mgnhPk28VoYCi0fdFYpgzCd4AJndQvQ== +"@unrs/resolver-binding-linux-x64-gnu@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.0.tgz#c3fa31d0b4cc49d54c956dec43bead5a0c4127cf" + integrity sha512-fNosEzDMYItA4It+R0tioHwKlEfx/3TkkJdP2x9B5o9R946NDC4ZZj5ZjA+Y4NQD2V/imB3QPAKmeh3vHQGQyA== -"@unrs/resolver-binding-wasm32-wasi@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.5.0.tgz#7a2ae7467c4c52d53c20ad7fc2bace1b23de8168" - integrity sha512-LtJMhwu7avhoi+kKfAZOKN773RtzLBVVF90YJbB0wyMpUj9yQPeA+mteVUI9P70OG/opH47FeV5AWeaNWWgqJg== +"@unrs/resolver-binding-linux-x64-musl@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.0.tgz#a447f7261958f688950be70a26b79a7955fb10d3" + integrity sha512-gHIw42dmnVcw7osjNPRybaXhONhggWkkzqiOZzXco1q3OKkn4KsbDylATeemnq3TP+L1BrzSqzl0H9UTJ6ji+w== + +"@unrs/resolver-binding-wasm32-wasi@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.0.tgz#fc9c486ffddef353daef71488f8f77e4de44dd8b" + integrity sha512-yq7POusv63/yTkNTaNsnXU/SAcBzckHyk1oYrDXqjS1m/goaWAaU9J9HrsovgTHkljxTcDd6PMAsJ5WZVBuGEQ== dependencies: - "@napi-rs/wasm-runtime" "^0.2.8" + "@napi-rs/wasm-runtime" "^0.2.9" -"@unrs/resolver-binding-win32-arm64-msvc@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.5.0.tgz#11deb282b8ce73fab26f1d04df0fa4d6363752c2" - integrity sha512-FTZBxLL4SO1mgIM86KykzJmPeTPisBDHQV6xtfDXbTMrentuZ6SdQKJUV5BWaoUK3p8kIULlrCcucqdCnk8Npg== +"@unrs/resolver-binding-win32-arm64-msvc@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.0.tgz#c316d889d29293faab926d1260b16a2d4c430ed4" + integrity sha512-/IPZPbdri9jglHonwB3F7EpQZvBK3ObH+g4ma/KDrqTEAECwvgE10Unvo0ox3LQFR/iMMAkVY+sGNMrMiIV/QQ== -"@unrs/resolver-binding-win32-ia32-msvc@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.5.0.tgz#2a5d414912379425bd395ea15901a5dd5febc7c1" - integrity sha512-i5bB7vJ1waUsFciU/FKLd4Zw0VnAkvhiJ4//jYQXyDUuiLKodmtQZVTcOPU7pp97RrNgCFtXfC1gnvj/DHPJTw== +"@unrs/resolver-binding-win32-ia32-msvc@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.0.tgz#14c9e08990dd0cf10d4962c40e9b368ea06b9789" + integrity sha512-NGVKbHEdrLuJdpcuGqV5zXO3v8t4CWOs0qeCGjO47RiwwufOi/yYcrtxtCzZAaMPBrffHL7c6tJ1Hxr17cPUGg== -"@unrs/resolver-binding-win32-x64-msvc@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.5.0.tgz#5768c6bba4a27833a48a8a77e50eb01b520d0962" - integrity sha512-wAvXp4k7jhioi4SebXW/yfzzYwsUCr9kIX4gCsUFKpCTUf8Mi7vScJXI3S+kupSUf0LbVHudR8qBbe2wFMSNUw== +"@unrs/resolver-binding-win32-x64-msvc@1.7.0": + version "1.7.0" + 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" @@ -4605,17 +4610,17 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-import-resolver-typescript@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.2.tgz#1d2371be6d073bade177ee04a4548dbacdc334c0" - integrity sha512-T2LqBXj87ndEC9t1LrDiPkzalSFzD4rrXr6BTzGdgMx1jdQM4T972guQvg7Ih+LNO51GURXI/qMHS5GF3h1ilw== +eslint-import-resolver-typescript@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-4.3.4.tgz#3d04161698925b5dc9c297966442c2761a319de4" + integrity sha512-buzw5z5VtiQMysYLH9iW9BV04YyZebsw+gPi+c4FCjfS9i6COYOrEWw9t3m3wA9PFBfqcBCqWf32qrXLbwafDw== dependencies: debug "^4.4.0" get-tsconfig "^4.10.0" is-bun-module "^2.0.0" stable-hash "^0.0.5" - tinyglobby "^0.2.12" - unrs-resolver "^1.4.1" + tinyglobby "^0.2.13" + unrs-resolver "^1.6.3" eslint-module-utils@^2.12.0: version "2.12.0" @@ -5048,10 +5053,10 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" -fdir@^6.4.3: - version "6.4.3" - resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.3.tgz#011cdacf837eca9b811c89dbb902df714273db72" - integrity sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw== +fdir@^6.4.4: + version "6.4.4" + resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.4.tgz#1cfcf86f875a883e19a8fab53622cfe992e8d2f9" + integrity sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg== file-entry-cache@^6.0.1: version "6.0.1" @@ -7701,6 +7706,11 @@ nanoid@^3.3.6: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +napi-postinstall@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/napi-postinstall/-/napi-postinstall-0.1.6.tgz#7682101f43fc66c233b625ee8ebf07826c6eedde" + integrity sha512-w1bClprmjwpybo+7M1Rd0N4QK5Ein8kH/1CQ0Wv8Q9vrLbDMakxc4rZpv8zYc8RVErUELJlFhM8UzOF3IqlYKw== + natural-compare-lite@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" @@ -9578,12 +9588,12 @@ timers-ext@^0.1.7: es5-ext "^0.10.64" next-tick "^1.1.0" -tinyglobby@^0.2.12: - version "0.2.12" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5" - integrity sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww== +tinyglobby@^0.2.13: + version "0.2.13" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.13.tgz#a0e46515ce6cbcd65331537e57484af5a7b2ff7e" + integrity sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw== dependencies: - fdir "^6.4.3" + fdir "^6.4.4" picomatch "^4.0.2" title@~3.4.1: @@ -10055,27 +10065,30 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -unrs-resolver@^1.4.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.5.0.tgz#d0a608f08321d8e90ba8eb10a3240e7995997275" - integrity sha512-6aia3Oy7SEe0MuUGQm2nsyob0L2+g57w178K5SE/3pvSGAIp28BB2O921fKx424Ahc/gQ6v0DXFbhcpyhGZdOA== +unrs-resolver@^1.6.3: + version "1.7.0" + resolved "https://registry.yarnpkg.com/unrs-resolver/-/unrs-resolver-1.7.0.tgz#2d1523d0a9c9271d0dc5b400520b776b947893ea" + integrity sha512-b76tVoT9KPniDY1GoYghDUQX20gjzXm/TONfHfgayLaiuo+oGyT9CsQkGCEJs+1/uryVBEOGOt3yYWDXbJhL7g== + dependencies: + napi-postinstall "^0.1.6" optionalDependencies: - "@unrs/resolver-binding-darwin-arm64" "1.5.0" - "@unrs/resolver-binding-darwin-x64" "1.5.0" - "@unrs/resolver-binding-freebsd-x64" "1.5.0" - "@unrs/resolver-binding-linux-arm-gnueabihf" "1.5.0" - "@unrs/resolver-binding-linux-arm-musleabihf" "1.5.0" - "@unrs/resolver-binding-linux-arm64-gnu" "1.5.0" - "@unrs/resolver-binding-linux-arm64-musl" "1.5.0" - "@unrs/resolver-binding-linux-ppc64-gnu" "1.5.0" - "@unrs/resolver-binding-linux-riscv64-gnu" "1.5.0" - "@unrs/resolver-binding-linux-s390x-gnu" "1.5.0" - "@unrs/resolver-binding-linux-x64-gnu" "1.5.0" - "@unrs/resolver-binding-linux-x64-musl" "1.5.0" - "@unrs/resolver-binding-wasm32-wasi" "1.5.0" - "@unrs/resolver-binding-win32-arm64-msvc" "1.5.0" - "@unrs/resolver-binding-win32-ia32-msvc" "1.5.0" - "@unrs/resolver-binding-win32-x64-msvc" "1.5.0" + "@unrs/resolver-binding-darwin-arm64" "1.7.0" + "@unrs/resolver-binding-darwin-x64" "1.7.0" + "@unrs/resolver-binding-freebsd-x64" "1.7.0" + "@unrs/resolver-binding-linux-arm-gnueabihf" "1.7.0" + "@unrs/resolver-binding-linux-arm-musleabihf" "1.7.0" + "@unrs/resolver-binding-linux-arm64-gnu" "1.7.0" + "@unrs/resolver-binding-linux-arm64-musl" "1.7.0" + "@unrs/resolver-binding-linux-ppc64-gnu" "1.7.0" + "@unrs/resolver-binding-linux-riscv64-gnu" "1.7.0" + "@unrs/resolver-binding-linux-riscv64-musl" "1.7.0" + "@unrs/resolver-binding-linux-s390x-gnu" "1.7.0" + "@unrs/resolver-binding-linux-x64-gnu" "1.7.0" + "@unrs/resolver-binding-linux-x64-musl" "1.7.0" + "@unrs/resolver-binding-wasm32-wasi" "1.7.0" + "@unrs/resolver-binding-win32-arm64-msvc" "1.7.0" + "@unrs/resolver-binding-win32-ia32-msvc" "1.7.0" + "@unrs/resolver-binding-win32-x64-msvc" "1.7.0" update-browserslist-db@^1.1.0: version "1.1.0" From 8d8315eb19e613635c23ef30047eda0dee661dea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 11:21:30 +0000 Subject: [PATCH 162/266] build(deps-dev): bump cypress from 14.3.1 to 14.3.2 in the cypress group (#8442) Bumps the cypress group with 1 update: [cypress](https://github.com/cypress-io/cypress). Updates `cypress` from 14.3.1 to 14.3.2 - [Release notes](https://github.com/cypress-io/cypress/releases) - [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md) - [Commits](https://github.com/cypress-io/cypress/compare/v14.3.1...v14.3.2) --- updated-dependencies: - dependency-name: cypress dependency-version: 14.3.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: cypress ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 8 ++++---- package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4174bf891..17de00757 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "auto-changelog": "^2.5.0", "bcryptjs": "^3.0.2", "cross-env": "^7.0.3", - "cypress": "^14.3.1", + "cypress": "^14.3.2", "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.5.0", @@ -7762,9 +7762,9 @@ "optional": true }, "node_modules/cypress": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.3.1.tgz", - "integrity": "sha512-/2q06qvHMK3PNiadnRW1Je0lJ43gAFPQJUAK2zIxjr22kugtWxVQznTBLVu1AvRH+RP3oWZhCdWqiEi+0NuqCg==", + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-14.3.2.tgz", + "integrity": "sha512-n+yGD2ZFFKgy7I3YtVpZ7BcFYrrDMcKj713eOZdtxPttpBjCyw/R8dLlFSsJPouneGN7A/HOSRyPJ5+3/gKDoA==", "dev": true, "hasInstallScript": true, "license": "MIT", diff --git a/package.json b/package.json index e86458b61..c208969f5 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "auto-changelog": "^2.5.0", "bcryptjs": "^3.0.2", "cross-env": "^7.0.3", - "cypress": "^14.3.1", + "cypress": "^14.3.2", "cypress-network-idle": "^1.15.0", "date-fns": "^3.6.0", "dotenv": "^16.5.0", diff --git a/yarn.lock b/yarn.lock index a5a09d086..1e3983be3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4074,10 +4074,10 @@ cypress-network-idle@^1.15.0: resolved "https://registry.yarnpkg.com/cypress-network-idle/-/cypress-network-idle-1.15.0.tgz#e249f08695a46f1ddce18a95d5293937f277cbb3" integrity sha512-8zU16zhc7S3nMl1NTEEcNsZYlJy/ZzP2zPTTrngGxyXH32Ipake/xfHLZsgrzeWCieiS2AVhQsakhWqFzO3hpw== -cypress@^14.3.1: - version "14.3.1" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.3.1.tgz#b0570c0e5b198d930a2c0f640d099e777bec2d2f" - integrity sha512-/2q06qvHMK3PNiadnRW1Je0lJ43gAFPQJUAK2zIxjr22kugtWxVQznTBLVu1AvRH+RP3oWZhCdWqiEi+0NuqCg== +cypress@^14.3.2: + version "14.3.2" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-14.3.2.tgz#04a6ea66c1715119ef41dda5851d75801cc1e226" + integrity sha512-n+yGD2ZFFKgy7I3YtVpZ7BcFYrrDMcKj713eOZdtxPttpBjCyw/R8dLlFSsJPouneGN7A/HOSRyPJ5+3/gKDoA== dependencies: "@cypress/request" "^3.0.8" "@cypress/xvfb" "^1.2.4" From 0d8552cb000be76322ba67bd956ba3ff367eaa02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 17:23:13 +0200 Subject: [PATCH 163/266] build(deps): bump docker/build-push-action from 6.15.0 to 6.16.0 (#8444) Bumps [docker/build-push-action](https://github.com/docker/build-push-action) from 6.15.0 to 6.16.0. - [Release notes](https://github.com/docker/build-push-action/releases) - [Commits](https://github.com/docker/build-push-action/compare/471d1dc4e07e5cdedd4c2171150001c434f0b7a4...14487ce63c7a62a4a324b0bfb37086795e31c6c1) --- updated-dependencies: - dependency-name: docker/build-push-action dependency-version: 6.16.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker-push.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-push.yml b/.github/workflows/docker-push.yml index 406d8304b..cc84c6e3e 100644 --- a/.github/workflows/docker-push.yml +++ b/.github/workflows/docker-push.yml @@ -81,7 +81,7 @@ jobs: type=sha - name: Build and push Docker images id: push - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 + uses: docker/build-push-action@14487ce63c7a62a4a324b0bfb37086795e31c6c1 with: context: ${{ matrix.app.context }} target: ${{ matrix.app.target }} From 7c37a239e26a3bd8c3fc8852124c142abc4e568f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 15:53:08 +0000 Subject: [PATCH 164/266] build(deps-dev): bump @types/node from 22.14.1 to 22.15.2 in /backend (#8446) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.14.1 to 22.15.2. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 22.15.2 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index 39a0016bc..b04ab206f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -104,7 +104,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0", "@faker-js/faker": "9.7.0", "@types/jest": "^29.5.14", - "@types/node": "^22.14.1", + "@types/node": "^22.15.2", "@types/uuid": "~9.0.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index f37877286..811fbbe32 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2079,10 +2079,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>=6", "@types/node@^22.14.1": - version "22.14.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.1.tgz#53b54585cec81c21eee3697521e31312d6ca1e6f" - integrity sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw== +"@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== dependencies: undici-types "~6.21.0" From ce19450b0d804ffc15173d3e527618d46927a888 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:23:22 +0000 Subject: [PATCH 165/266] build(deps-dev): bump nodemon from 3.1.9 to 3.1.10 in /backend (#8447) Bumps [nodemon](https://github.com/remy/nodemon) from 3.1.9 to 3.1.10. - [Release notes](https://github.com/remy/nodemon/releases) - [Commits](https://github.com/remy/nodemon/compare/v3.1.9...v3.1.10) --- updated-dependencies: - dependency-name: nodemon dependency-version: 3.1.10 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 17 +++++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/backend/package.json b/backend/package.json index b04ab206f..1c0e5f6ad 100644 --- a/backend/package.json +++ b/backend/package.json @@ -121,7 +121,7 @@ "eslint-plugin-promise": "^7.2.1", "eslint-plugin-security": "^3.0.1", "jest": "^29.7.0", - "nodemon": "~3.1.9", + "nodemon": "~3.1.10", "prettier": "^3.5.3", "require-json5": "^1.3.0", "rosie": "^2.1.1", diff --git a/backend/yarn.lock b/backend/yarn.lock index 811fbbe32..aefdcf732 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -7566,14 +7566,7 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.1, minimatch@^9.0.4: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.5: +minimatch@^9.0.1, minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== @@ -7873,10 +7866,10 @@ nodemailer@^6.10.1: resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.1.tgz#cbc434c54238f83a51c07eabd04e2b3e832da623" integrity sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA== -nodemon@~3.1.9: - version "3.1.9" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.9.tgz#df502cdc3b120e1c3c0c6e4152349019efa7387b" - integrity sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg== +nodemon@~3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.10.tgz#5015c5eb4fffcb24d98cf9454df14f4fecec9bc1" + integrity sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw== dependencies: chokidar "^3.5.2" debug "^4" From 3c853d57379d5f4df13e7c6603ada0f949f27ee7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 16:57:17 +0000 Subject: [PATCH 166/266] build(deps): bump peter-evans/repository-dispatch (#8443) Bumps [peter-evans/repository-dispatch](https://github.com/peter-evans/repository-dispatch) from 7d980a9b9f8ecf8955ea90507b3ed89122f53215 to 44966f0098fd4ab26380bb099e1edf6d57eb2c90. - [Release notes](https://github.com/peter-evans/repository-dispatch/releases) - [Commits](https://github.com/peter-evans/repository-dispatch/compare/7d980a9b9f8ecf8955ea90507b3ed89122f53215...44966f0098fd4ab26380bb099e1edf6d57eb2c90) --- updated-dependencies: - dependency-name: peter-evans/repository-dispatch dependency-version: 44966f0098fd4ab26380bb099e1edf6d57eb2c90 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b66413f22..425da269b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -64,7 +64,7 @@ jobs: echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV - run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV #- name: Repository Dispatch - # uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0 + # uses: peter-evans/repository-dispatch@44966f0098fd4ab26380bb099e1edf6d57eb2c90 # v3.0.0 # with: # token: ${{ github.token }} # event-type: trigger-ocelot-build-success @@ -72,7 +72,7 @@ jobs: # client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}' - name: Repository Dispatch stage.ocelot.social - uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0 + uses: peter-evans/repository-dispatch@44966f0098fd4ab26380bb099e1edf6d57eb2c90 # v3.0.0 with: token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository event-type: trigger-ocelot-build-success @@ -80,7 +80,7 @@ jobs: client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}' - name: Repository Dispatch stage.yunite.me - uses: peter-evans/repository-dispatch@7d980a9b9f8ecf8955ea90507b3ed89122f53215 # v3.0.0 + uses: peter-evans/repository-dispatch@44966f0098fd4ab26380bb099e1edf6d57eb2c90 # v3.0.0 with: token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository event-type: trigger-ocelot-build-success From ba0cc147e79d2618161a3293f926a82c7256cc3d Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 26 Apr 2025 19:25:27 +0200 Subject: [PATCH 167/266] fix(webapp): fix admin badges settings (#8438) * Remove proxy from nuxt.config, instead add proxy filter * Show message when there are no badges --- .../Admin/Badges/BadgesSection.spec.js | 37 +++++++++++++------ .../features/Admin/Badges/BadgesSection.vue | 7 +++- .../__snapshots__/BadgesSection.spec.js.snap | 22 ++++++++++- webapp/locales/de.json | 1 + webapp/locales/en.json | 1 + webapp/locales/es.json | 1 + webapp/locales/fr.json | 1 + webapp/locales/it.json | 1 + webapp/locales/nl.json | 1 + webapp/locales/pl.json | 1 + webapp/locales/pt.json | 1 + webapp/locales/ru.json | 1 + webapp/nuxt.config.js | 9 ----- webapp/pages/admin/users/_id.spec.js | 5 +++ webapp/pages/admin/users/_id.vue | 5 ++- 15 files changed, 70 insertions(+), 24 deletions(-) diff --git a/webapp/components/_new/features/Admin/Badges/BadgesSection.spec.js b/webapp/components/_new/features/Admin/Badges/BadgesSection.spec.js index 8baddc692..8abf8d679 100644 --- a/webapp/components/_new/features/Admin/Badges/BadgesSection.spec.js +++ b/webapp/components/_new/features/Admin/Badges/BadgesSection.spec.js @@ -21,26 +21,41 @@ const badge2 = { describe('Admin/BadgesSection', () => { let wrapper - const Wrapper = () => { + const Wrapper = (withBadges = true) => { return render(BadgesSection, { localVue, propsData: { - badges: [badge1, badge2], + badges: withBadges ? [badge1, badge2] : [], + }, + mocks: { + $t: jest.fn((t) => t), }, }) } - beforeEach(() => { - wrapper = Wrapper() + describe('without badges', () => { + beforeEach(() => { + wrapper = Wrapper(false) + }) + + it('renders', () => { + expect(wrapper.baseElement).toMatchSnapshot() + }) }) - it('renders', () => { - expect(wrapper.baseElement).toMatchSnapshot() - }) + describe('with badges', () => { + beforeEach(() => { + wrapper = Wrapper(true) + }) - it('emits toggleButton', async () => { - const button = screen.getByAltText(badge1.description) - await fireEvent.click(button) - expect(wrapper.emitted().toggleBadge[0][0]).toEqual(badge1) + it('renders', () => { + expect(wrapper.baseElement).toMatchSnapshot() + }) + + it('emits toggleButton', async () => { + const button = screen.getByAltText(badge1.description) + await fireEvent.click(button) + expect(wrapper.emitted().toggleBadge[0][0]).toEqual(badge1) + }) }) }) diff --git a/webapp/components/_new/features/Admin/Badges/BadgesSection.vue b/webapp/components/_new/features/Admin/Badges/BadgesSection.vue index 8ff9da7ed..fc89d2a50 100644 --- a/webapp/components/_new/features/Admin/Badges/BadgesSection.vue +++ b/webapp/components/_new/features/Admin/Badges/BadgesSection.vue @@ -1,16 +1,19 @@ diff --git a/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap b/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap index c09a50725..a78f44edc 100644 --- a/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap +++ b/webapp/components/_new/features/Admin/Badges/__snapshots__/BadgesSection.spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Admin/BadgesSection renders 1`] = ` +exports[`Admin/BadgesSection with badges renders 1`] = `
`; + +exports[`Admin/BadgesSection without badges renders 1`] = ` + +
+
+

+ +

+ +
+ + admin.badges.noBadges + +
+
+
+ +`; diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 4fe39ce24..6b588f746 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": "Stelle die verfügbaren Auszeichnungen für diesen Nutzer ein.", + "noBadges": "Keine Auszeichnungen vorhanden.", "revokeTrophy": { "error": "Trophäe konnte nicht widerrufen werden!", "success": "Trophäe erfolgreich widerrufen" diff --git a/webapp/locales/en.json b/webapp/locales/en.json index bdd9cdefb..24fd16440 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": "Configure the available badges for this user", + "noBadges": "There are no badges available", "revokeTrophy": { "error": "Trophy could not be revoked!", "success": "Trophy successfully revoked!" diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 60e65ca20..5bc6b1b6d 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index e7b4fcd4a..90164c47a 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 703b27fff..1674a9cb3 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index f6a53d0c8..0944dc472 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index 4e928f417..ba5a61bca 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 005eb77f4..6b6f07132 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index bc862bcc2..7bc33e628 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -12,6 +12,7 @@ "admin": { "badges": { "description": null, + "noBadges": null, "revokeTrophy": { "error": null, "success": null diff --git a/webapp/nuxt.config.js b/webapp/nuxt.config.js index 07cfa6bc4..263c3f149 100644 --- a/webapp/nuxt.config.js +++ b/webapp/nuxt.config.js @@ -207,15 +207,6 @@ export default { 'X-API-TOKEN': CONFIG.BACKEND_TOKEN, }, }, - '/img': { - // make this configurable (nuxt-dotenv) - target: CONFIG.GRAPHQL_URI, - toProxy: true, // cloudflare needs that - headers: { - 'X-UI-Request': true, - 'X-API-TOKEN': CONFIG.BACKEND_TOKEN, - }, - }, }, // Give apollo module options diff --git a/webapp/pages/admin/users/_id.spec.js b/webapp/pages/admin/users/_id.spec.js index 933de58de..d38b13022 100644 --- a/webapp/pages/admin/users/_id.spec.js +++ b/webapp/pages/admin/users/_id.spec.js @@ -58,6 +58,11 @@ describe('.vue', () => { query: jest.fn(), }, mutate: jest.fn(), + queries: { + Badge: { + loading: false, + }, + }, }, $toast: { success: jest.fn(), diff --git a/webapp/pages/admin/users/_id.vue b/webapp/pages/admin/users/_id.vue index 808e1653a..a6c4dafaa 100644 --- a/webapp/pages/admin/users/_id.vue +++ b/webapp/pages/admin/users/_id.vue @@ -8,7 +8,7 @@ {{ $t('admin.badges.description') }} - + userBadge.id === badge.id), })) }, + isLoadingBadges() { + return this.$apollo.queries.Badge.loading + }, }, methods: { toggleBadge(badge) { From a5ee90a95d0e7b8562a2dac8d8b0a8455fd00356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Mon, 28 Apr 2025 15:32:23 +0200 Subject: [PATCH 168/266] fix(webapp): refine little things (#8382) * Refine locals of some internal pages headlines * Fix tool tip text * Fix 'email' -> 'e-mail' * Syncronize 'metadata.ts' with webapp * Refine e-mail notifications * Adjust notification settings buttons * Refine third party setting * Fix post teaser counter icon tooltips translations * Refine e-mail notifications * Refine third party setting * notification spec snapshot --------- Co-authored-by: Ulf Gebhardt --- backend/src/config/metadata.ts | 3 +- webapp/components/PostTeaser/PostTeaser.vue | 20 ++++- webapp/locales/de.json | 32 ++++---- webapp/locales/en.json | 34 ++++---- .../__snapshots__/notifications.spec.js.snap | 79 ++++++++++--------- webapp/pages/settings/embeds.vue | 47 +++++++---- webapp/pages/settings/notifications.vue | 32 +++++--- 7 files changed, 147 insertions(+), 100 deletions(-) diff --git a/backend/src/config/metadata.ts b/backend/src/config/metadata.ts index 282fcb655..9c87818ae 100644 --- a/backend/src/config/metadata.ts +++ b/backend/src/config/metadata.ts @@ -1,9 +1,10 @@ // this file is duplicated in `backend/src/config/metadata` and `webapp/constants/metadata.js` and replaced on rebranding export default { APPLICATION_NAME: 'ocelot.social', - APPLICATION_SHORT_NAME: 'ocelot', + APPLICATION_SHORT_NAME: 'ocelot.social', APPLICATION_DESCRIPTION: 'ocelot.social Community Network', COOKIE_NAME: 'ocelot-social-token', ORGANIZATION_NAME: 'ocelot.social Community', ORGANIZATION_JURISDICTION: 'City of Angels', + THEME_COLOR: 'rgb(23, 181, 63)', // $color-primary – as the main color in general. e.g. the color in the background of the app that is visible behind the transparent iPhone status bar to name one use case, or the current color of SVGs to name another use case } diff --git a/webapp/components/PostTeaser/PostTeaser.vue b/webapp/components/PostTeaser/PostTeaser.vue index ad43a9d31..32a07d5a3 100644 --- a/webapp/components/PostTeaser/PostTeaser.vue +++ b/webapp/components/PostTeaser/PostTeaser.vue @@ -72,22 +72,34 @@

settings.notifications.post @@ -66,11 +66,11 @@ exports[`notifications.vue mount renders 1`] = `

settings.notifications.group @@ -155,42 +155,47 @@ exports[`notifications.vue mount renders 1`] = `

- - + - settings.notifications.checkAll - - - - - - + +
diff --git a/webapp/pages/settings/embeds.vue b/webapp/pages/settings/embeds.vue index 53db65e5e..aee47dfe7 100644 --- a/webapp/pages/settings/embeds.vue +++ b/webapp/pages/settings/embeds.vue @@ -17,20 +17,29 @@ {{ $t('settings.embeds.status.change.question') }} - - {{ $t('settings.embeds.status.change.deny') }} - - - {{ $t('settings.embeds.status.change.allow') }} - - -

{{ $t('settings.embeds.info-description') }}

-
    -
  • - {{ provider.provider_name }}, - {{ provider.provider_url }} -
  • -
+ + + {{ $t('settings.embeds.status.change.deny') }} + + + {{ $t('settings.embeds.status.change.allow') }} + + +

{{ $t('settings.embeds.info-description') }}

+ +
    +
  • + + {{ provider.provider_name }}, + {{ provider.provider_url }} + +
  • +
+
@@ -93,3 +102,13 @@ export default { }, } + + diff --git a/webapp/pages/settings/notifications.vue b/webapp/pages/settings/notifications.vue index 36e0d9081..8a383dc4f 100644 --- a/webapp/pages/settings/notifications.vue +++ b/webapp/pages/settings/notifications.vue @@ -1,8 +1,13 @@ @@ -138,4 +145,7 @@ export default { .label { margin-left: $space-xx-small; } +button + button { + margin-left: $space-x-small; +} From fffaebcbca91410c7e77ea3df08b61453004cfe5 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 28 Apr 2025 16:39:54 +0200 Subject: [PATCH 169/266] Replace edit link by pencil button (#8453) We show now a round button with pencil icon to edit the badges. --- webapp/locales/de.json | 3 +- webapp/locales/en.json | 3 +- webapp/locales/es.json | 3 +- webapp/locales/fr.json | 3 +- webapp/locales/it.json | 3 +- webapp/locales/nl.json | 3 +- webapp/locales/pl.json | 3 +- webapp/locales/pt.json | 3 +- webapp/locales/ru.json | 3 +- .../users/__snapshots__/index.spec.js.snap | 32 +++++++++++++++---- webapp/pages/admin/users/index.vue | 6 +++- 11 files changed, 40 insertions(+), 25 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index 96a2b323a..df050b191 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -98,8 +98,7 @@ "number": "Nr.", "role": "Rolle", "slug": "Alias" - }, - "edit": "Bearbeiten" + } } } }, diff --git a/webapp/locales/en.json b/webapp/locales/en.json index 760937b06..ecd0ec18d 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -98,8 +98,7 @@ "number": "No.", "role": "Role", "slug": "Slug" - }, - "edit": "Edit" + } } } }, diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 5bc6b1b6d..15096b9d8 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -98,8 +98,7 @@ "number": "No.", "role": "Rol", "slug": "Alias" - }, - "edit": null + } } } }, diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index 90164c47a..2da2a9801 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -98,8 +98,7 @@ "number": "Num.", "role": "Rôle", "slug": "Slug" - }, - "edit": null + } } } }, diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 1674a9cb3..485abff3a 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -98,8 +98,7 @@ "number": null, "role": null, "slug": null - }, - "edit": null + } } } }, diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 0944dc472..40f9aca2e 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -98,8 +98,7 @@ "number": null, "role": null, "slug": null - }, - "edit": null + } } } }, diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index ba5a61bca..ee332b84b 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -98,8 +98,7 @@ "number": null, "role": null, "slug": null - }, - "edit": null + } } } }, diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 6b6f07132..54f9b5d99 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -98,8 +98,7 @@ "number": "N.º", "role": "Função", "slug": "Slug" - }, - "edit": null + } } } }, diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index 7bc33e628..4d2e2a357 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -98,8 +98,7 @@ "number": "№", "role": "Роль", "slug": "Алиас" - }, - "edit": null + } } } }, diff --git a/webapp/pages/admin/users/__snapshots__/index.spec.js.snap b/webapp/pages/admin/users/__snapshots__/index.spec.js.snap index b72a2617f..0fff4016b 100644 --- a/webapp/pages/admin/users/__snapshots__/index.spec.js.snap +++ b/webapp/pages/admin/users/__snapshots__/index.spec.js.snap @@ -709,9 +709,19 @@ exports[`Users given badges are enabled renders 1`] = ` - - admin.users.table.edit - + @@ -793,9 +803,19 @@ exports[`Users given badges are enabled renders 1`] = ` - - admin.users.table.edit - + diff --git a/webapp/pages/admin/users/index.vue b/webapp/pages/admin/users/index.vue index fd08f1e0c..0bd592bad 100644 --- a/webapp/pages/admin/users/index.vue +++ b/webapp/pages/admin/users/index.vue @@ -70,7 +70,7 @@ params: { id: scope.row.id }, }" > - {{ $t('admin.users.table.edit') }} + @@ -224,4 +224,8 @@ export default { .admin-users > .base-card:first-child { margin-bottom: $space-small; } + +.ds-table-col { + vertical-align: middle; +} From d7d8a242cdb51cdbf83e766daf322bf2e2c36d1a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 28 Apr 2025 18:17:18 +0200 Subject: [PATCH 170/266] fix(backend): fixes for branding (#8449) * copy from branding folder to backend public folder provide default branding/public folder * copy public folder correctly * copy files again for providers.json * copy more public folders * more copy * revert change * fix naming of called script when using db:data:branding * prod command for branding data * close database connection * lint fixes * increase test timeout again --- backend/Dockerfile | 3 ++- backend/branding/public/.gitkeep | 0 backend/package.json | 5 +++-- backend/src/db/data-branding.ts | 11 ++++++++--- backend/test/setup.ts | 2 +- 5 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 backend/branding/public/.gitkeep diff --git a/backend/Dockerfile b/backend/Dockerfile index e1c244069..1e76cf841 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -24,11 +24,12 @@ ONBUILD COPY ./branding/constants/ src/config/tmp ONBUILD RUN tools/replace-constants.sh ONBUILD COPY ./branding/email/ src/middleware/helpers/email/ ONBUILD COPY ./branding/data/ src/db/data +ONBUILD COPY ./branding/public/ public/ ONBUILD RUN yarn install --production=false --frozen-lockfile --non-interactive ONBUILD RUN yarn run build ONBUILD RUN mkdir /build ONBUILD RUN cp -r ./build /build -ONBUILD RUN cp -r ./public /build/build +ONBUILD RUN cp -r ./public /build ONBUILD RUN cp -r ./package.json yarn.lock /build ONBUILD RUN cd /build && yarn install --production=true --frozen-lockfile --non-interactive diff --git a/backend/branding/public/.gitkeep b/backend/branding/public/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/backend/package.json b/backend/package.json index 1c0e5f6ad..7d884317e 100644 --- a/backend/package.json +++ b/backend/package.json @@ -19,11 +19,12 @@ "db:seed": "ts-node --require tsconfig-paths/register src/db/seed.ts", "db:data:admin": "ts-node --require tsconfig-paths/register src/db/admin.ts", "db:data:badges": "ts-node --require tsconfig-paths/register src/db/badges.ts", - "db:data:branding": "ts-node --require tsconfig-paths/register src/db/data-production.ts", + "db:data:branding": "ts-node --require tsconfig-paths/register src/db/data-branding.ts", "db:data:categories": "ts-node --require tsconfig-paths/register src/db/categories.ts", "db:migrate": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --store ./src/db/migrate/store.ts", "db:migrate:create": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create", - "prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js" + "prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js", + "prod:db:data:branding": "node build/src/db/data-branding.js" }, "dependencies": { "@babel/cli": "~7.27.0", diff --git a/backend/src/db/data-branding.ts b/backend/src/db/data-branding.ts index e9af41840..eceaf391b 100644 --- a/backend/src/db/data-branding.ts +++ b/backend/src/db/data-branding.ts @@ -1,16 +1,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-misused-promises */ /* eslint-disable @typescript-eslint/no-floating-promises */ import { readdir } from 'node:fs/promises' import path from 'node:path' +import { getNeode } from './neo4j' + const dataFolder = path.join(__dirname, 'data/') +const neode = getNeode() ;(async function () { const files = await readdir(dataFolder) - files.forEach(async (file) => { + for await (const file of files) { if (file.slice(0, -3).endsWith('-branding')) { const importedModule = await import(path.join(dataFolder, file)) if (!importedModule.default) { @@ -18,5 +20,8 @@ const dataFolder = path.join(__dirname, 'data/') } await importedModule.default() } - }) + } + + // close database connection + neode.close() })() diff --git a/backend/test/setup.ts b/backend/test/setup.ts index 128830f13..d1d32be5b 100644 --- a/backend/test/setup.ts +++ b/backend/test/setup.ts @@ -1,2 +1,2 @@ // Metascraper takes longer nowadays, double time -// jest.setTimeout(10000) +jest.setTimeout(10000) From 48c7bd0033ecfe1db3effa6982dd6e037596a28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Mon, 28 Apr 2025 18:58:35 +0200 Subject: [PATCH 171/266] refactor(webapp): make login, registration, password-reset layout brandable (#8440) * Make login, registration, password-reset layout brandable - Rename some variables related to this * Remove experimental code * add lodash types * fix build fix type --------- Co-authored-by: Ulf Gebhardt --- backend/package.json | 1 + backend/src/constants/registration.ts | 7 ++----- backend/src/constants/registrationBranded.ts | 12 ++++++++++++ backend/src/schema/resolvers/embeds/scraper.ts | 7 ++++--- .../resolvers/helpers/generateInviteCode.ts | 4 ++-- .../src/schema/resolvers/helpers/generateNonce.ts | 4 ++-- backend/src/schema/resolvers/inviteCodes.spec.ts | 6 +++--- .../src/schema/resolvers/passwordReset.spec.ts | 4 ++-- backend/src/schema/resolvers/passwordReset.ts | 4 ++-- backend/yarn.lock | 5 +++++ .../Registration/RegistrationSlideInvite.vue | 8 ++++---- .../Registration/RegistrationSlideNonce.vue | 8 ++++---- webapp/constants/login.js | 1 + webapp/constants/loginBranded.js | 8 ++++++++ webapp/constants/registration.js | 7 ++----- webapp/constants/registrationBranded.js | 12 ++++++++++++ webapp/pages/login.vue | 11 +++++++---- webapp/pages/password-reset.vue | 3 ++- webapp/pages/registration.spec.js | 2 +- webapp/pages/registration.vue | 15 +++++++++------ 20 files changed, 85 insertions(+), 44 deletions(-) create mode 100644 backend/src/constants/registrationBranded.ts create mode 100644 webapp/constants/login.js create mode 100644 webapp/constants/loginBranded.js create mode 100644 webapp/constants/registrationBranded.js diff --git a/backend/package.json b/backend/package.json index 7d884317e..2c451ec57 100644 --- a/backend/package.json +++ b/backend/package.json @@ -105,6 +105,7 @@ "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0", "@faker-js/faker": "9.7.0", "@types/jest": "^29.5.14", + "@types/lodash": "^4.17.16", "@types/node": "^22.15.2", "@types/uuid": "~9.0.1", "@typescript-eslint/eslint-plugin": "^5.62.0", diff --git a/backend/src/constants/registration.ts b/backend/src/constants/registration.ts index a08be3521..8ebb40573 100644 --- a/backend/src/constants/registration.ts +++ b/backend/src/constants/registration.ts @@ -1,5 +1,2 @@ -// this file is duplicated in `backend/src/config/metadata` and `webapp/constants/metadata.js` -export default { - NONCE_LENGTH: 5, - INVITE_CODE_LENGTH: 6, -} +// this file is duplicated in `backend/src/config/registration.ts` and `webapp/constants/registration.js` +export default {} diff --git a/backend/src/constants/registrationBranded.ts b/backend/src/constants/registrationBranded.ts new file mode 100644 index 000000000..2ce1d6965 --- /dev/null +++ b/backend/src/constants/registrationBranded.ts @@ -0,0 +1,12 @@ +// this file is duplicated in `backend/src/config/registrationBranded.ts` and `webapp/constants/registrationBranded.js` +import { merge } from 'lodash' + +import registration from '@constants/registration' + +const defaultRegistration = { + NONCE_LENGTH: 5, + INVITE_CODE_LENGTH: 6, + LAYOUT: 'no-header', +} + +export default merge(defaultRegistration, registration) diff --git a/backend/src/schema/resolvers/embeds/scraper.ts b/backend/src/schema/resolvers/embeds/scraper.ts index bcd25046b..a8cd07a76 100644 --- a/backend/src/schema/resolvers/embeds/scraper.ts +++ b/backend/src/schema/resolvers/embeds/scraper.ts @@ -87,8 +87,9 @@ export default async function scrape(url) { throw new ApolloError('Not found', 'NOT_FOUND') } - return { - type: 'link', - ...output, + if (!output.type) { + output.type = 'link' } + + return output } diff --git a/backend/src/schema/resolvers/helpers/generateInviteCode.ts b/backend/src/schema/resolvers/helpers/generateInviteCode.ts index 6e580fab9..980af4593 100644 --- a/backend/src/schema/resolvers/helpers/generateInviteCode.ts +++ b/backend/src/schema/resolvers/helpers/generateInviteCode.ts @@ -1,9 +1,9 @@ -import CONSTANTS_REGISTRATION from '@constants/registration' +import registrationConstants from '@constants/registrationBranded' export default function generateInviteCode() { // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) return Array.from( - { length: CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH }, + { length: registrationConstants.INVITE_CODE_LENGTH }, (n: number = Math.floor(Math.random() * 36)) => { // n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65 // else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48 diff --git a/backend/src/schema/resolvers/helpers/generateNonce.ts b/backend/src/schema/resolvers/helpers/generateNonce.ts index 7e0f7542c..b7585b24f 100644 --- a/backend/src/schema/resolvers/helpers/generateNonce.ts +++ b/backend/src/schema/resolvers/helpers/generateNonce.ts @@ -1,9 +1,9 @@ -import CONSTANTS_REGISTRATION from '@constants/registration' +import registrationConstants from '@constants/registrationBranded' // TODO: why this is not used in resolver 'requestPasswordReset'? export default function generateNonce() { return Array.from( - { length: CONSTANTS_REGISTRATION.NONCE_LENGTH }, + { length: registrationConstants.NONCE_LENGTH }, (n: number = Math.floor(Math.random() * 10)) => { return String.fromCharCode(n + 48) }, diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/schema/resolvers/inviteCodes.spec.ts index 7d335077a..f44721cc9 100644 --- a/backend/src/schema/resolvers/inviteCodes.spec.ts +++ b/backend/src/schema/resolvers/inviteCodes.spec.ts @@ -5,7 +5,7 @@ import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' -import CONSTANTS_REGISTRATION from '@constants/registration' +import registrationConstants from '@constants/registrationBranded' import Factory, { cleanDatabase } from '@db/factories' import { getDriver } from '@db/neo4j' import createServer from '@src/server' @@ -116,7 +116,7 @@ describe('inviteCodes', () => { GenerateInviteCode: { code: expect.stringMatching( new RegExp( - `^[0-9A-Z]{${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH},${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH}}$`, + `^[0-9A-Z]{${registrationConstants.INVITE_CODE_LENGTH},${registrationConstants.INVITE_CODE_LENGTH}}$`, ), ), expiresAt: null, @@ -142,7 +142,7 @@ describe('inviteCodes', () => { GenerateInviteCode: { code: expect.stringMatching( new RegExp( - `^[0-9A-Z]{${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH},${CONSTANTS_REGISTRATION.INVITE_CODE_LENGTH}}$`, + `^[0-9A-Z]{${registrationConstants.INVITE_CODE_LENGTH},${registrationConstants.INVITE_CODE_LENGTH}}$`, ), ), expiresAt: nextWeek.toISOString(), diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/schema/resolvers/passwordReset.spec.ts index 66823cc5d..d5d08265c 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/schema/resolvers/passwordReset.spec.ts @@ -5,7 +5,7 @@ import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' -import CONSTANTS_REGISTRATION from '@constants/registration' +import registrationConstants from '@constants/registrationBranded' import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' @@ -118,7 +118,7 @@ describe('passwordReset', () => { const resets = await getAllPasswordResets() const [reset] = resets const { nonce } = reset.properties - expect(nonce).toHaveLength(CONSTANTS_REGISTRATION.NONCE_LENGTH) + expect(nonce).toHaveLength(registrationConstants.NONCE_LENGTH) }) }) }) diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/schema/resolvers/passwordReset.ts index 3159d7006..f806f7249 100644 --- a/backend/src/schema/resolvers/passwordReset.ts +++ b/backend/src/schema/resolvers/passwordReset.ts @@ -7,7 +7,7 @@ import bcrypt from 'bcryptjs' import { v4 as uuid } from 'uuid' -import CONSTANTS_REGISTRATION from '@constants/registration' +import registrationConstants from '@constants/registrationBranded' import createPasswordReset from './helpers/createPasswordReset' @@ -15,7 +15,7 @@ export default { Mutation: { requestPasswordReset: async (_parent, { email }, { driver }) => { // TODO: why this is generated differntly from 'backend/src/schema/resolvers/helpers/generateNonce.js'? - const nonce = uuid().substring(0, CONSTANTS_REGISTRATION.NONCE_LENGTH) + const nonce = uuid().substring(0, registrationConstants.NONCE_LENGTH) return createPasswordReset({ driver, nonce, email }) }, resetPassword: async (_parent, { email, nonce, newPassword }, { driver }) => { diff --git a/backend/yarn.lock b/backend/yarn.lock index aefdcf732..c702695e1 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2061,6 +2061,11 @@ "@types/koa-compose" "*" "@types/node" "*" +"@types/lodash@^4.17.16": + version "4.17.16" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.16.tgz#94ae78fab4a38d73086e962d0b65c30d816bfb0a" + integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g== + "@types/long@^4.0.0": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" diff --git a/webapp/components/Registration/RegistrationSlideInvite.vue b/webapp/components/Registration/RegistrationSlideInvite.vue index 723071510..9041fd345 100644 --- a/webapp/components/Registration/RegistrationSlideInvite.vue +++ b/webapp/components/Registration/RegistrationSlideInvite.vue @@ -23,7 +23,7 @@ + + From 21f343b8cf320af6481b24c7f13dd7bf38e23aec Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 30 Apr 2025 20:12:20 +0200 Subject: [PATCH 177/266] feat(backend): distanceToMe (#8462) * calculate distance between current user and queried user * fix query for unset location * use database to calculate distance * rename distance to distance to me, 100% calculation done in DB * distanceToMe tests * lint fixes --- backend/src/graphql/types/type/User.gql | 1 + backend/src/schema/resolvers/users.spec.ts | 196 +++++++++++++++++++++ backend/src/schema/resolvers/users.ts | 30 ++++ 3 files changed, 227 insertions(+) diff --git a/backend/src/graphql/types/type/User.gql b/backend/src/graphql/types/type/User.gql index 81dd9cf5b..c2f067f12 100644 --- a/backend/src/graphql/types/type/User.gql +++ b/backend/src/graphql/types/type/User.gql @@ -50,6 +50,7 @@ type User { locationName: String location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l") + distanceToMe: Int about: String socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN") diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index ad37e2024..d666e5fc4 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -660,6 +660,202 @@ const emailNotificationSettingsMutation = gql` } ` +const distanceToMeQuery = gql` + query ($id: ID!) { + User(id: $id) { + distanceToMe + } + } +` +let 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 null', 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: [ + { + distanceToMe: null, + }, + ], + }, + 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: [ + { + 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: [ + { + 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: [ + { + distanceToMe: null, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + + describe('for noPlaceUser', () => { + it('returns null', 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: [ + { + distanceToMe: null, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + }) +}) + describe('emailNotificationSettings', () => { beforeEach(async () => { user = await Factory.build('user', { diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index f549e79a3..7c906d9a9 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -467,6 +467,36 @@ export default { }, }, User: { + distanceToMe: async (parent, _params, context, _resolveInfo) => { + // is it myself? + if (parent.id === context.user.id) { + return null + } + + const session = context.driver.session() + + const query = session.readTransaction(async (transaction) => { + const result = await transaction.run( + ` + MATCH (user:User {id: $parent.id})-[:IS_IN]->(userLoc:Location) + MATCH (me:User {id: $user.id})-[:IS_IN]->(meLoc:Location) + WITH + point({latitude: userLoc.lat, longitude: userLoc.lng}) as userPoint, + point({latitude: meLoc.lat, longitude: meLoc.lng}) as mePoint + RETURN round(point.distance(userPoint, mePoint) / 1000) as distance + `, + { parent, user: context.user }, + ) + + return result.records.map((record) => record.get('distance'))[0] + }) + + try { + return await query + } finally { + session.close() + } + }, emailNotificationSettings: async (parent, _params, _context, _resolveInfo) => { return [ { From f8864a779cae71dc7ab34e1b549f63476f301757 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 1 May 2025 10:33:53 +0200 Subject: [PATCH 178/266] move distanceToMe onto Location (#8464) --- backend/src/graphql/types/type/Location.gql | 1 + backend/src/graphql/types/type/User.gql | 1 - .../src/middleware/permissionsMiddleware.ts | 3 + .../src/schema/resolvers/locations.spec.ts | 209 +++++++++++++++++- backend/src/schema/resolvers/locations.ts | 28 +++ backend/src/schema/resolvers/users.spec.ts | 196 ---------------- backend/src/schema/resolvers/users.ts | 30 --- 7 files changed, 240 insertions(+), 228 deletions(-) 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 c2f067f12..81dd9cf5b 100644 --- a/backend/src/graphql/types/type/User.gql +++ b/backend/src/graphql/types/type/User.gql @@ -50,7 +50,6 @@ type User { locationName: String location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l") - distanceToMe: Int about: String socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN") diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 3897a61e9..7eaed8a84 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -483,6 +483,9 @@ export default shield( email: or(isMyOwn, isAdmin), emailNotificationSettings: isMyOwn, }, + Location: { + distanceToMe: isAuthenticated, + }, Report: isModerator, }, { diff --git a/backend/src/schema/resolvers/locations.spec.ts b/backend/src/schema/resolvers/locations.spec.ts index aed85da54..8b3c5b779 100644 --- a/backend/src/schema/resolvers/locations.spec.ts +++ b/backend/src/schema/resolvers/locations.spec.ts @@ -8,7 +8,7 @@ import Factory, { cleanDatabase } from '@db/factories' import { getNeode, getDriver } from '@db/neo4j' import createServer from '@src/server' -let mutate, authenticatedUser +let query, mutate, authenticatedUser const driver = getDriver() const neode = getNeode() @@ -25,6 +25,7 @@ beforeAll(async () => { } }, }) + query = createTestClient(server).query mutate = createTestClient(server).mutate }) @@ -93,3 +94,209 @@ describe('resolvers', () => { }) }) }) + +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/schema/resolvers/locations.ts b/backend/src/schema/resolvers/locations.ts index bcefa2337..f375f287f 100644 --- a/backend/src/schema/resolvers/locations.ts +++ b/backend/src/schema/resolvers/locations.ts @@ -1,3 +1,6 @@ +/* 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' @@ -20,6 +23,31 @@ export default { '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) => { diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/schema/resolvers/users.spec.ts index d666e5fc4..ad37e2024 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/schema/resolvers/users.spec.ts @@ -660,202 +660,6 @@ const emailNotificationSettingsMutation = gql` } ` -const distanceToMeQuery = gql` - query ($id: ID!) { - User(id: $id) { - distanceToMe - } - } -` -let 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 null', 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: [ - { - distanceToMe: null, - }, - ], - }, - 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: [ - { - 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: [ - { - 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: [ - { - distanceToMe: null, - }, - ], - }, - errors: undefined, - }), - ) - }) - }) - - describe('for noPlaceUser', () => { - it('returns null', 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: [ - { - distanceToMe: null, - }, - ], - }, - errors: undefined, - }), - ) - }) - }) - }) -}) - describe('emailNotificationSettings', () => { beforeEach(async () => { user = await Factory.build('user', { diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/schema/resolvers/users.ts index 7c906d9a9..f549e79a3 100644 --- a/backend/src/schema/resolvers/users.ts +++ b/backend/src/schema/resolvers/users.ts @@ -467,36 +467,6 @@ export default { }, }, User: { - distanceToMe: async (parent, _params, context, _resolveInfo) => { - // is it myself? - if (parent.id === context.user.id) { - return null - } - - const session = context.driver.session() - - const query = session.readTransaction(async (transaction) => { - const result = await transaction.run( - ` - MATCH (user:User {id: $parent.id})-[:IS_IN]->(userLoc:Location) - MATCH (me:User {id: $user.id})-[:IS_IN]->(meLoc:Location) - WITH - point({latitude: userLoc.lat, longitude: userLoc.lng}) as userPoint, - point({latitude: meLoc.lat, longitude: meLoc.lng}) as mePoint - RETURN round(point.distance(userPoint, mePoint) / 1000) as distance - `, - { parent, user: context.user }, - ) - - return result.records.map((record) => record.get('distance'))[0] - }) - - try { - return await query - } finally { - session.close() - } - }, emailNotificationSettings: async (parent, _params, _context, _resolveInfo) => { return [ { From e709c4ba062ea7d687b5abbb6c7e8dfc1c5244f9 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 1 May 2025 11:07:17 +0200 Subject: [PATCH 179/266] fix(backend): fix backend dev and dev:debug command (#8439) * fix backend dev and dev:debug command * fix dev:debug command --- backend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/package.json b/backend/package.json index 6d12190ed..169b05c39 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,8 +10,8 @@ "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", + "dev": "nodemon --exec ts-node --require tsconfig-paths/register src/index.ts -e js,ts,gql", + "dev:debug": "nodemon --exec babel-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 .", "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", From a3dffb3cb5c19fea7f6ab3b0560a666899a36599 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 08:54:03 +0200 Subject: [PATCH 180/266] refactor(backend): remove unused packages (#8466) * remove unused packages * remove more unused packages * remove unused files --- backend/.codecov.yml | 2 - backend/.graphqlconfig | 3 - backend/babel.config.json | 15 - backend/package.json | 18 +- backend/yarn.lock | 1234 +------------------------------------ 5 files changed, 28 insertions(+), 1244 deletions(-) delete mode 100644 backend/.codecov.yml delete mode 100644 backend/.graphqlconfig delete mode 100644 backend/babel.config.json 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/.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/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/package.json b/backend/package.json index 169b05c39..f1c826557 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,7 +11,7 @@ "start": "node build/src/", "build": "tsc && tsc-alias && ./scripts/build.copy.files.sh", "dev": "nodemon --exec ts-node --require tsconfig-paths/register src/index.ts -e js,ts,gql", - "dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 build/src/index.js -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 .", "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", @@ -27,28 +27,13 @@ "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", "express": "^5.1.0", @@ -95,7 +80,6 @@ "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", diff --git a/backend/yarn.lock b/backend/yarn.lock index c702695e1..9f933d6b0 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": 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": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -1588,11 +883,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" @@ -2084,7 +1374,7 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>=6", "@types/node@^22.15.2": +"@types/node@*", "@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== @@ -2160,11 +1450,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 +1671,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" @@ -2540,39 +1817,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 +1858,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 +1867,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 +2034,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== @@ -3036,24 +2263,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 +2297,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,14 +2323,6 @@ 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= - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - backo2@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -3259,16 +2430,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 +2591,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" @@ -3491,7 +2647,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== @@ -3557,15 +2713,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,21 +2776,11 @@ 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: - version "6.2.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" - integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== - commander@^9.0.0: version "9.5.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" @@ -3708,24 +2845,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 +2865,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== @@ -4208,11 +3338,6 @@ 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== - emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -4419,18 +3544,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 +3617,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,7 +3668,7 @@ 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== @@ -4732,11 +3836,6 @@ 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: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" @@ -5114,22 +4213,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" @@ -5253,11 +4336,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" @@ -5447,7 +4525,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 +4840,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== @@ -5791,13 +4869,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" @@ -6244,13 +5315,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" @@ -6271,13 +5335,6 @@ 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= - dependencies: - has "^1.0.1" - is-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" @@ -6435,11 +5492,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" @@ -6952,16 +6004,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" @@ -7097,14 +6144,6 @@ linkifyjs@^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 +6163,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" @@ -7253,14 +6287,6 @@ 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== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - make-dir@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -7813,14 +6839,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 +6872,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" @@ -8034,14 +7047,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,13 +7094,6 @@ 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== - dependencies: - "@wry/context" "^0.4.0" - optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -8118,13 +7116,6 @@ p-finally@^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 +7130,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" @@ -8204,11 +7188,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" @@ -8266,11 +7245,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" @@ -8334,11 +7308,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" @@ -8349,28 +7318,11 @@ picomatch@^4.0.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - pirates@^4.0.4: version "4.0.5" 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" @@ -8614,30 +7566,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 +7576,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 +7600,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 +7675,7 @@ 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.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== @@ -8947,7 +7825,7 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -semver@^5.6.0, semver@^5.7.0: +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 +7950,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,11 +8055,6 @@ 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" @@ -9249,14 +8115,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" @@ -9468,7 +8326,7 @@ 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: +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 +8370,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== @@ -10014,29 +8872,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 +8931,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" @@ -10207,13 +9034,6 @@ 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" - validator@^13.15.0: version "13.15.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" From 5e24f51cf79b973439eba590670ffa677c57adbe Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 09:21:26 +0200 Subject: [PATCH 181/266] refactor(webapp): remove unused packages (#8468) * remove unused packages * fix lint --- webapp/package.json | 10 - webapp/yarn.lock | 690 ++++++++------------------------------------ 2 files changed, 119 insertions(+), 581 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 574c94378..4f041d25c 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -38,19 +38,15 @@ "cropperjs": "^1.6.2", "cross-env": "~7.0.3", "date-fns": "2.22.1", - "express": "~5.1.0", "graphql": "~15.10.1", "intersection-observer": "^0.12.0", "jest-serializer-vue": "^3.1.0", - "jsonwebtoken": "~9.0.2", "linkify-it": "~5.0.0", "mapbox-gl": "1.13.2", - "node-fetch": "^2.6.1", "nuxt": "~2.12.1", "nuxt-dropzone": "^1.0.4", "nuxt-env": "~0.1.0", "sass": "1.77.6", - "stack-utils": "^2.0.3", "tippy.js": "^4.3.5", "tiptap": "~1.26.6", "tiptap-extensions": "~1.28.8", @@ -81,24 +77,18 @@ "@storybook/vue": "~7.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/vue": "5", - "@vue/cli-shared-utils": "~4.3.1", - "@vue/eslint-config-prettier": "~6.0.0", - "@vue/server-test-utils": "~1.0.0-beta.31", "@vue/test-utils": "1.3.4", "@vue/vue2-jest": "29", "async-validator": "^3.2.4", - "babel-core": "^7.0.0-bridge.0", "babel-eslint": "~10.1.0", "babel-jest": "29.7", "babel-loader": "~8.1.0", "babel-plugin-require-context-hook": "^1.0.0", - "babel-preset-vue": "~2.0.2", "core-js": "~2.6.10", "css-loader": "~3.5.2", "eslint": "^7.28.0", "eslint-config-prettier": "~10.1.2", "eslint-config-standard": "~15.0.1", - "eslint-loader": "~4.0.0", "eslint-plugin-import": "~2.31.0", "eslint-plugin-jest": "~24.4.0", "eslint-plugin-node": "~11.1.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index e17834008..090b6a83a 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -137,6 +137,15 @@ "@babel/highlight" "^7.25.7" picocolors "^1.0.0" +"@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== + dependencies: + "@babel/helper-validator-identifier" "^7.27.1" + js-tokens "^4.0.0" + picocolors "^1.1.1" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.25.7", "@babel/compat-data@^7.25.8": version "7.25.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.25.8.tgz#0376e83df5ab0eb0da18885c0140041f0747a402" @@ -265,6 +274,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" +"@babel/generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.1.tgz#862d4fad858f7208edd487c28b58144036b76230" + integrity sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w== + dependencies: + "@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" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -703,6 +723,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== + "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" @@ -718,6 +743,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== + "@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" @@ -806,7 +836,7 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.1.3", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.24.0", "@babel/parser@^7.24.1", "@babel/parser@^7.24.4": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.4.tgz#234487a110d89ad5a3ed4a8a566c36b9453e8c88" integrity sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg== @@ -825,6 +855,13 @@ dependencies: "@babel/types" "^7.25.8" +"@babel/parser@^7.27.1", "@babel/parser@^7.7.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.1.tgz#c55d5bed74449d1223701f1869b9ee345cc94cc9" + integrity sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ== + dependencies: + "@babel/types" "^7.27.1" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.7": version "7.25.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz#93969ac50ef4d68b2504b01b758af714e4cbdd64" @@ -2005,6 +2042,15 @@ "@babel/parser" "^7.26.9" "@babel/types" "^7.26.9" +"@babel/template@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.1.tgz#b9e4f55c17a92312774dfbdde1b3c01c547bbae2" + integrity sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg== + dependencies: + "@babel/code-frame" "^7.27.1" + "@babel/parser" "^7.27.1" + "@babel/types" "^7.27.1" + "@babel/traverse@^7.16.8", "@babel/traverse@^7.25.9", "@babel/traverse@^7.26.9": version "7.26.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" @@ -2018,7 +2064,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/traverse@^7.24.1", "@babel/traverse@^7.7.0", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6": +"@babel/traverse@^7.24.1", "@babel/traverse@^7.8.3", "@babel/traverse@^7.8.6": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.1.tgz#d65c36ac9dd17282175d1e4a3c49d5b7988f530c" integrity sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ== @@ -2047,6 +2093,19 @@ debug "^4.3.1" globals "^11.1.0" +"@babel/traverse@^7.7.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" + integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== + dependencies: + "@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" + "@babel/types@7.6.3": version "7.6.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.6.3.tgz#3f07d96f854f98e2fbd45c64b0cb942d11e8ba09" @@ -2056,7 +2115,7 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.6.3", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7", "@babel/types@^7.9.0": +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.24.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.6.3", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.8.7", "@babel/types@^7.9.0": version "7.24.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.0.tgz#3b951f435a92e7333eba05b7566fd297960ea1bf" integrity sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w== @@ -2082,6 +2141,14 @@ "@babel/helper-validator-identifier" "^7.25.7" to-fast-properties "^2.0.0" +"@babel/types@^7.27.1", "@babel/types@^7.7.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560" + integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.27.1" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -2464,32 +2531,6 @@ resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-9.7.0.tgz#1cf1fecfcad5e2da2332140bf3b5f23cc1c2a7f4" integrity sha512-aozo5vqjCmDoXLNUJarFZx2IN/GgGaogY4TMJ6so/WLZOWpSV7fvj2dmrV6sEAnUm1O7aCrhTibjpzeDFgNqbg== -"@hapi/address@2.x.x": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.0.0.tgz#9f05469c88cb2fd3dcd624776b54ee95c312126a" - integrity sha512-mV6T0IYqb0xL1UALPFplXYQmR0twnXG0M6jUswpquqT2sD12BOiCiLy3EvMp/Fy7s3DZElC4/aPjEjo2jeZpvw== - -"@hapi/hoek@6.x.x": - version "6.2.4" - resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-6.2.4.tgz#4b95fbaccbfba90185690890bdf1a2fbbda10595" - integrity sha512-HOJ20Kc93DkDVvjwHyHawPwPkX44sIrbXazAUDiUXaY2R9JwQGo2PhFfnQtdrsIe4igjG2fPgMra7NYw7qhy0A== - -"@hapi/joi@^15.0.1": - version "15.0.3" - resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.0.3.tgz#e94568fd859e5e945126d5675e7dd218484638a7" - integrity sha512-z6CesJ2YBwgVCi+ci8SI8zixoj8bGFn/vZb9MBPbSyoxsS2PnWYjHcyTM17VLK6tx64YVK38SDIh10hJypB+ig== - dependencies: - "@hapi/address" "2.x.x" - "@hapi/hoek" "6.x.x" - "@hapi/topo" "3.x.x" - -"@hapi/topo@3.x.x": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.0.tgz#5c47cd9637c2953db185aa957a27bcb2a8b7a6f8" - integrity sha512-gZDI/eXOIk8kP2PkUKjWu9RW8GGVd2Hkgjxyr/S7Z+JF+0mr7bAlbw+DkTRxnD580o8Kqxlnba9wvqp5aOHBww== - dependencies: - "@hapi/hoek" "6.x.x" - "@human-connection/styleguide@0.5.22": version "0.5.22" resolved "https://registry.yarnpkg.com/@human-connection/styleguide/-/styleguide-0.5.22.tgz#444ec98b8f8d1c438e2e99736dcffe432b302755" @@ -4348,13 +4389,6 @@ "@types/node" "*" "@types/responselike" "^1.0.0" -"@types/cheerio@^0.22.10": - version "0.22.14" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.14.tgz#d150889891e7db892c6a0b16bd5583cc70b3fc44" - integrity sha512-SVtcP2fvPYrebTwpyqxjxb7K5v3ZOAdH409yAEWFPpZThCSGa1K2IFfx6Rg6ttvThCBQXP4fU9WF94sqLoiQGg== - dependencies: - "@types/node" "*" - "@types/connect@*": version "3.4.33" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" @@ -4922,25 +4956,6 @@ "@vue/babel-plugin-transform-vue-jsx" "^1.1.2" camelcase "^5.0.0" -"@vue/cli-shared-utils@~4.3.1": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@vue/cli-shared-utils/-/cli-shared-utils-4.3.1.tgz#a74bf4d53825d4a4b05a84b03e023974871bc38a" - integrity sha512-lcfRalou7Z9jZgIh9PeTIpwDK7RIjr9OxfLGwbdR8czUZYUeUa67zVEMJD0OPYh/CCoREtzNbVfLPb/IYYxWEA== - dependencies: - "@hapi/joi" "^15.0.1" - chalk "^2.4.2" - execa "^1.0.0" - launch-editor "^2.2.1" - lru-cache "^5.1.1" - node-ipc "^9.1.1" - open "^6.3.0" - ora "^3.4.0" - read-pkg "^5.1.1" - request "^2.88.2" - request-promise-native "^1.0.8" - semver "^6.1.0" - strip-ansi "^6.0.0" - "@vue/compiler-core@3.3.13": version "3.3.13" resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.3.13.tgz#b3d5f8f84caee5de3f31d95cb568d899fd19c599" @@ -4990,13 +5005,6 @@ resolved "https://registry.yarnpkg.com/@vue/composition-api/-/composition-api-1.7.2.tgz#0b656f3ec39fefc2cf40aaa8c12426bcfeae1b44" integrity sha512-M8jm9J/laYrYT02665HkZ5l2fWTK4dcVg3BsDHm/pfz+MjDYwX+9FUaZyGwEyXEDonQYRCo0H7aLgdklcIELjw== -"@vue/eslint-config-prettier@~6.0.0": - version "6.0.0" - resolved "https://registry.yarnpkg.com/@vue/eslint-config-prettier/-/eslint-config-prettier-6.0.0.tgz#ad5912b308f4ae468458e02a2b05db0b9d246700" - integrity sha512-wFQmv45c3ige5EA+ngijq40YpVcIkAy0Lihupnsnd1Dao5CBbPyfCzqtejFLZX1EwH/kCJdpz3t6s+5wd3+KxQ== - dependencies: - eslint-config-prettier "^6.0.0" - "@vue/reactivity-transform@^3.2.26": version "3.3.13" resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.3.13.tgz#dc8e9be961865dc666e367e1aaaea0716afa5c90" @@ -5008,14 +5016,6 @@ estree-walker "^2.0.2" magic-string "^0.30.5" -"@vue/server-test-utils@~1.0.0-beta.31": - version "1.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@vue/server-test-utils/-/server-test-utils-1.0.0-beta.32.tgz#698424d5d76fea10ee3d2ec45f2416e31681f01e" - integrity sha512-1dxJyrO805pr4tyNckAwRojxby3g37IHpmBURInz4yccsiwHsOhSi1tR23HovOocqmu1/NttiI5rHtv9MtL9Ig== - dependencies: - "@types/cheerio" "^0.22.10" - cheerio "^1.0.0-rc.2" - "@vue/shared@3.3.13": version "3.3.13" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.3.13.tgz#4cb73cda958d77ffd389c8640cf7d93a10ac676f" @@ -5249,14 +5249,6 @@ accepts@^1.3.5, accepts@~1.3.5, accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -accepts@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-2.0.0.tgz#bbcf4ba5075467f3f2131eab3cffc73c2f5d7895" - integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== - dependencies: - mime-types "^3.0.0" - negotiator "^1.0.0" - accounting@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/accounting/-/accounting-0.4.1.tgz#87dd4103eff7f4460f1e186f5c677ed6cf566883" @@ -6393,11 +6385,6 @@ axios@^0.19.2: dependencies: follow-redirects "1.5.10" -babel-core@^7.0.0-bridge.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" @@ -6410,11 +6397,6 @@ babel-eslint@~10.1.0: eslint-visitor-keys "^1.0.0" resolve "^1.12.0" -babel-helper-vue-jsx-merge-props@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6" - integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg== - babel-jest@29.7, babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" @@ -6515,20 +6497,6 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-jsx-event-modifiers@^2.0.2: - version "2.0.5" - resolved "https://registry.yarnpkg.com/babel-plugin-jsx-event-modifiers/-/babel-plugin-jsx-event-modifiers-2.0.5.tgz#93e6ebb5d7553bb08f9fedbf7a0bee3af09a0472" - integrity sha512-tWGnCk0whZ+nZcj9tYLw4+y08tPJXqaEjIxRJZS6DkUUae72Kz4BsoGpxt/Kow7mmgQJpvFCw8IPLSNh5rkZCg== - -babel-plugin-jsx-v-model@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-jsx-v-model/-/babel-plugin-jsx-v-model-2.0.3.tgz#c396416b99cb1af782087315ae1d3e62e070f47d" - integrity sha512-SIx3Y3XxwGEz56Q1atwr5GaZsxJ2IRYmn5dl38LFkaTAvjnbNQxsZHO+ylJPsd+Hmv+ixJBYYFEekPBTHwiGfQ== - dependencies: - babel-plugin-syntax-jsx "^6.18.0" - html-tags "^2.0.0" - svg-tags "^1.0.0" - babel-plugin-macros@^2.0.0: version "2.6.1" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.6.1.tgz#41f7ead616fc36f6a93180e89697f69f51671181" @@ -6572,13 +6540,6 @@ babel-plugin-syntax-jsx@^6.18.0: resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" integrity sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY= -babel-plugin-transform-vue-jsx@^3.5.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-3.7.0.tgz#d40492e6692a36b594f7e9a1928f43e969740960" - integrity sha512-W39X07/n3oJMQd8tALBO+440NraGSF//Lo1ydd/9Nme3+QiRGFBb1Q39T9iixh0jZPPbfv3so18tNoIgLatymw== - dependencies: - esutils "^2.0.2" - babel-polyfill@6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" @@ -6614,17 +6575,6 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" -babel-preset-vue@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/babel-preset-vue/-/babel-preset-vue-2.0.2.tgz#cfadf1bd736125397481b5f8525ced0049a0c71f" - integrity sha1-z63xvXNhJTl0gbX4UlztAEmgxx8= - dependencies: - babel-helper-vue-jsx-merge-props "^2.0.2" - babel-plugin-jsx-event-modifiers "^2.0.2" - babel-plugin-jsx-v-model "^2.0.1" - babel-plugin-syntax-jsx "^6.18.0" - babel-plugin-transform-vue-jsx "^3.5.0" - babel-runtime@^6.18.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" @@ -6741,21 +6691,6 @@ body-parser@1.20.3, body-parser@^1.18.3: type-is "~1.6.18" unpipe "1.0.0" -body-parser@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-2.2.0.tgz#f7a9656de305249a715b549b7b8fd1ab9dfddcfa" - integrity sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg== - dependencies: - bytes "^3.1.2" - content-type "^1.0.5" - debug "^4.4.0" - http-errors "^2.0.0" - iconv-lite "^0.6.3" - on-finished "^2.4.1" - qs "^6.14.0" - raw-body "^3.0.0" - type-is "^2.0.0" - boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -6931,11 +6866,6 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= - buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -6982,7 +6912,7 @@ bytes@3.0.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= -bytes@3.1.2, bytes@^3.1.2: +bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -7336,18 +7266,6 @@ check-types@^7.3.0: resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.4.0.tgz#0378ec1b9616ec71f774931a3c6516fad8c152f4" integrity sha512-YbulWHdfP99UfZ73NcUDlNJhEIDgm9Doq9GhpyXbF+7Aegi3CVV7qqMCKTTqJxlvEvnQBp9IA+dxsGN6xK/nSg== -cheerio@^1.0.0-rc.2: - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" - integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.1" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" - "chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.1, chokidar@^3.4.1: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" @@ -7466,11 +7384,6 @@ cli-cursor@^2.0.0, cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" -cli-spinners@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.1.0.tgz#22c34b4d51f573240885b201efda4e4ec9fff3c7" - integrity sha512-8B00fJOEh1HPrx4fo5eW16XmE1PcL1tGpGrxy63CXGP9nHdPBN63X75hA1zhvQuhVztJWLqV58Roj2qlNM7cAA== - cli-truncate@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574" @@ -7570,11 +7483,6 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -7819,14 +7727,7 @@ content-disposition@0.5.4: dependencies: safe-buffer "5.2.1" -content-disposition@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-1.0.0.tgz#844426cb398f934caefcbb172200126bc7ceace2" - integrity sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg== - dependencies: - safe-buffer "5.2.1" - -content-type@^1.0.4, content-type@^1.0.5, content-type@~1.0.4, content-type@~1.0.5: +content-type@^1.0.4, content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== @@ -7846,11 +7747,6 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie-signature@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.2.2.tgz#57c7fc3cc293acab9fec54d73e15690ebe4a1793" - integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== - cookie-universal-nuxt@~2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.2.2.tgz#107815f03f5b769de7018670d6370368205387bb" @@ -7882,11 +7778,6 @@ cookie@^0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -cookie@^0.7.1: - version "0.7.2" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" - integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== - copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -8186,7 +8077,7 @@ css-select-base-adapter@^0.1.1: resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== -css-select@^1.1.0, css-select@~1.2.0: +css-select@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= @@ -8498,7 +8389,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.4: 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== @@ -8610,13 +8501,6 @@ deepmerge@^4.1.1, deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - 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" @@ -8699,7 +8583,7 @@ denodeify@^1.2.1: resolved "https://registry.yarnpkg.com/denodeify/-/denodeify-1.2.1.tgz#3a36287f5034e699e7577901052c2e6c94251631" integrity sha1-OjYof1A05pnnV3kBBSwubJQlFjE= -depd@2.0.0, depd@^2.0.0: +depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -8838,7 +8722,7 @@ dom-event-types@^1.0.0: resolved "https://registry.yarnpkg.com/dom-event-types/-/dom-event-types-1.1.0.tgz#120c1f92ddea7758db1ccee0a100a33c39f4701b" integrity sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ== -dom-serializer@0, dom-serializer@~0.1.1: +dom-serializer@0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== @@ -9013,11 +8897,6 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -easy-stack@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/easy-stack/-/easy-stack-1.0.0.tgz#12c91b3085a37f0baa336e9486eac4bf94e3e788" - integrity sha1-EskbMIWjfwuqM26UhurEv5Tj54g= - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -9026,13 +8905,6 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -ecdsa-sig-formatter@1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - editorconfig@^0.15.3: version "0.15.3" resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" @@ -9139,16 +9011,16 @@ emotion-theming@^10.0.19: "@emotion/weak-memoize" "0.2.5" hoist-non-react-statics "^3.3.0" -encodeurl@^2.0.0, encodeurl@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" - integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encodeurl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" + integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== + encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" @@ -9196,7 +9068,7 @@ enquirer@^2.3.5: ansi-colors "^4.1.1" strip-ansi "^6.0.1" -entities@^1.1.1, entities@~1.1.1: +entities@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -9494,7 +9366,7 @@ escalade@^3.2.0: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== -escape-html@^1.0.3, 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= @@ -9538,13 +9410,6 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@^6.0.0: - version "6.15.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" - integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== - dependencies: - get-stdin "^6.0.0" - 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" @@ -9564,17 +9429,6 @@ eslint-import-resolver-node@^0.3.9: is-core-module "^2.13.0" resolve "^1.22.4" -eslint-loader@~4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-4.0.0.tgz#ab096ce9168fa167e4159afff66692c173fc7b79" - integrity sha512-QoaFRdh3oXt5i2uonSjO8dDnncsG05w7qvA7yYMvGDne8zAEk9R+R1rsfunp3OKVdO5mAJelf1x2Z1kYp664kA== - dependencies: - fs-extra "^9.0.0" - loader-fs-cache "^1.0.3" - loader-utils "^2.0.0" - object-hash "^2.0.3" - schema-utils "^2.6.5" - 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" @@ -9714,16 +9568,16 @@ eslint-utils@^2.1.0: dependencies: eslint-visitor-keys "^1.1.0" -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.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@^1.3.0: +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-visitor-keys@^1.1.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@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" @@ -9859,11 +9713,6 @@ etag@^1.8.1, etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-pubsub@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/event-pubsub/-/event-pubsub-4.3.0.tgz#f68d816bc29f1ec02c539dc58c8dd40ce72cb36e" - integrity sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ== - eventemitter3@^3.1.0: version "3.1.2" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" @@ -10015,39 +9864,6 @@ express@^4.16.3, express@^4.17.1: utils-merge "1.0.1" vary "~1.1.2" -express@~5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/express/-/express-5.1.0.tgz#d31beaf715a0016f0d53f47d3b4d7acf28c75cc9" - integrity sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA== - dependencies: - accepts "^2.0.0" - body-parser "^2.2.0" - content-disposition "^1.0.0" - content-type "^1.0.5" - cookie "^0.7.1" - cookie-signature "^1.2.1" - debug "^4.4.0" - encodeurl "^2.0.0" - escape-html "^1.0.3" - etag "^1.8.1" - finalhandler "^2.1.0" - fresh "^2.0.0" - http-errors "^2.0.0" - merge-descriptors "^2.0.0" - mime-types "^3.0.0" - on-finished "^2.4.1" - once "^1.4.0" - parseurl "^1.3.3" - proxy-addr "^2.0.7" - qs "^6.14.0" - range-parser "^1.2.1" - router "^2.2.0" - send "^1.1.0" - serve-static "^2.2.0" - statuses "^2.0.1" - type-is "^2.0.1" - vary "^1.1.2" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -10309,27 +10125,6 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" -finalhandler@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-2.1.0.tgz#72306373aa89d05a8242ed569ed86a1bff7c561f" - integrity sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q== - dependencies: - debug "^4.4.0" - encodeurl "^2.0.0" - escape-html "^1.0.3" - on-finished "^2.4.1" - parseurl "^1.3.3" - statuses "^2.0.1" - -find-cache-dir@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" - integrity sha1-yN765XyKUqinhPnjHFfHQumToLk= - dependencies: - commondir "^1.0.1" - mkdirp "^0.5.1" - pkg-dir "^1.0.0" - find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" @@ -10524,11 +10319,6 @@ fresh@0.5.2, fresh@^0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-2.0.0.tgz#8dd7df6a1b3a1b3a5cf186c05a5dd267622635a4" - integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== - from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -10583,16 +10373,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.0.0.tgz#b6afc31036e247b2466dc99c29ae797d5d4580a3" - integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^1.0.0" - fs-extra@^9.1.0: version "9.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" @@ -10770,11 +10550,6 @@ get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== - get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -11530,7 +11305,7 @@ htmlparser2@5.0.1: domutils "^2.4.2" entities "^2.0.0" -htmlparser2@^3.3.0, htmlparser2@^3.9.1: +htmlparser2@^3.3.0: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -11559,7 +11334,7 @@ http-call@^5.2.2: parse-json "^4.0.0" tunnel-agent "^0.6.0" -http-errors@2.0.0, http-errors@^2.0.0: +http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== @@ -11677,7 +11452,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3, iconv-lite@^0.6.3: +iconv-lite@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -12054,6 +11829,13 @@ is-core-module@^2.13.0, is-core-module@^2.15.1, is-core-module@^2.9.0: 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-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -12306,11 +12088,6 @@ is-promise@^2.1.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= -is-promise@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" - integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== - is-redirect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" @@ -12989,18 +12766,6 @@ js-beautify@^1.6.12: glob "^8.0.3" nopt "^6.0.0" -js-message@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.5.tgz#2300d24b1af08e89dd095bc1a4c9c9cfcb892d15" - integrity sha1-IwDSSxrwjondCVvBpMnJz8uJLRU= - -js-queue@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/js-queue/-/js-queue-2.0.0.tgz#362213cf860f468f0125fc6c96abc1742531f948" - integrity sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug= - dependencies: - easy-stack "^1.0.0" - "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -13191,22 +12956,6 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= -jsonwebtoken@~9.0.2: - version "9.0.2" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" - integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== - dependencies: - jws "^3.2.2" - lodash.includes "^4.3.0" - lodash.isboolean "^3.0.3" - lodash.isinteger "^4.0.4" - lodash.isnumber "^3.0.3" - lodash.isplainobject "^4.0.6" - lodash.isstring "^4.0.1" - lodash.once "^4.0.0" - ms "^2.1.1" - semver "^7.5.4" - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -13217,23 +12966,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -jwa@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" - integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" - integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== - dependencies: - jwa "^1.4.1" - safe-buffer "^5.0.1" - kdbush@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/kdbush/-/kdbush-3.0.0.tgz#f8484794d47004cc2d85ed3a79353dbe0abc2bf0" @@ -13449,14 +13181,6 @@ load-json-file@^5.2.0: strip-bom "^3.0.0" type-fest "^0.3.0" -loader-fs-cache@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.3.tgz#f08657646d607078be2f0a032f8bd69dd6f277d9" - integrity sha512-ldcgZpjNJj71n+2Mf6yetz+c9bM4xpKtNds4LbqXzU/PTdeAX0g3ytnU1AJMEcTk2Lex4Smpe3Q/eCTsvUBxbA== - dependencies: - find-cache-dir "^0.1.1" - mkdirp "^0.5.1" - loader-runner@^2.3.1, loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -13537,36 +13261,6 @@ lodash.identity@3.0.0: resolved "https://registry.yarnpkg.com/lodash.identity/-/lodash.identity-3.0.0.tgz#ad7bc6a4e647d79c972e1b80feef7af156267876" integrity sha1-rXvGpOZH15yXLhuA/u968VYmeHY= -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== - lodash.kebabcase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -13582,11 +13276,6 @@ lodash.merge@^4.6.1, lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - lodash.pickby@4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff" @@ -13644,13 +13333,6 @@ log-symbols@^1.0.2: dependencies: chalk "^1.0.0" -log-symbols@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" - integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== - dependencies: - chalk "^2.0.1" - log-update@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708" @@ -13904,11 +13586,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -media-typer@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-1.1.0.tgz#6ab74b8f2d3320f2064b2a87a38e7931ff3a5561" - integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== - memoizerific@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/memoizerific/-/memoizerific-1.11.3.tgz#7c87a4646444c32d75438570905f2dbd1b1a805a" @@ -13959,11 +13636,6 @@ merge-descriptors@1.0.3: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ== -merge-descriptors@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-2.0.0.tgz#ea922f660635a2249ee565e0449f951e6b603808" - integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== - merge-source-map@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" @@ -14294,11 +13966,6 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== -mime-db@^1.54.0: - version "1.54.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.54.0.tgz#cddb3ee4f9c64530dff640236661d42cb6a314f5" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - mime-types@^2.1.12, mime-types@^2.1.19, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" @@ -14306,13 +13973,6 @@ mime-types@^2.1.12, mime-types@^2.1.19, mime-types@~2.1.19, mime-types@~2.1.24, dependencies: mime-db "1.52.0" -mime-types@^3.0.0, mime-types@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-3.0.1.tgz#b1d94d6997a9b32fd69ebaed0db73de8acb519ce" - integrity sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA== - dependencies: - mime-db "^1.54.0" - mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" @@ -14611,11 +14271,6 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -negotiator@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" - integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== - neo-async@^2.5.0, neo-async@^2.6.0, neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" @@ -14658,7 +14313,7 @@ node-fetch@^2.0.0: dependencies: whatwg-url "^5.0.0" -node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1: +node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -14668,15 +14323,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-ipc@^9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/node-ipc/-/node-ipc-9.1.1.tgz#4e245ed6938e65100e595ebc5dc34b16e8dd5d69" - integrity sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w== - dependencies: - event-pubsub "4.3.0" - js-message "1.0.5" - js-queue "2.0.0" - node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" @@ -14978,11 +14624,6 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" - integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== - object-inspect@^1.13.1: version "1.13.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff" @@ -15121,7 +14762,7 @@ object.values@^1.1.0, object.values@^1.2.0: define-properties "^1.2.1" es-object-atoms "^1.0.0" -on-finished@2.4.1, on-finished@^2.3.0, on-finished@^2.4.1: +on-finished@2.4.1, on-finished@^2.3.0: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== @@ -15168,13 +14809,6 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -open@^6.3.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/open/-/open-6.4.0.tgz#5c13e96d0dc894686164f18965ecfe889ecfc8a9" - integrity sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg== - dependencies: - is-wsl "^1.1.0" - opener@1.5.1, opener@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" @@ -15224,18 +14858,6 @@ optionator@^0.9.1: prelude-ls "^1.2.1" type-check "^0.4.0" -ora@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" - integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== - dependencies: - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-spinners "^2.0.0" - log-symbols "^2.2.0" - strip-ansi "^5.2.0" - wcwidth "^1.0.1" - orderedmap@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.0.tgz#dc41a147130b51e203e8523ea5ea57724af2b63d" @@ -15454,13 +15076,6 @@ parse5@4.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== - dependencies: - "@types/node" "*" - parse5@^7.0.0, parse5@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" @@ -15468,7 +15083,7 @@ parse5@^7.0.0, parse5@^7.1.1: dependencies: entities "^4.4.0" -parseurl@^1.3.2, parseurl@^1.3.3, parseurl@~1.3.3: +parseurl@^1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -15566,11 +15181,6 @@ path-to-regexp@0.1.12: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== -path-to-regexp@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-8.2.0.tgz#73990cc29e57a3ff2a0d914095156df5db79e8b4" - integrity sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ== - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -15614,7 +15224,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picocolors@^1.1.0: +picocolors@^1.1.0, 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== @@ -15679,13 +15289,6 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - integrity sha1-ektQio1bstYp1EcFb/TpyTFM89Q= - dependencies: - find-up "^1.0.0" - pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -16686,7 +16289,7 @@ protocol-buffers-schema@^3.3.1: resolved "https://registry.yarnpkg.com/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz#77bc75a48b2ff142c1ad5b5b90c94cd0fa2efd03" integrity sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw== -proxy-addr@^2.0.7, proxy-addr@~2.0.7: +proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== @@ -16801,7 +16404,7 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -qs@^6.10.0, qs@^6.14.0, qs@^6.6.0: +qs@^6.10.0, qs@^6.6.0: version "6.14.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== @@ -16893,16 +16496,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-3.0.0.tgz#25b3476f07a51600619dae3fe82ddc28a36e5e0f" - integrity sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.6.3" - unpipe "1.0.0" - raw-loader@3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-3.1.0.tgz#5e9d399a5a222cc0de18f42c3bc5e49677532b3f" @@ -17115,7 +16708,7 @@ read-pkg@^1.0.0: normalize-package-data "^2.3.2" path-type "^1.0.0" -read-pkg@^5.1.1, read-pkg@^5.2.0: +read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== @@ -17466,7 +17059,7 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request-promise-native@^1.0.5, request-promise-native@^1.0.8: +request-promise-native@^1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== @@ -17475,7 +17068,7 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -"request@>=2.76.0 <3.0.0", request@^2.87.0, request@^2.88.2: +"request@>=2.76.0 <3.0.0", request@^2.87.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -17570,7 +17163,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.0.tgz#c1a0028c2d166ec2fbf7d0644584927e76e7400e" integrity sha512-6K/gDlqgQscOlg9fSRpWstA8sYe8rbELsSTNpx+3kTrsVCzvSl0zIvRErM7fdl9ERWDsKnrLnwB+Ne89918XOg== -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.2.0, resolve@^1.20.0, resolve@^1.8.1: +resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.14.2, resolve@^1.2.0, resolve@^1.20.0, resolve@^1.8.1: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -17579,6 +17172,15 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12. path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.12.0: + 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.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" @@ -17667,17 +17269,6 @@ rope-sequence@^1.3.0: resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b" integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg== -router@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" - integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== - dependencies: - debug "^4.4.0" - depd "^2.0.0" - is-promise "^4.0.0" - parseurl "^1.3.3" - path-to-regexp "^8.0.0" - run-parallel@^1.1.9: version "1.1.10" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" @@ -17877,23 +17468,6 @@ send@0.19.0: range-parser "~1.2.1" statuses "2.0.1" -send@^1.1.0, send@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/send/-/send-1.2.0.tgz#32a7554fb777b831dfa828370f773a3808d37212" - integrity sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw== - dependencies: - debug "^4.3.5" - encodeurl "^2.0.0" - escape-html "^1.0.3" - etag "^1.8.1" - fresh "^2.0.0" - http-errors "^2.0.0" - mime-types "^3.0.1" - ms "^2.1.3" - on-finished "^2.4.1" - range-parser "^1.2.1" - statuses "^2.0.1" - sentence-case@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-2.1.1.tgz#1f6e2dda39c168bf92d13f86d4a918933f667ed4" @@ -17929,16 +17503,6 @@ serve-static@1.16.2, serve-static@^1.14.1: parseurl "~1.3.3" send "0.19.0" -serve-static@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-2.2.0.tgz#9c02564ee259bdd2251b82d659a2e7e1938d66f9" - integrity sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ== - dependencies: - encodeurl "^2.0.0" - escape-html "^1.0.3" - parseurl "^1.3.3" - send "^1.2.0" - server-destroy@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/server-destroy/-/server-destroy-1.0.1.tgz#f13bf928e42b9c3e79383e61cc3998b5d14e6cdd" @@ -18405,7 +17969,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -statuses@2.0.1, statuses@^2.0.1: +statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== @@ -19546,15 +19110,6 @@ type-is@^1.6.16, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -type-is@^2.0.0, type-is@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-2.0.1.tgz#64f6cf03f92fce4015c2b224793f6bdd4b068c97" - integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== - dependencies: - content-type "^1.0.5" - media-typer "^1.1.0" - mime-types "^3.0.0" - typed-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" @@ -20443,13 +19998,6 @@ watchpack@^1.7.4: chokidar "^3.4.1" watchpack-chokidar2 "^2.0.1" -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From 839ffb29eeaf3274c16242deb59ca7ee10653bcf Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 09:47:39 +0200 Subject: [PATCH 182/266] refactor(backend): move resolvers into graphql folder (#8470) * move resolvers into graphql folder * lint fixes --- backend/src/db/factories.ts | 2 +- .../20200123150105-merge_duplicate_user_accounts.ts | 2 +- backend/src/{schema => graphql}/resolvers/Upload.ts | 0 backend/src/{schema => graphql}/resolvers/badges.spec.ts | 0 backend/src/{schema => graphql}/resolvers/badges.ts | 0 backend/src/{schema => graphql}/resolvers/comments.spec.ts | 0 backend/src/{schema => graphql}/resolvers/comments.ts | 0 backend/src/{schema => graphql}/resolvers/donations.spec.ts | 0 backend/src/{schema => graphql}/resolvers/donations.ts | 0 backend/src/{schema => graphql}/resolvers/emails.spec.ts | 0 backend/src/{schema => graphql}/resolvers/emails.ts | 0 backend/src/{schema => graphql}/resolvers/embeds.spec.ts | 0 backend/src/{schema => graphql}/resolvers/embeds.ts | 0 .../{schema => graphql}/resolvers/embeds/findProvider.spec.ts | 0 .../src/{schema => graphql}/resolvers/embeds/findProvider.ts | 0 backend/src/{schema => graphql}/resolvers/embeds/scraper.ts | 0 .../src/{schema => graphql}/resolvers/filter-posts.spec.ts | 0 backend/src/{schema => graphql}/resolvers/follow.spec.ts | 0 backend/src/{schema => graphql}/resolvers/follow.ts | 0 backend/src/{schema => graphql}/resolvers/groups.spec.ts | 0 backend/src/{schema => graphql}/resolvers/groups.ts | 0 backend/src/{schema => graphql}/resolvers/helpers/Resolver.ts | 0 .../resolvers/helpers/createPasswordReset.ts | 0 backend/src/{schema => graphql}/resolvers/helpers/events.ts | 0 .../resolvers/helpers/existingEmailAddress.ts | 0 .../resolvers/helpers/filterForMutedUsers.ts | 2 +- .../resolvers/helpers/filterInvisiblePosts.ts | 0 .../resolvers/helpers/filterPostsOfMyGroups.ts | 0 .../resolvers/helpers/generateInviteCode.ts | 0 .../{schema => graphql}/resolvers/helpers/generateNonce.ts | 0 .../{schema => graphql}/resolvers/helpers/normalizeEmail.ts | 0 backend/src/{schema => graphql}/resolvers/images.ts | 0 .../src/{schema => graphql}/resolvers/images/images.spec.ts | 0 backend/src/{schema => graphql}/resolvers/images/images.ts | 0 backend/src/{schema => graphql}/resolvers/index.ts | 0 backend/src/{schema => graphql}/resolvers/inviteCodes.spec.ts | 0 backend/src/{schema => graphql}/resolvers/inviteCodes.ts | 0 backend/src/{schema => graphql}/resolvers/locations.spec.ts | 0 backend/src/{schema => graphql}/resolvers/locations.ts | 0 backend/src/{schema => graphql}/resolvers/messages.spec.ts | 0 backend/src/{schema => graphql}/resolvers/messages.ts | 0 backend/src/{schema => graphql}/resolvers/moderation.spec.ts | 0 backend/src/{schema => graphql}/resolvers/moderation.ts | 0 .../src/{schema => graphql}/resolvers/notifications.spec.ts | 0 backend/src/{schema => graphql}/resolvers/notifications.ts | 0 .../src/{schema => graphql}/resolvers/observePosts.spec.ts | 0 .../src/{schema => graphql}/resolvers/passwordReset.spec.ts | 0 backend/src/{schema => graphql}/resolvers/passwordReset.ts | 0 backend/src/{schema => graphql}/resolvers/posts.spec.ts | 0 backend/src/{schema => graphql}/resolvers/posts.ts | 0 .../src/{schema => graphql}/resolvers/postsInGroups.spec.ts | 0 .../src/{schema => graphql}/resolvers/registration.spec.ts | 0 backend/src/{schema => graphql}/resolvers/registration.ts | 0 backend/src/{schema => graphql}/resolvers/reports.spec.ts | 0 backend/src/{schema => graphql}/resolvers/reports.ts | 0 backend/src/{schema => graphql}/resolvers/roles.ts | 0 backend/src/{schema => graphql}/resolvers/rooms.spec.ts | 0 backend/src/{schema => graphql}/resolvers/rooms.ts | 0 backend/src/{schema => graphql}/resolvers/searches.spec.ts | 0 backend/src/{schema => graphql}/resolvers/searches.ts | 0 .../resolvers/searches/queryString.spec.ts | 0 .../src/{schema => graphql}/resolvers/searches/queryString.ts | 0 backend/src/{schema => graphql}/resolvers/shout.spec.ts | 0 backend/src/{schema => graphql}/resolvers/shout.ts | 0 backend/src/{schema => graphql}/resolvers/socialMedia.spec.ts | 0 backend/src/{schema => graphql}/resolvers/socialMedia.ts | 0 backend/src/{schema => graphql}/resolvers/statistics.spec.ts | 0 backend/src/{schema => graphql}/resolvers/statistics.ts | 0 .../{schema => graphql}/resolvers/transactions/inviteCodes.ts | 0 backend/src/{schema => graphql}/resolvers/userData.spec.ts | 0 backend/src/{schema => graphql}/resolvers/userData.ts | 0 .../src/{schema => graphql}/resolvers/user_management.spec.ts | 0 backend/src/{schema => graphql}/resolvers/user_management.ts | 0 backend/src/{schema => graphql}/resolvers/users.spec.ts | 0 backend/src/{schema => graphql}/resolvers/users.ts | 0 .../src/{schema => graphql}/resolvers/users/location.spec.ts | 0 backend/src/{schema => graphql}/resolvers/users/location.ts | 0 .../{schema => graphql}/resolvers/users/mutedUsers.spec.ts | 0 .../{schema => graphql}/resolvers/viewedTeaserCount.spec.ts | 0 backend/src/{schema/index.ts => graphql/schema.ts} | 0 .../src/middleware/notifications/notificationsMiddleware.ts | 4 ++-- backend/src/middleware/permissionsMiddleware.ts | 2 +- backend/src/server.ts | 2 +- backend/tsconfig.json | 1 - 84 files changed, 7 insertions(+), 8 deletions(-) rename backend/src/{schema => graphql}/resolvers/Upload.ts (100%) rename backend/src/{schema => graphql}/resolvers/badges.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/badges.ts (100%) rename backend/src/{schema => graphql}/resolvers/comments.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/comments.ts (100%) rename backend/src/{schema => graphql}/resolvers/donations.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/donations.ts (100%) rename backend/src/{schema => graphql}/resolvers/emails.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/emails.ts (100%) rename backend/src/{schema => graphql}/resolvers/embeds.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/embeds.ts (100%) rename backend/src/{schema => graphql}/resolvers/embeds/findProvider.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/embeds/findProvider.ts (100%) rename backend/src/{schema => graphql}/resolvers/embeds/scraper.ts (100%) rename backend/src/{schema => graphql}/resolvers/filter-posts.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/follow.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/follow.ts (100%) rename backend/src/{schema => graphql}/resolvers/groups.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/groups.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/Resolver.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/createPasswordReset.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/events.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/existingEmailAddress.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/filterForMutedUsers.ts (93%) rename backend/src/{schema => graphql}/resolvers/helpers/filterInvisiblePosts.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/filterPostsOfMyGroups.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/generateInviteCode.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/generateNonce.ts (100%) rename backend/src/{schema => graphql}/resolvers/helpers/normalizeEmail.ts (100%) rename backend/src/{schema => graphql}/resolvers/images.ts (100%) rename backend/src/{schema => graphql}/resolvers/images/images.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/images/images.ts (100%) rename backend/src/{schema => graphql}/resolvers/index.ts (100%) rename backend/src/{schema => graphql}/resolvers/inviteCodes.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/inviteCodes.ts (100%) rename backend/src/{schema => graphql}/resolvers/locations.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/locations.ts (100%) rename backend/src/{schema => graphql}/resolvers/messages.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/messages.ts (100%) rename backend/src/{schema => graphql}/resolvers/moderation.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/moderation.ts (100%) rename backend/src/{schema => graphql}/resolvers/notifications.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/notifications.ts (100%) rename backend/src/{schema => graphql}/resolvers/observePosts.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/passwordReset.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/passwordReset.ts (100%) rename backend/src/{schema => graphql}/resolvers/posts.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/posts.ts (100%) rename backend/src/{schema => graphql}/resolvers/postsInGroups.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/registration.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/registration.ts (100%) rename backend/src/{schema => graphql}/resolvers/reports.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/reports.ts (100%) rename backend/src/{schema => graphql}/resolvers/roles.ts (100%) rename backend/src/{schema => graphql}/resolvers/rooms.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/rooms.ts (100%) rename backend/src/{schema => graphql}/resolvers/searches.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/searches.ts (100%) rename backend/src/{schema => graphql}/resolvers/searches/queryString.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/searches/queryString.ts (100%) rename backend/src/{schema => graphql}/resolvers/shout.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/shout.ts (100%) rename backend/src/{schema => graphql}/resolvers/socialMedia.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/socialMedia.ts (100%) rename backend/src/{schema => graphql}/resolvers/statistics.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/statistics.ts (100%) rename backend/src/{schema => graphql}/resolvers/transactions/inviteCodes.ts (100%) rename backend/src/{schema => graphql}/resolvers/userData.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/userData.ts (100%) rename backend/src/{schema => graphql}/resolvers/user_management.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/user_management.ts (100%) rename backend/src/{schema => graphql}/resolvers/users.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/users.ts (100%) rename backend/src/{schema => graphql}/resolvers/users/location.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/users/location.ts (100%) rename backend/src/{schema => graphql}/resolvers/users/mutedUsers.spec.ts (100%) rename backend/src/{schema => graphql}/resolvers/viewedTeaserCount.spec.ts (100%) rename backend/src/{schema/index.ts => graphql/schema.ts} (100%) 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/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 100% rename from backend/src/schema/resolvers/comments.spec.ts rename to backend/src/graphql/resolvers/comments.spec.ts 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 100% rename from backend/src/schema/resolvers/emails.ts rename to backend/src/graphql/resolvers/emails.ts 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 100% rename from backend/src/schema/resolvers/follow.ts rename to backend/src/graphql/resolvers/follow.ts diff --git a/backend/src/schema/resolvers/groups.spec.ts b/backend/src/graphql/resolvers/groups.spec.ts similarity index 100% rename from backend/src/schema/resolvers/groups.spec.ts rename to backend/src/graphql/resolvers/groups.spec.ts diff --git a/backend/src/schema/resolvers/groups.ts b/backend/src/graphql/resolvers/groups.ts similarity index 100% rename from backend/src/schema/resolvers/groups.ts rename to backend/src/graphql/resolvers/groups.ts 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 100% rename from backend/src/schema/resolvers/images/images.ts rename to backend/src/graphql/resolvers/images/images.ts 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/schema/resolvers/locations.spec.ts b/backend/src/graphql/resolvers/locations.spec.ts similarity index 100% rename from backend/src/schema/resolvers/locations.spec.ts rename to backend/src/graphql/resolvers/locations.spec.ts diff --git a/backend/src/schema/resolvers/locations.ts b/backend/src/graphql/resolvers/locations.ts similarity index 100% rename from backend/src/schema/resolvers/locations.ts rename to backend/src/graphql/resolvers/locations.ts diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/graphql/resolvers/messages.spec.ts similarity index 100% rename from backend/src/schema/resolvers/messages.spec.ts rename to backend/src/graphql/resolvers/messages.spec.ts diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/graphql/resolvers/messages.ts similarity index 100% rename from backend/src/schema/resolvers/messages.ts rename to backend/src/graphql/resolvers/messages.ts 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 100% rename from backend/src/schema/resolvers/notifications.ts rename to backend/src/graphql/resolvers/notifications.ts diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/graphql/resolvers/observePosts.spec.ts similarity index 100% rename from backend/src/schema/resolvers/observePosts.spec.ts rename to backend/src/graphql/resolvers/observePosts.spec.ts diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/graphql/resolvers/passwordReset.spec.ts similarity index 100% rename from backend/src/schema/resolvers/passwordReset.spec.ts rename to backend/src/graphql/resolvers/passwordReset.spec.ts 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 100% rename from backend/src/schema/resolvers/posts.spec.ts rename to backend/src/graphql/resolvers/posts.spec.ts 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 100% rename from backend/src/schema/resolvers/postsInGroups.spec.ts rename to backend/src/graphql/resolvers/postsInGroups.spec.ts diff --git a/backend/src/schema/resolvers/registration.spec.ts b/backend/src/graphql/resolvers/registration.spec.ts similarity index 100% rename from backend/src/schema/resolvers/registration.spec.ts rename to backend/src/graphql/resolvers/registration.spec.ts diff --git a/backend/src/schema/resolvers/registration.ts b/backend/src/graphql/resolvers/registration.ts similarity index 100% rename from backend/src/schema/resolvers/registration.ts rename to backend/src/graphql/resolvers/registration.ts 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 100% rename from backend/src/schema/resolvers/rooms.ts rename to backend/src/graphql/resolvers/rooms.ts 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 100% rename from backend/src/schema/resolvers/socialMedia.ts rename to backend/src/graphql/resolvers/socialMedia.ts 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 100% rename from backend/src/schema/resolvers/user_management.ts rename to backend/src/graphql/resolvers/user_management.ts diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/graphql/resolvers/users.spec.ts similarity index 100% rename from backend/src/schema/resolvers/users.spec.ts rename to backend/src/graphql/resolvers/users.spec.ts 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 100% rename from backend/src/schema/resolvers/users/location.ts rename to backend/src/graphql/resolvers/users/location.ts 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/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 4926dc94e..4459d23b8 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,9 +1,11 @@ +/* eslint-disable import/no-cycle */ /* 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-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */ +import { getUnreadRoomsCount } from '@graphql/resolvers/rooms' import { sendMail } from '@middleware/helpers/email/sendMail' import { chatMessageTemplate, @@ -11,8 +13,6 @@ import { } from '@middleware/helpers/email/templateBuilder' 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 extractMentionedUsers from './mentions/extractMentionedUsers' diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 7eaed8a84..7c562635e 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -8,8 +8,8 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield' import CONFIG from '@config/index' import { getNeode } from '@db/neo4j' +import { validateInviteCode } from '@graphql/resolvers/transactions/inviteCodes' import SocialMedia from '@models/SocialMedia' -import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes' const debug = !!CONFIG.DEBUG const allowExternalErrors = true diff --git a/backend/src/server.ts b/backend/src/server.ts index 372ec964b..a9fc43a0e 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -18,10 +18,10 @@ import Redis from 'ioredis' 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' diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 7de3aad0c..160664b66 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -38,7 +38,6 @@ "@jwt/*": ["./src/jwt/*"], "@middleware/*": ["./src/middleware/*"], "@models/*": ["./src/models/*"], - "@schema/*": ["./src/schema/*"], "@src/*": ["./src/*"], "@root/*": ["./*"] }, From 1536a32b3a26f6ea8916848022ac89b966e7b1a5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 11:59:18 +0200 Subject: [PATCH 183/266] refactor(backend): refactor badges (#8465) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * new badge descriptions * new user verification color * new svgs for elevated roles --------- Co-authored-by: Wolfgang Huß --- .../img/badges/default_verification.svg | 3 +- .../img/badges/verification_red_admin.svg | 46 ++++++++++++++- .../img/badges/verification_red_developer.svg | 39 ++++++++++++- .../img/badges/verification_red_moderator.svg | 30 +++++++++- backend/src/db/seed/badges.ts | 56 +++++++++---------- 5 files changed, 142 insertions(+), 32 deletions(-) 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/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', }), } From a38d24a8034f8b66df8168e999b26a645a2788fa Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 12:39:10 +0200 Subject: [PATCH 184/266] also lint cjs files (#8467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/.eslintrc.cjs | 3 +-- backend/{.prettierrc.js => .prettierrc.cjs} | 0 backend/jest.config.cjs | 3 ++- backend/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename backend/{.prettierrc.js => .prettierrc.cjs} (100%) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 1fe6b8779..3e8e942ba 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, }, }, 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/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 f1c826557..d5cc63a87 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "build": "tsc && tsc-alias && ./scripts/build.copy.files.sh", "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 .", + "lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs .", "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", From edce234745e7cf3cf2d2ba17516cdedf568a57aa Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 13:34:43 +0200 Subject: [PATCH 185/266] move models into database folder (#8471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/src/{ => db}/models/Badge.ts | 0 backend/src/{ => db}/models/Category.ts | 0 backend/src/{ => db}/models/Comment.ts | 0 backend/src/{ => db}/models/Donations.ts | 0 backend/src/{ => db}/models/EmailAddress.ts | 0 backend/src/{ => db}/models/Group.ts | 0 backend/src/{ => db}/models/Image.ts | 0 backend/src/{ => db}/models/InviteCode.ts | 0 backend/src/{ => db}/models/Location.ts | 0 backend/src/{ => db}/models/Migration.ts | 0 backend/src/{ => db}/models/Post.ts | 0 backend/src/{ => db}/models/Report.ts | 0 backend/src/{ => db}/models/SocialMedia.ts | 0 backend/src/{ => db}/models/Tag.ts | 0 backend/src/{ => db}/models/UnverifiedEmailAddress.ts | 0 backend/src/{ => db}/models/User.spec.ts | 0 backend/src/{ => db}/models/User.ts | 0 backend/src/{ => db}/models/index.ts | 0 backend/src/db/neo4j.ts | 2 +- backend/src/graphql/resolvers/posts.spec.ts | 2 +- backend/src/graphql/resolvers/registration.spec.ts | 4 ++-- backend/src/graphql/resolvers/users.spec.ts | 2 +- backend/src/jwt/decode.spec.ts | 2 +- backend/src/middleware/permissionsMiddleware.ts | 2 +- backend/tsconfig.json | 1 - 25 files changed, 7 insertions(+), 8 deletions(-) rename backend/src/{ => db}/models/Badge.ts (100%) rename backend/src/{ => db}/models/Category.ts (100%) rename backend/src/{ => db}/models/Comment.ts (100%) rename backend/src/{ => db}/models/Donations.ts (100%) rename backend/src/{ => db}/models/EmailAddress.ts (100%) rename backend/src/{ => db}/models/Group.ts (100%) rename backend/src/{ => db}/models/Image.ts (100%) rename backend/src/{ => db}/models/InviteCode.ts (100%) rename backend/src/{ => db}/models/Location.ts (100%) rename backend/src/{ => db}/models/Migration.ts (100%) rename backend/src/{ => db}/models/Post.ts (100%) rename backend/src/{ => db}/models/Report.ts (100%) rename backend/src/{ => db}/models/SocialMedia.ts (100%) rename backend/src/{ => db}/models/Tag.ts (100%) rename backend/src/{ => db}/models/UnverifiedEmailAddress.ts (100%) rename backend/src/{ => db}/models/User.spec.ts (100%) rename backend/src/{ => db}/models/User.ts (100%) rename backend/src/{ => db}/models/index.ts (100%) 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..5d084099a 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -6,7 +6,7 @@ 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/graphql/resolvers/posts.spec.ts b/backend/src/graphql/resolvers/posts.spec.ts index 0a05200fd..7574bef17 100644 --- a/backend/src/graphql/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/graphql/resolvers/registration.spec.ts b/backend/src/graphql/resolvers/registration.spec.ts index f19c6bf01..ccf5a9e10 100644 --- a/backend/src/graphql/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() diff --git a/backend/src/graphql/resolvers/users.spec.ts b/backend/src/graphql/resolvers/users.spec.ts index ad37e2024..4fc1c3efd 100644 --- a/backend/src/graphql/resolvers/users.spec.ts +++ b/backend/src/graphql/resolvers/users.spec.ts @@ -8,8 +8,8 @@ import gql from 'graphql-tag' import { categories } from '@constants/categories' import Factory, { cleanDatabase } from '@db/factories' +import User from '@db/models/User' import { getNeode, getDriver } from '@db/neo4j' -import User from '@models/User' import createServer from '@src/server' const categoryIds = ['cat9'] 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/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 7c562635e..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 { validateInviteCode } from '@graphql/resolvers/transactions/inviteCodes' -import SocialMedia from '@models/SocialMedia' const debug = !!CONFIG.DEBUG const allowExternalErrors = true diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 160664b66..e34f920be 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -37,7 +37,6 @@ "@helpers/*": ["./src/helpers/*"], "@jwt/*": ["./src/jwt/*"], "@middleware/*": ["./src/middleware/*"], - "@models/*": ["./src/models/*"], "@src/*": ["./src/*"], "@root/*": ["./*"] }, From 68edc47f65eb38f7e5d2a6e5b60c7e2575f5b61c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 2 May 2025 15:57:37 +0200 Subject: [PATCH 186/266] remove all helpers on src/helpers (#8469) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Wolfgang Huß --- backend/src/graphql/resolvers/registration.ts | 5 +-- .../src/graphql/resolvers/users/location.ts | 5 ++- backend/src/helpers/asyncForEach.ts | 18 ---------- backend/src/helpers/encryptPassword.ts | 11 ------ backend/src/helpers/jest.ts | 10 ------ backend/src/helpers/walkRecursive.ts | 36 ------------------- backend/src/middleware/xssMiddleware.ts | 35 ++++++++++++++++-- 7 files changed, 37 insertions(+), 83 deletions(-) delete mode 100644 backend/src/helpers/asyncForEach.ts delete mode 100644 backend/src/helpers/encryptPassword.ts delete mode 100644 backend/src/helpers/jest.ts delete mode 100644 backend/src/helpers/walkRecursive.ts diff --git a/backend/src/graphql/resolvers/registration.ts b/backend/src/graphql/resolvers/registration.ts index 138a21aea..d37d3663a 100644 --- a/backend/src/graphql/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/graphql/resolvers/users/location.ts b/backend/src/graphql/resolvers/users/location.ts index 6dfaede4e..dc515e70d 100644 --- a/backend/src/graphql/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/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/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' }, From fb09076f0f73dc37b1b08833a09f57ba53a8482b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 03:00:59 +0200 Subject: [PATCH 187/266] build(deps-dev): bump eslint-plugin-prettier in /webapp (#8332) Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.2.3 to 5.2.6. - [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases) - [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.2.3...v5.2.6) --- updated-dependencies: - dependency-name: eslint-plugin-prettier dependency-version: 5.2.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- webapp/package.json | 2 +- webapp/yarn.lock | 37 ++++++++++++++++--------------------- 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/webapp/package.json b/webapp/package.json index 4f041d25c..7af701437 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -92,7 +92,7 @@ "eslint-plugin-import": "~2.31.0", "eslint-plugin-jest": "~24.4.0", "eslint-plugin-node": "~11.1.0", - "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-prettier": "^5.2.6", "eslint-plugin-promise": "~7.2.1", "eslint-plugin-standard": "~5.0.0", "eslint-plugin-vue": "~9.33.0", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 090b6a83a..7ecc14023 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -3474,10 +3474,10 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@pkgr/core@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" - integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== +"@pkgr/core@^0.2.3": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c" + integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw== "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" @@ -9488,13 +9488,13 @@ eslint-plugin-node@~11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-prettier@^5.2.3: - version "5.2.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5" - integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw== +eslint-plugin-prettier@^5.2.6: + version "5.2.6" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.6.tgz#be39e3bb23bb3eeb7e7df0927cdb46e4d7945096" + integrity sha512-mUcf7QG2Tjk7H055Jk0lGBjbgDnfrvqjhXh9t2xLMSCjZVcw9Rb1V6sVNXO0th3jgeO7zllWPTNRil3JW94TnQ== dependencies: prettier-linter-helpers "^1.0.0" - synckit "^0.9.1" + synckit "^0.11.0" eslint-plugin-promise@~7.2.1: version "7.2.1" @@ -18506,13 +18506,13 @@ synchronous-promise@^2.0.15: resolved "https://registry.yarnpkg.com/synchronous-promise/-/synchronous-promise-2.0.17.tgz#38901319632f946c982152586f2caf8ddc25c032" integrity sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g== -synckit@^0.9.1: - version "0.9.2" - resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62" - integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw== +synckit@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.4.tgz#48972326b59723fc15b8d159803cf8302b545d59" + integrity sha512-Q/XQKRaJiLiFIBNN+mndW7S/RHxvwzuZS6ZwmRzUBqJBv/5QIKCEwkBC8GBf8EQJKYnaFs0wOZbKTXBPj8L9oQ== dependencies: - "@pkgr/core" "^0.1.0" - tslib "^2.6.2" + "@pkgr/core" "^0.2.3" + tslib "^2.8.1" table@5.4.6: version "5.4.6" @@ -19009,16 +19009,11 @@ tslib@^1, tslib@^1.10.0, 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.3.1: +tslib@^2.3.1, 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== -tslib@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" - integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== - tsutils@^3.17.1: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" From 913cc2f06dd87b97b87bdbed2af67a1272d6ec27 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 09:44:29 +0200 Subject: [PATCH 188/266] build(deps): bump amannn/action-semantic-pull-request (#8480) Bumps [amannn/action-semantic-pull-request](https://github.com/amannn/action-semantic-pull-request) from 04501d43b574e4c1d23c629ffe4dcec27acfdeff to 335288255954904a41ddda8947c8f2c844b8bfeb. - [Release notes](https://github.com/amannn/action-semantic-pull-request/releases) - [Changelog](https://github.com/amannn/action-semantic-pull-request/blob/main/CHANGELOG.md) - [Commits](https://github.com/amannn/action-semantic-pull-request/compare/04501d43b574e4c1d23c629ffe4dcec27acfdeff...335288255954904a41ddda8947c8f2c844b8bfeb) --- updated-dependencies: - dependency-name: amannn/action-semantic-pull-request dependency-version: 335288255954904a41ddda8947c8f2c844b8bfeb dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/test.lint_pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From c69cef47a1b889b8d08c03c803958e975f715cba Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 3 May 2025 11:43:08 +0200 Subject: [PATCH 189/266] refactor(backend): refactor context (#8434) * type for neo4j and neode * fix build * remove flakyness * wait for neode to install schema * remove flakyness * explain why we wait for a non-promise * refactor context missing change missing change * adjust test setup proper cleanup after test * lint fixes * fix failing test to use new context --- backend/src/constants/subscriptions.ts | 3 + backend/src/context/database.ts | 49 +++++++++++ backend/src/context/pubsub.ts | 25 ++++++ .../src/graphql/resolvers/comments.spec.ts | 31 +++---- backend/src/graphql/resolvers/follow.ts | 2 - backend/src/graphql/resolvers/groups.spec.ts | 34 ++++---- .../src/graphql/resolvers/messages.spec.ts | 34 ++++---- backend/src/graphql/resolvers/messages.ts | 4 +- .../src/graphql/resolvers/notifications.ts | 4 +- .../graphql/resolvers/observePosts.spec.ts | 33 ++++---- .../graphql/resolvers/postsInGroups.spec.ts | 32 ++++---- backend/src/graphql/resolvers/rooms.ts | 5 +- backend/src/graphql/resolvers/socialMedia.ts | 2 - .../src/graphql/resolvers/user_management.ts | 1 - backend/src/graphql/resolvers/users.spec.ts | 64 ++++++++------- .../src/middleware/helpers/email/sendMail.ts | 4 +- backend/src/middleware/index.ts | 1 - .../notificationsMiddleware.emails.spec.ts | 39 ++++----- ...ficationsMiddleware.followed-users.spec.ts | 42 +++++----- ...tionsMiddleware.mentions-in-groups.spec.ts | 39 ++++----- ...icationsMiddleware.observing-posts.spec.ts | 40 ++++----- ...ificationsMiddleware.online-status.spec.ts | 33 +++----- ...icationsMiddleware.posts-in-groups.spec.ts | 38 ++++----- .../notificationsMiddleware.spec.ts | 35 ++++---- .../notifications/notificationsMiddleware.ts | 13 +-- backend/src/server.ts | 82 ++++++++----------- backend/tsconfig.json | 1 + 27 files changed, 359 insertions(+), 331 deletions(-) create mode 100644 backend/src/constants/subscriptions.ts create mode 100644 backend/src/context/database.ts create mode 100644 backend/src/context/pubsub.ts 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..003347b16 --- /dev/null +++ b/backend/src/context/pubsub.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { RedisPubSub } from 'graphql-redis-subscriptions' +import { PubSub } from 'graphql-subscriptions' +import Redis from 'ioredis' + +import CONFIG from '@config/index' + +export default () => { + if (!CONFIG.REDIS_DOMAIN || CONFIG.REDIS_PORT || CONFIG.REDIS_PASSWORD) { + return new PubSub() + } + + const options = { + host: CONFIG.REDIS_DOMAIN, + port: CONFIG.REDIS_PORT, + password: CONFIG.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/graphql/resolvers/comments.spec.ts b/backend/src/graphql/resolvers/comments.spec.ts index a7177d754..9681abe9a 100644 --- a/backend/src/graphql/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/graphql/resolvers/follow.ts b/backend/src/graphql/resolvers/follow.ts index d08f243b1..8d69a7d5b 100644 --- a/backend/src/graphql/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/graphql/resolvers/groups.spec.ts b/backend/src/graphql/resolvers/groups.spec.ts index 664f57397..545865c20 100644 --- a/backend/src/graphql/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/graphql/resolvers/messages.spec.ts b/backend/src/graphql/resolvers/messages.spec.ts index 8061cf460..81799fdf1 100644 --- a/backend/src/graphql/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/graphql/resolvers/messages.ts b/backend/src/graphql/resolvers/messages.ts index c3f362660..6a5a59d27 100644 --- a/backend/src/graphql/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/graphql/resolvers/notifications.ts b/backend/src/graphql/resolvers/notifications.ts index 0168558c3..08a7c48f5 100644 --- a/backend/src/graphql/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/graphql/resolvers/observePosts.spec.ts b/backend/src/graphql/resolvers/observePosts.spec.ts index 76ad5b058..fd2786fc9 100644 --- a/backend/src/graphql/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/graphql/resolvers/postsInGroups.spec.ts b/backend/src/graphql/resolvers/postsInGroups.spec.ts index 7cb0bdc76..d50451468 100644 --- a/backend/src/graphql/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/graphql/resolvers/rooms.ts b/backend/src/graphql/resolvers/rooms.ts index 9c6751695..e3422a5ce 100644 --- a/backend/src/graphql/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/graphql/resolvers/socialMedia.ts b/backend/src/graphql/resolvers/socialMedia.ts index 952e4a27e..2c0cd4c94 100644 --- a/backend/src/graphql/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/graphql/resolvers/user_management.ts b/backend/src/graphql/resolvers/user_management.ts index 7bea1f53c..140a8d53c 100644 --- a/backend/src/graphql/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/graphql/resolvers/users.spec.ts b/backend/src/graphql/resolvers/users.spec.ts index 4fc1c3efd..f65f9eae2 100644 --- a/backend/src/graphql/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 User from '@db/models/User' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +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/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts index a7d223f1c..fc50107fb 100644 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ b/backend/src/middleware/helpers/email/sendMail.ts @@ -4,7 +4,7 @@ /* 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 { createTransport } from 'nodemailer' import { htmlToText } from 'nodemailer-html-to-text' import CONFIG from '@config/index' @@ -15,7 +15,7 @@ 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({ +const transporter = createTransport({ host: CONFIG.SMTP_HOST, port: CONFIG.SMTP_PORT, ignoreTLS: CONFIG.SMTP_IGNORE_TLS, diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 37fd33ef9..896e5b33b 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -15,7 +15,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/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 79d95e43e..8b41498ab 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -1,18 +1,18 @@ -/* 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 @@ -21,13 +21,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(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', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 21d4a14a0..f595f441e 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -1,16 +1,16 @@ /* 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 @@ -19,13 +19,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(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', 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..539022262 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -1,19 +1,19 @@ -/* 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 @@ -22,13 +22,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(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', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index a0864fe07..2fff0195d 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -1,14 +1,16 @@ +/* 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 @@ -17,13 +19,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(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', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts index 3a47d376d..8d06396ce 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -1,15 +1,14 @@ /* 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 @@ -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 () => { 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..461fa6996 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -1,19 +1,19 @@ -/* 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 @@ -22,13 +22,10 @@ jest.mock('@middleware/helpers/email/sendMail', () => ({ sendMail: (notification) => sendMailMock(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', () => { @@ -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', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index ab0a6a5b2..985a19193 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,7 +18,7 @@ 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', () => ({ @@ -35,12 +37,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 +70,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 +87,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { @@ -104,7 +105,7 @@ beforeEach(async () => { password: '1234', }, ) - await neode.create('Category', { + await database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 4459d23b8..5737a6587 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -1,10 +1,14 @@ -/* eslint-disable import/no-cycle */ /* 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-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */ +import { + NOTIFICATION_ADDED, + ROOM_COUNT_UPDATED, + CHAT_MESSAGE_ADDED, +} from '@constants/subscriptions' import { getUnreadRoomsCount } from '@graphql/resolvers/rooms' import { sendMail } from '@middleware/helpers/email/sendMail' import { @@ -13,7 +17,6 @@ import { } from '@middleware/helpers/email/templateBuilder' import { isUserOnline } from '@middleware/helpers/isUserOnline' import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' -import { pubsub, NOTIFICATION_ADDED, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' import extractMentionedUsers from './mentions/extractMentionedUsers' @@ -25,7 +28,7 @@ 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) && @@ -482,11 +485,11 @@ 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, }) diff --git a/backend/src/server.ts b/backend/src/server.ts index a9fc43a0e..457ea3684 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' -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( diff --git a/backend/tsconfig.json b/backend/tsconfig.json index e34f920be..7ef3f47b0 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -32,6 +32,7 @@ "paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */ "@config/*": ["./src/config/*"], "@constants/*": ["./src/constants/*"], + "@context/*": ["./src/context/*"], "@db/*": ["./src/db/*"], "@graphql/*": ["./src/graphql/*"], "@helpers/*": ["./src/helpers/*"], From 83b7e09bb6b66f67c97a2d2aa50a931a527852a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 10:19:46 +0000 Subject: [PATCH 190/266] build(deps-dev): bump @types/node from 22.15.2 to 22.15.3 in /backend (#8479) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.15.2 to 22.15.3. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 22.15.3 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- backend/package.json | 2 +- backend/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/package.json b/backend/package.json index d5cc63a87..645bfd83b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -90,7 +90,7 @@ "@faker-js/faker": "9.7.0", "@types/jest": "^29.5.14", "@types/lodash": "^4.17.16", - "@types/node": "^22.15.2", + "@types/node": "^22.15.3", "@types/uuid": "~9.0.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 9f933d6b0..e3e1d9d09 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1374,10 +1374,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@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" From 43f4fd130e5563d62c102ce48f5a0749866d4d83 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 3 May 2025 11:08:28 +0000 Subject: [PATCH 191/266] build(deps-dev): bump the babel group with 3 updates (#8478) Bumps the babel group with 3 updates: [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core), [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) and [@babel/register](https://github.com/babel/babel/tree/HEAD/packages/babel-register). Updates `@babel/core` from 7.26.10 to 7.27.1 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.1/packages/babel-core) Updates `@babel/preset-env` from 7.26.9 to 7.27.1 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.1/packages/babel-preset-env) Updates `@babel/register` from 7.25.9 to 7.27.1 - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.27.1/packages/babel-register) --- updated-dependencies: - dependency-name: "@babel/core" dependency-version: 7.27.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: babel - dependency-name: "@babel/preset-env" dependency-version: 7.27.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: babel - dependency-name: "@babel/register" dependency-version: 7.27.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: babel ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 1041 +++++++++++++++++++++-------------------- package.json | 6 +- yarn.lock | 1131 ++++++++++++++++++++++----------------------- 3 files changed, 1114 insertions(+), 1064 deletions(-) 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..2754268fc 100644 --- a/package.json +++ b/package.json @@ -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/yarn.lock b/yarn.lock index 1e3983be3..0709e09df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38,48 +38,48 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.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" - integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" + integrity sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg== 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" -"@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": - 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/compat-data@^7.20.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.27.1.tgz#db7cf122745e0a332c44e847ddc4f5e5221a43f6" + integrity sha512-Q+E+rd/yBzNQhXkG+zQnF58e4zoZfBedaxwzPmicKsiK3nt8iJYrSrDbjwFFDGC4f+rPafqRaPH6TsDoSvMf7A== -"@babel/core@^7.16.0", "@babel/core@^7.26.10": - version "7.26.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" - integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== +"@babel/core@^7.16.0", "@babel/core@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.27.1.tgz#89de51e86bd12246003e3524704c49541b16c3e6" + integrity sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ== 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" json5 "^2.2.3" semver "^6.3.1" -"@babel/generator@^7.26.10", "@babel/generator@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.0.tgz#764382b5392e5b9aff93cadb190d0745866cbc2c" - integrity sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw== +"@babel/generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.27.1.tgz#862d4fad858f7208edd487c28b58144036b76230" + integrity sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w== 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" @@ -91,20 +91,20 @@ 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== +"@babel/helper-annotate-as-pure@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz#4345d81a9a46a6486e24d069469f13e60445c05d" + integrity sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow== dependencies: - "@babel/types" "^7.25.9" + "@babel/types" "^7.27.1" -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@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== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.1.tgz#eac1096c7374f161e4f33fc8ae38f4ddf122087a" + integrity sha512-2YaDd/Rd9E598B5+WIc8wJPmWETiiJXFYVE60oX8FDohv7rAUU3CQj+A1MgeEmcsk2+dQuEjIe/GDvig0SqL4g== 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" @@ -124,17 +124,17 @@ "@babel/helper-split-export-declaration" "^7.22.6" 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== +"@babel/helper-create-class-features-plugin@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz#5bee4262a6ea5ddc852d0806199eb17ca3de9281" + integrity sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A== 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" "@babel/helper-create-regexp-features-plugin@^7.18.6": @@ -146,13 +146,13 @@ 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== +"@babel/helper-create-regexp-features-plugin@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz#05b0882d97ba1d4d03519e4bce615d70afa18c53" + integrity sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ== 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" "@babel/helper-define-polyfill-provider@^0.4.3": @@ -219,13 +219,13 @@ dependencies: "@babel/types" "^7.23.0" -"@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== +"@babel/helper-member-expression-to-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz#ea1211276be93e798ce19037da6f06fbb994fa44" + integrity sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" "@babel/helper-module-imports@^7.22.15": version "7.22.15" @@ -234,22 +234,22 @@ dependencies: "@babel/types" "^7.22.15" -"@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" - integrity sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw== +"@babel/helper-module-imports@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz#7ef769a323e2655e126673bb6d2d6913bbead204" + integrity sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" -"@babel/helper-module-transforms@^7.25.9", "@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== +"@babel/helper-module-transforms@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz#e1663b8b71d2de948da5c4fb2a20ca4f3ec27a6f" + integrity sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g== 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" "@babel/helper-optimise-call-expression@^7.22.5": version "7.22.5" @@ -258,26 +258,26 @@ dependencies: "@babel/types" "^7.22.5" -"@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== +"@babel/helper-optimise-call-expression@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz#c65221b61a643f3e62705e5dd2b5f115e35f9200" + integrity sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw== dependencies: - "@babel/types" "^7.25.9" + "@babel/types" "^7.27.1" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5", "@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-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.27.1", "@babel/helper-plugin-utils@^7.8.0": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz#ddb2f876534ff8013e6c2b299bf4d39b3c51d44c" + integrity sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw== -"@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== +"@babel/helper-remap-async-to-generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz#4601d5c7ce2eb2aea58328d43725523fcd362ce6" + integrity sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA== 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" "@babel/helper-replace-supers@^7.22.9": version "7.22.20" @@ -288,14 +288,14 @@ "@babel/helper-member-expression-to-functions" "^7.22.15" "@babel/helper-optimise-call-expression" "^7.22.5" -"@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== +"@babel/helper-replace-supers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz#b1ed2d634ce3bdb730e4b52de30f8cccfd692bc0" + integrity sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA== 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" "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": version "7.22.5" @@ -304,13 +304,13 @@ dependencies: "@babel/types" "^7.22.5" -"@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== +"@babel/helper-skip-transparent-expression-wrappers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz#62bb91b3abba8c7f1fec0252d9dbea11b3ee7a56" + integrity sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg== dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" + "@babel/traverse" "^7.27.1" + "@babel/types" "^7.27.1" "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" @@ -319,83 +319,83 @@ dependencies: "@babel/types" "^7.22.5" -"@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" - integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== +"@babel/helper-string-parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" + integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== +"@babel/helper-validator-identifier@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8" + integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow== -"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" - integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== +"@babel/helper-validator-option@^7.22.15", "@babel/helper-validator-option@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" + integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== -"@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== +"@babel/helper-wrap-function@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz#b88285009c31427af318d4fe37651cd62a142409" + integrity sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ== 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" -"@babel/helpers@^7.26.10": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" - integrity sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg== +"@babel/helpers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.1.tgz#ffc27013038607cdba3288e692c3611c06a18aa4" + integrity sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ== dependencies: - "@babel/template" "^7.27.0" - "@babel/types" "^7.27.0" + "@babel/template" "^7.27.1" + "@babel/types" "^7.27.1" -"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" - integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== +"@babel/parser@^7.24.4", "@babel/parser@^7.24.7", "@babel/parser@^7.25.3", "@babel/parser@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.1.tgz#c55d5bed74449d1223701f1869b9ee345cc94cc9" + integrity sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ== dependencies: - "@babel/types" "^7.27.0" + "@babel/types" "^7.27.1" -"@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== +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz#61dd8a8e61f7eb568268d1b5f129da3eee364bf9" + integrity sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA== 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" -"@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== +"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz#43f70a6d7efd52370eefbdf55ae03d91b293856d" + integrity sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.27.1": + version "7.27.1" + 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.27.1.tgz#beb623bd573b8b6f3047bd04c32506adc3e58a72" + integrity sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz#e134a5479eb2ba9c02714e8c1ebf1ec9076124fd" + integrity sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw== 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" -"@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== +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz#bb1c25af34d75115ce229a1de7fa44bf8f955670" + integrity sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw== 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" "@babel/plugin-proposal-class-properties@^7.16.0": version "7.18.6" @@ -421,19 +421,19 @@ 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-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== +"@babel/plugin-syntax-import-assertions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz#88894aefd2b03b5ee6ad1562a7c8e1587496aecd" + integrity sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-syntax-import-attributes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz#34c017d54496f9b11b61474e7ea3dfd5563ffe07" + integrity sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-syntax-jsx@^7.23.3": version "7.23.3" @@ -464,302 +464,302 @@ "@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== +"@babel/plugin-transform-arrow-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz#6e2061067ba3ab0266d834a9f94811196f2aba9a" + integrity sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-async-generator-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz#ca433df983d68e1375398e7ca71bf2a4f6fd89d7" + integrity sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA== 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" -"@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== +"@babel/plugin-transform-async-to-generator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz#9a93893b9379b39466c74474f55af03de78c66e7" + integrity sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA== 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" -"@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== +"@babel/plugin-transform-block-scoped-functions@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz#558a9d6e24cf72802dd3b62a4b51e0d62c0f57f9" + integrity sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-block-scoping@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.1.tgz#bc0dbe8ac6de5602981ba58ef68c6df8ef9bfbb3" + integrity sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-class-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz#dd40a6a370dfd49d32362ae206ddaf2bb082a925" + integrity sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA== 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" -"@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== +"@babel/plugin-transform-class-static-block@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz#7e920d5625b25bbccd3061aefbcc05805ed56ce4" + integrity sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA== 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" -"@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== +"@babel/plugin-transform-classes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz#03bb04bea2c7b2f711f0db7304a8da46a85cced4" + integrity sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA== 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" -"@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== +"@babel/plugin-transform-computed-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz#81662e78bf5e734a97982c2b7f0a793288ef3caa" + integrity sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw== 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" -"@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== +"@babel/plugin-transform-destructuring@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz#d5916ef7089cb254df0418ae524533c1b72ba656" + integrity sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-dotall-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz#aa6821de864c528b1fecf286f0a174e38e826f4d" + integrity sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw== 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" -"@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== +"@babel/plugin-transform-duplicate-keys@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz#f1fbf628ece18e12e7b32b175940e68358f546d1" + integrity sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz#5043854ca620a94149372e69030ff8cb6a9eb0ec" + integrity sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ== 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" -"@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== +"@babel/plugin-transform-dynamic-import@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz#4c78f35552ac0e06aa1f6e3c573d67695e8af5a4" + integrity sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-exponentiation-operator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz#fc497b12d8277e559747f5a3ed868dd8064f83e1" + integrity sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-export-namespace-from@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz#71ca69d3471edd6daa711cf4dfc3400415df9c23" + integrity sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-for-of@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz#bc24f7080e9ff721b63a70ac7b2564ca15b6c40a" + integrity sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw== 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" -"@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== +"@babel/plugin-transform-function-name@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz#4d0bf307720e4dce6d7c30fcb1fd6ca77bdeb3a7" + integrity sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ== 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" -"@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== +"@babel/plugin-transform-json-strings@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz#a2e0ce6ef256376bd527f290da023983527a4f4c" + integrity sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz#baaefa4d10a1d4206f9dcdda50d7d5827bb70b24" + integrity sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-logical-assignment-operators@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz#890cb20e0270e0e5bebe3f025b434841c32d5baa" + integrity sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-member-expression-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz#37b88ba594d852418e99536f5612f795f23aeaf9" + integrity sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-modules-amd@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz#a4145f9d87c2291fe2d05f994b65dba4e3e7196f" + integrity sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA== 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" -"@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== +"@babel/plugin-transform-modules-commonjs@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz#8e44ed37c2787ecc23bdc367f49977476614e832" + integrity sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw== 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" -"@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== +"@babel/plugin-transform-modules-systemjs@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz#00e05b61863070d0f3292a00126c16c0e024c4ed" + integrity sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA== 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" -"@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== +"@babel/plugin-transform-modules-umd@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz#63f2cf4f6dc15debc12f694e44714863d34cd334" + integrity sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w== 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" -"@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== +"@babel/plugin-transform-named-capturing-groups-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz#f32b8f7818d8fc0cc46ee20a8ef75f071af976e1" + integrity sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng== 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" -"@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== +"@babel/plugin-transform-new-target@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz#259c43939728cad1706ac17351b7e6a7bea1abeb" + integrity sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-nullish-coalescing-operator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz#4f9d3153bf6782d73dd42785a9d22d03197bc91d" + integrity sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-numeric-separator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz#614e0b15cc800e5997dadd9bd6ea524ed6c819c6" + integrity sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-object-rest-spread@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.1.tgz#845bdcd74c87b8f565c25cc6812f7f4f43c9ed79" + integrity sha512-/sSliVc9gHE20/7D5qsdGlq7RG5NCDTWsAhyqzGuq174EtWJoGzIu1BQ7G56eDsTcy1jseBZwv50olSdXOlGuA== 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" -"@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== +"@babel/plugin-transform-object-super@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz#1c932cd27bf3874c43a5cac4f43ebf970c9871b5" + integrity sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng== 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" -"@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== +"@babel/plugin-transform-optional-catch-binding@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz#84c7341ebde35ccd36b137e9e45866825072a30c" + integrity sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-optional-chaining@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz#874ce3c4f06b7780592e946026eb76a32830454f" + integrity sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg== 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" -"@babel/plugin-transform-parameters@^7.20.7", "@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== +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz#80334b54b9b1ac5244155a0c8304a187a618d5a7" + integrity sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-private-methods@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz#fdacbab1c5ed81ec70dfdbb8b213d65da148b6af" + integrity sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA== 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" -"@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== +"@babel/plugin-transform-private-property-in-object@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz#4dbbef283b5b2f01a21e81e299f76e35f900fb11" + integrity sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ== 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" -"@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== +"@babel/plugin-transform-property-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz#07eafd618800591e88073a0af1b940d9a42c6424" + integrity sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-react-display-name@^7.23.3": version "7.23.3" @@ -794,28 +794,27 @@ "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" -"@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== +"@babel/plugin-transform-regenerator@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.1.tgz#0a471df9213416e44cd66bf67176b66f65768401" + integrity sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - regenerator-transform "^0.15.2" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-regexp-modifiers@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz#df9ba5577c974e3f1449888b70b76169998a6d09" + integrity sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA== 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" -"@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== +"@babel/plugin-transform-reserved-words@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz#40fba4878ccbd1c56605a4479a3a891ac0274bb4" + integrity sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" "@babel/plugin-transform-runtime@^7.16.0": version "7.23.4" @@ -829,141 +828,141 @@ babel-plugin-polyfill-regenerator "^0.5.3" semver "^6.3.1" -"@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== +"@babel/plugin-transform-shorthand-properties@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz#532abdacdec87bfee1e0ef8e2fcdee543fe32b90" + integrity sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-spread@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz#1a264d5fc12750918f50e3fe3e24e437178abb08" + integrity sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q== 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" -"@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== +"@babel/plugin-transform-sticky-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz#18984935d9d2296843a491d78a014939f7dcd280" + integrity sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-template-literals@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz#1a0eb35d8bb3e6efc06c9fd40eb0bcef548328b8" + integrity sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-typeof-symbol@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz#70e966bb492e03509cf37eafa6dcc3051f844369" + integrity sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw== dependencies: - "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-unicode-escapes@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz#3e3143f8438aef842de28816ece58780190cf806" + integrity sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg== dependencies: - "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-plugin-utils" "^7.27.1" -"@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== +"@babel/plugin-transform-unicode-property-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz#bdfe2d3170c78c5691a3c3be934c8c0087525956" + integrity sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q== 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" -"@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== +"@babel/plugin-transform-unicode-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz#25948f5c395db15f609028e370667ed8bae9af97" + integrity sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw== 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" -"@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== +"@babel/plugin-transform-unicode-sets-regex@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz#6ab706d10f801b5c72da8bb2548561fa04193cd1" + integrity sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw== 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" -"@babel/preset-env@^7.16.0", "@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== +"@babel/preset-env@^7.16.0", "@babel/preset-env@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.27.1.tgz#23463ab94f36540630924f5de3b4c7a8dde3b6a2" + integrity sha512-TZ5USxFpLgKDpdEt8YWBR7p6g+bZo6sHaXLqP2BY/U0acaoI8FTVflcYCr/v94twM1C5IWFdZ/hscq9WjUeLXA== 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" @@ -992,10 +991,10 @@ "@babel/plugin-transform-react-jsx-development" "^7.22.5" "@babel/plugin-transform-react-pure-annotations" "^7.23.3" -"@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== +"@babel/register@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.27.1.tgz#ea4d701649d788d7cb8a064b7540fd21083147f1" + integrity sha512-K13lQpoV54LATKkzBpBAEu1GGSIRzxR9f4IN4V8DCDgiUMo2UDGagEZr3lPeVNJPLkWUi5JE4hCHKneVTwQlYQ== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" @@ -1008,42 +1007,42 @@ resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.16.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4": +"@babel/runtime@^7.16.0", "@babel/runtime@^7.5.5": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.4.tgz#36fa1d2b36db873d25ec631dcc4923fdc1cf2e2e" integrity sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg== dependencies: regenerator-runtime "^0.14.0" -"@babel/template@^7.22.15", "@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" - integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== +"@babel/template@^7.22.15", "@babel/template@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.1.tgz#b9e4f55c17a92312774dfbdde1b3c01c547bbae2" + integrity sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg== 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" -"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.8": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" - integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== +"@babel/traverse@^7.27.1": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.1.tgz#4db772902b133bbddd1c4f7a7ee47761c1b9f291" + integrity sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg== 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" -"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.4.4": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" - integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.27.1", "@babel/types@^7.4.4": + version "7.27.1" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.1.tgz#9defc53c16fc899e46941fc6901a9eea1c9d8560" + integrity sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q== 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" "@badeball/cypress-cucumber-preprocessor@^22.0.1": version "22.0.1" @@ -7185,11 +7184,6 @@ photoswipe@^5.4.4: resolved "https://registry.yarnpkg.com/photoswipe/-/photoswipe-5.4.4.tgz#e045dc036453493188d5c8665b0e8f1000ac4d6e" integrity sha512-WNFHoKrkZNnvFFhbHL93WDkW3ifwVOXSW3w1UuZZelSmgXpIGiZSNlZJq37rR8YejqME2rHs9EhH9ZvlvFH2NA== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - picocolors@^1.0.1, picocolors@^1.1.0, picocolors@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" @@ -7534,13 +7528,6 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== -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" - regex@^4.3.2: version "4.3.3" resolved "https://registry.yarnpkg.com/regex/-/regex-4.3.3.tgz#8cda73ccbdfa7c5691881d02f9bb142dba9daa6a" @@ -7580,15 +7567,15 @@ regexpu-core@^5.3.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== +regexpu-core@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.2.0.tgz#0e5190d79e542bf294955dccabae04d3c7d53826" + integrity sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA== 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" @@ -7597,10 +7584,10 @@ regjsgen@^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== +regjsparser@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.12.0.tgz#0e846df6c6530586429377de56e0475583b088dc" + integrity sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ== dependencies: jsesc "~3.0.2" From 4f05b852af0276bd52c560b6952e567296cfcd0d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sat, 3 May 2025 13:51:22 +0200 Subject: [PATCH 192/266] remove some dependabot groups & no alpine version to allow update (#8475) Co-authored-by: mahula --- .github/dependabot.yml | 9 --------- backend/Dockerfile | 2 +- webapp/Dockerfile | 2 +- webapp/Dockerfile.maintenance | 2 +- 4 files changed, 3 insertions(+), 12 deletions(-) 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/backend/Dockerfile b/backend/Dockerfile index f89fa2d3d..7e694c9f9 100644 --- a/backend/Dockerfile +++ b/backend/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: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" 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..3e4b9b054 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 From e4ae0dfe50909480bc56ac63d4a4e5face4fb275 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Sat, 3 May 2025 21:11:44 +0200 Subject: [PATCH 193/266] feat(backend): emails for notifications (#8435) * email templates with pug for all possible notification emails * more information in emails * Individual email subjects to all notification emails --------- Co-authored-by: Ulf Gebhardt Co-authored-by: mahula --- .github/workflows/test-e2e.yml | 4 +- backend/.env.test_e2e | 41 + backend/package.json | 9 +- backend/src/config/index.ts | 1 + backend/src/db/seed.ts | 2 + backend/src/db/types/User.ts | 32 + .../sendChatMessageMail.spec.ts.snap | 247 ++ .../sendNotificationMail.spec.ts.snap | 2209 +++++++++++++++++ backend/src/emails/locales/de.json | 39 + backend/src/emails/locales/en.json | 39 + .../src/emails/sendChatMessageMail.spec.ts | 87 + backend/src/emails/sendEmail.ts | 204 ++ .../src/emails/sendNotificationMail.spec.ts | 475 ++++ .../changed_group_member_role/html.pug | 7 + .../changed_group_member_role/subject.pug | 1 + .../emails/templates/chat_message/html.pug | 8 + .../emails/templates/chat_message/subject.pug | 1 + .../templates/commented_on_post/html.pug | 8 + .../templates/commented_on_post/subject.pug | 1 + .../templates/followed_user_posted/html.pug | 8 + .../followed_user_posted/subject.pug | 1 + .../src/emails/templates/includes/footer.pug | 5 + .../emails/templates/includes/greeting.pug | 14 + .../src/emails/templates/includes/header.pug | 9 + .../emails/templates/includes/salutation.pug | 1 + .../src/emails/templates/includes/webflow.css | 65 + backend/src/emails/templates/layout.pug | 26 + .../templates/mentioned_in_comment/html.pug | 8 + .../mentioned_in_comment/subject.pug | 1 + .../templates/mentioned_in_post/html.pug | 8 + .../templates/mentioned_in_post/subject.pug | 1 + .../emails/templates/post_in_group/html.pug | 7 + .../templates/post_in_group/subject.pug | 1 + .../removed_user_from_group/html.pug | 5 + .../removed_user_from_group/subject.pug | 1 + .../templates/user_joined_group/html.pug | 8 + .../templates/user_joined_group/subject.pug | 1 + .../emails/templates/user_left_group/html.pug | 8 + .../templates/user_left_group/subject.pug | 1 + .../notificationsMiddleware.emails.spec.ts | 50 +- ...ficationsMiddleware.followed-users.spec.ts | 20 +- ...tionsMiddleware.mentions-in-groups.spec.ts | 72 +- ...icationsMiddleware.observing-posts.spec.ts | 32 +- ...ificationsMiddleware.online-status.spec.ts | 10 +- ...icationsMiddleware.posts-in-groups.spec.ts | 24 +- .../notificationsMiddleware.spec.ts | 121 +- .../notifications/notificationsMiddleware.ts | 15 +- backend/yarn.lock | 1043 +++++++- 48 files changed, 4779 insertions(+), 202 deletions(-) create mode 100644 backend/.env.test_e2e create mode 100644 backend/src/db/types/User.ts create mode 100644 backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap create mode 100644 backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap create mode 100644 backend/src/emails/locales/de.json create mode 100644 backend/src/emails/locales/en.json create mode 100644 backend/src/emails/sendChatMessageMail.spec.ts create mode 100644 backend/src/emails/sendEmail.ts create mode 100644 backend/src/emails/sendNotificationMail.spec.ts create mode 100644 backend/src/emails/templates/changed_group_member_role/html.pug create mode 100644 backend/src/emails/templates/changed_group_member_role/subject.pug create mode 100644 backend/src/emails/templates/chat_message/html.pug create mode 100644 backend/src/emails/templates/chat_message/subject.pug create mode 100644 backend/src/emails/templates/commented_on_post/html.pug create mode 100644 backend/src/emails/templates/commented_on_post/subject.pug create mode 100644 backend/src/emails/templates/followed_user_posted/html.pug create mode 100644 backend/src/emails/templates/followed_user_posted/subject.pug create mode 100644 backend/src/emails/templates/includes/footer.pug create mode 100644 backend/src/emails/templates/includes/greeting.pug create mode 100644 backend/src/emails/templates/includes/header.pug create mode 100644 backend/src/emails/templates/includes/salutation.pug create mode 100644 backend/src/emails/templates/includes/webflow.css create mode 100644 backend/src/emails/templates/layout.pug create mode 100644 backend/src/emails/templates/mentioned_in_comment/html.pug create mode 100644 backend/src/emails/templates/mentioned_in_comment/subject.pug create mode 100644 backend/src/emails/templates/mentioned_in_post/html.pug create mode 100644 backend/src/emails/templates/mentioned_in_post/subject.pug create mode 100644 backend/src/emails/templates/post_in_group/html.pug create mode 100644 backend/src/emails/templates/post_in_group/subject.pug create mode 100644 backend/src/emails/templates/removed_user_from_group/html.pug create mode 100644 backend/src/emails/templates/removed_user_from_group/subject.pug create mode 100644 backend/src/emails/templates/user_joined_group/html.pug create mode 100644 backend/src/emails/templates/user_joined_group/subject.pug create mode 100644 backend/src/emails/templates/user_left_group/html.pug create mode 100644 backend/src/emails/templates/user_left_group/subject.pug diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 8e3570d95..a8f99e8da 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -14,7 +14,7 @@ jobs: run: | cp webapp/.env.template webapp/.env cp frontend/.env.dist frontend/.env - cp backend/.env.template backend/.env + cp backend/.env.test_e2e backend/.env - name: Build docker images run: | @@ -77,7 +77,7 @@ jobs: 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 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 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/package.json b/backend/package.json index 645bfd83b..76b0a30b6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -36,6 +36,7 @@ "cheerio": "~1.0.0", "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", @@ -77,6 +78,8 @@ "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", @@ -88,6 +91,7 @@ "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.3", @@ -119,7 +123,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/src/config/index.ts b/backend/src/config/index.ts index 9b82299ae..e50f96dd2 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -25,6 +25,7 @@ const environment = { DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV as string) ? (env.DISABLED_MIDDLEWARES?.split(',') ?? []) : [], + SEND_MAIL: env.NODE_ENV !== 'test', } const required = { 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/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..fd7b90395 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap @@ -0,0 +1,247 @@ +// 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__/sendNotificationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap new file mode 100644 index 000000000..698ae9082 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -0,0 +1,2209 @@ +// 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/locales/de.json b/backend/src/emails/locales/de.json new file mode 100644 index 000000000..d09991262 --- /dev/null +++ b/backend/src/emails/locales/de.json @@ -0,0 +1,39 @@ +{ + "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", + "removedUserFromGroup": "Aus Gruppe entfernt", + "postInGroup": "Neuer Beitrag in Gruppe", + "userJoinedGroup": "Nutzer tritt Gruppe bei", + "userLeftGroup": "Nutzer verlässt Gruppe" + }, + "buttons": { + "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" + }, + "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..f14f469ae --- /dev/null +++ b/backend/src/emails/locales/en.json @@ -0,0 +1,39 @@ +{ + "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", + "removedUserFromGroup": "Removed from group", + "postInGroup": "New post in group", + "userJoinedGroup": "User joined group", + "userLeftGroup": "User left group" + }, + "buttons": { + "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" + }, + "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..460a3984a --- /dev/null +++ b/backend/src/emails/sendEmail.ts @@ -0,0 +1,204 @@ +/* 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 from '@config/index' +import logosWebapp from '@config/logos' +import metadata from '@config/metadata' +import { UserDbProperties } from '@db/types/User' + +const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD +const hasDKIMData = + CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY + +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, +} + +export const transport = 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, + }, +}) + +const email = new Email({ + message: { + from: `${CONFIG.APPLICATION_NAME}`, + }, + 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) + } +} 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/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/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..26ae259c5 --- /dev/null +++ b/backend/src/emails/templates/includes/greeting.pug @@ -0,0 +1,14 @@ +//- This sets the greeting at the end of every e-mail +.text-block + - var organizationUrl = ORGANIZATION_URL + - var team = APPLICATION_NAME + - var settingsUrl = settingsUrl + p= t('general.seeYou') + a.organization(href=organizationUrl)= team + | ! + p= t('general.yourTeam', { team }) + 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..c7ea12921 --- /dev/null +++ b/backend/src/emails/templates/includes/webflow.css @@ -0,0 +1,65 @@ +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; +} + +.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/layout.pug b/backend/src/emails/templates/layout.pug new file mode 100644 index 000000000..898776323 --- /dev/null +++ b/backend/src/emails/templates/layout.pug @@ -0,0 +1,26 @@ +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 + + body + div.container + include includes/header.pug + include includes/salutation.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/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/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/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 8b41498ab..27aeb8cf4 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -16,9 +16,9 @@ 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 query, mutate, authenticatedUser, emaillessMember @@ -208,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 () => { @@ -280,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 () => { @@ -353,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 () => { @@ -427,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 () => { @@ -521,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 () => { @@ -603,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 () => { @@ -686,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 f595f441e..3bb0d48e3 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -14,9 +14,9 @@ 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 query, mutate, authenticatedUser @@ -268,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 539022262..9eb26e57f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -17,9 +17,9 @@ 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 query, mutate, authenticatedUser @@ -394,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', + }), + ) }) }) @@ -490,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', + }), + ) }) }) @@ -586,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', + }), + ) }) }) @@ -670,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', + }), + ) }) }) @@ -761,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', + }), + ) }) }) @@ -852,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 2fff0195d..e8c25a16f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -14,9 +14,9 @@ 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 query, mutate, authenticatedUser @@ -213,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', }), ) }) @@ -303,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', }), ) }) @@ -417,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 8d06396ce..1cbb6a2a1 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -12,9 +12,9 @@ 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) @@ -109,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() }) }) }) @@ -135,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 461fa6996..9a7e830ef 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -17,9 +17,9 @@ 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 query, mutate, authenticatedUser @@ -137,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', }, ) @@ -295,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', () => { @@ -337,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', () => { @@ -392,7 +398,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -433,7 +439,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -473,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 985a19193..56fddfd74 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -20,16 +20,11 @@ import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' 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() @@ -240,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`', () => { @@ -276,8 +276,7 @@ describe('notifications', () => { ) // No Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -398,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`', () => { @@ -434,8 +438,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -941,8 +944,7 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -977,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', + }), + }) }) }) @@ -998,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() }) }) @@ -1019,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() }) }) @@ -1056,8 +1068,7 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) }) @@ -1137,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`', () => { @@ -1170,8 +1186,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -1240,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`', () => { @@ -1285,8 +1311,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) }) }) }) @@ -1345,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`', () => { @@ -1378,8 +1408,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -1437,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`', () => { @@ -1470,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 5737a6587..559c72b06 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -10,13 +10,9 @@ import { CHAT_MESSAGE_ADDED, } from '@constants/subscriptions' import { getUnreadRoomsCount } from '@graphql/resolvers/rooms' -import { sendMail } from '@middleware/helpers/email/sendMail' -import { - chatMessageTemplate, - notificationTemplate, -} from '@middleware/helpers/email/templateBuilder' import { isUserOnline } from '@middleware/helpers/isUserOnline' import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' +import { sendNotificationMail, sendChatMessageMail } from '@src/emails/sendEmail' import extractMentionedUsers from './mentions/extractMentionedUsers' @@ -35,12 +31,7 @@ const publishNotifications = async ( !isUserOnline(notificationAdded.to) && !emailsSent.includes(notificationAdded.email) ) { - sendMail( - notificationTemplate({ - email: notificationAdded.email, - variables: { notification: notificationAdded }, - }), - ) + void sendNotificationMail(notificationAdded) emailsSent.push(notificationAdded.email) } }) @@ -496,7 +487,7 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => // 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/yarn.lock b/backend/yarn.lock index e3e1d9d09..209c482e4 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -166,7 +166,7 @@ "@babel/template" "^7.27.0" "@babel/types" "^7.27.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.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== @@ -316,7 +316,7 @@ debug "^4.3.1" globals "^11.1.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.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== @@ -476,6 +476,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" @@ -486,6 +493,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" @@ -821,6 +833,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" @@ -995,6 +1078,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" @@ -1219,6 +1310,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" @@ -1272,6 +1372,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" @@ -1366,6 +1471,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" @@ -1386,6 +1496,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" @@ -1714,6 +1831,11 @@ 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.4.1: version "8.9.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" @@ -1760,6 +1882,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" @@ -1772,11 +1907,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" @@ -1796,11 +1926,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" @@ -2087,6 +2212,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" @@ -2168,6 +2298,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" @@ -2175,6 +2315,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" @@ -2323,6 +2468,13 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" +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: + "@babel/types" "^7.9.6" + backo2@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" @@ -2398,6 +2550,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" @@ -2605,6 +2762,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" @@ -2618,6 +2783,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" @@ -2630,6 +2813,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" @@ -2686,6 +2895,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" @@ -2776,6 +2990,11 @@ 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.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" + integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== + commander@^9.0.0: version "9.5.0" resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" @@ -2796,6 +3015,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" @@ -2907,6 +3134,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" @@ -2925,6 +3163,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" @@ -2936,7 +3185,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== @@ -3051,7 +3300,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== @@ -3082,6 +3331,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" @@ -3097,6 +3351,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" @@ -3174,7 +3433,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== @@ -3203,6 +3467,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" @@ -3217,6 +3489,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" @@ -3226,6 +3503,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" @@ -3250,6 +3536,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" @@ -3257,6 +3550,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" @@ -3264,6 +3564,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" @@ -3301,11 +3610,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" @@ -3338,6 +3642,21 @@ 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== +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" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -3348,11 +3667,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" @@ -3363,6 +3677,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" @@ -3673,11 +3992,21 @@ escalade@^3.1.2: 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" @@ -3904,6 +4233,11 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +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" @@ -3923,6 +4257,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" @@ -3961,6 +4300,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" @@ -4087,6 +4439,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" @@ -4143,6 +4500,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" @@ -4229,6 +4591,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" @@ -4438,6 +4812,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" @@ -4859,7 +5245,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== @@ -4891,6 +5277,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" @@ -4901,7 +5308,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== @@ -4999,6 +5406,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" @@ -5087,6 +5513,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" @@ -5242,6 +5673,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" @@ -5254,6 +5692,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" @@ -5325,7 +5776,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== @@ -5335,6 +5786,16 @@ 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.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: + 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" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.0.tgz#ece38e389e490df0dc21caea2bd596f987f767ff" @@ -5462,6 +5923,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" @@ -5947,6 +6415,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" @@ -6077,6 +6550,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" @@ -6116,6 +6619,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" @@ -6129,6 +6637,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" @@ -6139,6 +6667,13 @@ 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" @@ -6228,7 +6763,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== @@ -6287,6 +6822,31 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= +mailparser@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.7.2.tgz#00feec656e23c0ae805163581b460c2f72ca75d1" + integrity sha512-iI0p2TCcIodR1qGiRoDBBwboSSff50vQAWytM5JRggLfABa4hHYCf3YVujtuzV454xrOP352VsAPIzviqMTo4Q== + dependencies: + 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" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" @@ -6317,6 +6877,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" @@ -6324,6 +6889,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" @@ -6358,6 +6928,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" @@ -6568,6 +7143,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" @@ -6695,6 +7275,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" @@ -6705,6 +7290,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" @@ -6831,6 +7427,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" @@ -6879,7 +7480,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== @@ -7094,6 +7700,14 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +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: + is-docker "^2.0.0" + is-wsl "^2.1.1" + optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -7111,6 +7725,13 @@ 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" @@ -7156,11 +7777,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" @@ -7203,6 +7838,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" @@ -7218,6 +7860,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" @@ -7232,6 +7879,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" @@ -7255,7 +7910,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== @@ -7293,6 +7948,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" @@ -7318,6 +7978,11 @@ picomatch@^4.0.2: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + pirates@^4.0.4: version "4.0.5" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" @@ -7384,6 +8049,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" @@ -7397,6 +8079,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" @@ -7433,6 +8122,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" @@ -7441,6 +8233,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" @@ -7473,7 +8270,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== @@ -7525,6 +8322,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" @@ -7675,6 +8482,15 @@ 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.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" @@ -7729,6 +8545,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" @@ -7766,6 +8589,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" @@ -7825,6 +8653,18 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.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" @@ -8060,6 +8900,11 @@ slash@^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" @@ -8201,23 +9046,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" @@ -8280,27 +9116,13 @@ string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm: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@^6.0.0, strip-ansi@^6.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" @@ -8326,6 +9148,11 @@ 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== +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" @@ -8467,15 +9294,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" @@ -8518,6 +9355,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" @@ -8686,7 +9528,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== @@ -8847,6 +9689,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" @@ -8862,6 +9709,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" @@ -9015,7 +9872,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== @@ -9034,6 +9891,11 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" +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" resolved "https://registry.yarnpkg.com/validator/-/validator-13.15.0.tgz#2dc7ce057e7513a55585109eec29b2c8e8c1aefd" @@ -9063,6 +9925,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" @@ -9077,6 +9944,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" @@ -9193,7 +10072,17 @@ whoops@~5.0.1: resolved "https://registry.yarnpkg.com/whoops/-/whoops-5.0.1.tgz#ce2fd6f255aca09b6fd8ec00c99f9761420296d4" integrity sha512-H2sKu1asxnFE2mNUeRzNY0CQuvl+n6iyE6phvzOaBfZblItNKpC1EzKWy2Zx+woZ3qUFK/wbmmNiLeqXwzk+FA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +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", 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== @@ -9202,24 +10091,6 @@ whoops@~5.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.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== - dependencies: - ansi-styles "^4.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" From fac818a3e4bb7768be1cb2d6343d907a4e0df819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Sun, 4 May 2025 07:44:31 +0800 Subject: [PATCH 194/266] refactor(backend): types for context + `slug` (#8486) Also these changes saw merge conflicts in #8463 so let's get them merged already. Co-authored-by: mahula --- backend/package.json | 1 + backend/src/graphql/resolvers/groups.ts | 53 ++++++++++--------- backend/src/middleware/sluggifyMiddleware.ts | 46 ++++++++++++---- .../src/middleware/slugify/uniqueSlug.spec.ts | 6 ++- backend/src/middleware/slugify/uniqueSlug.ts | 11 ++-- backend/src/server.ts | 1 + backend/yarn.lock | 5 ++ 7 files changed, 77 insertions(+), 46 deletions(-) diff --git a/backend/package.json b/backend/package.json index 76b0a30b6..5dc8ca81f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -95,6 +95,7 @@ "@types/jest": "^29.5.14", "@types/lodash": "^4.17.16", "@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", diff --git a/backend/src/graphql/resolvers/groups.ts b/backend/src/graphql/resolvers/groups.ts index 96d806bf8..8e24117e1 100644 --- a/backend/src/graphql/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/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/server.ts b/backend/src/server.ts index 457ea3684..1f98aab2d 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -102,3 +102,4 @@ const createServer = (options?) => { } export default createServer +export type Context = Awaited>> diff --git a/backend/yarn.lock b/backend/yarn.lock index 209c482e4..688f5faad 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1533,6 +1533,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" From 5d5a5b17b878de7391ec4aa23da15d1f324d4219 Mon Sep 17 00:00:00 2001 From: mahula Date: Sun, 4 May 2025 12:53:34 +0200 Subject: [PATCH 195/266] refactor(workflow): parallelize e2e preparation (#8481) * e2e preparation: separate image buildings * e2e preparation: fix job naming * e2e preparation: set required jobs in cleanup job * e2e preparation: set restore keys * e2e preparation: reduce cleanup job dependencies * e2e preparation: remove primary cache key pattern * Revert "e2e preparation: remove primary cache key pattern" This reverts commit 9df95de90083cc296f38ae87b127f76ac4e59295. * e2e preparation: split cache restore steps * e2e preparation: set concrete archive paths * e2e preparation: remove redundant step id * e2e preparation: fix typo * e2e preparation: fix typo * e2e preparation: move env copying to cypress preaparation job * e2e preparation: update cache cleaning job * e2e preparation: refactor job and step naming * e2e preparation: add repo checkout step to cache cleanup job * e2e preparation: correct docker compose step * e2e preparation: correct backend env copying * e2e preparation: correct cypress cache restore key --- .github/workflows/test-e2e.yml | 139 ++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 37 deletions(-) diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index a8f99e8da..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,19 +67,8 @@ jobs: - name: Copy env files run: | cp webapp/.env.template webapp/.env - cp frontend/.env.dist frontend/.env cp backend/.env.test_e2e 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 - - name: Install cypress requirements run: | wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386" @@ -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,25 +100,41 @@ 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 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 @@ -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 From b54e9773f472a50a5e49149cd884f01ea765306e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Sun, 4 May 2025 14:46:29 +0200 Subject: [PATCH 196/266] fix backend node23 (#8488) --- backend/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 7e694c9f9..2897fe2f6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.1-alpine 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"] From 30080a44c28500f9627c045a8f63c2e8c1bd0fd6 Mon Sep 17 00:00:00 2001 From: mahula Date: Sun, 4 May 2025 22:00:36 +0200 Subject: [PATCH 197/266] refactor(other): cypress: simplify cucumber preprocessor imports and some linting (#8489) * cypress: a little linting * cypress: import badeball preprocessor globally * cypress: reintroduce accidentally removed semicolons * cypress: set new e2e support file in config * Revert "cypress: import badeball preprocessor globally" This reverts commit 55fde3de2f9c355fe25bf9b49485b6bf64ca01cf. * Revert "cypress: set new e2e support file in config" This reverts commit 525cb5cf3766e402dadfd17f48e5b0f6c6ba1f9b. * cypress: change preprocessor import to correct method in step definition file * cypress: change preprocessor import to correct method in step definition files --- ...ins_goal_{string}_and_progress_{string}.js | 6 +-- .../the_donation_info_is_{string}.js | 4 +- ..._open_the_content_menu_of_post_{string}.js | 4 +- ..._{string}_has_a_ribbon_for_pinned_posts.js | 10 ++--- .../there_is_no_button_to_pin_a_post.js | 10 ++--- .../I_see_a_button_with_the_label_{string}.js | 8 ++-- .../I_select_{string}_in_the_language_menu.js | 14 +++--- ...hole_user_interface_appears_in_{string}.js | 12 +++--- ...ld_see_only_{int}_posts_on_the_newsfeed.js | 10 ++--- ...ing}_returns_a_404_error_with_a_message.js | 14 +++--- .../I_can_t_see_the_moderation_menu_item.js | 4 +- .../I_can_visit_the_post_page.js | 4 +- ..._Post_from_the_content_menu_of_the_post.js | 4 +- .../I_click_on_the_author.js | 4 +- ...the_avatar_menu_in_the_top_right_corner.js | 12 +++--- .../I_confirm_the_reporting_dialog.js | 4 +- ...ts_including_from_the_user_who_muted_me.js | 4 +- ...rted_posts_including_the_one_from_above.js | 4 +- .../each_list_item_links_to_the_post_page.js | 6 +-- .../somebody_reported_the_following_posts.js | 4 +- ...re_is_an_annoying_user_who_has_muted_me.js | 14 +++--- ..._with_the_title_{string}_beginning_with.js | 12 +++--- .../mention_{string}_in_the_text.js | 14 +++--- ...cation_menu_and_click_on_the_first_item.js | 14 +++--- ...t}_unread_notifications_in_the_top_menu.js | 10 ++--- ...ton_links_to_the_all_notifications_page.js | 14 +++--- .../the_unread_counter_is_removed.js | 8 ++-- .../Post.Comment/I_comment_the_following.js | 10 ++--- ...ee_an_abbreviated_version_of_my_comment.js | 10 ++--- .../Post.Comment/I_should_see_my_comment.js | 24 +++++------ ...I_should_see_the_entirety_of_my_comment.js | 10 ++--- ...type_in_a_comment_with_{int}_characters.js | 12 +++--- ...uld_create_a_mention_in_the_CommentForm.js | 6 +-- ..._comment_should_be_successfully_created.js | 8 ++-- .../the_editor_should_be_cleared.js | 8 ++-- .../I_choose_{string}_as_the_title.js | 10 ++--- .../the_post_was_saved_successfully.js | 10 ++--- .../Post.Images/I_add_all_required_fields.js | 8 ++-- ...ould_be_able_to_{string}_a_teaser_image.js | 43 +++++++++---------- .../Post.Images/my_post_has_a_teaser_image.js | 4 +- ...t_image_should_not_be_displayed_anymore.js | 6 +-- ...essfully_with_the_{string}_teaser_image.js | 16 +++---- ...ved_successfully_without_a_teaser_image.js | 4 +- ...ws_up_on_the_newsfeed_at_position_{int}.js | 13 +++--- .../Search/I_select_a_post_entry.js | 10 ++--- .../Search/I_select_a_user_entry.js | 8 ++-- ...ld_have_one_item_in_the_select_dropdown.js | 12 +++--- ...earched-for_term_in_the_select_dropdown.js | 10 ++--- ...earched-for_term_in_the_select_dropdown.js | 10 ++--- ...lowing_posts_on_the_search_results_page.js | 12 +++--- ..._following_users_in_the_select_dropdown.js | 12 +++--- .../Search/I_type_{string}_and_press_Enter.js | 10 ++--- .../I_type_{string}_and_press_escape.js | 10 ++--- .../Search/the_search_field_should_clear.js | 10 ++--- .../the_search_parameter_equals_{string}.js | 10 ++--- .../I_am_logged_in_with_username_{string}.js | 12 +++--- .../User.Block/I_block_the_user_{string}.js | 14 +++--- .../I_should_not_see_{string}_button.js | 4 +- ...d_see_no_users_in_my_blocked_users_list.js | 6 +-- .../I_should_see_the_{string}_button.js | 4 +- ...m_the_content_menu_in_the_user_info_box.js | 8 ++-- .../User.Block/a_user_has_blocked_me.js | 16 +++---- .../they_should_not_see_the_comment_form.js | 6 +-- ...plaining_why_commenting_is_not_possible.js | 6 +-- .../User.Mute/I_mute_the_user_{string}.js | 16 +++---- ...the_list_of_posts_of_this_user_is_empty.js | 10 ++--- ...search_should_contain_the_annoying_user.js | 16 +++---- ..._not_contain_posts_by_the_annoying_user.js | 16 +++---- .../I_click_on_element_with_ID_{string}.js | 4 +- .../User.SettingNotifications/I_click_save.js | 6 +-- .../I_cannot_upload_a_picture.js | 12 +++--- ...ld_be_able_to_change_my_profile_picture.js | 26 +++++------ .../I_can_login_successfully.js | 11 +++-- .../I_cannot_login_anymore.js | 11 +++-- .../I_cannot_submit_the_form.js | 10 ++--- .../I_fill_the_password_form_with.js | 20 ++++----- .../I_submit_the_form.js | 8 ++-- ..._on_my_profile_picture_in_the_top_right.js | 8 ++-- .../I_have_the_following_self-description.js | 4 +- .../I_save_{string}_as_my_location.js | 4 +- .../I_save_{string}_as_my_new_name.js | 4 +- ...ng_text_in_the_info_box_below_my_avatar.js | 4 +- ...string}_in_the_info_box_below_my_avatar.js | 4 +- .../I_add_a_social_media_link.js | 4 +- .../I_can_cancel_editing.js | 4 +- .../I_delete_a_social_media_link.js | 4 +- ...I_delete_the_social_media_link_{string}.js | 4 +- .../I_edit_and_save_the_link.js | 4 +- .../I_have_added_a_social_media_link.js | 4 +- ...ve_added_the_social_media_link_{string}.js | 4 +- .../I_start_editing_a_social_media_link.js | 4 +- ..._social_media_link_shows_up_on_the_page.js | 4 +- .../the_new_url_is_displayed.js | 4 +- .../the_old_url_is_not_displayed.js | 4 +- ...ld_be_able_to_see_my_social_media_links.js | 4 +- .../common/I_am_logged_in_as_{string}.js | 6 +-- .../common/I_am_on_page_{string}.js | 10 ++--- .../common/I_can_see_the_following_table.js | 4 +- .../I_choose_the_following_text_as_content.js | 10 ++--- .../common/I_click_on_{string}.js | 8 ++-- ...m_the_content_menu_in_the_user_info_box.js | 12 +++--- ...ox_show_donations_progress_bar_and_save.js | 10 ++--- ...ill_in_my_credentials_{string}_{string}.js | 16 +++---- .../common/I_follow_the_user_{string}.js | 16 +++---- ...et_removed_from_his_follower_collection.js | 14 +++--- .../step_definitions/common/I_log_out.js | 20 ++++----- ...I_navigate_to_my_{string}_settings_page.js | 18 ++++---- .../common/I_navigate_to_page_{string}.js | 10 ++--- .../common/I_refresh_the_page.js | 8 ++-- .../common/I_search_for_{string}.js | 18 ++++---- .../I_see_a_toaster_with_status_{string}.js | 10 ++--- .../common/I_see_a_toaster_with_{string}.js | 6 +-- .../common/I_see_a_{string}_message.js | 8 ++-- ..._following_posts_in_the_select_dropdown.js | 12 +++--- .../common/I_wait_for_{int}_milliseconds.js | 6 +-- ...eckbox_with_ID_{string}_should_{string}.js | 4 +- ...irst_post_on_the_newsfeed_has_the_title.js | 10 ++--- ..._following_{string}_are_in_the_database.js | 16 +++---- .../common/{string}_wrote_a_post_{string}.js | 8 ++-- 119 files changed, 559 insertions(+), 563 deletions(-) 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..707a7397f 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, - }); -}); + }) +}) From 63dd2152976e48d12c386fe6f6868d12fe631d1e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 5 May 2025 14:35:13 +0200 Subject: [PATCH 198/266] lint json (#8472) revert providers path --- backend/.eslintrc.cjs | 5 + backend/package.json | 3 +- backend/public/providers.json | 489 ++++++++++++++++------------------ backend/tsconfig.json | 23 +- backend/yarn.lock | 103 ++++++- 5 files changed, 351 insertions(+), 272 deletions(-) diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 3e8e942ba..9883fae83 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -223,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/package.json b/backend/package.json index 5dc8ca81f..49c35012b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,7 +12,7 @@ "build": "tsc && tsc-alias && ./scripts/build.copy.files.sh", "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 .", + "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", @@ -106,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", 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/tsconfig.json b/backend/tsconfig.json index 7ef3f47b0..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,11 +25,12 @@ // "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/*"], @@ -66,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. */ @@ -88,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. */ @@ -115,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 688f5faad..4d6b2e2d3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -408,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" @@ -1841,6 +1848,11 @@ acorn@^7.1.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" @@ -4034,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" @@ -4065,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" @@ -4113,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" @@ -4170,11 +4210,16 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" -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" @@ -4229,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== @@ -4238,6 +4283,15 @@ 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" @@ -4255,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" @@ -6529,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" @@ -9121,7 +9192,14 @@ string_decoder@^1.3.0: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm: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: +"strip-ansi-cjs@npm:strip-ansi@^6.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@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== @@ -9225,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" @@ -10087,7 +10173,16 @@ with@^7.0.0: assert-never "^1.2.1" babel-walk "3.0.0-canary-5" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0, wrap-ansi@^8.1.0: +"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" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.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== From 65f764f6d93db15e34c23e98b5041254f44043e8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 5 May 2025 20:44:14 +0200 Subject: [PATCH 199/266] feat(backend): signup email localized (#8459) * localized registration email * localized email verification email * localized reset password email --- .../sendChatMessageMail.spec.ts.snap | 8 + .../sendEmailVerification.spec.ts.snap | 261 ++++++++ .../sendNotificationMail.spec.ts.snap | 72 +++ .../sendRegistrationMail.spec.ts.snap | 559 ++++++++++++++++++ .../sendResetPasswordMail.spec.ts.snap | 260 ++++++++ .../__snapshots__/sendWrongEmail.spec.ts.snap | 255 ++++++++ backend/src/emails/locales/de.json | 36 +- backend/src/emails/locales/en.json | 35 +- backend/src/emails/sendEmail.ts | 135 +++++ .../src/emails/sendEmailVerification.spec.ts | 35 ++ .../src/emails/sendRegistrationMail.spec.ts | 63 ++ .../src/emails/sendResetPasswordMail.spec.ts | 35 ++ backend/src/emails/sendWrongEmail.spec.ts | 31 + .../templates/emailVerification/html.pug | 10 + .../templates/emailVerification/subject.pug | 1 + .../emails/templates/includes/greeting.pug | 11 +- .../src/emails/templates/includes/webflow.css | 4 + .../src/emails/templates/includes/welcome.pug | 1 + backend/src/emails/templates/layout.pug | 8 +- .../emails/templates/registration/html.pug | 15 + .../emails/templates/registration/subject.pug | 1 + .../emails/templates/resetPassword/html.pug | 9 + .../templates/resetPassword/subject.pug | 1 + .../src/emails/templates/wrongEmail/html.pug | 10 + .../emails/templates/wrongEmail/subject.pug | 1 + backend/src/graphql/resolvers/emails.ts | 1 + .../graphql/resolvers/passwordReset.spec.ts | 8 +- .../graphql/resolvers/registration.spec.ts | 6 +- .../src/graphql/types/type/EmailAddress.gql | 6 +- backend/src/graphql/types/type/User.gql | 2 +- .../src/middleware/login/loginMiddleware.ts | 32 +- .../middleware/permissionsMiddleware.spec.ts | 8 +- .../components/PasswordReset/Request.spec.js | 14 +- webapp/components/PasswordReset/Request.vue | 6 +- .../Registration/RegistrationSlideEmail.vue | 6 +- webapp/components/Registration/Signup.spec.js | 5 +- webapp/components/Registration/Signup.vue | 6 +- 37 files changed, 1907 insertions(+), 50 deletions(-) create mode 100644 backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap create mode 100644 backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap create mode 100644 backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap create mode 100644 backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap create mode 100644 backend/src/emails/sendEmailVerification.spec.ts create mode 100644 backend/src/emails/sendRegistrationMail.spec.ts create mode 100644 backend/src/emails/sendResetPasswordMail.spec.ts create mode 100644 backend/src/emails/sendWrongEmail.spec.ts create mode 100644 backend/src/emails/templates/emailVerification/html.pug create mode 100644 backend/src/emails/templates/emailVerification/subject.pug create mode 100644 backend/src/emails/templates/includes/welcome.pug create mode 100644 backend/src/emails/templates/registration/html.pug create mode 100644 backend/src/emails/templates/registration/subject.pug create mode 100644 backend/src/emails/templates/resetPassword/html.pug create mode 100644 backend/src/emails/templates/resetPassword/subject.pug create mode 100644 backend/src/emails/templates/wrongEmail/html.pug create mode 100644 backend/src/emails/templates/wrongEmail/subject.pug diff --git a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap index fd7b90395..57b256a12 100644 --- a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap @@ -62,6 +62,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -185,6 +189,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; 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..34c945d65 --- /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 index 698ae9082..0fec27b7c 100644 --- a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -62,6 +62,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -184,6 +188,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -308,6 +316,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -432,6 +444,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -556,6 +572,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -679,6 +699,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -801,6 +825,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -920,6 +948,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1043,6 +1075,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1166,6 +1202,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1288,6 +1328,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1412,6 +1456,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1536,6 +1584,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1660,6 +1712,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1783,6 +1839,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -1905,6 +1965,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -2024,6 +2088,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; @@ -2147,6 +2215,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; 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..3b8d1c077 --- /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..3d8c6ac27 --- /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..72acc52cd --- /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 index d09991262..9e0ce843a 100644 --- a/backend/src/emails/locales/de.json +++ b/backend/src/emails/locales/de.json @@ -7,12 +7,32 @@ "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" + "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", @@ -23,7 +43,19 @@ "seeYou": "Bis bald bei ", "yourTeam": "– Dein {team} Team", "settingsHint": "PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine ", - "settingsName": "Benachrichtigungseinstellungen" + "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 ", diff --git a/backend/src/emails/locales/en.json b/backend/src/emails/locales/en.json index f14f469ae..30ca64655 100644 --- a/backend/src/emails/locales/en.json +++ b/backend/src/emails/locales/en.json @@ -7,12 +7,32 @@ "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" + "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", @@ -23,7 +43,18 @@ "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" + "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 ", diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts index 460a3984a..7b7ea76b3 100644 --- a/backend/src/emails/sendEmail.ts +++ b/backend/src/emails/sendEmail.ts @@ -28,6 +28,7 @@ const defaultParams = { ORGANIZATION_URL: CONFIG.ORGANIZATION_URL, supportUrl: CONFIG.SUPPORT_URL, settingsUrl, + renderSettingsUrl: true, } export const transport = createTransport({ @@ -202,3 +203,137 @@ export const sendChatMessageMail = async ( 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/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/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/includes/greeting.pug b/backend/src/emails/templates/includes/greeting.pug index 26ae259c5..6b682fc2d 100644 --- a/backend/src/emails/templates/includes/greeting.pug +++ b/backend/src/emails/templates/includes/greeting.pug @@ -3,12 +3,15 @@ - 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 }) - br - p= t('general.settingsHint') - a.settings(href=settingsUrl)= t('general.settingsName') - | ! + + if renderSettingsUrl + br + p= t('general.settingsHint') + a.settings(href=settingsUrl)= t('general.settingsName') + | ! diff --git a/backend/src/emails/templates/includes/webflow.css b/backend/src/emails/templates/includes/webflow.css index c7ea12921..1dc1f0b24 100644 --- a/backend/src/emails/templates/includes/webflow.css +++ b/backend/src/emails/templates/includes/webflow.css @@ -50,6 +50,10 @@ a.button { border-radius: 4px; } +span { + color: #17b53e; +} + .text-block { margin-top: 20px; color: #000000; 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 index 898776323..faaadb5d3 100644 --- a/backend/src/emails/templates/layout.pug +++ b/backend/src/emails/templates/layout.pug @@ -13,11 +13,15 @@ html(lang=locale) .wf-force-outline-none[tabindex="-1"]:focus{outline:none;} style include includes/webflow.css - + + - var name = name body div.container include includes/header.pug - include includes/salutation.pug + if name + include includes/salutation.pug + else + include includes/welcome.pug .wrapper block content 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/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/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/graphql/resolvers/emails.ts b/backend/src/graphql/resolvers/emails.ts index be721dda5..0491c86ad 100644 --- a/backend/src/graphql/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/graphql/resolvers/passwordReset.spec.ts b/backend/src/graphql/resolvers/passwordReset.spec.ts index d5d08265c..3bc4d53ba 100644 --- a/backend/src/graphql/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/graphql/resolvers/registration.spec.ts b/backend/src/graphql/resolvers/registration.spec.ts index ccf5a9e10..d959b348a 100644 --- a/backend/src/graphql/resolvers/registration.spec.ts +++ b/backend/src/graphql/resolvers/registration.spec.ts @@ -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/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/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/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/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/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(() => { From 33274e5b9a557b3031060fd54c5e48be4f40b7ee Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 6 May 2025 01:54:13 +0200 Subject: [PATCH 200/266] feat(webapp): user teaser popover (#8450) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * calculate distance between current user and queried user * fix query for unset location * use database to calculate distance * rename distance to distance to me, 100% calculation done in DB * distanceToMe tests * lint fixes * remove comments * Show user teaser popover with badges, Desktop * Refactor UserTeaser and add mobile popover support * Avoid click propagation (WIP) * Prevent event propagation * Adjust alignment and font sizes * More spacing for statistics * Add distance, simplify user link * Refactor location info into own component * Add tests for UserTeaserPopup * Refactor and test LocationInfo * Query distanceToMe, rename distance to distanceToMe * Update test * Improve tests for UserTeaser, WIP * Fix tests * DistanceToMe on User instead of Location * Revert "DistanceToMe on User instead of Location" This reverts commit 96c9db00a44cd120e47bfe9534d3e066a194744c. * Fix notifications * Refactor UserTeaser and fix location info * Fix group member crash * Show 0 distance * Fit in popover on small screens * Allow access to profile on desktop * Revert backend changes * Load user teaser popover data only when needed * Fix type mismatch * Refactor for clarity and accessibility * Litte refactorings and improvements * Fix popover test * Adapt and fix tests * Fix tests and bugs * Add placeholder * cypress: adapt user teaser locator to changes * Remove delays and scrolling * Disable popovers in notification list and fix layout * Remove flickering * Make overlay catch all pointer events on touch devices * Re-add attribute for E2E test * Fix test, return to mouseover * fix snapshot --------- Co-authored-by: Ulf Gebhardt Co-authored-by: Wolfgang Huß Co-authored-by: mahula --- .../Post.Comment/I_should_see_my_comment.js | 2 +- webapp/assets/_new/styles/tokens.scss | 14 +- webapp/assets/styles/main.scss | 12 +- webapp/components/Dropdown.vue | 30 +- .../components/Notification/Notification.vue | 1 + .../NotificationMenu/NotificationMenu.vue | 2 +- .../NotificationsTable.spec.js | 4 +- .../UserTeaser/LocationInfo.spec.js | 31 + webapp/components/UserTeaser/LocationInfo.vue | 44 + .../components/UserTeaser/UserTeaser.spec.js | 301 +- webapp/components/UserTeaser/UserTeaser.vue | 118 +- .../UserTeaser/UserTeaserHelper.spec.js | 71 + .../UserTeaser/UserTeaserHelper.vue | 46 + .../UserTeaser/UserTeaserNonAnonymous.vue | 123 + .../UserTeaser/UserTeaserPopover.spec.js | 99 + .../UserTeaser/UserTeaserPopover.vue | 103 + .../__snapshots__/LocationInfo.spec.js.snap | 51 + .../__snapshots__/UserTeaser.spec.js.snap | 1136 +++++++ .../UserTeaserHelper.spec.js.snap | 19 + .../UserTeaserPopover.spec.js.snap | 547 ++++ webapp/components/utils/isTouchDevice.js | 2 + webapp/graphql/Fragments.js | 15 + webapp/graphql/User.js | 15 +- webapp/locales/de.json | 8 + webapp/locales/en.json | 8 + webapp/locales/es.json | 8 + webapp/locales/fr.json | 8 + webapp/locales/it.json | 8 + webapp/locales/nl.json | 8 + webapp/locales/pl.json | 8 + webapp/locales/pt.json | 8 + webapp/locales/ru.json | 8 + .../_id/__snapshots__/_slug.spec.js.snap | 2784 ++++++++++------- 33 files changed, 4277 insertions(+), 1365 deletions(-) create mode 100644 webapp/components/UserTeaser/LocationInfo.spec.js create mode 100644 webapp/components/UserTeaser/LocationInfo.vue create mode 100644 webapp/components/UserTeaser/UserTeaserHelper.spec.js create mode 100644 webapp/components/UserTeaser/UserTeaserHelper.vue create mode 100644 webapp/components/UserTeaser/UserTeaserNonAnonymous.vue create mode 100644 webapp/components/UserTeaser/UserTeaserPopover.spec.js create mode 100644 webapp/components/UserTeaser/UserTeaserPopover.vue create mode 100644 webapp/components/UserTeaser/__snapshots__/LocationInfo.spec.js.snap create mode 100644 webapp/components/UserTeaser/__snapshots__/UserTeaser.spec.js.snap create mode 100644 webapp/components/UserTeaser/__snapshots__/UserTeaserHelper.spec.js.snap create mode 100644 webapp/components/UserTeaser/__snapshots__/UserTeaserPopover.spec.js.snap create mode 100644 webapp/components/utils/isTouchDevice.js 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 707a7397f..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 @@ -8,6 +8,6 @@ defineStep('I should see my comment', () => { .get('.profile-avatar img') .should('have.attr', 'src') .and('contain', 'https://') // some url - .get('.user-teaser > .info > .text') + .get('.user-teaser .info > .text') .should('contain', 'today at') }) 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/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/locales/de.json b/webapp/locales/de.json index df050b191..e2b641b08 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": "Sprache wählen" }, + "location": { + "distance": "{distance} km von mir entfernt" + }, "login": { "email": "Deine E-Mail", "failure": "Fehlerhafte E-Mail-Adresse oder Passwort.", @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "Neue Nutzungsbedingungen", "termsAndConditionsNewConfirm": "Ich habe die neuen Nutzungsbedingungen durchgelesen und stimme zu.", "termsAndConditionsNewConfirmText": "Bitte lies Dir die neuen Nutzungsbedingungen jetzt durch!" + }, + "user-teaser": { + "popover": { + "open-profile": "Profil öffnen" + } } } diff --git a/webapp/locales/en.json b/webapp/locales/en.json index ecd0ec18d..ab34ba66a 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": "Choose language" }, + "location": { + "distance": "{distance} km away from me" + }, "login": { "email": "Your E-mail", "failure": "Incorrect email address or password.", @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "New Terms and Conditions", "termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.", "termsAndConditionsNewConfirmText": "Please read the new terms of use now!" + }, + "user-teaser": { + "popover": { + "open-profile": "Open profile" + } } } diff --git a/webapp/locales/es.json b/webapp/locales/es.json index 15096b9d8..b7d95d11c 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "Su correo electrónico", "failure": "Dirección de correo electrónico o contraseña incorrecta.", @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "Nuevos términos de uso", "termsAndConditionsNewConfirm": "He leído y acepto los nuevos términos de uso.", "termsAndConditionsNewConfirmText": "¡Por favor, lea los nuevos términos de uso ahora!" + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index 2da2a9801..37a182c28 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "Votre mail", "failure": "Adresse mail ou mot de passe incorrect.", @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "Nouvelles conditions générales", "termsAndConditionsNewConfirm": "J'ai lu et accepté les nouvelles conditions générales.", "termsAndConditionsNewConfirmText": "Veuillez lire les nouvelles conditions d'utilisation dès maintenant !" + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 485abff3a..6b686502c 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "La tua email", "failure": null, @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "Nuovi Termini e Condizioni", "termsAndConditionsNewConfirm": "Ho letto e accetto le nuove condizioni generali di contratto.", "termsAndConditionsNewConfirmText": "Si prega di leggere le nuove condizioni d'uso ora!" + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 40f9aca2e..714ed2b01 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "Uw E-mail", "failure": null, @@ -1173,5 +1176,10 @@ "newTermsAndConditions": null, "termsAndConditionsNewConfirm": null, "termsAndConditionsNewConfirmText": null + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index ee332b84b..61a6acf24 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "Twój adres e-mail", "failure": null, @@ -1173,5 +1176,10 @@ "newTermsAndConditions": null, "termsAndConditionsNewConfirm": null, "termsAndConditionsNewConfirmText": null + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 54f9b5d99..80172daa3 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "Seu email", "failure": "Endereço de e-mail ou senha incorretos.", @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "Novos Termos e Condições", "termsAndConditionsNewConfirm": "Eu li e concordo com os novos termos de condições.", "termsAndConditionsNewConfirmText": "Por favor, leia os novos termos de uso agora!" + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index 4d2e2a357..f7956755c 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -636,6 +636,9 @@ "localeSwitch": { "tooltip": null }, + "location": { + "distance": null + }, "login": { "email": "Электронная почта", "failure": "Неверный адрес электронной почты или пароль.", @@ -1173,5 +1176,10 @@ "newTermsAndConditions": "Новые условия и положения", "termsAndConditionsNewConfirm": "Я прочитал(а) и согласен(на) с новыми условиями.", "termsAndConditionsNewConfirmText": "Пожалуйста, ознакомьтесь с новыми условиями использования!" + }, + "user-teaser": { + "popover": { + "open-profile": null + } } } diff --git a/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap b/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap index cb43b8526..68c2b50ba 100644 --- a/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap +++ b/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap @@ -493,39 +493,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a close class="" placement="top-start" > -
- + -
- - PL - - - - - -
-
- -
-
- + PL + + + + + +
+ + +
+
+ Peter Lustig - - - - + + + + + +
- -
-
+
+ +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -2304,39 +2364,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a close class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -3197,39 +3317,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -3956,39 +4136,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -4713,39 +4953,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -5539,39 +5839,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -6479,39 +6839,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • @@ -7393,39 +7813,52 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde class="" placement="top-start" > -
    - + -
    - - PL - - - - - -
    -
    - -
    -
    - + PL + + + + + +
    + + +
    +
    + Peter Lustig - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - JR - - - - - -
    -
    - -
    -
    - + JR + + + + + +
    + + +
    +
    + Jenny Rostock - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - BDB - - - - - -
    -
    - -
    -
    - + BDB + + + + + +
    + + +
    +
    + Bob der Baumeister - - - - + + + + + +
    - -
    -
    +
    + +
  • -
    - + -
    - - H - - - - - -
    -
    - -
    -
    - + H + + + + + +
    + + +
    +
    + Huey - - - - + + + + + +
    - -
    -
    +
    + +
  • From b280b1f3f04cf247ffe5305837a4c84b5eefdced Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 6 May 2025 05:28:51 +0200 Subject: [PATCH 201/266] v3.5.0 (#8492) --- CHANGELOG.md | 32 +++++++++++++++++++ backend/package.json | 2 +- .../helm/charts/ocelot-neo4j/Chart.yaml | 2 +- .../helm/charts/ocelot-social/Chart.yaml | 2 +- frontend/package-lock.json | 4 +-- frontend/package.json | 2 +- package.json | 2 +- webapp/maintenance/source/package.json | 2 +- webapp/package.json | 2 +- 9 files changed, 41 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b1a955a..f5cca07ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,40 @@ 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.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.4.0...3.5.0) + +- 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) diff --git a/backend/package.json b/backend/package.json index 49c35012b..c53a7d48c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "3.4.0", + "version": "3.5.0", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/deployment/helm/charts/ocelot-neo4j/Chart.yaml b/deployment/helm/charts/ocelot-neo4j/Chart.yaml index e9f8e2354..989e1a683 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.0" diff --git a/deployment/helm/charts/ocelot-social/Chart.yaml b/deployment/helm/charts/ocelot-social/Chart.yaml index 2a872480e..32793ee79 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.0" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4a8a0442c..58af310dd 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.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.4.0", + "version": "3.5.0", "license": "Apache-2.0", "dependencies": { "@intlify/unplugin-vue-i18n": "^2.0.0", diff --git a/frontend/package.json b/frontend/package.json index 5e2235be5..3278dfffd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-frontend", - "version": "3.4.0", + "version": "3.5.0", "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.json b/package.json index 2754268fc..9c03f0323 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "3.4.0", + "version": "3.5.0", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json index df76a77ca..031729a40 100644 --- a/webapp/maintenance/source/package.json +++ b/webapp/maintenance/source/package.json @@ -1,6 +1,6 @@ { "name": "@ocelot-social/maintenance", - "version": "3.4.0", + "version": "3.5.0", "description": "Maintenance page for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/package.json b/webapp/package.json index 7af701437..05f00b2ac 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-webapp", - "version": "3.4.0", + "version": "3.5.0", "description": "ocelot.social Frontend", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", From 5c5550092e78091c8166bf85992e4c9c804f2a86 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 6 May 2025 11:52:24 +0200 Subject: [PATCH 202/266] fix emails in production (#8493) --- backend/scripts/build.copy.files.sh | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/backend/scripts/build.copy.files.sh b/backend/scripts/build.copy.files.sh index 7279291d6..0eb92cfbc 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/templates/ -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/locales/ # gql files mkdir -p build/src/graphql/types/ From 4960f2800bf7b7c7ddeaea9a341b91759fda4282 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 6 May 2025 12:30:40 +0200 Subject: [PATCH 203/266] v3.5.1 (#8496) --- CHANGELOG.md | 7 +++++++ backend/package.json | 2 +- deployment/helm/charts/ocelot-neo4j/Chart.yaml | 2 +- deployment/helm/charts/ocelot-social/Chart.yaml | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- package.json | 2 +- webapp/maintenance/source/package.json | 2 +- webapp/package.json | 2 +- 9 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5cca07ed..c29384be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ 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.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.0...3.5.1) + +- 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) diff --git a/backend/package.json b/backend/package.json index c53a7d48c..279a35b08 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "3.5.0", + "version": "3.5.1", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/deployment/helm/charts/ocelot-neo4j/Chart.yaml b/deployment/helm/charts/ocelot-neo4j/Chart.yaml index 989e1a683..41e9a8a5a 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.5.0" +appVersion: "3.5.1" diff --git a/deployment/helm/charts/ocelot-social/Chart.yaml b/deployment/helm/charts/ocelot-social/Chart.yaml index 32793ee79..8b620c1b8 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.5.0" +appVersion: "3.5.1" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 58af310dd..2bc7c0a7a 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social-frontend", - "version": "3.5.0", + "version": "3.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.5.0", + "version": "3.5.1", "license": "Apache-2.0", "dependencies": { "@intlify/unplugin-vue-i18n": "^2.0.0", diff --git a/frontend/package.json b/frontend/package.json index 3278dfffd..d3e8468cc 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-frontend", - "version": "3.5.0", + "version": "3.5.1", "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.json b/package.json index 9c03f0323..8b023ca82 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "3.5.0", + "version": "3.5.1", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json index 031729a40..5ec698475 100644 --- a/webapp/maintenance/source/package.json +++ b/webapp/maintenance/source/package.json @@ -1,6 +1,6 @@ { "name": "@ocelot-social/maintenance", - "version": "3.5.0", + "version": "3.5.1", "description": "Maintenance page for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/package.json b/webapp/package.json index 05f00b2ac..6c18c1355 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-webapp", - "version": "3.5.0", + "version": "3.5.1", "description": "ocelot.social Frontend", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", From 2b63df1930a150849a8245e23edcdb36b2a11161 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 6 May 2025 13:18:34 +0200 Subject: [PATCH 204/266] fix emails2 (#8497) --- backend/scripts/build.copy.files.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/scripts/build.copy.files.sh b/backend/scripts/build.copy.files.sh index 0eb92cfbc..1daf19b55 100755 --- a/backend/scripts/build.copy.files.sh +++ b/backend/scripts/build.copy.files.sh @@ -5,10 +5,10 @@ cp -r public/ build/public/ # email files mkdir -p build/src/emails/templates/ -cp -r src/emails/templates/ build/src/emails/templates/ +cp -r src/emails/templates/ build/src/emails/ mkdir -p build/src/emails/locales/ -cp -r src/emails/locales/ build/src/emails/locales/ +cp -r src/emails/locales/ build/src/emails/ # gql files mkdir -p build/src/graphql/types/ From 5d348c2eaf066a3cc2d80faabb0754ceef66b9ef Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 6 May 2025 13:44:58 +0200 Subject: [PATCH 205/266] v3.5.2 (#8498) --- CHANGELOG.md | 7 +++++++ backend/package.json | 2 +- deployment/helm/charts/ocelot-neo4j/Chart.yaml | 2 +- deployment/helm/charts/ocelot-social/Chart.yaml | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- package.json | 2 +- webapp/maintenance/source/package.json | 2 +- webapp/package.json | 2 +- 9 files changed, 16 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c29384be6..95c747d24 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,15 @@ 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.2](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.1...3.5.2) + +- 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) diff --git a/backend/package.json b/backend/package.json index 279a35b08..aa706eb78 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "3.5.1", + "version": "3.5.2", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/deployment/helm/charts/ocelot-neo4j/Chart.yaml b/deployment/helm/charts/ocelot-neo4j/Chart.yaml index 41e9a8a5a..da08678d3 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.5.1" +appVersion: "3.5.2" diff --git a/deployment/helm/charts/ocelot-social/Chart.yaml b/deployment/helm/charts/ocelot-social/Chart.yaml index 8b620c1b8..7d2b93821 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.5.1" +appVersion: "3.5.2" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2bc7c0a7a..e4965713c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social-frontend", - "version": "3.5.1", + "version": "3.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.5.1", + "version": "3.5.2", "license": "Apache-2.0", "dependencies": { "@intlify/unplugin-vue-i18n": "^2.0.0", diff --git a/frontend/package.json b/frontend/package.json index d3e8468cc..a8487569b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-frontend", - "version": "3.5.1", + "version": "3.5.2", "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.json b/package.json index 8b023ca82..b93be2e39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "3.5.1", + "version": "3.5.2", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json index 5ec698475..969a66db7 100644 --- a/webapp/maintenance/source/package.json +++ b/webapp/maintenance/source/package.json @@ -1,6 +1,6 @@ { "name": "@ocelot-social/maintenance", - "version": "3.5.1", + "version": "3.5.2", "description": "Maintenance page for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/package.json b/webapp/package.json index 6c18c1355..05a4e0169 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-webapp", - "version": "3.5.1", + "version": "3.5.2", "description": "ocelot.social Frontend", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", From 32026dacfc50d8b15ff3c9e06e707e920adab228 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Wed, 7 May 2025 00:46:51 +0200 Subject: [PATCH 206/266] fix warning in workflow for lower case as (#8494) --- webapp/Dockerfile.maintenance | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/Dockerfile.maintenance b/webapp/Dockerfile.maintenance index 3e4b9b054..bc930f7d0 100644 --- a/webapp/Dockerfile.maintenance +++ b/webapp/Dockerfile.maintenance @@ -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 From 290a17640723fc1bc04360d7b6c4c81fc22058bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Wed, 7 May 2025 21:57:35 +0800 Subject: [PATCH 207/266] refactor(backend): types for global config (#8485) * refactor(backend): types for global config I saw merge conflicts in these files for #8463 so let's get some parts of this PR into `master` already. I believe this fixes a small bug. They guard clause didn't ensure that all of REDIS_ configurations were set. * remove old email mechanism * refactor(backend: react to @ulfgebhardt's review See: https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8485#pullrequestreview-2813528327 * build(backend): optional commit @ulfgebhardt this is how I tested the configurations. We don't need to include this commit but I wouldn't expect to send out real emails from a `docker-compose` setup. --------- Co-authored-by: Moriz Wahl --- backend/src/config/index.ts | 75 +++-- backend/src/context/pubsub.ts | 10 +- .../20200312140328-bulk_upload_to_s3.ts | 117 -------- backend/src/db/neo4j.ts | 3 - backend/src/emails/sendEmail.ts | 26 +- .../src/graphql/resolvers/images/images.ts | 6 + backend/src/index.ts | 2 +- .../src/middleware/helpers/email/sendMail.ts | 80 ----- .../helpers/email/templateBuilder.spec.ts | 283 ------------------ .../helpers/email/templateBuilder.ts | 140 --------- .../helpers/email/templates/chatMessage.html | 105 ------- .../helpers/email/templates/de/index.ts | 9 - .../email/templates/de/notification.html | 83 ----- .../email/templates/emailVerification.html | 186 ------------ .../helpers/email/templates/en/index.ts | 9 - .../email/templates/en/notification.html | 83 ----- .../helpers/email/templates/index.ts | 15 - .../helpers/email/templates/layout.html | 197 ------------ .../email/templates/resetPassword.html | 185 ------------ .../helpers/email/templates/signup.html | 214 ------------- .../helpers/email/templates/wrongAccount.html | 185 ------------ backend/src/middleware/index.ts | 4 +- docker-compose.override.yml | 2 + 23 files changed, 67 insertions(+), 1952 deletions(-) delete mode 100644 backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts delete mode 100644 backend/src/middleware/helpers/email/sendMail.ts delete mode 100644 backend/src/middleware/helpers/email/templateBuilder.spec.ts delete mode 100644 backend/src/middleware/helpers/email/templateBuilder.ts delete mode 100644 backend/src/middleware/helpers/email/templates/chatMessage.html delete mode 100644 backend/src/middleware/helpers/email/templates/de/index.ts delete mode 100644 backend/src/middleware/helpers/email/templates/de/notification.html delete mode 100644 backend/src/middleware/helpers/email/templates/emailVerification.html delete mode 100644 backend/src/middleware/helpers/email/templates/en/index.ts delete mode 100644 backend/src/middleware/helpers/email/templates/en/notification.html delete mode 100644 backend/src/middleware/helpers/email/templates/index.ts delete mode 100644 backend/src/middleware/helpers/email/templates/layout.html delete mode 100644 backend/src/middleware/helpers/email/templates/resetPassword.html delete mode 100644 backend/src/middleware/helpers/email/templates/signup.html delete mode 100644 backend/src/middleware/helpers/email/templates/wrongAccount.html diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index e50f96dd2..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,16 +15,17 @@ 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', @@ -35,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 = { @@ -70,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, } @@ -110,10 +132,11 @@ export default { ...environment, ...server, ...required, - ...smtp, ...neo4j, ...sentry, ...redis, ...s3, ...options, } + +export { nodemailerTransportOptions } diff --git a/backend/src/context/pubsub.ts b/backend/src/context/pubsub.ts index 003347b16..3d99bba6d 100644 --- a/backend/src/context/pubsub.ts +++ b/backend/src/context/pubsub.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { RedisPubSub } from 'graphql-redis-subscriptions' import { PubSub } from 'graphql-subscriptions' import Redis from 'ioredis' @@ -6,14 +5,15 @@ import Redis from 'ioredis' import CONFIG from '@config/index' export default () => { - if (!CONFIG.REDIS_DOMAIN || CONFIG.REDIS_PORT || CONFIG.REDIS_PASSWORD) { + const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG + if (!(REDIS_DOMAIN && REDIS_PORT && REDIS_PASSWORD)) { return new PubSub() } const options = { - host: CONFIG.REDIS_DOMAIN, - port: CONFIG.REDIS_PORT, - password: CONFIG.REDIS_PASSWORD, + host: REDIS_DOMAIN, + port: REDIS_PORT, + password: REDIS_PASSWORD, retryStrategy: (times) => { return Math.min(times * 50, 2000) }, 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/db/neo4j.ts b/backend/src/db/neo4j.ts index 5d084099a..ecaef68b5 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,6 +1,3 @@ -/* 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' diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts index 7b7ea76b3..8e5d8e83c 100644 --- a/backend/src/emails/sendEmail.ts +++ b/backend/src/emails/sendEmail.ts @@ -7,17 +7,14 @@ 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 from '@config/index' +import CONFIG, { nodemailerTransportOptions } from '@config/index' import logosWebapp from '@config/logos' import metadata from '@config/metadata' import { UserDbProperties } from '@db/types/User' -const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD -const hasDKIMData = - CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY - const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI) const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI) @@ -31,24 +28,7 @@ const defaultParams = { renderSettingsUrl: true, } -export const transport = 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, - }, -}) +const transport = createTransport(nodemailerTransportOptions) const email = new Email({ message: { diff --git a/backend/src/graphql/resolvers/images/images.ts b/backend/src/graphql/resolvers/images/images.ts index 2e76a7889..217d26553 100644 --- a/backend/src/graphql/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/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/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts deleted file mode 100644 index fc50107fb..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 { createTransport } 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 = 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 896e5b33b..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' 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 From 50bc62428ef4987959b6e63b0074aaf056b99d83 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 7 May 2025 19:42:39 +0200 Subject: [PATCH 208/266] fix(backend): correct email from (#8501) --- .../sendChatMessageMail.spec.ts.snap | 4 +-- .../sendEmailVerification.spec.ts.snap | 4 +-- .../sendNotificationMail.spec.ts.snap | 36 +++++++++---------- .../sendRegistrationMail.spec.ts.snap | 8 ++--- .../sendResetPasswordMail.spec.ts.snap | 4 +-- .../__snapshots__/sendWrongEmail.spec.ts.snap | 4 +-- backend/src/emails/sendEmail.ts | 4 ++- 7 files changed, 33 insertions(+), 31 deletions(-) diff --git a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap index 57b256a12..786bad9c0 100644 --- a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap @@ -3,7 +3,7 @@ exports[`sendChatMessageMail English chat_message template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -130,7 +130,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendChatMessageMail German chat_message template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " diff --git a/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap b/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap index 34c945d65..87815f5e6 100644 --- a/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap @@ -3,7 +3,7 @@ exports[`sendEmailVerification English renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -133,7 +133,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendEmailVerification German renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " diff --git a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap index 0fec27b7c..1c4f0dc8e 100644 --- a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -3,7 +3,7 @@ exports[`sendNotificationMail English changed_group_member_role template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -129,7 +129,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English commented_on_post template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -257,7 +257,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English followed_user_posted template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -385,7 +385,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English mentioned_in_comment template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -513,7 +513,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English mentioned_in_post template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -640,7 +640,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English post_in_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -766,7 +766,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English removed_user_from_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -889,7 +889,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English user_joined_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1016,7 +1016,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail English user_left_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1143,7 +1143,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German changed_group_member_role template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1269,7 +1269,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German commented_on_post template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1397,7 +1397,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German followed_user_posted template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1525,7 +1525,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German mentioned_in_comment template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1653,7 +1653,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German mentioned_in_post template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1780,7 +1780,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German post_in_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -1906,7 +1906,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German removed_user_from_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -2029,7 +2029,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German user_joined_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -2156,7 +2156,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendNotificationMail German user_left_group template 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " diff --git a/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap index 3b8d1c077..d4a1ded8a 100644 --- a/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap @@ -3,7 +3,7 @@ exports[`sendRegistrationMail with invite code English renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -142,7 +142,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendRegistrationMail with invite code German renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -282,7 +282,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendRegistrationMail without invite code English renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -421,7 +421,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendRegistrationMail without invite code German renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " diff --git a/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap index 3d8c6ac27..da62c9a34 100644 --- a/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap @@ -3,7 +3,7 @@ exports[`sendResetPasswordMail English renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -132,7 +132,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendResetPasswordMail German renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " diff --git a/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap b/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap index 72acc52cd..b2052d808 100644 --- a/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap @@ -3,7 +3,7 @@ exports[`sendWrongEmail English renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " @@ -130,7 +130,7 @@ ocelot.social Community [https://ocelot.social]", exports[`sendWrongEmail German renders correctly 1`] = ` { "attachments": [], - "from": "ocelot.social", + "from": "ocelot.social ", "html": " diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts index 8e5d8e83c..580cc2f58 100644 --- a/backend/src/emails/sendEmail.ts +++ b/backend/src/emails/sendEmail.ts @@ -28,11 +28,13 @@ const defaultParams = { renderSettingsUrl: true, } +const from = `${CONFIG.APPLICATION_NAME} <${CONFIG.EMAIL_DEFAULT_SENDER}>` + const transport = createTransport(nodemailerTransportOptions) const email = new Email({ message: { - from: `${CONFIG.APPLICATION_NAME}`, + from, }, transport, i18n: { From 20d14f3a2ff0163e48bfdadce4dc5e030184bef3 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 7 May 2025 20:44:44 +0200 Subject: [PATCH 209/266] chore(release): v3.5.3 (#8503) * Release v3.5.3 --- CHANGELOG.md | 35 ++++++++++++++++--- backend/package.json | 2 +- .../helm/charts/ocelot-neo4j/Chart.yaml | 2 +- .../helm/charts/ocelot-social/Chart.yaml | 2 +- frontend/package-lock.json | 4 +-- frontend/package.json | 2 +- package.json | 2 +- webapp/maintenance/source/package.json | 2 +- webapp/package.json | 2 +- 9 files changed, 39 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c747d24..332b24ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,17 @@ 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) @@ -1429,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/package.json b/backend/package.json index aa706eb78..38bf966ac 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "3.5.2", + "version": "3.5.3", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/deployment/helm/charts/ocelot-neo4j/Chart.yaml b/deployment/helm/charts/ocelot-neo4j/Chart.yaml index da08678d3..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.5.2" +appVersion: "3.5.3" diff --git a/deployment/helm/charts/ocelot-social/Chart.yaml b/deployment/helm/charts/ocelot-social/Chart.yaml index 7d2b93821..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.5.2" +appVersion: "3.5.3" diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e4965713c..250de7cce 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social-frontend", - "version": "3.5.2", + "version": "3.5.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.5.2", + "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 a8487569b..62532f76f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-frontend", - "version": "3.5.2", + "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.json b/package.json index b93be2e39..4581d134a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "3.5.2", + "version": "3.5.3", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", diff --git a/webapp/maintenance/source/package.json b/webapp/maintenance/source/package.json index 969a66db7..da4027f84 100644 --- a/webapp/maintenance/source/package.json +++ b/webapp/maintenance/source/package.json @@ -1,6 +1,6 @@ { "name": "@ocelot-social/maintenance", - "version": "3.5.2", + "version": "3.5.3", "description": "Maintenance page for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", diff --git a/webapp/package.json b/webapp/package.json index 05a4e0169..de3c0cc2d 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-webapp", - "version": "3.5.2", + "version": "3.5.3", "description": "ocelot.social Frontend", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", From e3864b1f9d8c922b06e1b5d52b628bfa001a4e4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Thu, 8 May 2025 18:48:26 +0200 Subject: [PATCH 210/266] feat(webapp): change german to `du` and `dich` (#8507) * Change 'Du' -> 'du' * Change 'Dich' -> 'dich' * Change backend e-mails 'Dich' -> 'dich' * Change backend e-mails 'Du' -> 'du' * Fix e-mail snapshots * Revert "Auxiliary commit to revert individual files from eea84f60ba9c17d48a735632709a66452f0494e9" This reverts commit d80994c3b8bff311422cb44aab1275c31286595a. * Change backend e-mails 'Du' -> 'du', 'Dich' -> 'dich' * Change webapp 'Dein' -> 'dein' * Change backend 'Dein' -> 'dein' --- .../sendEmailVerification.spec.ts.snap | 14 +-- .../sendNotificationMail.spec.ts.snap | 4 +- .../sendRegistrationMail.spec.ts.snap | 48 +++++----- .../sendResetPasswordMail.spec.ts.snap | 8 +- backend/src/emails/locales/de.json | 24 ++--- webapp/locales/de.json | 96 +++++++++---------- 6 files changed, 97 insertions(+), 97 deletions(-) diff --git a/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap b/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap index 87815f5e6..7f718d936 100644 --- a/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap @@ -221,9 +221,9 @@ footer {

    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

    +

    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

    @@ -239,16 +239,16 @@ footer { "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: +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 +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 +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]! diff --git a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap index 1c4f0dc8e..7fcf4d833 100644 --- a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -1741,7 +1741,7 @@ footer {

    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: +

    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!

    @@ -1758,7 +1758,7 @@ footer { "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 +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] diff --git a/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap index d4a1ded8a..16f7584e5 100644 --- a/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap @@ -230,12 +230,12 @@ footer {

    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. +

    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. ;)

    +

    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

    @@ -252,21 +252,21 @@ footer { 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: +… Bitte bestätige deine E-Mail Adresse: -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 +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 +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 +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 +PS: Wenn du keinen Account bei uns möchtest, kannst du diese E-Mail einfach ignorieren. ;) Bis bald bei ocelot.social [https://ocelot.social]! @@ -509,12 +509,12 @@ footer {

    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. +

    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. ;)

    +

    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

    @@ -531,21 +531,21 @@ footer { 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: +… Bitte bestätige deine E-Mail Adresse: -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 +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 +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 +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 +PS: Wenn du keinen Account bei uns möchtest, kannst du diese E-Mail einfach ignorieren. ;) Bis bald bei ocelot.social [https://ocelot.social]! diff --git a/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap index da62c9a34..da8c041cb 100644 --- a/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap @@ -220,9 +220,9 @@ footer {

    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 +

    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

    +

    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

    @@ -240,14 +240,14 @@ footer { 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 +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 +dein Browserfenster kopieren: 123456 Bis bald bei ocelot.social [https://ocelot.social]! diff --git a/backend/src/emails/locales/de.json b/backend/src/emails/locales/de.json index 9e0ce843a..677c3b7f1 100644 --- a/backend/src/emails/locales/de.json +++ b/backend/src/emails/locales/de.json @@ -16,20 +16,20 @@ "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 ", + "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. ;)" + "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. " + "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", + "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", @@ -47,12 +47,12 @@ "welcome": "Willkommen bei" }, "resetPassword": { - "codeHint": "Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in Dein Browserfenster kopieren: ", + "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: ", + "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?" @@ -63,7 +63,7 @@ "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:", + "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:", diff --git a/webapp/locales/de.json b/webapp/locales/de.json index e2b641b08..ea7b8aeb2 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -65,7 +65,7 @@ "tagCountUnique": "Nutzer" }, "invites": { - "description": "Einladungen sind eine wunderbare Möglichkeit, Deine Freunde in Deinem Netzwerk zu haben …", + "description": "Einladungen sind eine wunderbare Möglichkeit, deine Freunde in deinem Netzwerk zu haben …", "name": "Nutzer einladen", "title": "Leute einladen" }, @@ -222,7 +222,7 @@ "buttonTitle": "Weiter", "form": { "click-next": "Click auf Weiter.", - "description": "Öffne Dein E-Mail Postfach und gib den Code ein, den wir geschickt haben.", + "description": "Öffne dein E-Mail Postfach und gib den Code ein, den wir geschickt haben.", "next": "Weiter", "nonce": "E-Mail-Code: 32143", "validations": { @@ -252,12 +252,12 @@ "signup": { "form": { "data-privacy": "Ich habe die Datenschutzerklärung gelesen und verstanden.", - "description": "Um loszulegen, kannst Du Dich hier kostenfrei registrieren:", + "description": "Um loszulegen, kannst du dich hier kostenfrei registrieren:", "errors": { "email-exists": "Es gibt schon ein Nutzerkonto mit dieser E-Mail-Adresse!" }, "submit": "Konto erstellen", - "success": "Eine E-Mail mit einem Link zum Abschließen Deiner Registrierung wurde an {email} geschickt", + "success": "Eine E-Mail mit einem Link zum Abschließen deiner Registrierung wurde an {email} geschickt", "terms-and-condition": "Ich stimme den Nutzungsbedingungen zu." }, "title": "Mach mit bei {APPLICATION_NAME}!", @@ -335,7 +335,7 @@ }, "filterMyGroups": "Meine Gruppen", "inappropriatePicture": "Dieses Bild kann für einige Menschen unangemessen sein.", - "languageSelectLabel": "Sprache Deines Beitrags", + "languageSelectLabel": "Sprache deines Beitrags", "languageSelectText": "Sprache wählen", "newEvent": "Erstelle einen neue Veranstaltung", "newPost": "Erstelle einen neuen Beitrag", @@ -355,13 +355,13 @@ "delete": { "cancel": "Abbrechen", "comment": { - "message": "Bist Du sicher, dass Du den Kommentar „{name}“ löschen möchtest?", + "message": "Bist du sicher, dass du den Kommentar „{name}“ löschen möchtest?", "success": "Kommentar erfolgreich gelöscht!", "title": "Lösche Kommentar", "type": "Kommentar" }, "contribution": { - "message": "Bist Du sicher, dass Du den Beitrag „{name}“ löschen möchtest?", + "message": "Bist du sicher, dass du den Beitrag „{name}“ löschen möchtest?", "success": "Beitrag erfolgreich gelöscht!", "title": "Lösche Beitrag", "type": "Beitrag" @@ -371,19 +371,19 @@ "disable": { "cancel": "Abbrechen", "comment": { - "message": "Bist Du sicher, dass Du den Kommentar „{name}“ deaktivieren möchtest?", + "message": "Bist du sicher, dass du den Kommentar „{name}“ deaktivieren möchtest?", "title": "Kommentar sperren", "type": "Kommentar" }, "contribution": { - "message": "Bist Du sicher, dass Du den Beitrag von „{name}“ deaktivieren möchtest?", + "message": "Bist du sicher, dass du den Beitrag von „{name}“ deaktivieren möchtest?", "title": "Beitrag sperren", "type": "Beitrag" }, "submit": "Deaktivieren", "success": "Erfolgreich deaktiviert", "user": { - "message": "Bist Du sicher, dass Du den Nutzer „{name}“ sperren möchtest?", + "message": "Bist du sicher, dass du den Nutzer „{name}“ sperren möchtest?", "title": "Nutzer sperren", "type": "Nutzer" } @@ -395,8 +395,8 @@ "editor": { "embed": { "always_allow": "Einzubettende Inhalte von Drittanbietern immer erlauben (diese Einstellung ist jederzeit änderbar)", - "data_privacy_info": "Deine Daten wurden noch nicht an Drittanbieter weitergegeben. Wenn Du diesen Inhalt jetzt abspielst, registriert der folgende Anbieter wahrscheinlich Deine Nutzerdaten:", - "data_privacy_warning": "Achte auf Deine Daten!", + "data_privacy_info": "Deine Daten wurden noch nicht an Drittanbieter weitergegeben. Wenn du diesen Inhalt jetzt abspielst, registriert der folgende Anbieter wahrscheinlich deine Nutzerdaten:", + "data_privacy_warning": "Achte auf deine Daten!", "play_now": "Jetzt ansehen" }, "hashtag": { @@ -656,7 +656,7 @@ }, "maintenance": { "explanation": "Derzeit führen wir einige geplante Wartungsarbeiten durch, bitte versuche es später erneut.", - "questions": "Bei Fragen oder Problemen erreichst Du uns per E-Mail an", + "questions": "Bei Fragen oder Problemen erreichst du uns per E-Mail an", "title": "{APPLICATION_NAME} befindet sich in der Wartung" }, "map": { @@ -697,32 +697,32 @@ "cancel": "Abbruch", "Comment": { "disable": { - "message": "Möchtest Du den Kommentar „{name}“ wirklich gesperrt lassen?", + "message": "Möchtest du den Kommentar „{name}“ wirklich gesperrt lassen?", "title": "Sperre den Kommentar abschließend" }, "enable": { - "message": "Möchtest Du den Kommentar „{name}“ wirklich entsperrt lassen?", + "message": "Möchtest du den Kommentar „{name}“ wirklich entsperrt lassen?", "title": "Entsperre den Kommentar abschließend" } }, "Post": { "disable": { - "message": "Möchtest Du den Beitrag „{name}“ wirklich gesperrt lassen?", + "message": "Möchtest du den Beitrag „{name}“ wirklich gesperrt lassen?", "title": "Sperre den Beitrag abschließend" }, "enable": { - "message": "Möchtest Du den Beitrag „{name}“ wirklich entsperrt lassen?", + "message": "Möchtest du den Beitrag „{name}“ wirklich entsperrt lassen?", "title": "Entsperre den Beitrag abschließend" } }, "submit": "Bestätige Entscheidung", "User": { "disable": { - "message": "Möchtest Du den Nutzer „{name}“ wirklich gesperrt lassen?", + "message": "Möchtest du den Nutzer „{name}“ wirklich gesperrt lassen?", "title": "Sperre den Nutzer abschließend" }, "enable": { - "message": "Möchtest Du den Nutzer „{name}“ wirklich entsperrt lassen?", + "message": "Möchtest du den Nutzer „{name}“ wirklich entsperrt lassen?", "title": "Entsperre den Nutzer abschließend" } } @@ -757,7 +757,7 @@ "notifications": { "comment": "Kommentar", "content": "Inhalt oder Beschreibung", - "empty": "Bedaure, Du hast momentan keinerlei Benachrichtigungen.", + "empty": "Bedaure, du hast momentan keinerlei Benachrichtigungen.", "filterLabel": { "all": "Alle", "read": "Gelesen", @@ -768,14 +768,14 @@ "pageLink": "Alle Benachrichtigungen", "post": "Beitrag oder Gruppe", "reason": { - "changed_group_member_role": "Hat Deine Rolle in der Gruppe geändert …", + "changed_group_member_role": "Hat deine Rolle in der Gruppe geändert …", "commented_on_post": "Hat einen Beitrag den du beobachtest kommentiert …", "followed_user_posted": "Hat einen neuen Betrag geschrieben …", - "mentioned_in_comment": "Hat Dich in einem Kommentar erwähnt …", - "mentioned_in_post": "Hat Dich in einem Beitrag erwähnt …", + "mentioned_in_comment": "Hat dich in einem Kommentar erwähnt …", + "mentioned_in_post": "Hat dich in einem Beitrag erwähnt …", "post_in_group": "Hat einen Beitrag in der Gruppe geschrieben …", - "removed_user_from_group": "Hat Dich aus der Gruppe entfernt …", - "user_joined_group": "Ist Deiner Gruppe beigetreten …", + "removed_user_from_group": "Hat dich aus der Gruppe entfernt …", + "user_joined_group": "Ist deiner Gruppe beigetreten …", "user_left_group": "Hat deine Gruppe verlassen …" }, "title": "Benachrichtigungen", @@ -879,22 +879,22 @@ "release": { "cancel": "Abbrechen", "comment": { - "error": "Den Kommentar hast Du schon gemeldet!", - "message": "Bist Du sicher, dass Du den Kommentar „{name}“ freigeben möchtest?", + "error": "Den Kommentar hast du schon gemeldet!", + "message": "Bist du sicher, dass du den Kommentar „{name}“ freigeben möchtest?", "title": "Kommentar freigeben", "type": "Kommentar" }, "contribution": { - "error": "Den Beitrag hast Du schon gemeldet!", - "message": "Bist Du sicher, dass Du den Beitrag „{name}“ freigeben möchtest?", + "error": "Den Beitrag hast du schon gemeldet!", + "message": "Bist du sicher, dass du den Beitrag „{name}“ freigeben möchtest?", "title": "Beitrag freigeben", "type": "Beitrag" }, "submit": "freigeben", "success": "Erfolgreich freigegeben!", "user": { - "error": "Den Nutzer hast Du schon gemeldet!", - "message": "Bist Du sicher, dass Du den Nutzer „{name}“ freigeben möchtest?", + "error": "Den Nutzer hast du schon gemeldet!", + "message": "Bist du sicher, dass du den Nutzer „{name}“ freigeben möchtest?", "title": "Nutzer freigeben", "type": "Nutzer" } @@ -903,13 +903,13 @@ "cancel": "Abbrechen", "comment": { "error": "Du hast den Kommentar bereits gemeldet!", - "message": "Bist Du sicher, dass Du den Kommentar von „{name}“ melden möchtest?", + "message": "Bist du sicher, dass du den Kommentar von „{name}“ melden möchtest?", "title": "Kommentar melden", "type": "Kommentar" }, "contribution": { "error": "Du hast den Beitrag bereits gemeldet!", - "message": "Bist Du sicher, dass Du den Beitrag „{name}“ melden möchtest?", + "message": "Bist du sicher, dass du den Beitrag „{name}“ melden möchtest?", "title": "Beitrag melden", "type": "Beitrag" }, @@ -930,7 +930,7 @@ "placeholder": "Thema …" }, "description": { - "label": "Bitte erkläre: Warum möchtest Du dies melden?", + "label": "Bitte erkläre: Warum möchtest du dies melden?", "placeholder": "Zusätzliche Information …" } }, @@ -938,7 +938,7 @@ "success": "Vielen Dank für diese Meldung!", "user": { "error": "Du hast den Nutzer bereits gemeldet!", - "message": "Bist Du sicher, dass Du den Nutzer „{name}“ melden möchtest?", + "message": "Bist du sicher, dass du den Nutzer „{name}“ melden möchtest?", "title": "Nutzer melden", "type": "Nutzer" } @@ -977,15 +977,15 @@ "slug": "Alias", "unblock": "Entsperren" }, - "empty": "Bislang hast Du niemanden blockiert.", + "empty": "Bislang hast du niemanden blockiert.", "explanation": { - "closing": "Das sollte fürs Erste genügen, damit blockierte Nutzer Dich nicht mehr länger belästigen können.", + "closing": "Das sollte fürs Erste genügen, damit blockierte Nutzer dich nicht mehr länger belästigen können.", "commenting-disabled": "Du kannst den Beitrag derzeit nicht kommentieren.", "commenting-explanation": "Dafür kann es mehrere Gründe geben, bitte schau in unsere ", - "intro": "Wenn ein anderer Nutzer durch Dich blockiert wurde, dann passiert Folgendes:", - "notifications": "Von Dir blockierte Nutzer werden keine Benachrichtigungen mehr erhalten, falls sie in Deinen Beiträgen erwähnt werden.", - "their-perspective": "Umgekehrt das gleiche: Die blockierte Person bekommt auch in ihren Benachrichtigungen Deine Beiträge nicht mehr zu sehen.", - "your-perspective": "In Deinen Benachrichtigungen tauchen keine Beiträge der blockierten Person mehr auf." + "intro": "Wenn ein anderer Nutzer durch dich blockiert wurde, dann passiert Folgendes:", + "notifications": "Von Dir blockierte Nutzer werden keine Benachrichtigungen mehr erhalten, falls sie in deinen Beiträgen erwähnt werden.", + "their-perspective": "Umgekehrt das gleiche: Die blockierte Person bekommt auch in ihren Benachrichtigungen deine Beiträge nicht mehr zu sehen.", + "your-perspective": "In deinen Benachrichtigungen tauchen keine Beiträge der blockierten Person mehr auf." }, "how-to": "Du kannst andere Nutzer auf deren Profilseite über das Inhaltsmenü blockieren.", "name": "Blockierte Nutzer", @@ -993,7 +993,7 @@ "unblocked": "{name} ist wieder entsperrt" }, "data": { - "labelBio": "Über Dich", + "labelBio": "Über dich", "labelCity": "Deine Stadt oder Region", "labelCityHint": "(zeigt ungefähre Position auf der Landkarte)", "labelName": "Dein Name", @@ -1026,17 +1026,17 @@ "labelNewEmail": "Neue E-Mail-Adresse", "labelNonce": "Bestätigungscode eingeben", "name": "Deine E-Mail", - "submitted": "Eine E-Mail zur Bestätigung Deiner Adresse wurde an {email} gesendet.", + "submitted": "Eine E-Mail zur Bestätigung deiner Adresse wurde an {email} gesendet.", "success": "Eine neue E-Mail-Adresse wurde registriert.", "validation": { - "same-email": "Das ist Deine aktuelle E-Mail-Adresse" + "same-email": "Das ist deine aktuelle E-Mail-Adresse" }, "verification-error": { "explanation": "Das kann verschiedene Ursachen haben:", "message": "Deine E-Mail-Adresse konnte nicht verifiziert werden.", "reason": { "invalid-nonce": "Ist der Bestätigungscode falsch?", - "no-email-request": "Bist Du Dir sicher, dass Du eine Änderung Deiner E-Mail-Adresse angefragt hattest?" + "no-email-request": "Bist du Dir sicher, dass du eine Änderung deiner E-Mail-Adresse angefragt hattest?" }, "support": "Wenn das Problem weiterhin besteht, kontaktiere uns gerne per E-Mail an" } @@ -1114,12 +1114,12 @@ "change-password": { "button": "Passwort ändern", "label-new-password": "Dein neues Passwort", - "label-new-password-confirm": "Bestätige Dein neues Passwort", + "label-new-password-confirm": "Bestätige dein neues Passwort", "label-old-password": "Dein altes Passwort", - "message-new-password-confirm-required": "Bestätige Dein neues Passwort", + "message-new-password-confirm-required": "Bestätige dein neues Passwort", "message-new-password-missmatch": "Gib dasselbe Passwort nochmals ein", "message-new-password-required": "Gib ein neues Passwort ein", - "message-old-password-required": "Gib Dein altes Passwort ein", + "message-old-password-required": "Gib dein altes Passwort ein", "passwordSecurity": "Passwortsicherheit", "passwordStrength0": "Sehr unsicheres Passwort", "passwordStrength1": "Unsicheres Passwort", From 3f4d648562db8682cb8f8c6f63997a3bdffb4cd0 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 8 May 2025 21:18:40 +0200 Subject: [PATCH 211/266] feat(backend): group invite codes (#8499) * invite codes refactor typo * lint fixes * remove duplicate initeCodes on User * fix typo * clean permissionMiddleware * dummy permissions * separate validateInviteCode call * permissions group & user * test validateInviteCode + adjustments * more validateInviteCode fixes * missing test * generatePersonalInviteCode * generateGroupInviteCode * old tests * lint fixes * more lint fixes * fix validateInviteCode * fix redeemInviteCode, fix signup * fix all tests * fix lint * uniform types in config * test & fix invalidateInviteCode * cleanup test * fix & test redeemInviteCode * permissions * fix Group->inviteCodes * more cleanup * improve tests * fix code generation * cleanup * order inviteCodes result on User and Group * lint * test max invite codes + fix * better description of collision * tests: properly define group ids * reused old group query * reuse old Groupmembers query * remove duplicate skip * update comment * fix uniqueInviteCode * fix test --- backend/src/config/index.ts | 4 + backend/src/context/database.ts | 8 +- backend/src/db/factories.ts | 16 +- backend/src/db/models/InviteCode.ts | 6 + backend/src/graphql/queries/Group.ts | 36 + backend/src/graphql/queries/GroupMembers.ts | 12 + backend/src/graphql/queries/currentUser.ts | 15 + .../queries/generateGroupInviteCode.ts | 36 + .../queries/generatePersonalInviteCode.ts | 36 + .../src/graphql/queries/groupMembersQuery.ts | 14 - backend/src/graphql/queries/groupQuery.ts | 38 - .../graphql/queries/invalidateInviteCode.ts | 36 + .../src/graphql/queries/redeemInviteCode.ts | 7 + .../src/graphql/queries/validateInviteCode.ts | 49 + backend/src/graphql/resolvers/badges.spec.ts | 61 +- backend/src/graphql/resolvers/groups.spec.ts | 60 +- backend/src/graphql/resolvers/groups.ts | 30 + .../resolvers/helpers/generateInviteCode.ts | 13 - .../src/graphql/resolvers/inviteCodes.spec.ts | 1311 +++++++++++++++-- backend/src/graphql/resolvers/inviteCodes.ts | 376 +++-- backend/src/graphql/resolvers/locations.ts | 3 + backend/src/graphql/resolvers/posts.spec.ts | 86 +- .../graphql/resolvers/registration.spec.ts | 75 +- backend/src/graphql/resolvers/registration.ts | 92 +- .../resolvers/transactions/inviteCodes.ts | 26 - backend/src/graphql/resolvers/users.ts | 18 + backend/src/graphql/types/type/Group.gql | 3 + backend/src/graphql/types/type/InviteCode.gql | 19 +- backend/src/graphql/types/type/User.gql | 9 +- backend/src/middleware/index.ts | 1 + .../middleware/permissionsMiddleware.spec.ts | 65 +- .../src/middleware/permissionsMiddleware.ts | 62 +- .../src/middleware/slugifyMiddleware.spec.ts | 41 +- backend/src/server.ts | 1 + 34 files changed, 2034 insertions(+), 631 deletions(-) create mode 100644 backend/src/graphql/queries/Group.ts create mode 100644 backend/src/graphql/queries/GroupMembers.ts create mode 100644 backend/src/graphql/queries/currentUser.ts create mode 100644 backend/src/graphql/queries/generateGroupInviteCode.ts create mode 100644 backend/src/graphql/queries/generatePersonalInviteCode.ts delete mode 100644 backend/src/graphql/queries/groupMembersQuery.ts delete mode 100644 backend/src/graphql/queries/groupQuery.ts create mode 100644 backend/src/graphql/queries/invalidateInviteCode.ts create mode 100644 backend/src/graphql/queries/redeemInviteCode.ts create mode 100644 backend/src/graphql/queries/validateInviteCode.ts delete mode 100644 backend/src/graphql/resolvers/helpers/generateInviteCode.ts delete mode 100644 backend/src/graphql/resolvers/transactions/inviteCodes.ts diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 658c7e97c..a079c2ae5 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -117,6 +117,10 @@ const options = { ORGANIZATION_URL: emails.ORGANIZATION_LINK, PUBLIC_REGISTRATION: env.PUBLIC_REGISTRATION === 'true' || false, INVITE_REGISTRATION: env.INVITE_REGISTRATION !== 'false', // default = true + INVITE_CODES_PERSONAL_PER_USER: + (env.INVITE_CODES_PERSONAL_PER_USER && parseInt(env.INVITE_CODES_PERSONAL_PER_USER)) || 7, + INVITE_CODES_GROUP_PER_USER: + (env.INVITE_CODES_GROUP_PER_USER && parseInt(env.INVITE_CODES_GROUP_PER_USER)) || 7, CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false, } diff --git a/backend/src/context/database.ts b/backend/src/context/database.ts index f6ccdc9ca..c1dc244d9 100644 --- a/backend/src/context/database.ts +++ b/backend/src/context/database.ts @@ -4,7 +4,7 @@ import type { Driver } from 'neo4j-driver' export const query = (driver: Driver) => - async ({ query, variables = {} }: { driver; query: string; variables: object }) => { + async ({ query, variables = {} }: { query: string; variables: object }) => { const session = driver.session() const result = session.readTransaction(async (transaction) => { @@ -19,9 +19,9 @@ export const query = } } -export const mutate = +export const write = (driver: Driver) => - async ({ query, variables = {} }: { driver; query: string; variables: object }) => { + async ({ query, variables = {} }: { query: string; variables: object }) => { const session = driver.session() const result = session.writeTransaction(async (transaction) => { @@ -44,6 +44,6 @@ export default () => { driver, neode, query: query(driver), - mutate: mutate(driver), + write: write(driver), } } diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index 95db5a859..a5237dada 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 '@graphql/resolvers/helpers/generateInviteCode' +import { generateInviteCode } from '@graphql/resolvers/inviteCodes' import { getDriver, getNeode } from './neo4j' @@ -268,17 +268,27 @@ const inviteCodeDefaults = { Factory.define('inviteCode') .attrs(inviteCodeDefaults) + .option('groupId', null) + .option('group', ['groupId'], (groupId) => { + if (groupId) { + return neode.find('Group', groupId) + } + }) .option('generatedById', null) .option('generatedBy', ['generatedById'], (generatedById) => { if (generatedById) return neode.find('User', generatedById) return Factory.build('user') }) .after(async (buildObject, options) => { - const [inviteCode, generatedBy] = await Promise.all([ + const [inviteCode, generatedBy, group] = await Promise.all([ neode.create('InviteCode', buildObject), options.generatedBy, + options.group, ]) - await Promise.all([inviteCode.relateTo(generatedBy, 'generated')]) + await inviteCode.relateTo(generatedBy, 'generated') + if (group) { + await inviteCode.relateTo(group, 'invitesTo') + } return inviteCode }) diff --git a/backend/src/db/models/InviteCode.ts b/backend/src/db/models/InviteCode.ts index 7204f1b38..0617529ac 100644 --- a/backend/src/db/models/InviteCode.ts +++ b/backend/src/db/models/InviteCode.ts @@ -14,4 +14,10 @@ export default { target: 'User', direction: 'in', }, + invitesTo: { + type: 'relationship', + relationship: 'INVITES_TO', + target: 'Group', + direction: 'out', + }, } diff --git a/backend/src/graphql/queries/Group.ts b/backend/src/graphql/queries/Group.ts new file mode 100644 index 000000000..ee01a9177 --- /dev/null +++ b/backend/src/graphql/queries/Group.ts @@ -0,0 +1,36 @@ +import gql from 'graphql-tag' + +export const Group = gql` + query Group($isMember: Boolean, $id: ID, $slug: String) { + Group(isMember: $isMember, id: $id, slug: $slug) { + id + name + slug + createdAt + updatedAt + disabled + deleted + about + description + descriptionExcerpt + groupType + actionRadius + categories { + id + slug + name + icon + } + avatar { + url + } + locationName + location { + name + nameDE + nameEN + } + myRole + } + } +` diff --git a/backend/src/graphql/queries/GroupMembers.ts b/backend/src/graphql/queries/GroupMembers.ts new file mode 100644 index 000000000..5950952cb --- /dev/null +++ b/backend/src/graphql/queries/GroupMembers.ts @@ -0,0 +1,12 @@ +import gql from 'graphql-tag' + +export const GroupMembers = gql` + query GroupMembers($id: ID!) { + GroupMembers(id: $id) { + id + name + slug + myRoleInGroup + } + } +` diff --git a/backend/src/graphql/queries/currentUser.ts b/backend/src/graphql/queries/currentUser.ts new file mode 100644 index 000000000..753fe5288 --- /dev/null +++ b/backend/src/graphql/queries/currentUser.ts @@ -0,0 +1,15 @@ +import gql from 'graphql-tag' + +export const currentUser = gql` + query currentUser { + currentUser { + following { + name + } + inviteCodes { + code + redeemedByCount + } + } + } +` diff --git a/backend/src/graphql/queries/generateGroupInviteCode.ts b/backend/src/graphql/queries/generateGroupInviteCode.ts new file mode 100644 index 000000000..5633b41b7 --- /dev/null +++ b/backend/src/graphql/queries/generateGroupInviteCode.ts @@ -0,0 +1,36 @@ +import gql from 'graphql-tag' + +export const generateGroupInviteCode = gql` + mutation generateGroupInviteCode($groupId: ID!, $expiresAt: String, $comment: String) { + generateGroupInviteCode(groupId: $groupId, expiresAt: $expiresAt, comment: $comment) { + code + createdAt + generatedBy { + id + name + avatar { + url + } + } + redeemedBy { + id + name + avatar { + url + } + } + expiresAt + comment + invitedTo { + id + groupType + name + about + avatar { + url + } + } + isValid + } + } +` diff --git a/backend/src/graphql/queries/generatePersonalInviteCode.ts b/backend/src/graphql/queries/generatePersonalInviteCode.ts new file mode 100644 index 000000000..429b25549 --- /dev/null +++ b/backend/src/graphql/queries/generatePersonalInviteCode.ts @@ -0,0 +1,36 @@ +import gql from 'graphql-tag' + +export const generatePersonalInviteCode = gql` + mutation generatePersonalInviteCode($expiresAt: String, $comment: String) { + generatePersonalInviteCode(expiresAt: $expiresAt, comment: $comment) { + code + createdAt + generatedBy { + id + name + avatar { + url + } + } + redeemedBy { + id + name + avatar { + url + } + } + expiresAt + comment + invitedTo { + id + groupType + name + about + avatar { + url + } + } + isValid + } + } +` diff --git a/backend/src/graphql/queries/groupMembersQuery.ts b/backend/src/graphql/queries/groupMembersQuery.ts deleted file mode 100644 index b1b8cb313..000000000 --- a/backend/src/graphql/queries/groupMembersQuery.ts +++ /dev/null @@ -1,14 +0,0 @@ -import gql from 'graphql-tag' - -export const groupMembersQuery = () => { - return gql` - query ($id: ID!) { - GroupMembers(id: $id) { - id - name - slug - myRoleInGroup - } - } - ` -} diff --git a/backend/src/graphql/queries/groupQuery.ts b/backend/src/graphql/queries/groupQuery.ts deleted file mode 100644 index 463e9e13e..000000000 --- a/backend/src/graphql/queries/groupQuery.ts +++ /dev/null @@ -1,38 +0,0 @@ -import gql from 'graphql-tag' - -export const groupQuery = () => { - return gql` - query ($isMember: Boolean, $id: ID, $slug: String) { - Group(isMember: $isMember, id: $id, slug: $slug) { - id - name - slug - createdAt - updatedAt - disabled - deleted - about - description - descriptionExcerpt - groupType - actionRadius - categories { - id - slug - name - icon - } - avatar { - url - } - locationName - location { - name - nameDE - nameEN - } - myRole - } - } - ` -} diff --git a/backend/src/graphql/queries/invalidateInviteCode.ts b/backend/src/graphql/queries/invalidateInviteCode.ts new file mode 100644 index 000000000..1b8581be3 --- /dev/null +++ b/backend/src/graphql/queries/invalidateInviteCode.ts @@ -0,0 +1,36 @@ +import gql from 'graphql-tag' + +export const invalidateInviteCode = gql` + mutation invalidateInviteCode($code: String!) { + invalidateInviteCode(code: $code) { + code + createdAt + generatedBy { + id + name + avatar { + url + } + } + redeemedBy { + id + name + avatar { + url + } + } + expiresAt + comment + invitedTo { + id + groupType + name + about + avatar { + url + } + } + isValid + } + } +` diff --git a/backend/src/graphql/queries/redeemInviteCode.ts b/backend/src/graphql/queries/redeemInviteCode.ts new file mode 100644 index 000000000..0852c564a --- /dev/null +++ b/backend/src/graphql/queries/redeemInviteCode.ts @@ -0,0 +1,7 @@ +import gql from 'graphql-tag' + +export const redeemInviteCode = gql` + mutation redeemInviteCode($code: String!) { + redeemInviteCode(code: $code) + } +` diff --git a/backend/src/graphql/queries/validateInviteCode.ts b/backend/src/graphql/queries/validateInviteCode.ts new file mode 100644 index 000000000..bcae09254 --- /dev/null +++ b/backend/src/graphql/queries/validateInviteCode.ts @@ -0,0 +1,49 @@ +import gql from 'graphql-tag' + +export const unauthenticatedValidateInviteCode = gql` + query validateInviteCode($code: String!) { + validateInviteCode(code: $code) { + code + invitedTo { + groupType + name + about + avatar { + url + } + } + generatedBy { + name + avatar { + url + } + } + isValid + } + } +` + +export const authenticatedValidateInviteCode = gql` + query validateInviteCode($code: String!) { + validateInviteCode(code: $code) { + code + invitedTo { + id + groupType + name + about + avatar { + url + } + } + generatedBy { + id + name + avatar { + url + } + } + isValid + } + } +` diff --git a/backend/src/graphql/resolvers/badges.spec.ts b/backend/src/graphql/resolvers/badges.spec.ts index e6b5173a9..dd0cf4730 100644 --- a/backend/src/graphql/resolvers/badges.spec.ts +++ b/backend/src/graphql/resolvers/badges.spec.ts @@ -1,41 +1,43 @@ -/* 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 */ +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 instance = getNeode() +let regularUser, administrator, moderator, badge, verification -let authenticatedUser, regularUser, administrator, moderator, badge, verification, query, mutate +const database = databaseContext() + +let server: ApolloServer +let authenticatedUser +let query, mutate + +beforeAll(async () => { + await cleanDatabase() + + // 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 + + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate +}) + +afterAll(() => { + void server.stop() + void database.driver.close() + database.neode.close() +}) describe('Badges', () => { - beforeAll(async () => { - await cleanDatabase() - - const { server } = createServer({ - context: () => { - return { - driver, - neode: instance, - user: authenticatedUser, - } - }, - }) - query = createTestClient(server).query - mutate = createTestClient(server).mutate - }) - - afterAll(async () => { - await cleanDatabase() - await driver.close() - }) - beforeEach(async () => { regularUser = await Factory.build( 'user', @@ -83,7 +85,6 @@ describe('Badges', () => { }) }) - // 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() }) @@ -122,7 +123,7 @@ describe('Badges', () => { }) describe('authenticated as moderator', () => { - beforeEach(async () => { + beforeEach(() => { authenticatedUser = moderator.toJson() }) @@ -322,7 +323,7 @@ describe('Badges', () => { }) describe('authenticated as moderator', () => { - beforeEach(async () => { + beforeEach(() => { authenticatedUser = moderator.toJson() }) diff --git a/backend/src/graphql/resolvers/groups.spec.ts b/backend/src/graphql/resolvers/groups.spec.ts index 545865c20..333bc03c1 100644 --- a/backend/src/graphql/resolvers/groups.spec.ts +++ b/backend/src/graphql/resolvers/groups.spec.ts @@ -10,8 +10,8 @@ import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' -import { groupMembersQuery } from '@graphql/queries/groupMembersQuery' -import { groupQuery } from '@graphql/queries/groupQuery' +import { Group as groupQuery } from '@graphql/queries/Group' +import { GroupMembers as groupMembersQuery } from '@graphql/queries/GroupMembers' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' @@ -423,7 +423,7 @@ describe('in mode', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { - const { errors } = await query({ query: groupQuery(), variables: {} }) + const { errors } = await query({ query: groupQuery, variables: {} }) expect(errors![0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -541,7 +541,7 @@ describe('in mode', () => { describe('in general finds only listed groups – no hidden groups where user is none or pending member', () => { describe('without any filters', () => { it('finds all listed groups – including the set descriptionExcerpts and locations', async () => { - const result = await query({ query: groupQuery(), variables: {} }) + const result = await query({ query: groupQuery, variables: {} }) expect(result).toMatchObject({ data: { Group: expect.arrayContaining([ @@ -586,9 +586,7 @@ describe('in mode', () => { }) it('has set categories', async () => { - await expect( - query({ query: groupQuery(), variables: {} }), - ).resolves.toMatchObject({ + await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({ data: { Group: expect.arrayContaining([ expect.objectContaining({ @@ -622,7 +620,7 @@ describe('in mode', () => { describe('with given id', () => { describe("id = 'my-group'", () => { it('finds only the listed group with this id', async () => { - const result = await query({ query: groupQuery(), variables: { id: 'my-group' } }) + const result = await query({ query: groupQuery, variables: { id: 'my-group' } }) expect(result).toMatchObject({ data: { Group: [ @@ -642,7 +640,7 @@ describe('in mode', () => { describe("id = 'third-hidden-group'", () => { it("finds only the hidden group where I'm 'usual' member", async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { id: 'third-hidden-group' }, }) expect(result).toMatchObject({ @@ -664,7 +662,7 @@ describe('in mode', () => { describe("id = 'second-hidden-group'", () => { it("finds no hidden group where I'm 'pending' member", async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { id: 'second-hidden-group' }, }) expect(result.data?.Group.length).toBe(0) @@ -674,7 +672,7 @@ describe('in mode', () => { describe("id = 'hidden-group'", () => { it("finds no hidden group where I'm not(!) a member at all", async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { id: 'hidden-group' }, }) expect(result.data?.Group.length).toBe(0) @@ -686,7 +684,7 @@ describe('in mode', () => { describe("slug = 'the-best-group'", () => { it('finds only the listed group with this slug', async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { slug: 'the-best-group' }, }) expect(result).toMatchObject({ @@ -708,7 +706,7 @@ describe('in mode', () => { describe("slug = 'third-investigative-journalism-group'", () => { it("finds only the hidden group where I'm 'usual' member", async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { slug: 'third-investigative-journalism-group' }, }) expect(result).toMatchObject({ @@ -730,7 +728,7 @@ describe('in mode', () => { describe("slug = 'second-investigative-journalism-group'", () => { it("finds no hidden group where I'm 'pending' member", async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { slug: 'second-investigative-journalism-group' }, }) expect(result.data?.Group.length).toBe(0) @@ -740,7 +738,7 @@ describe('in mode', () => { describe("slug = 'investigative-journalism-group'", () => { it("finds no hidden group where I'm not(!) a member at all", async () => { const result = await query({ - query: groupQuery(), + query: groupQuery, variables: { slug: 'investigative-journalism-group' }, }) expect(result.data?.Group.length).toBe(0) @@ -750,7 +748,7 @@ describe('in mode', () => { describe('isMember = true', () => { it('finds only listed groups where user is member', async () => { - const result = await query({ query: groupQuery(), variables: { isMember: true } }) + const result = await query({ query: groupQuery, variables: { isMember: true } }) expect(result).toMatchObject({ data: { Group: expect.arrayContaining([ @@ -774,7 +772,7 @@ describe('in mode', () => { describe('isMember = false', () => { it('finds only listed groups where user is not(!) member', async () => { - const result = await query({ query: groupQuery(), variables: { isMember: false } }) + const result = await query({ query: groupQuery, variables: { isMember: false } }) expect(result).toMatchObject({ data: { Group: expect.arrayContaining([ @@ -1039,7 +1037,7 @@ describe('in mode', () => { variables = { id: 'not-existing-group', } - const { errors } = await query({ query: groupMembersQuery(), variables }) + const { errors } = await query({ query: groupMembersQuery, variables }) expect(errors![0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -1212,7 +1210,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1245,7 +1243,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1278,7 +1276,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1321,7 +1319,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1354,7 +1352,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1386,7 +1384,7 @@ describe('in mode', () => { }) it('throws authorization error', async () => { - const { errors } = await query({ query: groupMembersQuery(), variables }) + const { errors } = await query({ query: groupMembersQuery, variables }) expect(errors![0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -1397,7 +1395,7 @@ describe('in mode', () => { }) it('throws authorization error', async () => { - const { errors } = await query({ query: groupMembersQuery(), variables }) + const { errors } = await query({ query: groupMembersQuery, variables }) expect(errors![0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -1419,7 +1417,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1456,7 +1454,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1493,7 +1491,7 @@ describe('in mode', () => { it('finds all members', async () => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables, }) expect(result).toMatchObject({ @@ -1529,7 +1527,7 @@ describe('in mode', () => { }) it('throws authorization error', async () => { - const { errors } = await query({ query: groupMembersQuery(), variables }) + const { errors } = await query({ query: groupMembersQuery, variables }) expect(errors![0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -1540,7 +1538,7 @@ describe('in mode', () => { }) it('throws authorization error', async () => { - const { errors } = await query({ query: groupMembersQuery(), variables }) + const { errors } = await query({ query: groupMembersQuery, variables }) expect(errors![0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -2418,7 +2416,7 @@ describe('in mode', () => { describe('here "closed-group" for example', () => { const memberInGroup = async (userId, groupId) => { const result = await query({ - query: groupMembersQuery(), + query: groupMembersQuery, variables: { id: groupId, }, diff --git a/backend/src/graphql/resolvers/groups.ts b/backend/src/graphql/resolvers/groups.ts index 8e24117e1..a3ce3285a 100644 --- a/backend/src/graphql/resolvers/groups.ts +++ b/backend/src/graphql/resolvers/groups.ts @@ -436,6 +436,24 @@ export default { }, }, Group: { + inviteCodes: async (parent, _args, context: Context, _resolveInfo) => { + if (!parent.id) { + throw new Error('Can not identify selected Group!') + } + return ( + await context.database.query({ + query: ` + MATCH (user:User {id: $user.id})-[:GENERATED]->(inviteCodes:InviteCode)-[:INVITES_TO]->(g:Group {id: $parent.id}) + RETURN inviteCodes {.*} + ORDER BY inviteCodes.createdAt ASC + `, + variables: { + user: context.user, + parent, + }, + }) + ).records.map((r) => r.get('inviteCodes')) + }, ...Resolver('Group', { undefinedToNull: ['deleted', 'disabled', 'locationName', 'about'], hasMany: { @@ -451,6 +469,18 @@ export default { 'MATCH (this) RETURN EXISTS( (this)<-[:MUTED]-(:User {id: $cypherParams.currentUserId}) )', }, }), + name: async (parent, _args, context: Context, _resolveInfo) => { + if (!context.user) { + return parent.groupType === 'hidden' ? '' : parent.name + } + return parent.name + }, + about: async (parent, _args, context: Context, _resolveInfo) => { + if (!context.user) { + return parent.groupType === 'hidden' ? '' : parent.about + } + return parent.about + }, }, } diff --git a/backend/src/graphql/resolvers/helpers/generateInviteCode.ts b/backend/src/graphql/resolvers/helpers/generateInviteCode.ts deleted file mode 100644 index 980af4593..000000000 --- a/backend/src/graphql/resolvers/helpers/generateInviteCode.ts +++ /dev/null @@ -1,13 +0,0 @@ -import registrationConstants from '@constants/registrationBranded' - -export default function generateInviteCode() { - // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) - return Array.from( - { length: registrationConstants.INVITE_CODE_LENGTH }, - (n: number = Math.floor(Math.random() * 36)) => { - // n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65 - // else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48 - return String.fromCharCode(n > 9 ? n + 55 : n + 48) - }, - ).join('') -} diff --git a/backend/src/graphql/resolvers/inviteCodes.spec.ts b/backend/src/graphql/resolvers/inviteCodes.spec.ts index f44721cc9..94829553c 100644 --- a/backend/src/graphql/resolvers/inviteCodes.spec.ts +++ b/backend/src/graphql/resolvers/inviteCodes.spec.ts @@ -1,214 +1,1205 @@ /* 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-regexp */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' -import registrationConstants from '@constants/registrationBranded' +import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getDriver } from '@db/neo4j' -import createServer from '@src/server' +import { createGroupMutation } from '@graphql/queries/createGroupMutation' +import { currentUser } from '@graphql/queries/currentUser' +import { generateGroupInviteCode } from '@graphql/queries/generateGroupInviteCode' +import { generatePersonalInviteCode } from '@graphql/queries/generatePersonalInviteCode' +import { Group } from '@graphql/queries/Group' +import { GroupMembers } from '@graphql/queries/GroupMembers' +import { invalidateInviteCode } from '@graphql/queries/invalidateInviteCode' +import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' +import { redeemInviteCode } from '@graphql/queries/redeemInviteCode' +import { + authenticatedValidateInviteCode, + unauthenticatedValidateInviteCode, +} from '@graphql/queries/validateInviteCode' +import createServer, { getContext } from '@src/server' -let user -let query -let mutate +const database = databaseContext() -const driver = getDriver() - -const generateInviteCodeMutation = gql` - mutation ($expiresAt: String = null) { - GenerateInviteCode(expiresAt: $expiresAt) { - code - createdAt - expiresAt - } - } -` -const myInviteCodesQuery = gql` - query { - MyInviteCodes { - code - createdAt - expiresAt - } - } -` -const isValidInviteCodeQuery = gql` - query ($code: ID!) { - isValidInviteCode(code: $code) - } -` +let server: ApolloServer +let authenticatedUser +let query, mutate beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - user, - } - }, + // 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 + + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate +}) + +afterAll(() => { + void server.stop() + void database.driver.close() + database.neode.close() +}) + +describe('validateInviteCode', () => { + let invitingUser, user + beforeEach(async () => { + await cleanDatabase() + invitingUser = await Factory.build('user', { + id: 'inviting-user', + role: 'user', + name: 'Inviting User', + }) + user = await Factory.build('user', { + id: 'normal-user', + role: 'user', + name: 'Normal User', + }) + + authenticatedUser = await invitingUser.toJson() + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'hidden-group', + name: 'Hidden Group', + about: 'We are hidden', + description: 'anything', + groupType: 'hidden', + actionRadius: 'global', + categoryIds: ['cat6', 'cat12', 'cat16'], + locationName: 'Hamburg, Germany', + }, + }) + + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'public-group', + name: 'Public Group', + about: 'We are public', + description: 'anything', + groupType: 'public', + actionRadius: 'interplanetary', + categoryIds: ['cat4', 'cat5', 'cat17'], + }, + }) + + await Factory.build( + 'inviteCode', + { + code: 'EXPIRD', + expiresAt: new Date(1970, 1).toISOString(), + }, + { + generatedBy: invitingUser, + }, + ) + await Factory.build( + 'inviteCode', + { + code: 'PERSNL', + }, + { + generatedBy: invitingUser, + }, + ) + await Factory.build( + 'inviteCode', + { + code: 'GRPPBL', + }, + { + generatedBy: invitingUser, + groupId: 'public-group', + }, + ) + await Factory.build( + 'inviteCode', + { + code: 'GRPHDN', + }, + { + generatedBy: invitingUser, + groupId: 'hidden-group', + }, + ) }) - query = createTestClient(server).query - mutate = createTestClient(server).mutate -}) - -afterAll(async () => { - await cleanDatabase() - await driver.close() -}) - -describe('inviteCodes', () => { describe('as unauthenticated user', () => { - it('cannot generate invite codes', async () => { - await expect(mutate({ mutation: generateInviteCodeMutation })).resolves.toEqual( + beforeEach(() => { + authenticatedUser = null + }) + + it('returns null when the code does not exist', async () => { + await expect( + query({ query: unauthenticatedValidateInviteCode, variables: { code: 'INVALD' } }), + ).resolves.toEqual( expect.objectContaining({ - errors: expect.arrayContaining([ - expect.objectContaining({ - extensions: { code: 'INTERNAL_SERVER_ERROR' }, - }), - ]), data: { - GenerateInviteCode: null, + validateInviteCode: null, }, + errors: undefined, }), ) }) - it('cannot query invite codes', async () => { - await expect(query({ query: myInviteCodesQuery })).resolves.toEqual( + it('returns null when the code has expired', async () => { + await expect( + query({ query: unauthenticatedValidateInviteCode, variables: { code: 'EXPIRD' } }), + ).resolves.toEqual( expect.objectContaining({ - errors: expect.arrayContaining([ - expect.objectContaining({ - extensions: { code: 'INTERNAL_SERVER_ERROR' }, - }), - ]), data: { - MyInviteCodes: null, + validateInviteCode: null, }, + errors: undefined, }), ) }) + + it('returns the inviteCode when the code exists and hs not expired', async () => { + await expect( + query({ query: unauthenticatedValidateInviteCode, variables: { code: 'PERSNL' } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + validateInviteCode: { + code: 'PERSNL', + generatedBy: { + avatar: { + url: expect.any(String), + }, + name: 'Inviting User', + }, + invitedTo: null, + isValid: true, + }, + }, + errors: undefined, + }), + ) + }) + + it('returns the inviteCode with group details if the code invites to a public group', async () => { + await expect( + query({ query: unauthenticatedValidateInviteCode, variables: { code: 'GRPPBL' } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + validateInviteCode: { + code: 'GRPPBL', + generatedBy: { + avatar: { + url: expect.any(String), + }, + name: 'Inviting User', + }, + invitedTo: { + groupType: 'public', + name: 'Public Group', + about: 'We are public', + avatar: null, + }, + isValid: true, + }, + }, + errors: undefined, + }), + ) + }) + + it('returns the inviteCode with redacted group details if the code invites to a hidden group', async () => { + await expect( + query({ query: unauthenticatedValidateInviteCode, variables: { code: 'GRPHDN' } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + validateInviteCode: { + code: 'GRPHDN', + generatedBy: { + avatar: { + url: expect.any(String), + }, + name: 'Inviting User', + }, + invitedTo: { + groupType: 'hidden', + name: '', + about: '', + avatar: null, + }, + isValid: true, + }, + }, + errors: undefined, + }), + ) + }) + + it('throws authorization error when querying extended fields', async () => { + await expect( + query({ query: authenticatedValidateInviteCode, variables: { code: 'PERSNL' } }), + ).resolves.toMatchObject({ + data: { + validateInviteCode: { + code: 'PERSNL', + generatedBy: null, + invitedTo: null, + isValid: true, + }, + }, + errors: [{ message: 'Not Authorized!' }], + }) + }) }) describe('as authenticated user', () => { beforeAll(async () => { - const authenticatedUser = await Factory.build( - 'user', - { - role: 'user', - }, - { - email: 'user@example.org', - password: '1234', - }, - ) - user = await authenticatedUser.toJson() + authenticatedUser = await user.toJson() }) - it('generates an invite code without expiresAt', async () => { - await expect(mutate({ mutation: generateInviteCodeMutation })).resolves.toEqual( - expect.objectContaining({ - errors: undefined, - data: { - GenerateInviteCode: { - code: expect.stringMatching( - new RegExp( - `^[0-9A-Z]{${registrationConstants.INVITE_CODE_LENGTH},${registrationConstants.INVITE_CODE_LENGTH}}$`, - ), - ), - expiresAt: null, - createdAt: expect.any(String), + it('throws no authorization error when querying extended fields', async () => { + await expect( + query({ query: authenticatedValidateInviteCode, variables: { code: 'PERSNL' } }), + ).resolves.toMatchObject({ + data: { + validateInviteCode: { + code: 'PERSNL', + generatedBy: { + id: 'inviting-user', + name: 'Inviting User', + avatar: { + url: expect.any(String), + }, }, + invitedTo: null, + isValid: true, }, - }), - ) + }, + errors: undefined, + }) }) - it('generates an invite code with expiresAt', async () => { - const nextWeek = new Date() - nextWeek.setDate(nextWeek.getDate() + 7) + it('throws no authorization error when querying extended public group fields', async () => { + await expect( + query({ query: authenticatedValidateInviteCode, variables: { code: 'GRPPBL' } }), + ).resolves.toMatchObject({ + data: { + validateInviteCode: { + code: 'GRPPBL', + generatedBy: { + id: 'inviting-user', + name: 'Inviting User', + avatar: { + url: expect.any(String), + }, + }, + invitedTo: { + id: 'public-group', + groupType: 'public', + name: 'Public Group', + about: 'We are public', + avatar: null, + }, + isValid: true, + }, + }, + errors: undefined, + }) + }) + + // This doesn't work because group permissions are fucked + // eslint-disable-next-line jest/no-disabled-tests + it.skip('throws authorization error when querying extended hidden group fields', async () => { + await expect( + query({ query: authenticatedValidateInviteCode, variables: { code: 'GRPHDN' } }), + ).resolves.toMatchObject({ + data: { + validateInviteCode: { + code: 'GRPHDN', + generatedBy: null, + invitedTo: null, + isValid: true, + }, + }, + errors: [{ message: 'Not Authorized!' }], + }) + }) + + // eslint-disable-next-line jest/no-disabled-tests, @typescript-eslint/no-empty-function + it.skip('throws no authorization error when querying extended hidden group fields as member', async () => {}) + }) +}) + +describe('generatePersonalInviteCode', () => { + let invitingUser + beforeEach(async () => { + await cleanDatabase() + invitingUser = await Factory.build('user', { + id: 'inviting-user', + role: 'user', + name: 'Inviting User', + }) + }) + describe('as unauthenticated user', () => { + beforeEach(() => { + authenticatedUser = null + }) + + it('throws authorization error', async () => { + await expect(mutate({ mutation: generatePersonalInviteCode })).resolves.toMatchObject({ + data: null, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + + describe('as authenticated user', () => { + beforeEach(async () => { + authenticatedUser = await invitingUser.toJson() + }) + + it('returns a new invite code', async () => { + await expect(mutate({ mutation: generatePersonalInviteCode })).resolves.toMatchObject({ + data: { + generatePersonalInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: null, + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: null, + isValid: true, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + + it('returns a new invite code with comment', async () => { + await expect( + mutate({ mutation: generatePersonalInviteCode, variables: { comment: 'some text' } }), + ).resolves.toMatchObject({ + data: { + generatePersonalInviteCode: { + code: expect.any(String), + comment: 'some text', + createdAt: expect.any(String), + expiresAt: null, + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: null, + isValid: true, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + + it('returns a new invite code with expireDate', async () => { + const date = new Date() + date.setFullYear(date.getFullYear() + 1) await expect( mutate({ - mutation: generateInviteCodeMutation, - variables: { expiresAt: nextWeek.toISOString() }, + mutation: generatePersonalInviteCode, + variables: { expiresAt: date.toISOString() }, }), - ).resolves.toEqual( - expect.objectContaining({ - errors: undefined, - data: { - GenerateInviteCode: { - code: expect.stringMatching( - new RegExp( - `^[0-9A-Z]{${registrationConstants.INVITE_CODE_LENGTH},${registrationConstants.INVITE_CODE_LENGTH}}$`, - ), - ), - expiresAt: nextWeek.toISOString(), - createdAt: expect.any(String), + ).resolves.toMatchObject({ + data: { + generatePersonalInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: date.toISOString(), + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', }, + invitedTo: null, + isValid: true, + redeemedBy: [], }, + }, + errors: undefined, + }) + }) + + it('returns a new invalid invite code with expireDate in the past', async () => { + const date = new Date() + date.setFullYear(date.getFullYear() - 1) + await expect( + mutate({ + mutation: generatePersonalInviteCode, + variables: { expiresAt: date.toISOString() }, }), - ) - }) - - let inviteCodes - - it('returns the created invite codes when queried', async () => { - const response = await query({ query: myInviteCodesQuery }) - inviteCodes = response.data.MyInviteCodes - expect(inviteCodes).toHaveLength(2) - }) - - it('does not return the created invite codes of other users when queried', async () => { - await Factory.build('inviteCode') - const response = await query({ query: myInviteCodesQuery }) - inviteCodes = response.data.MyInviteCodes - expect(inviteCodes).toHaveLength(2) - }) - - it('validates an invite code without expiresAt', async () => { - const unExpiringInviteCode = inviteCodes.filter((ic) => ic.expiresAt === null)[0].code - const result = await query({ - query: isValidInviteCodeQuery, - variables: { code: unExpiringInviteCode }, + ).resolves.toMatchObject({ + data: { + generatePersonalInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: date.toISOString(), + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: null, + isValid: false, + redeemedBy: [], + }, + }, + errors: undefined, }) - expect(result.data.isValidInviteCode).toBeTruthy() }) - it('validates an invite code in lower case', async () => { - const unExpiringInviteCode = inviteCodes.filter((ic) => ic.expiresAt === null)[0].code - const result = await query({ - query: isValidInviteCodeQuery, - variables: { code: unExpiringInviteCode.toLowerCase() }, + it('throws an error when the max amount of invite links was reached', async () => { + let lastCode + for (let i = 0; i < CONFIG.INVITE_CODES_PERSONAL_PER_USER; i++) { + lastCode = await mutate({ mutation: generatePersonalInviteCode }) + expect(lastCode).toMatchObject({ + errors: undefined, + }) + } + await expect(mutate({ mutation: generatePersonalInviteCode })).resolves.toMatchObject({ + errors: [ + { + message: 'You have reached the maximum of Invite Codes you can generate', + }, + ], }) - expect(result.data.isValidInviteCode).toBeTruthy() - }) - - it('validates an invite code with expiresAt in the future', async () => { - const expiringInviteCode = inviteCodes.filter((ic) => ic.expiresAt !== null)[0].code - const result = await query({ - query: isValidInviteCodeQuery, - variables: { code: expiringInviteCode }, + await mutate({ + mutation: invalidateInviteCode, + variables: { code: lastCode.data.generatePersonalInviteCode.code }, }) - expect(result.data.isValidInviteCode).toBeTruthy() - }) - - it('does not validate an invite code which expired in the past', async () => { - const lastWeek = new Date() - lastWeek.setDate(lastWeek.getDate() - 7) - const inviteCode = await Factory.build('inviteCode', { - expiresAt: lastWeek.toISOString(), + await expect(mutate({ mutation: generatePersonalInviteCode })).resolves.toMatchObject({ + errors: undefined, }) - const code = inviteCode.get('code') - const result = await query({ query: isValidInviteCodeQuery, variables: { code } }) - expect(result.data.isValidInviteCode).toBeFalsy() }) - it('does not validate an invite code which does not exits', async () => { - const result = await query({ query: isValidInviteCodeQuery, variables: { code: 'AAA' } }) - expect(result.data.isValidInviteCode).toBeFalsy() + // eslint-disable-next-line jest/no-disabled-tests, @typescript-eslint/no-empty-function + it.skip('returns a new invite code when colliding with an existing one', () => {}) + }) +}) + +describe('generateGroupInviteCode', () => { + let invitingUser, notMemberUser, pendingMemberUser + beforeEach(async () => { + await cleanDatabase() + invitingUser = await Factory.build('user', { + id: 'inviting-user', + role: 'user', + name: 'Inviting User', + }) + + notMemberUser = await Factory.build('user', { + id: 'not-member-user', + role: 'user', + name: 'Not a Member User', + }) + + pendingMemberUser = await Factory.build('user', { + id: 'pending-member-user', + role: 'user', + name: 'Pending Member User', + }) + + authenticatedUser = await invitingUser.toJson() + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'hidden-group', + name: 'Hidden Group', + about: 'We are hidden', + description: 'anything', + groupType: 'hidden', + actionRadius: 'global', + categoryIds: ['cat6', 'cat12', 'cat16'], + locationName: 'Hamburg, Germany', + }, + }) + + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'public-group', + name: 'Public Group', + about: 'We are public', + description: 'anything', + groupType: 'public', + actionRadius: 'interplanetary', + categoryIds: ['cat4', 'cat5', 'cat17'], + }, + }) + + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'closed-group', + name: 'Closed Group', + about: 'We are closed', + description: 'anything', + groupType: 'closed', + actionRadius: 'interplanetary', + categoryIds: ['cat4', 'cat5', 'cat17'], + }, + }) + + await mutate({ + mutation: joinGroupMutation(), + variables: { + groupId: 'closed-group', + userId: 'pending-member-user', + }, + }) + }) + + describe('as unauthenticated user', () => { + beforeEach(() => { + authenticatedUser = null + }) + + it('throws authorization error', async () => { + await expect( + mutate({ mutation: generateGroupInviteCode, variables: { groupId: 'public-group' } }), + ).resolves.toMatchObject({ + data: null, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + describe('as authenticated member', () => { + beforeEach(async () => { + authenticatedUser = await invitingUser.toJson() + }) + + it('returns a new group invite code', async () => { + await expect( + mutate({ mutation: generateGroupInviteCode, variables: { groupId: 'public-group' } }), + ).resolves.toMatchObject({ + data: { + generateGroupInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: null, + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: { + id: 'public-group', + groupType: 'public', + name: 'Public Group', + about: 'We are public', + avatar: null, + }, + isValid: true, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + + it('returns a new group invite code with comment', async () => { + await expect( + mutate({ + mutation: generateGroupInviteCode, + variables: { groupId: 'public-group', comment: 'some text' }, + }), + ).resolves.toMatchObject({ + data: { + generateGroupInviteCode: { + code: expect.any(String), + comment: 'some text', + createdAt: expect.any(String), + expiresAt: null, + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: { + id: 'public-group', + groupType: 'public', + name: 'Public Group', + about: 'We are public', + avatar: null, + }, + isValid: true, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + + it('returns a new group invite code with expireDate', async () => { + const date = new Date() + date.setFullYear(date.getFullYear() + 1) + await expect( + mutate({ + mutation: generateGroupInviteCode, + variables: { groupId: 'public-group', expiresAt: date.toISOString() }, + }), + ).resolves.toMatchObject({ + data: { + generateGroupInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: date.toISOString(), + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: { + id: 'public-group', + groupType: 'public', + name: 'Public Group', + about: 'We are public', + avatar: null, + }, + isValid: true, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + + it('returns a new invalid group invite code with expireDate in the past', async () => { + const date = new Date() + date.setFullYear(date.getFullYear() - 1) + await expect( + mutate({ + mutation: generateGroupInviteCode, + variables: { groupId: 'public-group', expiresAt: date.toISOString() }, + }), + ).resolves.toMatchObject({ + data: { + generateGroupInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: date.toISOString(), + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: { + id: 'public-group', + groupType: 'public', + name: 'Public Group', + about: 'We are public', + avatar: null, + }, + isValid: false, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + + it('throws an error when the max amount of invite links was reached', async () => { + let lastCode + for (let i = 0; i < CONFIG.INVITE_CODES_GROUP_PER_USER; i++) { + lastCode = await mutate({ + mutation: generateGroupInviteCode, + variables: { groupId: 'public-group' }, + }) + expect(lastCode).toMatchObject({ + errors: undefined, + }) + } + await expect( + mutate({ mutation: generateGroupInviteCode, variables: { groupId: 'public-group' } }), + ).resolves.toMatchObject({ + errors: [ + { + message: 'You have reached the maximum of Invite Codes you can generate for this group', + }, + ], + }) + await mutate({ + mutation: invalidateInviteCode, + variables: { code: lastCode.data.generateGroupInviteCode.code }, + }) + await expect( + mutate({ mutation: generateGroupInviteCode, variables: { groupId: 'public-group' } }), + ).resolves.toMatchObject({ + errors: undefined, + }) + }) + + // eslint-disable-next-line jest/no-disabled-tests, @typescript-eslint/no-empty-function + it.skip('returns a new group invite code when colliding with an existing one', () => {}) + }) + + describe('as authenticated not-member', () => { + beforeEach(async () => { + authenticatedUser = await notMemberUser.toJson() + }) + + it('throws authorization error', async () => { + const date = new Date() + date.setFullYear(date.getFullYear() - 1) + await expect( + mutate({ + mutation: generateGroupInviteCode, + variables: { groupId: 'public-group' }, + }), + ).resolves.toMatchObject({ + data: null, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + + describe('as pending-member user', () => { + beforeEach(async () => { + authenticatedUser = await pendingMemberUser.toJson() + }) + + it('throws authorization error', async () => { + await expect( + mutate({ + mutation: generateGroupInviteCode, + variables: { groupId: 'hidden-group' }, + }), + ).resolves.toMatchObject({ + data: null, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) +}) + +describe('invalidateInviteCode', () => { + let invitingUser, otherUser + beforeEach(async () => { + await cleanDatabase() + invitingUser = await Factory.build('user', { + id: 'inviting-user', + role: 'user', + name: 'Inviting User', + }) + + otherUser = await Factory.build('user', { + id: 'other-user', + role: 'user', + name: 'Other User', + }) + + await Factory.build( + 'inviteCode', + { + code: 'CODE33', + }, + { + generatedBy: invitingUser, + }, + ) + }) + + describe('as unauthenticated user', () => { + beforeEach(() => { + authenticatedUser = null + }) + + it('throws authorization error', async () => { + await expect( + mutate({ mutation: invalidateInviteCode, variables: { code: 'CODE33' } }), + ).resolves.toMatchObject({ + data: { + invalidateInviteCode: null, + }, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + + describe('as authenticated user', () => { + describe('as link owner', () => { + beforeEach(async () => { + authenticatedUser = await invitingUser.toJson() + }) + + it('returns the invalidated InviteCode', async () => { + await expect( + mutate({ mutation: invalidateInviteCode, variables: { code: 'CODE33' } }), + ).resolves.toMatchObject({ + data: { + invalidateInviteCode: { + code: expect.any(String), + comment: null, + createdAt: expect.any(String), + expiresAt: expect.any(String), + generatedBy: { + avatar: { + url: expect.any(String), + }, + id: 'inviting-user', + name: 'Inviting User', + }, + invitedTo: null, + isValid: false, + redeemedBy: [], + }, + }, + errors: undefined, + }) + }) + }) + + describe('as not link owner', () => { + beforeEach(async () => { + authenticatedUser = await otherUser.toJson() + }) + + it('throws authorization error', async () => { + await expect( + mutate({ mutation: invalidateInviteCode, variables: { code: 'CODE33' } }), + ).resolves.toMatchObject({ + data: { + invalidateInviteCode: null, + }, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + }) +}) + +describe('redeemInviteCode', () => { + let invitingUser, otherUser + beforeEach(async () => { + await cleanDatabase() + invitingUser = await Factory.build('user', { + id: 'inviting-user', + role: 'user', + name: 'Inviting User', + }) + + otherUser = await Factory.build('user', { + id: 'other-user', + role: 'user', + name: 'Other User', + }) + + authenticatedUser = await invitingUser.toJson() + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'hidden-group', + name: 'Hidden Group', + about: 'We are hidden', + description: 'anything', + groupType: 'hidden', + actionRadius: 'global', + categoryIds: ['cat6', 'cat12', 'cat16'], + locationName: 'Hamburg, Germany', + }, + }) + + await mutate({ + mutation: createGroupMutation(), + variables: { + id: 'public-group', + name: 'Public Group', + about: 'We are public', + description: 'anything', + groupType: 'public', + actionRadius: 'interplanetary', + categoryIds: ['cat4', 'cat5', 'cat17'], + }, + }) + + await Factory.build( + 'inviteCode', + { + code: 'CODE33', + }, + { + generatedBy: invitingUser, + }, + ) + await Factory.build( + 'inviteCode', + { + code: 'GRPPBL', + }, + { + generatedBy: invitingUser, + groupId: 'public-group', + }, + ) + await Factory.build( + 'inviteCode', + { + code: 'GRPHDN', + }, + { + generatedBy: invitingUser, + groupId: 'hidden-group', + }, + ) + }) + + describe('as unauthenticated user', () => { + beforeEach(() => { + authenticatedUser = null + }) + + it('throws authorization error', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'CODE33' } }), + ).resolves.toMatchObject({ + data: null, + errors: [{ message: 'Not Authorized!' }], + }) + }) + }) + + describe('as authenticated user', () => { + beforeEach(async () => { + authenticatedUser = await otherUser.toJson() + }) + + it('returns false for an invalid inviteCode', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'INVALD' } }), + ).resolves.toMatchObject({ + data: { + redeemInviteCode: false, + }, + errors: undefined, + }) + }) + + it('returns true for a personal inviteCode, but does nothing', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'CODE33' } }), + ).resolves.toMatchObject({ + data: { + redeemInviteCode: true, + }, + errors: undefined, + }) + authenticatedUser = await invitingUser.toJson() + await expect(query({ query: currentUser })).resolves.toMatchObject({ + data: { + currentUser: { + following: [], + inviteCodes: expect.arrayContaining([ + { + code: 'CODE33', + redeemedByCount: 0, + }, + ]), + }, + }, + errors: undefined, + }) + }) + + it('returns true for a public group inviteCode and makes the user a group member', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'GRPPBL' } }), + ).resolves.toMatchObject({ + data: { + redeemInviteCode: true, + }, + errors: undefined, + }) + await expect( + query({ query: Group, variables: { id: 'public-group' } }), + ).resolves.toMatchObject({ + data: { + Group: [ + { + myRole: 'usual', + }, + ], + }, + errors: undefined, + }) + authenticatedUser = await invitingUser.toJson() + await expect(query({ query: currentUser })).resolves.toMatchObject({ + data: { + currentUser: { + inviteCodes: expect.arrayContaining([ + { + code: 'GRPPBL', + redeemedByCount: 1, + }, + { + code: 'GRPHDN', + redeemedByCount: 0, + }, + ]), + }, + }, + errors: undefined, + }) + }) + + it('returns true for a hidden group inviteCode and makes the user a pending member', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'GRPHDN' } }), + ).resolves.toMatchObject({ + data: { + redeemInviteCode: true, + }, + errors: undefined, + }) + authenticatedUser = await invitingUser.toJson() + await expect( + query({ query: GroupMembers, variables: { id: 'hidden-group' } }), + ).resolves.toMatchObject({ + data: { + GroupMembers: expect.arrayContaining([ + { + id: 'inviting-user', + myRoleInGroup: 'owner', + name: 'Inviting User', + slug: 'inviting-user', + }, + { + id: 'other-user', + myRoleInGroup: 'pending', + name: 'Other User', + slug: 'other-user', + }, + ]), + }, + errors: undefined, + }) + await expect(query({ query: currentUser })).resolves.toMatchObject({ + data: { + currentUser: { + inviteCodes: expect.arrayContaining([ + { + code: 'GRPPBL', + redeemedByCount: 0, + }, + { + code: 'GRPHDN', + redeemedByCount: 1, + }, + ]), + }, + }, + errors: undefined, + }) + }) + }) + + describe('as authenticated self', () => { + beforeEach(async () => { + authenticatedUser = await invitingUser.toJson() + }) + + it('returns true for a personal inviteCode, but does nothing', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'CODE33' } }), + ).resolves.toMatchObject({ + data: { + redeemInviteCode: true, + }, + errors: undefined, + }) + await expect(query({ query: currentUser })).resolves.toMatchObject({ + data: { + currentUser: { + following: [], + inviteCodes: expect.arrayContaining([ + { + code: 'CODE33', + redeemedByCount: 0, + }, + ]), + }, + }, + errors: undefined, + }) + }) + + it('returns true for a public group inviteCode, but does nothing', async () => { + await expect( + mutate({ mutation: redeemInviteCode, variables: { code: 'GRPPBL' } }), + ).resolves.toMatchObject({ + data: { + redeemInviteCode: true, + }, + errors: undefined, + }) + await expect( + query({ query: Group, variables: { id: 'public-group' } }), + ).resolves.toMatchObject({ + data: { + Group: [ + { + myRole: 'owner', + }, + ], + }, + errors: undefined, + }) + await expect(query({ query: currentUser })).resolves.toMatchObject({ + data: { + currentUser: { + following: [], + inviteCodes: expect.arrayContaining([ + { + code: 'GRPPBL', + redeemedByCount: 0, + }, + { + code: 'GRPHDN', + redeemedByCount: 0, + }, + ]), + }, + }, + errors: undefined, + }) }) }) }) diff --git a/backend/src/graphql/resolvers/inviteCodes.ts b/backend/src/graphql/resolvers/inviteCodes.ts index 02680b5bc..5d4638d1d 100644 --- a/backend/src/graphql/resolvers/inviteCodes.ts +++ b/backend/src/graphql/resolvers/inviteCodes.ts @@ -1,136 +1,294 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import generateInviteCode from './helpers/generateInviteCode' -import Resolver from './helpers/Resolver' -import { validateInviteCode } from './transactions/inviteCodes' +import CONFIG from '@config/index' +import registrationConstants from '@constants/registrationBranded' +// eslint-disable-next-line import/no-cycle +import { Context } from '@src/server' -const uniqueInviteCode = async (session, code) => { - return session.readTransaction(async (txc) => { - const result = await txc.run(`MATCH (ic:InviteCode { id: $code }) RETURN count(ic) AS count`, { - code, +import Resolver from './helpers/Resolver' + +export const generateInviteCode = () => { + // 6 random numbers in [ 0, 35 ] are 36 possible numbers (10 [0-9] + 26 [A-Z]) + return Array.from( + { length: registrationConstants.INVITE_CODE_LENGTH }, + (n: number = Math.floor(Math.random() * 36)) => { + // n > 9: it is a letter (ASCII 65 is A) -> 10 + 55 = 65 + // else: it is a number (ASCII 48 is 0) -> 0 + 48 = 48 + return String.fromCharCode(n > 9 ? n + 55 : n + 48) + }, + ).join('') +} + +const uniqueInviteCode = async (context: Context, code: string) => { + return ( + ( + await context.database.query({ + query: `MATCH (inviteCode:InviteCode { code: toUpper($code) }) + WHERE inviteCode.expiresAt IS NULL + OR inviteCode.expiresAt >= datetime() + RETURN toString(count(inviteCode)) AS count`, + variables: { code }, + }) + ).records[0].get('count') === '0' + ) +} + +export const validateInviteCode = async (context: Context, inviteCode) => { + const result = ( + await context.database.query({ + query: ` + OPTIONAL MATCH (inviteCode:InviteCode { code: toUpper($inviteCode) }) + RETURN + CASE + WHEN inviteCode IS NULL THEN false + WHEN inviteCode.expiresAt IS NULL THEN true + WHEN datetime(inviteCode.expiresAt) >= datetime() THEN true + ELSE false END AS result + `, + variables: { inviteCode }, }) - return parseInt(String(result.records[0].get('count'))) === 0 - }) + ).records + return result[0].get('result') === true +} + +export const redeemInviteCode = async (context: Context, code, newUser = false) => { + const result = ( + await context.database.query({ + query: ` + MATCH (inviteCode:InviteCode {code: toUpper($code)})<-[:GENERATED]-(host:User) + OPTIONAL MATCH (inviteCode)-[:INVITES_TO]->(group:Group) + WHERE inviteCode.expiresAt IS NULL + OR datetime(inviteCode.expiresAt) >= datetime() + RETURN inviteCode {.*}, group {.*}, host {.*}`, + variables: { code }, + }) + ).records + + if (result.length !== 1) { + return false + } + + const inviteCode = result[0].get('inviteCode') + const group = result[0].get('group') + const host = result[0].get('host') + + if (!inviteCode || !host) { + return false + } + + // self + if (host.id === context.user.id) { + return true + } + + // Personal Invite Link + if (!group) { + // We redeemed this link while having an account, hence we do nothing, but return true + if (!newUser) { + return true + } + + await context.database.write({ + query: ` + MATCH (user:User {id: $user.id}), (inviteCode:InviteCode {code: toUpper($code)})<-[:GENERATED]-(host:User) + MERGE (user)-[:REDEEMED { createdAt: toString(datetime()) }]->(inviteCode) + MERGE (host)-[:INVITED { createdAt: toString(datetime()) }]->(user) + MERGE (user)-[:FOLLOWS { createdAt: toString(datetime()) }]->(host) + MERGE (host)-[:FOLLOWS { createdAt: toString(datetime()) }]->(user) + `, + variables: { user: context.user, code }, + }) + // Group Invite Link + } else { + const role = ['closed', 'hidden'].includes(group.groupType as string) ? 'pending' : 'usual' + + const optionalInvited = newUser + ? 'MERGE (host)-[:INVITED { createdAt: toString(datetime()) }]->(user)' + : '' + + await context.database.write({ + query: ` + MATCH (user:User {id: $user.id}), (group:Group)<-[:INVITES_TO]-(inviteCode:InviteCode {code: toUpper($code)})<-[:GENERATED]-(host:User) + MERGE (user)-[:REDEEMED { createdAt: toString(datetime()) }]->(inviteCode) + ${optionalInvited} + MERGE (user)-[membership:MEMBER_OF]->(group) + ON CREATE SET + membership.createdAt = toString(datetime()), + membership.updatedAt = null, + membership.role = $role + `, + variables: { user: context.user, code, role }, + }) + } + return true } export default { Query: { - getInviteCode: async (_parent, args, context, _resolveInfo) => { - const { - user: { id: userId }, - } = context - const session = context.driver.session() - const readTxResultPromise = session.readTransaction(async (txc) => { - const result = await txc.run( - `MATCH (user:User {id: $userId})-[:GENERATED]->(ic:InviteCode) - WHERE ic.expiresAt IS NULL - OR datetime(ic.expiresAt) >= datetime() - RETURN properties(ic) AS inviteCodes`, - { - userId, - }, - ) - return result.records.map((record) => record.get('inviteCodes')) - }) - try { - const inviteCode = await readTxResultPromise - if (inviteCode && inviteCode.length > 0) return inviteCode[0] - let code = generateInviteCode() - while (!(await uniqueInviteCode(session, code))) { - code = generateInviteCode() - } - const writeTxResultPromise = session.writeTransaction(async (txc) => { - const result = await txc.run( - `MATCH (user:User {id: $userId}) - MERGE (user)-[:GENERATED]->(ic:InviteCode { code: $code }) - ON CREATE SET - ic.createdAt = toString(datetime()), - ic.expiresAt = $expiresAt - RETURN ic AS inviteCode`, - { - userId, - code, - expiresAt: null, - }, - ) - return result.records.map((record) => record.get('inviteCode').properties) + validateInviteCode: async (_parent, args, context: Context, _resolveInfo) => { + const result = ( + await context.database.query({ + query: ` + MATCH (inviteCode:InviteCode { code: toUpper($args.code) }) + WHERE inviteCode.expiresAt IS NULL + OR datetime(inviteCode.expiresAt) >= datetime() + RETURN inviteCode {.*}`, + variables: { args }, }) - const txResult = await writeTxResultPromise - return txResult[0] - } finally { - session.close() + ).records + + if (result.length !== 1) { + return null } - }, - MyInviteCodes: async (_parent, args, context, _resolveInfo) => { - const { - user: { id: userId }, - } = context - const session = context.driver.session() - const readTxResultPromise = session.readTransaction(async (txc) => { - const result = await txc.run( - `MATCH (user:User {id: $userId})-[:GENERATED]->(ic:InviteCode) - RETURN properties(ic) AS inviteCodes`, - { - userId, - }, - ) - return result.records.map((record) => record.get('inviteCodes')) - }) - try { - const txResult = await readTxResultPromise - return txResult - } finally { - session.close() - } - }, - isValidInviteCode: async (_parent, args, context, _resolveInfo) => { - const { code } = args - const session = context.driver.session() - if (!code) return false - return validateInviteCode(session, code) + + return result[0].get('inviteCode') }, }, Mutation: { - GenerateInviteCode: async (_parent, args, context, _resolveInfo) => { - const { - user: { id: userId }, - } = context - const session = context.driver.session() + generatePersonalInviteCode: async (_parent, args, context: Context, _resolveInfo) => { + const userInviteCodeAmount = ( + await context.database.query({ + query: ` + MATCH (inviteCode:InviteCode)<-[:GENERATED]-(user:User {id: $user.id}) + WHERE NOT (inviteCode)-[:INVITES_TO]-(:Group) + AND (inviteCode.expiresAt IS NULL OR inviteCode.expiresAt >= datetime()) + RETURN toString(count(inviteCode)) as count + `, + variables: { user: context.user }, + }) + ).records[0].get('count') + + if (parseInt(userInviteCodeAmount as string) >= CONFIG.INVITE_CODES_PERSONAL_PER_USER) { + throw new Error('You have reached the maximum of Invite Codes you can generate') + } + let code = generateInviteCode() - while (!(await uniqueInviteCode(session, code))) { + while (!(await uniqueInviteCode(context, code))) { code = generateInviteCode() } - const writeTxResultPromise = session.writeTransaction(async (txc) => { - const result = await txc.run( - `MATCH (user:User {id: $userId}) - MERGE (user)-[:GENERATED]->(ic:InviteCode { code: $code }) - ON CREATE SET - ic.createdAt = toString(datetime()), - ic.expiresAt = $expiresAt - RETURN ic AS inviteCode`, - { - userId, - code, - expiresAt: args.expiresAt, - }, + + return ( + await context.database.write({ + // We delete a potential old invite code if there is a collision on an expired code + query: ` + MATCH (user:User {id: $user.id}) + OPTIONAL MATCH (oldInviteCode:InviteCode { code: toUpper($code) }) + DETACH DELETE oldInviteCode + MERGE (user)-[:GENERATED]->(inviteCode:InviteCode { code: toUpper($code)}) + ON CREATE SET + inviteCode.createdAt = toString(datetime()), + inviteCode.expiresAt = $args.expiresAt, + inviteCode.comment = $args.comment + RETURN inviteCode {.*}`, + variables: { user: context.user, code, args }, + }) + ).records[0].get('inviteCode') + }, + generateGroupInviteCode: async (_parent, args, context: Context, _resolveInfo) => { + const userInviteCodeAmount = ( + await context.database.query({ + query: ` + MATCH (:Group {id: $args.groupId})<-[:INVITES_TO]-(inviteCode:InviteCode)<-[:GENERATED]-(user:User {id: $user.id}) + WHERE inviteCode.expiresAt IS NULL + OR inviteCode.expiresAt >= datetime() + RETURN toString(count(inviteCode)) as count + `, + variables: { user: context.user, args }, + }) + ).records[0].get('count') + + if (parseInt(userInviteCodeAmount as string) >= CONFIG.INVITE_CODES_GROUP_PER_USER) { + throw new Error( + 'You have reached the maximum of Invite Codes you can generate for this group', ) - return result.records.map((record) => record.get('inviteCode').properties) - }) - try { - const txResult = await writeTxResultPromise - return txResult[0] - } finally { - session.close() } + + let code = generateInviteCode() + while (!(await uniqueInviteCode(context, code))) { + code = generateInviteCode() + } + + const inviteCode = ( + await context.database.write({ + query: ` + MATCH + (user:User {id: $user.id})-[membership:MEMBER_OF]->(group:Group {id: $args.groupId}) + WHERE NOT membership.role = 'pending' + OPTIONAL MATCH (oldInviteCode:InviteCode { code: toUpper($code) }) + DETACH DELETE oldInviteCode + MERGE (user)-[:GENERATED]->(inviteCode:InviteCode { code: toUpper($code) })-[:INVITES_TO]->(group) + ON CREATE SET + inviteCode.createdAt = toString(datetime()), + inviteCode.expiresAt = $args.expiresAt, + inviteCode.comment = $args.comment + RETURN inviteCode {.*}`, + variables: { user: context.user, code, args }, + }) + ).records + + if (inviteCode.length !== 1) { + // Not a member + throw new Error('Not Authorized!') + } + + return inviteCode[0].get('inviteCode') + }, + invalidateInviteCode: async (_parent, args, context: Context, _resolveInfo) => { + const result = ( + await context.database.write({ + query: ` + MATCH (user:User {id: $user.id})-[:GENERATED]-(inviteCode:InviteCode {code: toUpper($args.code)}) + SET inviteCode.expiresAt = toString(datetime()) + RETURN inviteCode {.*}`, + variables: { args, user: context.user }, + }) + ).records + + if (result.length !== 1) { + // Link not generated by this user or does not exist + throw new Error('Not Authorized!') + } + + return result[0].get('inviteCode') + }, + redeemInviteCode: async (_parent, args, context: Context, _resolveInfo) => { + return redeemInviteCode(context, args.code) }, }, InviteCode: { + invitedTo: async (parent, _args, context: Context, _resolveInfo) => { + if (!parent.code) { + return null + } + + const result = ( + await context.database.query({ + query: ` + MATCH (inviteCode:InviteCode {code: $parent.code})-[:INVITES_TO]->(group:Group) + RETURN group {.*} + `, + variables: { parent }, + }) + ).records + + if (result.length !== 1) { + return null + } + return result[0].get('group') + }, + isValid: async (parent, _args, context: Context, _resolveInfo) => { + if (!parent.code) { + return false + } + return validateInviteCode(context, parent.code) + }, ...Resolver('InviteCode', { idAttribute: 'code', - undefinedToNull: ['expiresAt'], + undefinedToNull: ['expiresAt', 'comment'], + count: { + redeemedByCount: '<-[:REDEEMED]-(related:User)', + }, hasOne: { generatedBy: '<-[:GENERATED]-(related:User)', }, diff --git a/backend/src/graphql/resolvers/locations.ts b/backend/src/graphql/resolvers/locations.ts index f375f287f..fc69fab94 100644 --- a/backend/src/graphql/resolvers/locations.ts +++ b/backend/src/graphql/resolvers/locations.ts @@ -24,6 +24,9 @@ export default { ], }), distanceToMe: async (parent, _params, context, _resolveInfo) => { + if (!parent.id) { + throw new Error('Can not identify selected Location!') + } const session = context.driver.session() const query = session.readTransaction(async (transaction) => { diff --git a/backend/src/graphql/resolvers/posts.spec.ts b/backend/src/graphql/resolvers/posts.spec.ts index 7574bef17..8d9bf355b 100644 --- a/backend/src/graphql/resolvers/posts.spec.ts +++ b/backend/src/graphql/resolvers/posts.spec.ts @@ -1,55 +1,51 @@ -/* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* 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 Image from '@db/models/Image' -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 = true -const driver = getDriver() -const neode = getNeode() - -let query -let mutate -let authenticatedUser let user -const categoryIds = ['cat9', 'cat4', 'cat15'] -let variables +const database = databaseContext() + +let server: ApolloServer +let authenticatedUser +let query, mutate beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - query = createTestClient(server).query - mutate = createTestClient(server).mutate + // 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 + + const createTestClientResult = createTestClient(server) + mutate = createTestClientResult.mutate + query = createTestClientResult.query }) -afterAll(async () => { - await cleanDatabase() - await driver.close() +afterAll(() => { + void server.stop() + void database.driver.close() + database.neode.close() }) +const categoryIds = ['cat9', 'cat4', 'cat15'] +let variables + beforeEach(async () => { variables = {} user = await Factory.build( @@ -64,22 +60,22 @@ beforeEach(async () => { }, ) await Promise.all([ - neode.create('Category', { + database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat4', name: 'Environment & Nature', icon: 'tree', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat15', name: 'Consumption & Sustainability', icon: 'shopping-cart', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat27', name: 'Animal Protection', icon: 'paw', @@ -88,7 +84,6 @@ beforeEach(async () => { authenticatedUser = null }) -// 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() }) @@ -233,7 +228,6 @@ describe('Post', () => { Post(filter: $filter) { id author { - id name } } @@ -249,7 +243,7 @@ describe('Post', () => { Post: [ { id: 'post-by-followed-user', - author: { id: 'followed-by-me', name: 'Followed User' }, + author: { name: 'Followed User' }, }, ], }, @@ -976,11 +970,11 @@ describe('UpdatePost', () => { }) it('updates the image', async () => { await expect( - neode.first('Image', { sensitive: true }, undefined), + database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeFalsy() await mutate({ mutation: updatePostMutation, variables }) await expect( - neode.first('Image', { sensitive: true }, undefined), + database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeTruthy() }) }) @@ -990,9 +984,9 @@ describe('UpdatePost', () => { variables = { ...variables, image: null } }) it('deletes the image', async () => { - await expect(neode.all('Image')).resolves.toHaveLength(6) + await expect(database.neode.all('Image')).resolves.toHaveLength(6) await mutate({ mutation: updatePostMutation, variables }) - await expect(neode.all('Image')).resolves.toHaveLength(5) + await expect(database.neode.all('Image')).resolves.toHaveLength(5) }) }) @@ -1002,11 +996,11 @@ describe('UpdatePost', () => { }) it('keeps the image unchanged', async () => { await expect( - neode.first('Image', { sensitive: true }, undefined), + database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeFalsy() await mutate({ mutation: updatePostMutation, variables }) await expect( - neode.first('Image', { sensitive: true }, undefined), + database.neode.first('Image', { sensitive: true }, undefined), ).resolves.toBeFalsy() }) }) @@ -1253,18 +1247,18 @@ describe('pin posts', () => { it('removes previous `pinned` attribute', async () => { const cypher = 'MATCH (post:Post) WHERE post.pinned IS NOT NULL RETURN post' - pinnedPost = await neode.cypher(cypher, {}) + pinnedPost = await database.neode.cypher(cypher, {}) expect(pinnedPost.records).toHaveLength(1) variables = { ...variables, id: 'only-pinned-post' } await mutate({ mutation: pinPostMutation, variables }) - pinnedPost = await neode.cypher(cypher, {}) + pinnedPost = await database.neode.cypher(cypher, {}) expect(pinnedPost.records).toHaveLength(1) }) it('removes previous PINNED relationship', async () => { variables = { ...variables, id: 'only-pinned-post' } await mutate({ mutation: pinPostMutation, variables }) - pinnedPost = await neode.cypher( + pinnedPost = await database.neode.cypher( `MATCH (:User)-[pinned:PINNED]->(post:Post) RETURN post, pinned`, {}, ) @@ -1593,7 +1587,7 @@ describe('emotions', () => { ` beforeEach(async () => { - author = await neode.create('User', { id: 'u257' }) + author = await database.neode.create('User', { id: 'u257' }) postToEmote = await Factory.build( 'post', { @@ -1628,7 +1622,7 @@ describe('emotions', () => { ` let postsEmotionsQueryVariables - beforeEach(async () => { + beforeEach(() => { postsEmotionsQueryVariables = { id: 'p1376' } }) diff --git a/backend/src/graphql/resolvers/registration.spec.ts b/backend/src/graphql/resolvers/registration.spec.ts index d959b348a..fe8dc40e0 100644 --- a/backend/src/graphql/resolvers/registration.spec.ts +++ b/backend/src/graphql/resolvers/registration.spec.ts @@ -1,49 +1,48 @@ -/* eslint-disable @typescript-eslint/require-await */ /* 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 EmailAddress from '@db/models/EmailAddress' import User from '@db/models/User' -import { getDriver, getNeode } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' -const neode = getNeode() - -let mutate -let authenticatedUser let variables -const driver = getDriver() + +const database = databaseContext() + +let server: ApolloServer +let authenticatedUser +let mutate beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, - }) - mutate = createTestClient(server).mutate + // 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 + + const createTestClientResult = createTestClient(server) + mutate = createTestClientResult.mutate }) -afterAll(async () => { - await cleanDatabase() - await driver.close() +afterAll(() => { + void server.stop() + void database.driver.close() + database.neode.close() }) -beforeEach(async () => { +beforeEach(() => { variables = {} }) -// 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() }) @@ -98,7 +97,7 @@ describe('Signup', () => { describe('creates a EmailAddress node', () => { it('with `createdAt` attribute', async () => { await mutate({ mutation, variables }) - const emailAddress = await neode.first( + const emailAddress = await database.neode.first( 'EmailAddress', { email: 'someuser@example.org' }, undefined, @@ -112,7 +111,7 @@ describe('Signup', () => { it('with a cryptographic `nonce`', async () => { await mutate({ mutation, variables }) - const emailAddress = await neode.first( + const emailAddress = await database.neode.first( 'EmailAddress', { email: 'someuser@example.org' }, undefined, @@ -153,12 +152,12 @@ describe('Signup', () => { it('creates no additional `EmailAddress` node', async () => { // admin account and the already existing user - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await expect(database.neode.all('EmailAddress')).resolves.toHaveLength(2) await expect(mutate({ mutation, variables })).resolves.toMatchObject({ data: { Signup: { email: 'someuser@example.org' } }, errors: undefined, }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await expect(database.neode.all('EmailAddress')).resolves.toHaveLength(2) }) }) }) @@ -194,7 +193,7 @@ describe('SignupVerification', () => { } ` describe('given valid password and email', () => { - beforeEach(async () => { + beforeEach(() => { variables = { ...variables, nonce: '12345', @@ -207,7 +206,7 @@ describe('SignupVerification', () => { }) describe('unauthenticated', () => { - beforeEach(async () => { + beforeEach(() => { authenticatedUser = null }) @@ -215,8 +214,8 @@ describe('SignupVerification', () => { beforeEach(async () => { const { email, nonce } = variables const [emailAddress, user] = await Promise.all([ - neode.model('EmailAddress').create({ email, nonce }), - neode + database.neode.model('EmailAddress').create({ email, nonce }), + database.neode .model('User') .create({ name: 'Somebody', password: '1234', email: 'john@example.org' }), ]) @@ -242,7 +241,7 @@ describe('SignupVerification', () => { email: 'john@example.org', nonce: '12345', } - await neode.model('EmailAddress').create(args) + await database.neode.model('EmailAddress').create(args) }) describe('sending a valid nonce', () => { @@ -258,7 +257,7 @@ describe('SignupVerification', () => { it('sets `verifiedAt` attribute of EmailAddress', async () => { await mutate({ mutation, variables }) - const email = await neode.first( + const email = await database.neode.first( 'EmailAddress', { email: 'john@example.org' }, undefined, @@ -276,14 +275,18 @@ describe('SignupVerification', () => { RETURN email ` await mutate({ mutation, variables }) - const { records: emails } = await neode.cypher(cypher, { name: 'John Doe' }) + const { records: emails } = await database.neode.cypher(cypher, { name: 'John Doe' }) expect(emails).toHaveLength(1) }) it('sets `about` attribute of User', async () => { variables = { ...variables, about: 'Find this description in the user profile' } await mutate({ mutation, variables }) - const user = await neode.first('User', { name: 'John Doe' }, undefined) + const user = await database.neode.first( + 'User', + { name: 'John Doe' }, + undefined, + ) await expect(user.toJson()).resolves.toMatchObject({ about: 'Find this description in the user profile', }) @@ -306,7 +309,7 @@ describe('SignupVerification', () => { RETURN email ` await mutate({ mutation, variables }) - const { records: emails } = await neode.cypher(cypher, { name: 'John Doe' }) + const { records: emails } = await database.neode.cypher(cypher, { name: 'John Doe' }) expect(emails).toHaveLength(1) }) diff --git a/backend/src/graphql/resolvers/registration.ts b/backend/src/graphql/resolvers/registration.ts index d37d3663a..de5eb962e 100644 --- a/backend/src/graphql/resolvers/registration.ts +++ b/backend/src/graphql/resolvers/registration.ts @@ -7,10 +7,12 @@ import { UserInputError } from 'apollo-server' import { hash } from 'bcryptjs' import { getNeode } from '@db/neo4j' +import { Context } from '@src/server' import existingEmailAddress from './helpers/existingEmailAddress' import generateNonce from './helpers/generateNonce' import normalizeEmail from './helpers/normalizeEmail' +import { redeemInviteCode } from './inviteCodes' const neode = getNeode() @@ -33,7 +35,7 @@ export default { throw new UserInputError(e.message) } }, - SignupVerification: async (_parent, args, context) => { + SignupVerification: async (_parent, args, context: Context) => { const { termsAndConditionsAgreedVersion } = args const regEx = /^[0-9]+\.[0-9]+\.[0-9]+$/g if (!regEx.test(termsAndConditionsAgreedVersion)) { @@ -52,14 +54,45 @@ export default { const { driver } = context const session = driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { - const createUserTransactionResponse = await transaction.run(signupCypher(inviteCode), { - args, - nonce, - email, - inviteCode, - }) + const createUserTransactionResponse = await transaction.run( + ` + MATCH (email:EmailAddress {nonce: $nonce, email: $email}) + WHERE NOT (email)-[:BELONGS_TO]->() + CREATE (user:User) + MERGE (user)-[:PRIMARY_EMAIL]->(email) + MERGE (user)<-[:BELONGS_TO]-(email) + SET user += $args + SET user.id = randomUUID() + SET user.role = 'user' + SET user.createdAt = toString(datetime()) + SET user.updatedAt = toString(datetime()) + SET user.allowEmbedIframes = false + SET user.showShoutsPublicly = false + SET email.verifiedAt = toString(datetime()) + WITH user + OPTIONAL MATCH (post:Post)-[:IN]->(group:Group) + WHERE NOT group.groupType = 'public' + WITH user, collect(post) AS invisiblePosts + FOREACH (invisiblePost IN invisiblePosts | + MERGE (user)-[:CANNOT_SEE]->(invisiblePost) + ) + RETURN user {.*} + `, + { + args, + nonce, + email, + inviteCode, + }, + ) const [user] = createUserTransactionResponse.records.map((record) => record.get('user')) if (!user) throw new UserInputError('Invalid email or nonce') + + // To allow redeeming and return an User object we require a User in the context + context.user = user + // join Group via invite Code + await redeemInviteCode(context, inviteCode, true) + return user }) try { @@ -70,51 +103,8 @@ export default { throw new UserInputError('User with this slug already exists!') throw new UserInputError(e.message) } finally { - session.close() + await session.close() } }, }, } - -const signupCypher = (inviteCode) => { - let optionalMatch = '' - let optionalMerge = '' - if (inviteCode) { - optionalMatch = ` - OPTIONAL MATCH - (inviteCode:InviteCode {code: $inviteCode})<-[:GENERATED]-(host:User) - ` - optionalMerge = ` - MERGE (user)-[:REDEEMED { createdAt: toString(datetime()) }]->(inviteCode) - MERGE (host)-[:INVITED { createdAt: toString(datetime()) }]->(user) - MERGE (user)-[:FOLLOWS { createdAt: toString(datetime()) }]->(host) - MERGE (host)-[:FOLLOWS { createdAt: toString(datetime()) }]->(user) - ` - } - const cypher = ` - MATCH (email:EmailAddress {nonce: $nonce, email: $email}) - WHERE NOT (email)-[:BELONGS_TO]->() - ${optionalMatch} - CREATE (user:User) - MERGE (user)-[:PRIMARY_EMAIL]->(email) - MERGE (user)<-[:BELONGS_TO]-(email) - ${optionalMerge} - SET user += $args - SET user.id = randomUUID() - SET user.role = 'user' - SET user.createdAt = toString(datetime()) - SET user.updatedAt = toString(datetime()) - SET user.allowEmbedIframes = false - SET user.showShoutsPublicly = false - SET email.verifiedAt = toString(datetime()) - WITH user - OPTIONAL MATCH (post:Post)-[:IN]->(group:Group) - WHERE NOT group.groupType = 'public' - WITH user, collect(post) AS invisiblePosts - FOREACH (invisiblePost IN invisiblePosts | - MERGE (user)-[:CANNOT_SEE]->(invisiblePost) - ) - RETURN user {.*} - ` - return cypher -} diff --git a/backend/src/graphql/resolvers/transactions/inviteCodes.ts b/backend/src/graphql/resolvers/transactions/inviteCodes.ts deleted file mode 100644 index 0381893ad..000000000 --- a/backend/src/graphql/resolvers/transactions/inviteCodes.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -export async function validateInviteCode(session, inviteCode) { - const readTxResultPromise = session.readTransaction(async (txc) => { - const result = await txc.run( - `MATCH (ic:InviteCode { code: toUpper($inviteCode) }) - RETURN - CASE - WHEN ic.expiresAt IS NULL THEN true - WHEN datetime(ic.expiresAt) >= datetime() THEN true - ELSE false END AS result`, - { - inviteCode, - }, - ) - return result.records.map((record) => record.get('result')) - }) - try { - const txResult = await readTxResultPromise - return !!txResult[0] - } finally { - session.close() - } -} diff --git a/backend/src/graphql/resolvers/users.ts b/backend/src/graphql/resolvers/users.ts index f549e79a3..294dd771f 100644 --- a/backend/src/graphql/resolvers/users.ts +++ b/backend/src/graphql/resolvers/users.ts @@ -10,6 +10,7 @@ import { neo4jgraphql } from 'neo4j-graphql-js' import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges' import { getNeode } from '@db/neo4j' +import { Context } from '@src/server' import { defaultTrophyBadge, defaultVerificationBadge } from './badges' import Resolver from './helpers/Resolver' @@ -467,6 +468,23 @@ export default { }, }, User: { + inviteCodes: async (_parent, _args, context: Context, _resolveInfo) => { + const { + user: { id: userId }, + } = context + + return ( + await context.database.query({ + query: ` + MATCH (user:User {id: $userId})-[:GENERATED]->(inviteCodes:InviteCode) + WHERE NOT (inviteCodes)-[:INVITES_TO]->(:Group) + RETURN inviteCodes {.*} + ORDER BY inviteCodes.createdAt ASC + `, + variables: { userId }, + }) + ).records + }, emailNotificationSettings: async (parent, _params, _context, _resolveInfo) => { return [ { diff --git a/backend/src/graphql/types/type/Group.gql b/backend/src/graphql/types/type/Group.gql index 9bcac5047..0adc7853b 100644 --- a/backend/src/graphql/types/type/Group.gql +++ b/backend/src/graphql/types/type/Group.gql @@ -43,6 +43,9 @@ type Group { posts: [Post] @relation(name: "IN", direction: "IN") isMutedByMe: Boolean! @cypher(statement: "MATCH (this) RETURN EXISTS( (this)<-[:MUTED]-(:User {id: $cypherParams.currentUserId}) )") + + "inviteCodes to this group the current user has generated" + inviteCodes: [InviteCode]! @neo4j_ignore } diff --git a/backend/src/graphql/types/type/InviteCode.gql b/backend/src/graphql/types/type/InviteCode.gql index 3293c735b..e0c83796a 100644 --- a/backend/src/graphql/types/type/InviteCode.gql +++ b/backend/src/graphql/types/type/InviteCode.gql @@ -3,16 +3,23 @@ type InviteCode { createdAt: String! generatedBy: User @relation(name: "GENERATED", direction: "IN") redeemedBy: [User] @relation(name: "REDEEMED", direction: "IN") + redeemedByCount: Int! @cypher(statement: "MATCH (this)<-[:REDEEMED]-(related:User)") expiresAt: String -} + comment: String + invitedTo: Group @neo4j_ignore + # invitedFrom: User! @neo4j_ignore # -> see generatedBy -type Mutation { - GenerateInviteCode(expiresAt: String = null): InviteCode + isValid: Boolean! @neo4j_ignore } type Query { - MyInviteCodes: [InviteCode] - isValidInviteCode(code: ID!): Boolean - getInviteCode: InviteCode + validateInviteCode(code: String!): InviteCode +} + +type Mutation { + generatePersonalInviteCode(expiresAt: String = null, comment: String = null): InviteCode! + generateGroupInviteCode(groupId: ID!, expiresAt: String = null, comment: String = null): InviteCode! + invalidateInviteCode(code: String!): InviteCode + redeemInviteCode(code: String!): Boolean! } diff --git a/backend/src/graphql/types/type/User.gql b/backend/src/graphql/types/type/User.gql index 83de35c37..7c78b38ec 100644 --- a/backend/src/graphql/types/type/User.gql +++ b/backend/src/graphql/types/type/User.gql @@ -72,9 +72,6 @@ type User { followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN") followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)") - inviteCodes: [InviteCode] @relation(name: "GENERATED", direction: "OUT") - redeemedInviteCode: InviteCode @relation(name: "REDEEMED", direction: "OUT") - # Is the currently logged in user following that user? followedByCurrentUser: Boolean! @cypher( statement: """ @@ -125,6 +122,7 @@ type User { categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT") + # Badges badgeVerification: Badge! @neo4j_ignore badgeTrophies: [Badge]! @relation(name: "REWARDED", direction: "IN") badgeTrophiesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)") @@ -132,6 +130,11 @@ type User { badgeTrophiesUnused: [Badge]! @neo4j_ignore badgeTrophiesUnusedCount: Int! @neo4j_ignore + "personal inviteCodes the user has generated" + inviteCodes: [InviteCode]! @neo4j_ignore + # inviteCodes: [InviteCode]! @relation(name: "GENERATED", direction: "OUT") + redeemedInviteCode: InviteCode @relation(name: "REDEEMED", direction: "OUT") + emotions: [EMOTED] activeCategories: [String] @cypher( diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index cc3af6bfc..558b0fdd3 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -15,6 +15,7 @@ import languages from './languages/languages' import login from './login/loginMiddleware' import notifications from './notifications/notificationsMiddleware' import orderBy from './orderByMiddleware' +// eslint-disable-next-line import/no-cycle import permissions from './permissionsMiddleware' import sentry from './sentryMiddleware' import sluggify from './sluggifyMiddleware' diff --git a/backend/src/middleware/permissionsMiddleware.spec.ts b/backend/src/middleware/permissionsMiddleware.spec.ts index e8089b7f3..f7422f59f 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.ts +++ b/backend/src/middleware/permissionsMiddleware.spec.ts @@ -1,42 +1,45 @@ -/* eslint-disable @typescript-eslint/require-await */ /* 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 { getDriver, getNeode } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' -const instance = getNeode() -const driver = getDriver() +let variables +let owner, anotherRegularUser, administrator, moderator -let query, mutate, variables -let authenticatedUser, owner, anotherRegularUser, administrator, moderator +const database = databaseContext() + +let server: ApolloServer +let authenticatedUser +let query, mutate + +beforeAll(async () => { + await cleanDatabase() + + // 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 + + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate +}) + +afterAll(() => { + void server.stop() + void database.driver.close() + database.neode.close() +}) describe('authorization', () => { - beforeAll(async () => { - await cleanDatabase() - - const { server } = createServer({ - context: () => ({ - driver, - instance, - 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() }) @@ -109,7 +112,7 @@ describe('authorization', () => { query({ query: userQuery, variables: { name: 'Owner' } }), ).resolves.toMatchObject({ errors: [{ message: 'Not Authorized!' }], - data: { User: [null] }, + data: { User: null }, }) }) }) @@ -242,7 +245,7 @@ describe('authorization', () => { }) describe('as anyone', () => { - beforeEach(async () => { + beforeEach(() => { authenticatedUser = null }) @@ -267,7 +270,7 @@ describe('authorization', () => { }) describe('as anyone with valid invite code', () => { - beforeEach(async () => { + beforeEach(() => { variables = { email: 'some@email.org', inviteCode: 'ABCDEF', @@ -287,7 +290,7 @@ describe('authorization', () => { }) describe('as anyone without valid invite', () => { - beforeEach(async () => { + beforeEach(() => { variables = { email: 'some@email.org', inviteCode: 'no valid invite code', diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 5725b2d98..1a598b972 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -9,7 +9,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 { validateInviteCode } from '@graphql/resolvers/transactions/inviteCodes' +// eslint-disable-next-line import/no-cycle +import { validateInviteCode } from '@graphql/resolvers/inviteCodes' +import { Context } from '@src/server' const debug = !!CONFIG.DEBUG const allowExternalErrors = true @@ -370,11 +372,28 @@ const noEmailFilter = rule({ const publicRegistration = rule()(() => CONFIG.PUBLIC_REGISTRATION) -const inviteRegistration = rule()(async (_parent, args, { _user, driver }) => { +const inviteRegistration = rule()(async (_parent, args, context: Context) => { if (!CONFIG.INVITE_REGISTRATION) return false const { inviteCode } = args - const session = driver.session() - return validateInviteCode(session, inviteCode) + return validateInviteCode(context, inviteCode) +}) + +const isAllowedToGenerateGroupInviteCode = rule({ + cache: 'no_cache', +})(async (_parent, args, context: Context) => { + if (!context.user) return false + + return !!( + await context.database.query({ + query: ` + MATCH (user:User{id: user.id})-[membership:MEMBER_OF]->(group:Group {id: $args.groupId}) + WHERE (group.type IN ['closed','hidden'] AND membership.role IN ['admin', 'owner']) + OR (NOT group.type IN ['closed','hidden'] AND NOT membership.role = 'pending') + RETURN count(group) as count + `, + variables: { user: context.user, args }, + }) + ).records[0].get('count') }) // Permissions @@ -399,7 +418,7 @@ export default shield( Post: allow, profilePagePosts: allow, Comment: allow, - User: or(noEmailFilter, isAdmin), + User: and(isAuthenticated, or(noEmailFilter, isAdmin)), Badge: allow, PostsEmotionsCountByEmotion: allow, PostsEmotionsByCurrentUser: isAuthenticated, @@ -408,15 +427,15 @@ export default shield( notifications: isAuthenticated, Donations: isAuthenticated, userData: isAuthenticated, - MyInviteCodes: isAuthenticated, - isValidInviteCode: allow, VerifyNonce: allow, queryLocations: isAuthenticated, availableRoles: isAdmin, - getInviteCode: isAuthenticated, // and inviteRegistration Room: isAuthenticated, Message: isAuthenticated, UnreadRooms: isAuthenticated, + + // Invite Code + validateInviteCode: allow, }, Mutation: { '*': deny, @@ -465,7 +484,13 @@ export default shield( pinPost: isAdmin, unpinPost: isAdmin, UpdateDonations: isAdmin, - GenerateInviteCode: isAuthenticated, + + // InviteCode + generatePersonalInviteCode: isAuthenticated, + generateGroupInviteCode: isAllowedToGenerateGroupInviteCode, + invalidateInviteCode: isAuthenticated, + redeemInviteCode: isAuthenticated, + switchUserRole: isAdmin, markTeaserAsViewed: allow, saveCategorySettings: isAuthenticated, @@ -480,8 +505,27 @@ export default shield( resetTrophyBadgesSelected: isAuthenticated, }, User: { + '*': isAuthenticated, + name: allow, + avatar: allow, email: or(isMyOwn, isAdmin), emailNotificationSettings: isMyOwn, + inviteCodes: isMyOwn, + }, + Group: { + '*': isAuthenticated, // TODO - only those who are allowed to see the group + avatar: allow, + name: allow, + about: allow, + groupType: allow, + }, + InviteCode: { + '*': allow, + redeemedBy: isAuthenticated, // TODO only for self generated, must be done in resolver + redeemedByCount: isAuthenticated, // TODO only for self generated, must be done in resolver + createdAt: isAuthenticated, // TODO only for self generated, must be done in resolver + expiresAt: isAuthenticated, // TODO only for self generated, must be done in resolver + comment: isAuthenticated, // TODO only for self generated, must be done in resolver }, Location: { distanceToMe: isAuthenticated, diff --git a/backend/src/middleware/slugifyMiddleware.spec.ts b/backend/src/middleware/slugifyMiddleware.spec.ts index 75a52e4cf..f40c2064a 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.ts +++ b/backend/src/middleware/slugifyMiddleware.spec.ts @@ -2,47 +2,46 @@ /* 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 Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createPostMutation } from '@graphql/queries/createPostMutation' import { signupVerificationMutation } from '@graphql/queries/signupVerificationMutation' import { updateGroupMutation } from '@graphql/queries/updateGroupMutation' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' -let authenticatedUser let variables const categoryIds = ['cat9'] -const driver = getDriver() -const neode = getNeode() const descriptionAdditional100 = ' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789' -const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, -}) +const database = databaseContext() -const { mutate } = createTestClient(server) +let server: ApolloServer +let authenticatedUser +let mutate beforeAll(async () => { await cleanDatabase() + + // 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 + + const createTestClientResult = createTestClient(server) + mutate = createTestClientResult.mutate }) -afterAll(async () => { - await cleanDatabase() - await driver.close() +afterAll(() => { + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { diff --git a/backend/src/server.ts b/backend/src/server.ts index 1f98aab2d..f56b01f34 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -19,6 +19,7 @@ import pubsubContext from '@context/pubsub' import CONFIG from './config' import schema from './graphql/schema' import decode from './jwt/decode' +// eslint-disable-next-line import/no-cycle import middleware from './middleware' const serverDatabase = databaseContext() From fbec8288b2417e6ac4f7b5b64a32b78296328f3f Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 8 May 2025 22:27:41 +0200 Subject: [PATCH 212/266] refactor(backend): category seed (#8505) * define ids and slugs in categories, check for existing ids, only seed the new ids * seed categories respecting existing categories --------- Co-authored-by: Ulf Gebhardt --- backend/Dockerfile | 2 + backend/package.json | 3 +- backend/src/constants/categories.ts | 58 +++++++++++++++++---------- backend/src/context/database.ts | 4 +- backend/src/db/categories.ts | 62 ++++++++++++++++------------- 5 files changed, 78 insertions(+), 51 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 2897fe2f6..e481da5a3 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -22,6 +22,8 @@ FROM base AS build COPY . . ONBUILD COPY ./branding/constants/ src/config/tmp ONBUILD RUN tools/replace-constants.sh +# copy categories to brand them (use yarn prod:db:data:categories) +ONBUILD COPY branding/constants/ src/constants/ ONBUILD COPY ./branding/email/ src/middleware/helpers/email/ ONBUILD COPY ./branding/middlewares/ src/middleware/branding/ ONBUILD COPY ./branding/data/ src/db/data diff --git a/backend/package.json b/backend/package.json index 38bf966ac..0cfa5a080 100644 --- a/backend/package.json +++ b/backend/package.json @@ -24,7 +24,8 @@ "db:migrate": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --store ./src/db/migrate/store.ts", "db:migrate:create": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create", "prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js", - "prod:db:data:branding": "node build/src/db/data-branding.js" + "prod:db:data:branding": "node build/src/db/data-branding.js", + "prod:db:data:categories": "node build/src/db/categories.js" }, "dependencies": { "@sentry/node": "^5.15.4", diff --git a/backend/src/constants/categories.ts b/backend/src/constants/categories.ts index 6365d268a..b6fce03ca 100644 --- a/backend/src/constants/categories.ts +++ b/backend/src/constants/categories.ts @@ -5,98 +5,116 @@ export const CATEGORIES_MAX = 3 export const categories = [ { icon: 'networking', + id: 'cat0', + slug: 'networking', name: 'networking', - description: 'Kooperation, Aktionsbündnisse, Solidarität, Hilfe', }, { icon: 'home', + id: 'cat1', + slug: 'home', name: 'home', - description: 'Bauen, Lebensgemeinschaften, Tiny Houses, Gemüsegarten', }, { icon: 'energy', + id: 'cat2', + slug: 'energy', name: 'energy', - description: 'Öl, Gas, Kohle, Wind, Wasserkraft, Biogas, Atomenergie, ...', }, { icon: 'psyche', + id: 'cat3', + slug: 'psyche', name: 'psyche', - description: 'Seele, Gefühle, Glück', }, { icon: 'movement', + id: 'cat4', + slug: 'body-and-excercise', name: 'body-and-excercise', - description: 'Sport, Yoga, Massage, Tanzen, Entspannung', }, { icon: 'balance-scale', + id: 'cat5', + slug: 'law', name: 'law', - description: 'Menschenrechte, Gesetze, Verordnungen', }, { icon: 'finance', + id: 'cat6', + slug: 'finance', name: 'finance', - description: 'Geld, Finanzsystem, Alternativwährungen, ...', }, { icon: 'child', + id: 'cat7', + slug: 'children', name: 'children', - description: 'Familie, Pädagogik, Schule, Prägung', }, { icon: 'mobility', + id: 'cat8', + slug: 'mobility', name: 'mobility', - description: 'Reise, Verkehr, Elektromobilität', }, { icon: 'shopping-cart', + id: 'cat9', + slug: 'economy', name: 'economy', - description: 'Handel, Konsum, Marketing, Lebensmittel, Lieferketten, ...', }, { icon: 'peace', + id: 'cat10', + slug: 'peace', name: 'peace', - description: 'Krieg, Militär, soziale Verteidigung, Waffen, Cyberattacken', }, { icon: 'politics', + id: 'cat11', + slug: 'politics', name: 'politics', - description: 'Demokratie, Mitbestimmung, Wahlen, Korruption, Parteien', }, { icon: 'nature', + id: 'cat12', + slug: 'nature', name: 'nature', - description: 'Tiere, Pflanzen, Landwirtschaft, Ökologie, Artenvielfalt', }, { icon: 'science', + id: 'cat13', + slug: 'science', name: 'science', - description: 'Bildung, Hochschule, Publikationen, ...', }, { icon: 'health', + id: 'cat14', + slug: 'health', name: 'health', - description: 'Medizin, Ernährung, WHO, Impfungen, Schadstoffe, ...', }, { icon: 'media', + id: 'cat15', + slug: 'it-and-media', name: 'it-and-media', - description: - 'Nachrichten, Manipulation, Datenschutz, Überwachung, Datenkraken, AI, Software, Apps', }, { icon: 'spirituality', + id: 'cat16', + slug: 'spirituality', name: 'spirituality', - description: 'Religion, Werte, Ethik', }, { icon: 'culture', + id: 'cat17', + slug: 'culture', name: 'culture', - description: 'Kunst, Theater, Musik, Fotografie, Film', }, { icon: 'miscellaneous', + id: 'cat18', + slug: 'miscellaneous', name: 'miscellaneous', - description: '', }, ] diff --git a/backend/src/context/database.ts b/backend/src/context/database.ts index c1dc244d9..dc623470d 100644 --- a/backend/src/context/database.ts +++ b/backend/src/context/database.ts @@ -4,7 +4,7 @@ import type { Driver } from 'neo4j-driver' export const query = (driver: Driver) => - async ({ query, variables = {} }: { query: string; variables: object }) => { + async ({ query, variables = {} }: { query: string; variables?: object }) => { const session = driver.session() const result = session.readTransaction(async (transaction) => { @@ -21,7 +21,7 @@ export const query = export const write = (driver: Driver) => - async ({ query, variables = {} }: { query: string; variables: object }) => { + async ({ query, variables = {} }: { query: string; variables?: object }) => { const session = driver.session() const result = session.writeTransaction(async (transaction) => { diff --git a/backend/src/db/categories.ts b/backend/src/db/categories.ts index a007b25ae..24421a400 100644 --- a/backend/src/db/categories.ts +++ b/backend/src/db/categories.ts @@ -1,38 +1,44 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/require-await */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { categories } from '@constants/categories' +import databaseContext from '@context/database' -import { getDriver } from './neo4j' +const { query, write, driver } = databaseContext() const createCategories = async () => { - const driver = getDriver() - const session = driver.session() - const createCategoriesTxResultPromise = session.writeTransaction(async (txc) => { - categories.forEach(({ icon, name }, index) => { - const id = `cat${index + 1}` - txc.run( - `MERGE (c:Category { - icon: "${icon}", - slug: "${name}", - name: "${name}", - id: "${id}", - createdAt: toString(datetime()) - })`, - ) - }) + const result = await query({ + query: 'MATCH (category:Category) RETURN category { .* }', }) - try { - await createCategoriesTxResultPromise - console.log('Successfully created categories!') // eslint-disable-line no-console - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (error) { - console.log(`Error creating categories: ${error}`) // eslint-disable-line no-console - } finally { - session.close() - driver.close() - } + + const existingCategories = result.records.map((r) => r.get('category')) + const existingCategoryIds = existingCategories.map((c) => c.id) + + const newCategories = categories.filter((c) => !existingCategoryIds.includes(c.id)) + + await write({ + query: `UNWIND $newCategories AS map + CREATE (category:Category) + SET category = map + SET category.createdAt = toString(datetime())`, + variables: { + newCategories, + }, + }) + + const categoryIds = categories.map((c) => c.id) + await write({ + query: `MATCH (category:Category) + WHERE NOT category.id IN $categoryIds + DETACH DELETE category`, + variables: { + categoryIds, + }, + }) + // eslint-disable-next-line no-console + console.log('Successfully created categories!') + await driver.close() } ;(async function () { From 989d5ff78186945c72868ad10c0efe6f333d2324 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 9 May 2025 00:57:55 +0200 Subject: [PATCH 213/266] fix(backend): invite codes - hotfix 1 (#8508) * hotfix invite codes * fix tests & ensure correct behaviour --- backend/src/graphql/queries/Group.ts | 4 + .../src/graphql/resolvers/inviteCodes.spec.ts | 81 +++++++++---------- backend/src/graphql/resolvers/inviteCodes.ts | 2 +- backend/src/graphql/resolvers/users.ts | 11 +-- 4 files changed, 45 insertions(+), 53 deletions(-) diff --git a/backend/src/graphql/queries/Group.ts b/backend/src/graphql/queries/Group.ts index ee01a9177..b6009ddc1 100644 --- a/backend/src/graphql/queries/Group.ts +++ b/backend/src/graphql/queries/Group.ts @@ -31,6 +31,10 @@ export const Group = gql` nameEN } myRole + inviteCodes { + code + redeemedByCount + } } } ` diff --git a/backend/src/graphql/resolvers/inviteCodes.spec.ts b/backend/src/graphql/resolvers/inviteCodes.spec.ts index 94829553c..d38788087 100644 --- a/backend/src/graphql/resolvers/inviteCodes.spec.ts +++ b/backend/src/graphql/resolvers/inviteCodes.spec.ts @@ -1027,12 +1027,12 @@ describe('redeemInviteCode', () => { data: { currentUser: { following: [], - inviteCodes: expect.arrayContaining([ + inviteCodes: [ { code: 'CODE33', redeemedByCount: 0, }, - ]), + ], }, }, errors: undefined, @@ -1061,20 +1061,18 @@ describe('redeemInviteCode', () => { errors: undefined, }) authenticatedUser = await invitingUser.toJson() - await expect(query({ query: currentUser })).resolves.toMatchObject({ + await expect(query({ query: Group })).resolves.toMatchObject({ data: { - currentUser: { - inviteCodes: expect.arrayContaining([ - { - code: 'GRPPBL', - redeemedByCount: 1, - }, - { - code: 'GRPHDN', - redeemedByCount: 0, - }, - ]), - }, + Group: expect.arrayContaining([ + expect.objectContaining({ + inviteCodes: expect.arrayContaining([ + { + code: 'GRPPBL', + redeemedByCount: 1, + }, + ]), + }), + ]), }, errors: undefined, }) @@ -1111,20 +1109,18 @@ describe('redeemInviteCode', () => { }, errors: undefined, }) - await expect(query({ query: currentUser })).resolves.toMatchObject({ + await expect(query({ query: Group })).resolves.toMatchObject({ data: { - currentUser: { - inviteCodes: expect.arrayContaining([ - { - code: 'GRPPBL', - redeemedByCount: 0, - }, - { - code: 'GRPHDN', - redeemedByCount: 1, - }, - ]), - }, + Group: expect.arrayContaining([ + expect.objectContaining({ + inviteCodes: expect.arrayContaining([ + { + code: 'GRPHDN', + redeemedByCount: 1, + }, + ]), + }), + ]), }, errors: undefined, }) @@ -1149,12 +1145,12 @@ describe('redeemInviteCode', () => { data: { currentUser: { following: [], - inviteCodes: expect.arrayContaining([ + inviteCodes: [ { code: 'CODE33', redeemedByCount: 0, }, - ]), + ], }, }, errors: undefined, @@ -1182,21 +1178,18 @@ describe('redeemInviteCode', () => { }, errors: undefined, }) - await expect(query({ query: currentUser })).resolves.toMatchObject({ + await expect(query({ query: Group })).resolves.toMatchObject({ data: { - currentUser: { - following: [], - inviteCodes: expect.arrayContaining([ - { - code: 'GRPPBL', - redeemedByCount: 0, - }, - { - code: 'GRPHDN', - redeemedByCount: 0, - }, - ]), - }, + Group: expect.arrayContaining([ + expect.objectContaining({ + inviteCodes: expect.arrayContaining([ + { + code: 'GRPPBL', + redeemedByCount: 0, + }, + ]), + }), + ]), }, errors: undefined, }) diff --git a/backend/src/graphql/resolvers/inviteCodes.ts b/backend/src/graphql/resolvers/inviteCodes.ts index 5d4638d1d..b17d32dd8 100644 --- a/backend/src/graphql/resolvers/inviteCodes.ts +++ b/backend/src/graphql/resolvers/inviteCodes.ts @@ -151,7 +151,7 @@ export default { await context.database.query({ query: ` MATCH (inviteCode:InviteCode)<-[:GENERATED]-(user:User {id: $user.id}) - WHERE NOT (inviteCode)-[:INVITES_TO]-(:Group) + WHERE NOT (inviteCode)-[:INVITES_TO]->(:Group) AND (inviteCode.expiresAt IS NULL OR inviteCode.expiresAt >= datetime()) RETURN toString(count(inviteCode)) as count `, diff --git a/backend/src/graphql/resolvers/users.ts b/backend/src/graphql/resolvers/users.ts index 294dd771f..ac1964beb 100644 --- a/backend/src/graphql/resolvers/users.ts +++ b/backend/src/graphql/resolvers/users.ts @@ -469,21 +469,17 @@ export default { }, User: { inviteCodes: async (_parent, _args, context: Context, _resolveInfo) => { - const { - user: { id: userId }, - } = context - return ( await context.database.query({ query: ` - MATCH (user:User {id: $userId})-[:GENERATED]->(inviteCodes:InviteCode) + MATCH (user:User {id: $user.id})-[:GENERATED]->(inviteCodes:InviteCode) WHERE NOT (inviteCodes)-[:INVITES_TO]->(:Group) RETURN inviteCodes {.*} ORDER BY inviteCodes.createdAt ASC `, - variables: { userId }, + variables: { user: context.user }, }) - ).records + ).records.map((record) => record.get('inviteCodes')) }, emailNotificationSettings: async (parent, _params, _context, _resolveInfo) => { return [ @@ -686,7 +682,6 @@ export default { shouted: '-[:SHOUTED]->(related:Post)', categories: '-[:CATEGORIZED]->(related:Category)', badgeTrophies: '<-[:REWARDED]-(related:Badge)', - inviteCodes: '-[:GENERATED]->(related:InviteCode)', }, }), }, From ac79452ce60ad0bce1308ba34c39b93b32c2cfb1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 9 May 2025 12:19:00 +0200 Subject: [PATCH 214/266] fix locales errors (german) (#8510) --- webapp/locales/de.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/locales/de.json b/webapp/locales/de.json index ea7b8aeb2..fbfaf87c0 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -109,7 +109,7 @@ "isOnline": "online", "isTyping": "tippt...", "lastSeen": "zuletzt gesehen ", - "messageDeleted": "Diese Nachricht wuerde gelöscht", + "messageDeleted": "Diese Nachricht wurde gelöscht", "messagesEmpty": "Keine Nachrichten", "newMessages": "Neue Nachrichten", "page": { @@ -269,7 +269,7 @@ "amount-clicks": "{amount} Klicks", "amount-comments": "{amount} Kommentare", "amount-shouts": "{amount} Empfehlungen", - "amount-views": "{amount} Aurufe", + "amount-views": "{amount} Aufrufe", "categories": { "infoSelectedNoOfMaxCategories": "{chosen} von {max} Themen ausgewählt" }, @@ -337,7 +337,7 @@ "inappropriatePicture": "Dieses Bild kann für einige Menschen unangemessen sein.", "languageSelectLabel": "Sprache deines Beitrags", "languageSelectText": "Sprache wählen", - "newEvent": "Erstelle einen neue Veranstaltung", + "newEvent": "Erstelle eine neue Veranstaltung", "newPost": "Erstelle einen neuen Beitrag", "success": "Gespeichert!", "teaserImage": { From 2278a9e3110d067147c9f274e7a8cab2fab13254 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 9 May 2025 13:20:56 +0200 Subject: [PATCH 215/266] fix(backend): fix registration with invite code (#8513) * fix registration with invite code * save one db call * fix --- backend/src/graphql/resolvers/registration.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolvers/registration.ts b/backend/src/graphql/resolvers/registration.ts index de5eb962e..fb8e83ec2 100644 --- a/backend/src/graphql/resolvers/registration.ts +++ b/backend/src/graphql/resolvers/registration.ts @@ -88,15 +88,18 @@ export default { const [user] = createUserTransactionResponse.records.map((record) => record.get('user')) if (!user) throw new UserInputError('Invalid email or nonce') - // To allow redeeming and return an User object we require a User in the context - context.user = user - // join Group via invite Code - await redeemInviteCode(context, inviteCode, true) - return user }) try { const user = await writeTxResultPromise + + // To allow redeeming and return an User object we require a User in the context + context.user = user + + if (inviteCode) { + await redeemInviteCode(context, inviteCode, true) + } + return user } catch (e) { if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed') From b471a8f92b19cb15464682887dc6e2631caf8250 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Fri, 9 May 2025 13:51:21 +0200 Subject: [PATCH 216/266] fix(backend): fix user profile and group links in e-mails (#8512) * Add e-mail .env settings * Fix user profile and group links * fix snapshots --------- Co-authored-by: Moriz Wahl --- backend/.env.template | 2 + .../sendChatMessageMail.spec.ts.snap | 8 +-- .../sendNotificationMail.spec.ts.snap | 62 +++++++++---------- backend/src/emails/sendEmail.ts | 8 +-- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/backend/.env.template b/backend/.env.template index 4d7ae42d2..eb710101e 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -26,6 +26,8 @@ SMTP_DKIM_PRIVATKEY= # SMTP_IGNORE_TLS=true # SMTP_USERNAME= # SMTP_PASSWORD= +# SMTP_MAX_CONNECTIONS=1 +# SMTP_MAX_MESSAGES= 10 JWT_SECRET="b/&&7b78BF&fv/Vd" JWT_EXPIRES="2y" diff --git a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap index 786bad9c0..67c141c0e 100644 --- a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap @@ -91,7 +91,7 @@ footer {

    Hello chatReceiver,

    -

    you have received a new chat message from chatSender. +

    you have received a new chat message from chatSender.

    Show Chat

    See you soon on ocelot.social!

    @@ -109,7 +109,7 @@ footer { "text": "HELLO CHATRECEIVER, you have received a new chat message from chatSender -[http://webapp:3000/user/chatSender/chatsender]. +[http://webapp:3000/profile/chatSender/chatsender]. Show Chat [http://webapp:3000/chat] @@ -218,7 +218,7 @@ footer {

    Hallo chatReceiver,

    -

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

    du hast eine neue Chat-Nachricht von chatSender erhalten.

    Chat anzeigen

    Bis bald bei ocelot.social!

    @@ -236,7 +236,7 @@ footer { "text": "HALLO CHATRECEIVER, du hast eine neue Chat-Nachricht von chatSender -[http://webapp:3000/user/chatSender/chatsender] erhalten. +[http://webapp:3000/profile/chatSender/chatsender] erhalten. Chat anzeigen [http://webapp:3000/chat] diff --git a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap index 7fcf4d833..05ec17e94 100644 --- a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -91,7 +91,7 @@ footer {

    Hello Jenny Rostock,

    -

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

    View group +

    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


    @@ -110,7 +110,7 @@ footer { 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] +View group [http://webapp:3000/groups/g1/the-group] See you soon on ocelot.social [https://ocelot.social]! @@ -217,7 +217,7 @@ footer {

    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: +

    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!

    @@ -234,9 +234,9 @@ footer { "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: +Peter Lustig [http://webapp:3000/profile/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] @@ -473,7 +473,7 @@ footer {

    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: +

    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!

    @@ -490,7 +490,7 @@ footer { "subject": "ocelot.social – Notification: Mentioned in comment", "text": "HELLO JENNY ROSTOCK, -Peter Lustig [http://webapp:3000/user/u2/peter-lustig] mentioned you in a +Peter Lustig [http://webapp:3000/profile/u2/peter-lustig] mentioned you in a comment to the post with the title “New Post”. Click on the button to view this comment: @@ -977,8 +977,8 @@ footer {

    Hello Jenny Rostock,

    -

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

    View group +

    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


    @@ -994,10 +994,10 @@ footer { "subject": "ocelot.social – Notification: User joined group", "text": "HELLO JENNY ROSTOCK, -Peter Lustig [http://webapp:3000/user/u2/peter-lustig] joined the group “The +Peter Lustig [http://webapp:3000/profile/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] +View group [http://webapp:3000/groups/g1/the-group] See you soon on ocelot.social [https://ocelot.social]! @@ -1104,8 +1104,8 @@ footer {

    Hello Jenny Rostock,

    -

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

    View group +

    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


    @@ -1121,10 +1121,10 @@ footer { "subject": "ocelot.social – Notification: User left group", "text": "HELLO JENNY ROSTOCK, -Peter Lustig [http://webapp:3000/user/u2/peter-lustig] left the group “The +Peter Lustig [http://webapp:3000/profile/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] +View group [http://webapp:3000/groups/g1/the-group] See you soon on ocelot.social [https://ocelot.social]! @@ -1231,7 +1231,7 @@ footer {

    Hallo Jenny Rostock,

    -

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

    Gruppe ansehen +

    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


    @@ -1250,7 +1250,7 @@ footer { 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] +Gruppe ansehen [http://webapp:3000/groups/g1/the-group] Bis bald bei ocelot.social [https://ocelot.social]! @@ -1357,7 +1357,7 @@ footer {

    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: +

    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!

    @@ -1374,8 +1374,8 @@ footer { "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 +Peter Lustig [http://webapp:3000/profile/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] @@ -1613,7 +1613,7 @@ footer {

    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: +

    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!

    @@ -1630,7 +1630,7 @@ footer { "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 +Peter Lustig [http://webapp:3000/profile/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: @@ -2117,8 +2117,8 @@ footer {

    Hallo Jenny Rostock,

    -

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

    Gruppe ansehen +

    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


    @@ -2134,10 +2134,10 @@ footer { "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 +Peter Lustig [http://webapp:3000/profile/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] +Gruppe ansehen [http://webapp:3000/groups/g1/the-group] Bis bald bei ocelot.social [https://ocelot.social]! @@ -2244,8 +2244,8 @@ footer {

    Hallo Jenny Rostock,

    -

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

    Gruppe ansehen +

    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


    @@ -2261,10 +2261,10 @@ footer { "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 +Peter Lustig [http://webapp:3000/profile/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] +Gruppe ansehen [http://webapp:3000/groups/g1/the-group] Bis bald bei ocelot.social [https://ocelot.social]! diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts index 580cc2f58..6c44ae5e7 100644 --- a/backend/src/emails/sendEmail.ts +++ b/backend/src/emails/sendEmail.ts @@ -115,7 +115,7 @@ export const sendNotificationMail = async (notification: any): Promise Date: Fri, 9 May 2025 14:21:18 +0200 Subject: [PATCH 217/266] feat(docu): update email snapshots (#8514) --- backend/README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/backend/README.md b/backend/README.md index 7d8bbfb15..e6a828848 100644 --- a/backend/README.md +++ b/backend/README.md @@ -87,9 +87,9 @@ A fresh database needs to be initialized and migrated. # in folder backend while database is running yarn db:migrate init # for docker environments: -docker exec backend yarn db:migrate init +docker exec ocelot-social-backend-1 yarn db:migrate init # for docker production: -docker exec backend yarn prod:migrate init +docker exec ocelot-social-backend-1 yarn prod:migrate init ``` ```sh @@ -97,9 +97,9 @@ docker exec backend yarn prod:migrate init yarn db:migrate up # for docker development: -docker exec backend yarn db:migrate up +docker exec ocelot-social-backend-1 yarn db:migrate up # for docker production -docker exec backend yarn prod:migrate up +docker exec ocelot-social-backend-1 yarn prod:migrate up ``` ### Optional Data @@ -131,7 +131,7 @@ To do so, run: yarn db:data:branding # for docker -docker exec backend yarn db:data:branding +docker exec ocelot-social-backend-1 yarn db:data:branding ``` ### Seed Data @@ -143,7 +143,7 @@ For a predefined set of test data you can seed the database with: yarn db:seed # for docker -docker exec backend yarn db:seed +docker exec ocelot-social-backend-1 yarn db:seed ``` ### Reset Data @@ -157,9 +157,9 @@ yarn db:reset yarn db:reset:withmigrations # for docker -docker exec backend yarn db:reset +docker exec ocelot-social-backend-1 yarn db:reset # or deleting the migrations as well -docker exec backend yarn db:reset:withmigrations +docker exec ocelot-social-backend-1 yarn db:reset:withmigrations # you could also wipe out your neo4j database and delete all volumes with: docker compose down -v ``` @@ -180,7 +180,7 @@ $ yarn run db:migrate:create your_data_migration # for docker # in main folder while docker compose is running -$ docker compose exec backend yarn run db:migrate:create your_data_migration +$ docker compose exec ocelot-social-backend-1 yarn run db:migrate:create your_data_migration # Edit the file in ./src/db/migrations/ ``` @@ -208,5 +208,12 @@ $ yarn run test # for docker # in main folder while docker compose is running -$ docker exec backend yarn run test +$ docker exec ocelot-social-backend-1 yarn run test +``` + +If the snapshots of the emails must be updated, you have to run the tests in docker! Otherwise the CI will fail. + +```sh +# in main folder while docker compose is running +$ docker exec ocelot-social-backend-1 yarn run test -u src/emails/ ``` From ff366a40751ef2110ff6c44751eb2737e8177d28 Mon Sep 17 00:00:00 2001 From: sebastian2357 <80636200+sebastian2357@users.noreply.github.com> Date: Fri, 9 May 2025 19:04:06 +0200 Subject: [PATCH 218/266] fix(webapp): mobile optimization (#8516) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * - optimized header - added possibility of extra mobile logo * - changed behavior of NotificationMenu link get directly open for mobile * - moved notification links to the top of the menu * - optimized chat view for mobile * - added logo branding structure * - added logo branding structure * - fixed chat height * - fixed paddings for internal pages * Fix linting * Fix linting --------- Co-authored-by: Sebastian Stein Co-authored-by: Wolfgang Huß --- backend/src/config/logos.ts | 11 +--- backend/src/config/logosBranded.ts | 32 ++++++++++ backend/src/emails/sendEmail.ts | 2 +- webapp/components/Chat/Chat.vue | 1 + webapp/components/HeaderMenu/HeaderMenu.vue | 27 +++++++-- webapp/components/Logo/Logo.vue | 59 ++++++++++++++----- .../NotificationMenu/NotificationMenu.vue | 19 +++++- .../features/InternalPage/InternalPage.vue | 13 ++++ .../_new/generic/BaseCard/BaseCard.story.js | 2 +- webapp/constants/logos.js | 23 +------- webapp/constants/logosBranded.js | 31 ++++++++++ webapp/layouts/basic.vue | 14 ++++- webapp/pages/chat.vue | 2 - 13 files changed, 177 insertions(+), 59 deletions(-) create mode 100644 backend/src/config/logosBranded.ts create mode 100644 webapp/constants/logosBranded.js diff --git a/backend/src/config/logos.ts b/backend/src/config/logos.ts index 41b83b30c..3c1db1128 100644 --- a/backend/src/config/logos.ts +++ b/backend/src/config/logos.ts @@ -1,10 +1,3 @@ -// this file is duplicated in `backend/src/config/logos` and `webapp/constants/logos.js` and replaced on rebranding +// this file is duplicated in `backend/src/config/logos.ts` and `webapp/constants/logos.js` and replaced on rebranding // this are the paths in the webapp -export default { - LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg', - LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg', - LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg', - LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg', - LOGO_PASSWORD_RESET_PATH: '/img/custom/logo-squared.svg', - LOGO_MAINTENACE_RESET_PATH: '/img/custom/logo-squared.svg', -} +export default {} diff --git a/backend/src/config/logosBranded.ts b/backend/src/config/logosBranded.ts new file mode 100644 index 000000000..3c9a85861 --- /dev/null +++ b/backend/src/config/logosBranded.ts @@ -0,0 +1,32 @@ +// this file is duplicated in `backend/src/config/logos.ts` and `webapp/constants/logos.js` and replaced on rebranding +// this are the paths in the webapp +import { merge } from 'lodash' + +import logos from '@config/logos' + +const defaultLogos = { + LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg', + LOGO_HEADER_MOBILE_PATH: '/img/custom/logo-horizontal.svg', + LOGO_HEADER_WIDTH: '130px', + LOGO_HEADER_MOBILE_WIDTH: '100px', + LOGO_HEADER_CLICK: { + // externalLink: { + // url: 'https://ocelot.social', + // target: '_blank', + // }, + externalLink: null, + internalPath: { + to: { + name: 'index', + }, + scrollTo: '.main-navigation', + }, + }, + LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg', + LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg', + LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg', + LOGO_PASSWORD_RESET_PATH: '/img/custom/logo-squared.svg', + LOGO_MAINTENACE_RESET_PATH: '/img/custom/logo-squared.svg', +} + +export default merge(defaultLogos, logos) diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts index 6c44ae5e7..c8e14d74d 100644 --- a/backend/src/emails/sendEmail.ts +++ b/backend/src/emails/sendEmail.ts @@ -11,7 +11,7 @@ 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 logosWebapp from '@config/logosBranded' import metadata from '@config/metadata' import { UserDbProperties } from '@db/types/User' diff --git a/webapp/components/Chat/Chat.vue b/webapp/components/Chat/Chat.vue index faffa45e0..3a52982a5 100644 --- a/webapp/components/Chat/Chat.vue +++ b/webapp/components/Chat/Chat.vue @@ -17,6 +17,7 @@ :loading-rooms="loadingRooms" show-files="false" show-audio="false" + :height="'calc(100dvh - 190px)'" :styles="JSON.stringify(computedChatStyle)" :show-footer="true" :responsive-breakpoint="responsiveBreakpoint" diff --git a/webapp/components/HeaderMenu/HeaderMenu.vue b/webapp/components/HeaderMenu/HeaderMenu.vue index 26e6aede7..78813f51b 100644 --- a/webapp/components/HeaderMenu/HeaderMenu.vue +++ b/webapp/components/HeaderMenu/HeaderMenu.vue @@ -136,12 +136,12 @@ -
    +
    -
    - +
    +
    @@ -284,7 +284,7 @@ import { mapGetters } from 'vuex' import isEmpty from 'lodash/isEmpty' import { SHOW_GROUP_BUTTON_IN_HEADER } from '~/constants/groups.js' import { SHOW_CONTENT_FILTER_HEADER_MENU } from '~/constants/filter.js' -import LOGOS from '~/constants/logos.js' +import LOGOS from '~/constants/logosBranded.js' import AvatarMenu from '~/components/AvatarMenu/AvatarMenu' import ChatNotificationMenu from '~/components/ChatNotificationMenu/ChatNotificationMenu' import CustomButton from '~/components/CustomButton/CustomButton' @@ -409,6 +409,25 @@ export default { flex-flow: row nowrap; align-items: center; justify-content: flex-end; + + & > div { + display: inline-flex; + + padding-right: 15px; + &:first-child { + padding-right: 10px; + } + + button { + overflow: visible; + .svg { + height: 1.8em; + } + } + } + .hamburger-button .svg { + height: 1.5em; + } } .mobile-menu { margin: 0 20px; diff --git a/webapp/components/Logo/Logo.vue b/webapp/components/Logo/Logo.vue index fee0d9140..b49bfebfb 100644 --- a/webapp/components/Logo/Logo.vue +++ b/webapp/components/Logo/Logo.vue @@ -1,29 +1,30 @@ + + diff --git a/webapp/components/_new/generic/BaseCard/BaseCard.story.js b/webapp/components/_new/generic/BaseCard/BaseCard.story.js index 928aba5e6..87c905fda 100644 --- a/webapp/components/_new/generic/BaseCard/BaseCard.story.js +++ b/webapp/components/_new/generic/BaseCard/BaseCard.story.js @@ -1,6 +1,6 @@ import { storiesOf } from '@storybook/vue' import helpers from '~/storybook/helpers' -import logos from '~/constants/logos.js' +import logos from '~/constants/logosBranded.js' import BaseCard from './BaseCard.vue' storiesOf('Generic/BaseCard', module) diff --git a/webapp/constants/logos.js b/webapp/constants/logos.js index 714e78a2c..163c2fd6a 100644 --- a/webapp/constants/logos.js +++ b/webapp/constants/logos.js @@ -1,24 +1,3 @@ // this file is duplicated in `backend/src/config/logos.js` and `webapp/constants/logos.js` and replaced on rebranding // this are the paths in the webapp -export default { - LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg', - LOGO_HEADER_WIDTH: '130px', - LOGO_HEADER_CLICK: { - // externalLink: { - // url: 'https://ocelot.social', - // target: '_blank', - // }, - externalLink: null, - internalPath: { - to: { - name: 'index', - }, - scrollTo: '.main-navigation', - }, - }, - LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg', - LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg', - LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg', - LOGO_PASSWORD_RESET_PATH: '/img/custom/logo-squared.svg', - LOGO_MAINTENACE_RESET_PATH: '/img/custom/logo-squared.svg', -} +export default {} diff --git a/webapp/constants/logosBranded.js b/webapp/constants/logosBranded.js new file mode 100644 index 000000000..25d1541a6 --- /dev/null +++ b/webapp/constants/logosBranded.js @@ -0,0 +1,31 @@ +// this file is duplicated in `backend/src/config/logos.js` and `webapp/constants/logos.js` and replaced on rebranding +// this are the paths in the webapp +import { merge } from 'lodash' +import logos from '~/constants/logos' + +const defaultLogos = { + LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg', + LOGO_HEADER_MOBILE_PATH: '/img/custom/logo-horizontal.svg', + LOGO_HEADER_WIDTH: '130px', + LOGO_HEADER_MOBILE_WIDTH: '100px', + LOGO_HEADER_CLICK: { + // externalLink: { + // url: 'https://ocelot.social', + // target: '_blank', + // }, + externalLink: null, + internalPath: { + to: { + name: 'index', + }, + scrollTo: '.main-navigation', + }, + }, + LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg', + LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg', + LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg', + LOGO_PASSWORD_RESET_PATH: '/img/custom/logo-squared.svg', + LOGO_MAINTENACE_RESET_PATH: '/img/custom/logo-squared.svg', +} + +export default merge(defaultLogos, logos) diff --git a/webapp/layouts/basic.vue b/webapp/layouts/basic.vue index 5eadb42af..ac0e63114 100644 --- a/webapp/layouts/basic.vue +++ b/webapp/layouts/basic.vue @@ -23,7 +23,7 @@
    -
    +
    @@ -61,7 +61,7 @@ export default { } - diff --git a/webapp/pages/chat.vue b/webapp/pages/chat.vue index 9f93fabcb..0bb4def38 100644 --- a/webapp/pages/chat.vue +++ b/webapp/pages/chat.vue @@ -1,12 +1,10 @@ - diff --git a/webapp/components/_new/features/Invitations/CreateInvitation.spec.js b/webapp/components/_new/features/Invitations/CreateInvitation.spec.js new file mode 100644 index 000000000..10e13d62c --- /dev/null +++ b/webapp/components/_new/features/Invitations/CreateInvitation.spec.js @@ -0,0 +1,51 @@ +import { render, screen, fireEvent } from '@testing-library/vue' + +import CreateInvitation from './CreateInvitation.vue' + +const localVue = global.localVue + +describe('CreateInvitation.vue', () => { + let wrapper + + const Wrapper = ({ isDisabled = false }) => { + return render(CreateInvitation, { + localVue, + propsData: { + isDisabled, + }, + mocks: { + $t: jest.fn((v) => v), + }, + }) + } + + it('renders', () => { + wrapper = Wrapper({}) + expect(wrapper.container).toMatchSnapshot() + }) + + it('renders with disabled button', () => { + wrapper = Wrapper({ isDisabled: true }) + expect(wrapper.container).toMatchSnapshot() + }) + + describe('when the form is submitted', () => { + beforeEach(() => { + wrapper = Wrapper({}) + }) + + it('emits generate-invite-code with empty comment', async () => { + const button = screen.getByRole('button') + await fireEvent.click(button) + expect(wrapper.emitted()['generate-invite-code']).toEqual([['']]) + }) + + it('emits generate-invite-code with comment', async () => { + const button = screen.getByRole('button') + const input = screen.getByPlaceholderText('invite-codes.comment-placeholder') + await fireEvent.update(input, 'Test comment') + await fireEvent.click(button) + expect(wrapper.emitted()['generate-invite-code']).toEqual([['Test comment']]) + }) + }) +}) diff --git a/webapp/components/_new/features/Invitations/CreateInvitation.vue b/webapp/components/_new/features/Invitations/CreateInvitation.vue new file mode 100644 index 000000000..6147ca682 --- /dev/null +++ b/webapp/components/_new/features/Invitations/CreateInvitation.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/webapp/components/_new/features/Invitations/Invitation.spec.js b/webapp/components/_new/features/Invitations/Invitation.spec.js new file mode 100644 index 000000000..dd62847ef --- /dev/null +++ b/webapp/components/_new/features/Invitations/Invitation.spec.js @@ -0,0 +1,115 @@ +import { render, screen, fireEvent } from '@testing-library/vue' +import '@testing-library/jest-dom' + +import Invitation from './Invitation.vue' + +const localVue = global.localVue + +Object.assign(navigator, { + clipboard: { + writeText: jest.fn(), + }, +}) + +const mutations = { + 'modal/SET_OPEN': jest.fn().mockResolvedValue(), +} + +describe('Invitation.vue', () => { + let wrapper + + const Wrapper = ({ wasRedeemed = false, withCopymessage = false }) => { + const propsData = { + inviteCode: { + code: 'test-invite-code', + comment: 'test-comment', + redeemedByCount: wasRedeemed ? 1 : 0, + }, + copyMessage: withCopymessage ? 'test-copy-message' : undefined, + } + return render(Invitation, { + localVue, + propsData, + mocks: { + $t: jest.fn((v) => v), + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + }, + mutations, + }) + } + + describe('when the invite code was redeemed', () => { + beforeEach(() => { + wrapper = Wrapper({ wasRedeemed: true }) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('says how many times the code was redeemed', () => { + const redeemedCount = screen.getByText('invite-codes.redeemed-count') + expect(redeemedCount).toBeInTheDocument() + }) + }) + + describe('when the invite code was not redeemed', () => { + beforeEach(() => { + wrapper = Wrapper({ wasRedeemed: false }) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + it('says it was not redeemed', () => { + const redeemedCount = screen.queryByText('invite-codes.redeemed-count-0') + expect(redeemedCount).toBeInTheDocument() + }) + }) + + describe('without copy message', () => { + beforeEach(() => { + wrapper = Wrapper({ withCopymessage: false }) + }) + + it('can copy the link', async () => { + const clipboardMock = jest.spyOn(navigator.clipboard, 'writeText').mockResolvedValue() + const copyButton = screen.getByLabelText('invite-codes.copy-code') + await fireEvent.click(copyButton) + expect(clipboardMock).toHaveBeenCalledWith( + 'http://localhost/registration?method=invite-code&inviteCode=test-invite-code', + ) + }) + }) + + describe('with copy message', () => { + beforeEach(() => { + wrapper = Wrapper({ withCopymessage: true }) + }) + + it('can copy the link with message', async () => { + const clipboardMock = jest.spyOn(navigator.clipboard, 'writeText').mockResolvedValue() + const copyButton = screen.getByLabelText('invite-codes.copy-code') + await fireEvent.click(copyButton) + expect(clipboardMock).toHaveBeenCalledWith( + 'test-copy-message http://localhost/registration?method=invite-code&inviteCode=test-invite-code', + ) + }) + }) + + describe.skip('invalidate button', () => { + beforeEach(() => { + wrapper = Wrapper({ wasRedeemed: false }) + }) + + it('opens the delete modal', async () => { + const deleteButton = screen.getByLabelText('invite-codes.invalidate') + await fireEvent.click(deleteButton) + expect(mutations['modal/SET_OPEN']).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/webapp/components/_new/features/Invitations/Invitation.vue b/webapp/components/_new/features/Invitations/Invitation.vue new file mode 100644 index 000000000..989af2e57 --- /dev/null +++ b/webapp/components/_new/features/Invitations/Invitation.vue @@ -0,0 +1,155 @@ + + + + + diff --git a/webapp/components/_new/features/Invitations/InvitationList.spec.js b/webapp/components/_new/features/Invitations/InvitationList.spec.js new file mode 100644 index 000000000..dc60bb93f --- /dev/null +++ b/webapp/components/_new/features/Invitations/InvitationList.spec.js @@ -0,0 +1,113 @@ +import { render, screen, fireEvent } from '@testing-library/vue' +import '@testing-library/jest-dom' + +import InvitationList from './InvitationList.vue' + +const localVue = global.localVue + +Object.assign(navigator, { + clipboard: { + writeText: jest.fn(), + }, +}) + +const sampleInviteCodes = [ + { + code: 'test-invite-code-1', + comment: 'test-comment', + redeemedByCount: 0, + isValid: true, + }, + { + code: 'test-invite-code-2', + comment: 'test-comment-2', + redeemedByCount: 1, + isValid: true, + }, + { + code: 'test-invite-code-3', + comment: 'test-comment-3', + redeemedByCount: 0, + isValid: false, + }, +] + +describe('InvitationList.vue', () => { + let wrapper + + const Wrapper = ({ withInviteCodes, withCopymessage = false, limit = 3 }) => { + const propsData = { + inviteCodes: withInviteCodes ? sampleInviteCodes : [], + copyMessage: withCopymessage ? 'test-copy-message' : undefined, + } + return render(InvitationList, { + localVue, + propsData, + mocks: { + $t: jest.fn((v) => v), + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + $env: { + INVITE_LINK_LIMIT: limit, + }, + }, + stubs: { + 'client-only': true, + }, + }) + } + + it('renders', () => { + wrapper = Wrapper({ withInviteCodes: true }) + expect(wrapper.container).toMatchSnapshot() + }) + + it('renders empty state', () => { + wrapper = Wrapper({ withInviteCodes: false }) + expect(wrapper.container).toMatchSnapshot() + }) + + it('does not render invalid invite codes', () => { + wrapper = Wrapper({ withInviteCodes: true }) + const invalidInviteCode = screen.queryByText('invite-codes.test-invite-code-3') + expect(invalidInviteCode).not.toBeInTheDocument() + }) + + describe('without copy message', () => { + beforeEach(() => { + wrapper = Wrapper({ withCopymessage: false, withInviteCodes: true }) + }) + + it('can copy a link', async () => { + const clipboardMock = jest.spyOn(navigator.clipboard, 'writeText').mockResolvedValue() + const copyButton = screen.getAllByLabelText('invite-codes.copy-code')[0] + await fireEvent.click(copyButton) + expect(clipboardMock).toHaveBeenCalledWith( + 'http://localhost/registration?method=invite-code&inviteCode=test-invite-code-1', + ) + }) + }) + + describe('with copy message', () => { + beforeEach(() => { + wrapper = Wrapper({ withCopymessage: true, withInviteCodes: true }) + }) + + it('can copy the link with message', async () => { + const clipboardMock = jest.spyOn(navigator.clipboard, 'writeText').mockResolvedValue() + const copyButton = screen.getAllByLabelText('invite-codes.copy-code')[0] + await fireEvent.click(copyButton) + expect(clipboardMock).toHaveBeenCalledWith( + 'test-copy-message http://localhost/registration?method=invite-code&inviteCode=test-invite-code-1', + ) + }) + }) + + it('cannot generate more than the limit of invite codes', () => { + wrapper = Wrapper({ withInviteCodes: true, limit: 2 }) + const generateButton = screen.getByLabelText('invite-codes.generate-code') + expect(generateButton).toBeDisabled() + }) +}) diff --git a/webapp/components/_new/features/Invitations/InvitationList.vue b/webapp/components/_new/features/Invitations/InvitationList.vue new file mode 100644 index 000000000..355139cd2 --- /dev/null +++ b/webapp/components/_new/features/Invitations/InvitationList.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/webapp/components/_new/features/Invitations/__snapshots__/CreateInvitation.spec.js.snap b/webapp/components/_new/features/Invitations/__snapshots__/CreateInvitation.spec.js.snap new file mode 100644 index 000000000..ac2eb0a09 --- /dev/null +++ b/webapp/components/_new/features/Invitations/__snapshots__/CreateInvitation.spec.js.snap @@ -0,0 +1,140 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CreateInvitation.vue renders 1`] = ` +
    +
    +
    + invite-codes.generate-code-explanation +
    + +
    +
    + +
    + + + +
    + + + +
    + + +
    +
    +
    +`; + +exports[`CreateInvitation.vue renders with disabled button 1`] = ` +
    +
    +
    + invite-codes.generate-code-explanation +
    + +
    +
    + +
    + + + +
    + + + +
    + + +
    +
    +
    +`; diff --git a/webapp/components/_new/features/Invitations/__snapshots__/Invitation.spec.js.snap b/webapp/components/_new/features/Invitations/__snapshots__/Invitation.spec.js.snap new file mode 100644 index 000000000..10e9e68a5 --- /dev/null +++ b/webapp/components/_new/features/Invitations/__snapshots__/Invitation.spec.js.snap @@ -0,0 +1,147 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Invitation.vue when the invite code was not redeemed renders 1`] = ` +
    +
  • +
    +
    + + test-invite-code + + + — + + + + test-comment + +
    + +
    + + + invite-codes.redeemed-count-0 + + +
    +
    + +
    + + + +
    +
  • +
    +`; + +exports[`Invitation.vue when the invite code was redeemed renders 1`] = ` +
    +
  • +
    +
    + + test-invite-code + + + — + + + + test-comment + +
    + +
    + + + invite-codes.redeemed-count + + +
    +
    + +
    + + + +
    +
  • +
    +`; diff --git a/webapp/components/_new/features/Invitations/__snapshots__/InvitationList.spec.js.snap b/webapp/components/_new/features/Invitations/__snapshots__/InvitationList.spec.js.snap new file mode 100644 index 000000000..6bad7db58 --- /dev/null +++ b/webapp/components/_new/features/Invitations/__snapshots__/InvitationList.spec.js.snap @@ -0,0 +1,296 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InvitationList.vue renders 1`] = ` +
    +
    +
      + +
    • +
      +
      + + test-invite-code-1 + + + — + + + + test-comment + +
      + +
      + + + invite-codes.redeemed-count-0 + + +
      +
      + +
      + + + +
      +
    • +
    • +
      +
      + + test-invite-code-2 + + + — + + + + test-comment-2 + +
      + +
      + + + invite-codes.redeemed-count + + +
      +
      + +
      + + + +
      +
    • +
      +
    + +
    +
    + invite-codes.generate-code-explanation +
    + +
    +
    + +
    + + + +
    + + + +
    + + +
    +
    +
    +
    +`; + +exports[`InvitationList.vue renders empty state 1`] = ` +
    +
    +
    + + invite-codes.no-links + +
    + +
    +
    + invite-codes.generate-code-explanation +
    + +
    +
    + +
    + + + +
    + + + +
    + + +
    +
    +
    +
    +`; diff --git a/webapp/config/index.js b/webapp/config/index.js index fb275a8ec..074cb0736 100644 --- a/webapp/config/index.js +++ b/webapp/config/index.js @@ -36,6 +36,8 @@ const options = { COOKIE_HTTPS_ONLY: process.env.COOKIE_HTTPS_ONLY || process.env.NODE_ENV === 'production', // ensure true in production if not set explicitly CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false, BADGES_ENABLED: process.env.BADGES_ENABLED === 'true' || false, + INVITE_LINK_LIMIT: process.env.INVITE_LINK_LIMIT || 7, + NETWORK_NAME: process.env.NETWORK_NAME || 'Ocelot.social', } const CONFIG = { diff --git a/webapp/graphql/InviteCode.js b/webapp/graphql/InviteCode.js new file mode 100644 index 000000000..99a8ee9f2 --- /dev/null +++ b/webapp/graphql/InviteCode.js @@ -0,0 +1,137 @@ +import gql from 'graphql-tag' + +export const validateInviteCode = () => gql` + query validateInviteCode($code: String!) { + validateInviteCode(code: $code) { + code + invitedTo { + groupType + name + about + avatar { + url + } + } + generatedBy { + name + avatar { + url + } + } + isValid + } + } +` + +export const generatePersonalInviteCode = () => gql` + mutation generatePersonalInviteCode($expiresAt: String, $comment: String) { + generatePersonalInviteCode(expiresAt: $expiresAt, comment: $comment) { + code + createdAt + generatedBy { + id + name + avatar { + url + } + } + redeemedBy { + id + name + avatar { + url + } + } + redeemedByCount + expiresAt + comment + invitedTo { + groupType + name + about + avatar { + url + } + } + isValid + } + } +` + +export const generateGroupInviteCode = () => gql` + mutation generateGroupInviteCode($groupId: ID!, $expiresAt: String, $comment: String) { + generateGroupInviteCode(groupId: $groupId, expiresAt: $expiresAt, comment: $comment) { + code + createdAt + generatedBy { + id + name + avatar { + url + } + } + redeemedBy { + id + name + avatar { + url + } + } + redeemedByCount + expiresAt + comment + invitedTo { + id + groupType + name + about + avatar { + url + } + } + isValid + } + } +` + +export const invalidateInviteCode = () => gql` + mutation invalidateInviteCode($code: String!) { + invalidateInviteCode(code: $code) { + code + createdAt + generatedBy { + id + name + avatar { + url + } + } + redeemedBy { + id + name + avatar { + url + } + } + redeemedByCount + expiresAt + comment + invitedTo { + id + groupType + name + about + avatar { + url + } + } + isValid + } + } +` + +export const redeemInviteCode = () => gql` + mutation redeemInviteCode($code: String!) { + redeemInviteCode(code: $code) + } +` diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 7440b5051..2f2a0ce65 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -406,6 +406,15 @@ export const currentUserQuery = gql` query { currentUser { ...user + inviteCodes { + code + isValid + redeemedBy { + id + } + comment + redeemedByCount + } badgeTrophiesSelected { id icon diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js index bd17b0484..5ce33407b 100644 --- a/webapp/graphql/groups.js +++ b/webapp/graphql/groups.js @@ -195,6 +195,16 @@ export const groupQuery = (i18n) => { lat } myRole + inviteCodes { + createdAt + code + isValid + redeemedBy { + id + } + comment + redeemedByCount + } } } ` diff --git a/webapp/locales/de.json b/webapp/locales/de.json index fbfaf87c0..ac58891c4 100644 --- a/webapp/locales/de.json +++ b/webapp/locales/de.json @@ -244,7 +244,10 @@ "length": "muss genau {inviteCodeLength} Buchstaben lang sein", "success": "Gültiger Einladungs-Code {inviteCode}!" } - } + }, + "invited-by": "Eingeladen von {invitedBy}", + "invited-by-and-to": "Einladung von {invitedBy} zur Grupppe {invitedTo}", + "invited-to-hidden-group": "Eingeladen von {invitedBy} zu einer versteckten Gruppe" }, "no-public-registrstion": { "title": "Keine öffentliche Registrierung möglich" @@ -509,6 +512,7 @@ "categoriesTitle": "Themen der Gruppe", "changeMemberRole": "Die Rolle wurde auf „{role}“ geändert!", "contentMenu": { + "inviteLinks": "Einladungslinks", "muteGroup": "Stummschalten", "unmuteGroup": "Nicht stummschalten", "visitGroupPage": "Gruppe anzeigen" @@ -531,6 +535,7 @@ "goal": "Ziel der Gruppe", "groupCreated": "Die Gruppe wurde angelegt!", "in": "in", + "invite-links": "Einladungslinks", "joinLeaveButton": { "iAmMember": "Bin Mitglied", "join": "Beitreten", @@ -628,10 +633,29 @@ "button": { "tooltip": "Freunde einladen" }, + "comment-placeholder": "Kommentar (optional)", "copy-code": "Einladungslink kopieren", "copy-success": "Einladungscode erfolgreich in die Zwischenablage kopiert", - "not-available": "Du hast keinen Einladungscode zur Verfügung!", - "your-code": "Sende diesen Link per E-Mail oder in sozialen Medien, um deine Freunde einzuladen:" + "create-error": "Einladungslink konnte nicht erstellt werden: {error}", + "create-success": "Einladungslink erfolgreich erstellt!", + "delete-modal": { + "message": "Möchtest du diesen Einladungslink wirklich ungültig machen?", + "title": "Einladungslink widerrufen" + }, + "generate-code": "Neuen Einladungslink erstellen", + "generate-code-explanation": "Erstelle einen neuen Link. Wenn du möchtest, füge einen Kommentar hinzu (nur für dich sichtbar). ", + "group-invite-links": "Gruppen-Einladungslinks", + "invalidate": "Widerrufen", + "invalidate-error": "Einladungslink konnte nicht ungültig gemacht werden: {error}", + "invalidate-success": "Einladungslink erfolgreich widerrufen", + "invite-link-message-group": "Du wurdest eingeladen, der Gruppe {groupName} auf {network} beizutreten.", + "invite-link-message-hidden-group": "Du wurdest eingeladen, einer versteckten Gruppe auf {network} beizutreten.", + "invite-link-message-personal": "Du wurdest eingeladen, dem Netzwerk {network} beizutreten", + "limit-reached": "Du hast die maximale Anzahl an Einladungslinks erreicht.", + "my-invite-links": "Meine Einladungslinks", + "no-links": "Keine Links vorhanden", + "redeemed-count": "{count} mal eingelöst", + "redeemed-count-0": "Noch von niemandem eingelöst" }, "localeSwitch": { "tooltip": "Sprache wählen" diff --git a/webapp/locales/en.json b/webapp/locales/en.json index ab34ba66a..33c3abfd9 100644 --- a/webapp/locales/en.json +++ b/webapp/locales/en.json @@ -244,7 +244,10 @@ "length": "must be {inviteCodeLength} characters long", "success": "Valid invite code {inviteCode}!" } - } + }, + "invited-by": "Invited by {invitedBy}.", + "invited-by-and-to": "Invited by {invitedBy} to group {invitedTo}.", + "invited-to-hidden-group": "Invited by {invitedBy} to a hidden group." }, "no-public-registrstion": { "title": "No Public Registration" @@ -509,6 +512,7 @@ "categoriesTitle": "Topics of the group", "changeMemberRole": "The role has been changed to “{role}”!", "contentMenu": { + "inviteLinks": "Invite links", "muteGroup": "Mute group", "unmuteGroup": "Unmute group", "visitGroupPage": "Show group" @@ -531,6 +535,7 @@ "goal": "Goal of group", "groupCreated": "The group was created!", "in": "in", + "invite-links": "Invite Links", "joinLeaveButton": { "iAmMember": "I'm a member", "join": "Join", @@ -628,10 +633,29 @@ "button": { "tooltip": "Invite friends" }, + "comment-placeholder": "Comment (optional)", "copy-code": "Copy Invite Link", "copy-success": "Invite code copied to clipboard", - "not-available": "You have no valid invite code available!", - "your-code": "Send this link per e-mail or in social media to invite your friends:" + "create-error": "Creating a new invite link failed! Error: {error}", + "create-success": "Invite link created successfully!", + "delete-modal": { + "message": "Do you really want to invalidate this invite link?", + "title": "Invalidate link?" + }, + "generate-code": "Create new link", + "generate-code-explanation": "Create a new link. You can add a comment if you like (only visible to you).", + "group-invite-links": "Group invite links", + "invalidate": "Invalidate link", + "invalidate-error": "Invalidating the invite link failed! Error: {error}", + "invalidate-success": "Invite link invalidated successfully!", + "invite-link-message-group": "You have been invited to join the group “{groupName}” on {network}.", + "invite-link-message-hidden-group": "You have been invited to join a hidden group on {network}.", + "invite-link-message-personal": "You have been invited to join {network}.", + "limit-reached": "You have reached the maximum number of invite links.", + "my-invite-links": "My invite links", + "no-links": "No invite links created yet.", + "redeemed-count": "This code has been used {count} times.", + "redeemed-count-0": "No one has used this code yet." }, "localeSwitch": { "tooltip": "Choose language" diff --git a/webapp/locales/es.json b/webapp/locales/es.json index b7d95d11c..3c4d06403 100644 --- a/webapp/locales/es.json +++ b/webapp/locales/es.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": "Silenciar grupo", "unmuteGroup": "Desactivar silencio del grupo", "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json index 37a182c28..583726c52 100644 --- a/webapp/locales/fr.json +++ b/webapp/locales/fr.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": null, "unmuteGroup": null, "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/locales/it.json b/webapp/locales/it.json index 6b686502c..2896c2c1f 100644 --- a/webapp/locales/it.json +++ b/webapp/locales/it.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": null, "unmuteGroup": null, "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json index 714ed2b01..e4ae47dc4 100644 --- a/webapp/locales/nl.json +++ b/webapp/locales/nl.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": null, "unmuteGroup": null, "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json index 61a6acf24..7783ef93d 100644 --- a/webapp/locales/pl.json +++ b/webapp/locales/pl.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": null, "unmuteGroup": null, "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json index 80172daa3..7cd7b2a4b 100644 --- a/webapp/locales/pt.json +++ b/webapp/locales/pt.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": null, "unmuteGroup": null, "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json index f7956755c..eb365dcdb 100644 --- a/webapp/locales/ru.json +++ b/webapp/locales/ru.json @@ -244,7 +244,10 @@ "length": null, "success": null } - } + }, + "invited-by": null, + "invited-by-and-to": null, + "invited-to-hidden-group": null }, "no-public-registrstion": { "title": null @@ -509,6 +512,7 @@ "categoriesTitle": null, "changeMemberRole": null, "contentMenu": { + "inviteLinks": null, "muteGroup": null, "unmuteGroup": null, "visitGroupPage": null @@ -531,6 +535,7 @@ "goal": null, "groupCreated": null, "in": null, + "invite-links": null, "joinLeaveButton": { "iAmMember": null, "join": null, @@ -628,10 +633,29 @@ "button": { "tooltip": null }, + "comment-placeholder": null, "copy-code": null, "copy-success": null, - "not-available": null, - "your-code": null + "create-error": null, + "create-success": null, + "delete-modal": { + "message": null, + "title": null + }, + "generate-code": null, + "generate-code-explanation": null, + "group-invite-links": null, + "invalidate": null, + "invalidate-error": null, + "invalidate-success": null, + "invite-link-message-group": null, + "invite-link-message-hidden-group": null, + "invite-link-message-personal": null, + "limit-reached": null, + "my-invite-links": null, + "no-links": null, + "redeemed-count": null, + "redeemed-count-0": null }, "localeSwitch": { "tooltip": null diff --git a/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap b/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap index 68c2b50ba..9c87ae4fd 100644 --- a/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap +++ b/webapp/pages/groups/_id/__snapshots__/_slug.spec.js.snap @@ -151,6 +151,27 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a close + + + +
  • + + + + + + group.contentMenu.inviteLinks + + + +
  • @@ -3009,6 +3030,27 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a curre + + + +
  • + + + + + + group.contentMenu.inviteLinks + + + +
  • @@ -6489,6 +6531,27 @@ exports[`GroupProfileSlug given a puplic group – "yoga-practice" given a hidde + + + +
  • + + + + + + group.contentMenu.inviteLinks + + + +
  • diff --git a/webapp/pages/groups/edit/_id.vue b/webapp/pages/groups/edit/_id.vue index 57c7d9f6a..66eecd364 100644 --- a/webapp/pages/groups/edit/_id.vue +++ b/webapp/pages/groups/edit/_id.vue @@ -13,7 +13,7 @@ - + @@ -39,9 +39,18 @@ export default { name: this.$t('group.members'), path: `/groups/edit/${this.group.id}/members`, }, + { + name: this.$t('group.invite-links'), + path: `/groups/edit/${this.group.id}/invites`, + }, ] }, }, + data() { + return { + group: {}, + } + }, async asyncData(context) { const { app, @@ -62,5 +71,10 @@ export default { } return { group } }, + methods: { + updateInviteCodes(inviteCodes) { + this.group.inviteCodes = inviteCodes + }, + }, } diff --git a/webapp/pages/groups/edit/_id/__snapshots__/invites.spec.js.snap b/webapp/pages/groups/edit/_id/__snapshots__/invites.spec.js.snap new file mode 100644 index 000000000..2d1155691 --- /dev/null +++ b/webapp/pages/groups/edit/_id/__snapshots__/invites.spec.js.snap @@ -0,0 +1,167 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`invites.vue renders 1`] = ` +
    +
    +
    +

    + invite-codes.group-invite-links +

    + +
    + +
    +
      + +
    • +
      +
      + + INVITE1 + + + — + + + + Test invite 1 + +
      + +
      + + + invite-codes.redeemed-count-0 + + +
      +
      + +
      + + + +
      +
    • +
      +
    + +
    +
    + invite-codes.generate-code-explanation +
    + +
    +
    + +
    + + + +
    + + + +
    + + +
    +
    +
    + + +
    +
    +
    +`; diff --git a/webapp/pages/groups/edit/_id/invites.spec.js b/webapp/pages/groups/edit/_id/invites.spec.js new file mode 100644 index 000000000..8c163a4e9 --- /dev/null +++ b/webapp/pages/groups/edit/_id/invites.spec.js @@ -0,0 +1,86 @@ +import { render, screen, fireEvent } from '@testing-library/vue' + +import invites from './invites.vue' + +const localVue = global.localVue + +describe('invites.vue', () => { + let wrapper + let mocks + + beforeEach(() => { + mocks = { + $t: jest.fn((v) => v), + $apollo: { + mutate: jest.fn(), + }, + $env: { + NETWORK_NAME: 'test-network', + INVITE_LINK_LIMIT: 5, + }, + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + localVue, + } + }) + + const Wrapper = () => { + return render(invites, { + localVue, + propsData: { + group: { + id: 'group1', + name: 'Group 1', + inviteCodes: [ + { + code: 'INVITE1', + comment: 'Test invite 1', + redeemedByCount: 0, + isValid: true, + }, + { + code: 'INVITE2', + comment: 'Test invite 2', + redeemedByCount: 1, + isValid: false, + }, + ], + }, + }, + mocks, + stubs: { + 'client-only': true, + }, + }) + } + + it('renders', () => { + wrapper = Wrapper() + expect(wrapper.container).toMatchSnapshot() + }) + + describe('when a new invite code is generated', () => { + beforeEach(async () => { + wrapper = Wrapper() + const createButton = screen.getByLabelText('invite-codes.generate-code') + await fireEvent.click(createButton) + }) + + it('calls the mutation to generate a new invite code', () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledWith({ + mutation: expect.anything(), + update: expect.anything(), + variables: { + groupId: 'group1', + comment: '', + }, + }) + }) + + it('shows a success message', () => { + expect(mocks.$toast.success).toHaveBeenCalledWith('invite-codes.create-success') + }) + }) +}) diff --git a/webapp/pages/groups/edit/_id/invites.vue b/webapp/pages/groups/edit/_id/invites.vue new file mode 100644 index 000000000..a19cdbf40 --- /dev/null +++ b/webapp/pages/groups/edit/_id/invites.vue @@ -0,0 +1,81 @@ + + + diff --git a/webapp/store/auth.js b/webapp/store/auth.js index 4ef63e3ea..0633c6e1e 100644 --- a/webapp/store/auth.js +++ b/webapp/store/auth.js @@ -18,6 +18,9 @@ export const mutations = { SET_USER(state, user) { state.user = user || null }, + SET_USER_PARTIAL(state, user) { + state.user = { ...state.user, ...user } + }, SET_TOKEN(state, token) { state.token = token || null }, From 4e4eff8dc9705a692fe18f19c0f5652eb1a71d03 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 10 May 2025 11:22:51 +0200 Subject: [PATCH 224/266] fix(webapp): fix layout break and hidden group name appearance (#8538) Fixes long comment overflow. There is some underlying problem with flex box and overflows. A better solution could be to use a grid, but this was the fastest I would come up with. Fixes hidden group name appearance --- webapp/components/InviteButton/InviteButton.vue | 1 + webapp/components/_new/features/Invitations/Invitation.vue | 1 + webapp/pages/groups/edit/_id/invites.vue | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/webapp/components/InviteButton/InviteButton.vue b/webapp/components/InviteButton/InviteButton.vue index 3eea98f74..473116c1b 100644 --- a/webapp/components/InviteButton/InviteButton.vue +++ b/webapp/components/InviteButton/InviteButton.vue @@ -114,5 +114,6 @@ export default { display: flex; flex-flow: column; gap: $space-small; + --invitation-column-max-width: 75%; } diff --git a/webapp/components/_new/features/Invitations/Invitation.vue b/webapp/components/_new/features/Invitations/Invitation.vue index 989af2e57..f34beeb58 100644 --- a/webapp/components/_new/features/Invitations/Invitation.vue +++ b/webapp/components/_new/features/Invitations/Invitation.vue @@ -131,6 +131,7 @@ export default { display: flex; flex-flow: column; gap: $space-xx-small; + max-width: var(--invitation-column-max-width, 100%); } .code { diff --git a/webapp/pages/groups/edit/_id/invites.vue b/webapp/pages/groups/edit/_id/invites.vue index a19cdbf40..5181c21f1 100644 --- a/webapp/pages/groups/edit/_id/invites.vue +++ b/webapp/pages/groups/edit/_id/invites.vue @@ -8,8 +8,8 @@ @invalidate-invite-code="invalidateInviteCode" :inviteCodes="group.inviteCodes" :copy-message=" - group.type === 'hidden' - ? $T('invite-codes.invite-link-message-hidden-group', { + group.groupType === 'hidden' + ? $t('invite-codes.invite-link-message-hidden-group', { network: $env.NETWORK_NAME, }) : $t('invite-codes.invite-link-message-group', { From d4a96946574534a420f771fccc0023e1da4039d2 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Sat, 10 May 2025 12:10:55 +0200 Subject: [PATCH 225/266] feat(webapp): redirect to group after registration with invite to group (#8540) --- backend/src/middleware/permissionsMiddleware.ts | 1 + .../Registration/RegistrationSlideCreate.vue | 12 +++++++++++- webapp/graphql/InviteCode.js | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 1a598b972..a775e2fe3 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -514,6 +514,7 @@ export default shield( }, Group: { '*': isAuthenticated, // TODO - only those who are allowed to see the group + slug: allow, avatar: allow, name: allow, about: allow, diff --git a/webapp/components/Registration/RegistrationSlideCreate.vue b/webapp/components/Registration/RegistrationSlideCreate.vue index 141db1c4a..9aab5d77a 100644 --- a/webapp/components/Registration/RegistrationSlideCreate.vue +++ b/webapp/components/Registration/RegistrationSlideCreate.vue @@ -269,7 +269,17 @@ export default { setTimeout(async () => { await this.$store.dispatch('auth/login', { email, password }) this.$toast.success(this.$t('login.success')) - this.$router.push('/') + const { validateInviteCode } = this.sliderData.sliders[0].data.response + if ( + validateInviteCode && + validateInviteCode.invitedTo && + validateInviteCode.invitedTo.groupType === 'public' + ) { + const { invitedTo } = validateInviteCode + this.$router.push(`/groups/${invitedTo.slug}`) + } else { + this.$router.push('/') + } this.sliderData.setSliderValuesCallback(null, { sliderSettings: { buttonLoading: false }, }) diff --git a/webapp/graphql/InviteCode.js b/webapp/graphql/InviteCode.js index 99a8ee9f2..10981327d 100644 --- a/webapp/graphql/InviteCode.js +++ b/webapp/graphql/InviteCode.js @@ -5,6 +5,7 @@ export const validateInviteCode = () => gql` validateInviteCode(code: $code) { code invitedTo { + slug groupType name about From be0a5c555ef9d4ca6abb326cff9463239f6b805c Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 10 May 2025 12:38:33 +0200 Subject: [PATCH 226/266] Show invititation dropdown until user clicks somewhere else (#8539) --- webapp/components/Dropdown.vue | 5 ++--- webapp/components/InviteButton/InviteButton.vue | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/webapp/components/Dropdown.vue b/webapp/components/Dropdown.vue index dd2b4a822..7b1aa4f80 100644 --- a/webapp/components/Dropdown.vue +++ b/webapp/components/Dropdown.vue @@ -29,11 +29,11 @@ export default { placement: { type: String, default: 'bottom-end' }, disabled: { type: Boolean, default: false }, offset: { type: [String, Number], default: '16' }, + noMouseLeaveClosing: { type: Boolean, default: false }, }, data() { return { isPopoverOpen: false, - developerNoAutoClosing: false, // stops automatic closing of menu for developer purposes: default is 'false' } }, computed: { @@ -94,8 +94,7 @@ export default { } }, popoverMouseLeave() { - if (this.developerNoAutoClosing) return - if (this.disabled) { + if (this.noMouseLeaveClosing || this.disabled) { return } this.clearTimeouts() diff --git a/webapp/components/InviteButton/InviteButton.vue b/webapp/components/InviteButton/InviteButton.vue index 473116c1b..51d8c3ff6 100644 --- a/webapp/components/InviteButton/InviteButton.vue +++ b/webapp/components/InviteButton/InviteButton.vue @@ -1,5 +1,5 @@