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 001/227] 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 002/227] 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 003/227] 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 004/227] 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 005/227] 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 006/227] 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 007/227] 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 008/227] 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 009/227] 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 010/227] 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 011/227] 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 012/227] 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 013/227] 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 014/227] 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 072/227] 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 073/227] 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 074/227] 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 075/227] 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 076/227] 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 077/227] 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 078/227] 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 079/227] 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 080/227] 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 081/227] 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 082/227] 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 083/227] 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 084/227] 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 085/227] 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 086/227] 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 087/227] 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 088/227] 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 089/227] 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 090/227] 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 091/227] 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 092/227] 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 093/227] 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 094/227] 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 095/227] 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 101/227] 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 102/227] 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 103/227] 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 104/227] 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 105/227] 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 106/227] 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 107/227] 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 108/227] 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 109/227] 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 110/227] 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 111/227] 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 112/227] 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 113/227] 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 114/227] 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 115/227] 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 116/227] 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 117/227] 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 118/227] 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 119/227] 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 120/227] 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 121/227] 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 122/227] 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 123/227] 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 124/227] 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 125/227] 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 126/227] 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 127/227] 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 128/227] 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 129/227] 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 130/227] 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 131/227] 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 132/227] 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 133/227] 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 134/227] 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 135/227] 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 136/227] 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 137/227] 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 138/227] 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 139/227] 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 140/227] 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 141/227] 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 142/227] 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 148/227] 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 149/227] 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 150/227] 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 @@