From 2a7c78c12a5d1ef269fb3e7ce7497e3e06971d76 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Sat, 28 Jan 2023 02:15:26 +0100 Subject: [PATCH] Multiple Tabs: Introduce Multiple Tabs Support (#2221) --- .eslintrc | 8 +- dev/eslint-multitab/.eslintrc.js | 19 + dev/eslint-multitab/lib/index.js | 22 + ...must-specify-action-handler-return-type.js | 39 + .../rules/must-update-global-after-await.js | 108 + .../lib/rules/no-getactions-in-actions.js | 40 + .../lib/rules/no-immediate-global.js | 42 + .../lib/rules/set-global-only-variable.js | 53 + dev/eslint-multitab/package-lock.json | 3647 +++++++++++++++++ dev/eslint-multitab/package.json | 39 + package-lock.json | 924 +++++ package.json | 4 +- src/App.tsx | 48 +- src/api/gramjs/index.ts | 8 +- src/api/gramjs/localDb.ts | 114 +- src/api/gramjs/methods/chats.ts | 2 +- src/api/gramjs/methods/client.ts | 4 +- src/api/gramjs/methods/index.ts | 4 + src/api/gramjs/methods/management.ts | 4 +- src/api/gramjs/methods/media.ts | 14 +- src/api/gramjs/methods/messages.ts | 2 +- src/api/gramjs/methods/settings.ts | 4 +- src/api/gramjs/provider.ts | 6 +- src/api/gramjs/worker/provider.ts | 202 +- src/api/gramjs/worker/types.ts | 3 +- src/api/gramjs/worker/worker.ts | 2 +- src/api/types/misc.ts | 4 +- src/bundles/main.ts | 5 +- src/components/auth/Auth.tsx | 30 +- src/components/auth/AuthCode.tsx | 6 +- src/components/auth/AuthPhoneNumber.tsx | 6 +- src/components/auth/AuthQrCode.tsx | 6 +- src/components/calls/ActiveCallHeader.tsx | 14 +- src/components/calls/group/GroupCall.tsx | 9 +- .../calls/group/GroupCallParticipantList.tsx | 14 +- .../calls/group/GroupCallParticipantMenu.tsx | 6 +- .../calls/group/GroupCallTopPane.tsx | 11 +- src/components/calls/phone/PhoneCall.tsx | 10 +- src/components/common/ChatExtra.tsx | 2 +- src/components/common/DeleteChatModal.tsx | 4 +- src/components/common/GroupCallLink.tsx | 6 +- src/components/common/PinMessageModal.tsx | 9 +- src/components/common/ProfileInfo.tsx | 3 +- src/components/common/ReportModal.tsx | 2 +- src/components/common/SeenByModal.tsx | 10 +- src/components/common/StickerSetModal.tsx | 2 +- src/components/common/UiLoader.tsx | 19 +- .../common/hooks/useAnimatedEmoji.ts | 14 +- src/components/left/LeftColumn.tsx | 27 +- src/components/left/main/Chat.tsx | 4 +- src/components/left/main/ChatFolders.tsx | 26 +- src/components/left/main/ForumPanel.tsx | 6 +- src/components/left/main/LeftMainHeader.tsx | 13 +- src/components/left/newChat/NewChatStep1.tsx | 3 +- src/components/left/newChat/NewChatStep2.tsx | 3 +- src/components/left/search/AudioResults.tsx | 5 +- .../left/search/ChatMessageResults.tsx | 8 +- src/components/left/search/ChatResults.tsx | 6 +- src/components/left/search/FileResults.tsx | 5 +- src/components/left/search/LeftSearch.tsx | 3 +- src/components/left/search/LinkResults.tsx | 5 +- src/components/left/search/MediaResults.tsx | 5 +- src/components/left/search/RecentContacts.tsx | 8 +- .../search/helpers/createMapStateToProps.ts | 7 +- .../left/settings/BlockUserModal.tsx | 3 +- .../left/settings/SettingsEditProfile.tsx | 6 +- .../left/settings/SettingsGeneral.tsx | 8 +- .../settings/SettingsGeneralBackground.tsx | 8 +- .../SettingsGeneralBackgroundColor.tsx | 4 +- .../left/settings/SettingsHeader.tsx | 2 +- .../left/settings/SettingsLanguage.tsx | 2 +- .../left/settings/SettingsPrivacy.tsx | 6 +- .../settings/SettingsPrivacyVisibility.tsx | 2 +- ...SettingsPrivacyVisibilityExceptionList.tsx | 2 +- .../settings/folders/SettingsFoldersEdit.tsx | 6 +- .../left/settings/twoFa/SettingsTwoFa.tsx | 2 +- .../main/AttachBotRecipientPicker.tsx | 4 +- src/components/main/ConfettiContainer.tsx | 7 +- src/components/main/DeleteFolderDialog.tsx | 2 +- src/components/main/Dialogs.tsx | 5 +- src/components/main/DownloadManager.tsx | 3 +- src/components/main/DraftRecipientPicker.tsx | 4 +- .../main/ForwardRecipientPicker.tsx | 3 +- src/components/main/GameModal.tsx | 4 +- src/components/main/HistoryCalendar.tsx | 3 +- src/components/main/LockScreen.tsx | 2 +- src/components/main/Main.tsx | 160 +- src/components/main/Notifications.tsx | 3 +- src/components/main/UrlAuthModal.tsx | 4 +- src/components/main/WebAppModal.tsx | 12 +- .../main/premium/GiftPremiumModal.tsx | 4 +- .../main/premium/PremiumMainModal.tsx | 21 +- src/components/mediaViewer/MediaViewer.tsx | 15 +- .../mediaViewer/MediaViewerContent.tsx | 4 +- src/components/mediaViewer/SenderInfo.tsx | 4 +- src/components/mediaViewer/VideoPlayer.tsx | 4 +- src/components/middle/ActionMessage.tsx | 6 +- .../middle/ActionMessageSuggestedAvatar.tsx | 11 +- src/components/middle/AudioPlayer.tsx | 4 +- src/components/middle/ChatReportPanel.tsx | 6 +- .../middle/DeleteSelectedMessageModal.tsx | 12 +- src/components/middle/HeaderActions.tsx | 4 +- src/components/middle/HeaderMenuContainer.tsx | 21 +- src/components/middle/MessageList.tsx | 8 +- .../middle/MessageListHistoryHandler.tsx | 10 +- .../middle/MessageSelectToolbar.tsx | 22 +- src/components/middle/MiddleColumn.tsx | 28 +- src/components/middle/MiddleHeader.tsx | 14 +- src/components/middle/MobileSearch.tsx | 10 +- src/components/middle/ReactorListModal.tsx | 8 +- .../middle/composer/BotCommandMenu.tsx | 1 - .../middle/composer/BotCommandTooltip.tsx | 1 - src/components/middle/composer/Composer.tsx | 36 +- .../composer/ComposerEmbeddedMessage.tsx | 19 +- src/components/middle/composer/SendAsMenu.tsx | 9 +- src/components/middle/composer/SymbolMenu.tsx | 4 +- .../middle/composer/WebPagePreview.tsx | 4 +- .../composer/helpers/buildAttachment.ts | 4 +- .../middle/composer/hooks/useEditing.ts | 1 - .../composer/hooks/useInlineBotTooltip.ts | 6 +- .../middle/message/ContextMenuContainer.tsx | 6 +- src/components/middle/message/Message.tsx | 10 +- .../middle/message/MessagePhoneCall.tsx | 6 +- .../middle/message/SponsoredMessage.tsx | 2 +- src/components/middle/message/Sticker.tsx | 12 +- .../middle/message/hooks/useInnerHandlers.ts | 10 +- src/components/payment/PasswordConfirm.tsx | 6 +- src/components/payment/PaymentModal.tsx | 28 +- src/components/payment/ReceiptModal.tsx | 3 +- src/components/right/AddChatMembers.tsx | 7 +- src/components/right/CreateTopic.tsx | 8 +- src/components/right/DeleteMemberModal.tsx | 2 +- src/components/right/EditTopic.tsx | 8 +- src/components/right/GifSearch.tsx | 6 +- src/components/right/PollAnswerResults.tsx | 3 +- src/components/right/PollResults.tsx | 6 +- src/components/right/Profile.tsx | 4 +- src/components/right/RightColumn.tsx | 17 +- src/components/right/RightHeader.tsx | 22 +- src/components/right/RightSearch.tsx | 8 +- src/components/right/StickerSearch.tsx | 4 +- .../right/management/ManageChannel.tsx | 7 +- .../management/ManageChatPrivacyType.tsx | 6 +- .../right/management/ManageDiscussion.tsx | 2 +- .../right/management/ManageGroup.tsx | 9 +- .../right/management/ManageGroupMembers.tsx | 4 +- .../right/management/ManageInvite.tsx | 5 +- .../right/management/ManageInviteInfo.tsx | 4 +- .../right/management/ManageInvites.tsx | 4 +- .../right/management/ManageUser.tsx | 4 +- .../right/statistics/MessageStatistics.tsx | 7 +- .../statistics/StatisticsPublicForward.tsx | 2 +- src/components/test/TestNoRedundancy.tsx | 1 + src/components/ui/Notification.tsx | 17 +- src/config.ts | 3 + src/global/actions/api/accounts.ts | 68 +- src/global/actions/api/bots.ts | 420 +- src/global/actions/api/calls.async.ts | 116 +- src/global/actions/api/chats.ts | 1089 ++--- src/global/actions/api/globalSearch.ts | 121 +- src/global/actions/api/initial.ts | 78 +- src/global/actions/api/localSearch.ts | 151 +- src/global/actions/api/management.ts | 184 +- src/global/actions/api/messages.ts | 595 +-- src/global/actions/api/payments.ts | 256 +- src/global/actions/api/reactions.ts | 153 +- src/global/actions/api/settings.ts | 277 +- src/global/actions/api/statistics.ts | 23 +- src/global/actions/api/symbols.ts | 456 ++- src/global/actions/api/sync.ts | 172 +- src/global/actions/api/twoFaSettings.ts | 43 +- src/global/actions/api/users.ts | 253 +- src/global/actions/apiUpdaters/calls.async.ts | 35 +- src/global/actions/apiUpdaters/calls.ts | 26 +- src/global/actions/apiUpdaters/chats.ts | 55 +- src/global/actions/apiUpdaters/initial.ts | 133 +- src/global/actions/apiUpdaters/messages.ts | 227 +- src/global/actions/apiUpdaters/misc.ts | 37 +- src/global/actions/apiUpdaters/payments.ts | 64 +- src/global/actions/apiUpdaters/settings.ts | 6 +- src/global/actions/apiUpdaters/symbols.ts | 3 +- .../actions/apiUpdaters/twoFaSettings.ts | 3 +- src/global/actions/apiUpdaters/users.ts | 19 +- src/global/actions/ui/calls.ts | 181 +- src/global/actions/ui/chats.ts | 127 +- src/global/actions/ui/globalSearch.ts | 51 +- src/global/actions/ui/initial.ts | 133 +- src/global/actions/ui/localSearch.ts | 49 +- src/global/actions/ui/mediaViewer.ts | 73 +- src/global/actions/ui/messages.ts | 473 ++- src/global/actions/ui/misc.ts | 437 +- src/global/actions/ui/passcode.ts | 64 +- src/global/actions/ui/payments.ts | 31 +- src/global/actions/ui/settings.ts | 67 +- src/global/actions/ui/stickerSearch.ts | 42 +- src/global/actions/ui/users.ts | 36 +- src/global/cache.ts | 166 +- src/global/helpers/messages.ts | 1 + src/global/index.ts | 44 +- src/global/init.ts | 106 +- src/global/initialState.ts | 139 +- src/global/reducers/bots.ts | 34 +- src/global/reducers/calls.ts | 24 +- src/global/reducers/chats.ts | 64 +- src/global/reducers/globalSearch.ts | 58 +- src/global/reducers/localSearch.ts | 100 +- src/global/reducers/management.ts | 34 +- src/global/reducers/messages.ts | 322 +- src/global/reducers/passcode.ts | 28 +- src/global/reducers/payments.ts | 106 +- src/global/reducers/reactions.ts | 18 +- src/global/reducers/settings.ts | 30 +- src/global/reducers/statistics.ts | 52 +- src/global/reducers/symbols.ts | 108 +- src/global/reducers/tabs.ts | 19 + src/global/reducers/twoFaSettings.ts | 6 +- src/global/reducers/users.ts | 58 +- src/global/selectors/calls.ts | 14 +- src/global/selectors/chats.ts | 66 +- src/global/selectors/globalSearch.ts | 11 +- src/global/selectors/index.ts | 1 + src/global/selectors/limits.ts | 4 +- src/global/selectors/localSearch.ts | 22 +- src/global/selectors/management.ts | 27 +- src/global/selectors/messages.ts | 341 +- src/global/selectors/payments.ts | 53 +- src/global/selectors/settings.ts | 4 +- src/global/selectors/statistics.ts | 29 +- src/global/selectors/symbols.ts | 40 +- src/global/selectors/tabs.ts | 10 + src/global/selectors/ui.ts | 60 +- src/global/selectors/users.ts | 18 +- src/global/types.ts | 2067 +++++++--- src/hooks/useAudioPlayer.ts | 8 +- src/hooks/useChatContextActions.ts | 17 +- src/hooks/useEnsureMessage.ts | 2 +- src/hooks/useModuleLoader.ts | 2 +- src/hooks/useSendMessageAction.ts | 1 + src/index.tsx | 103 +- src/lib/gramjs/.eslintrc | 3 +- .../client/mockUtils/getIdFromInputPeer.ts | 10 +- src/lib/teact/teact.ts | 17 +- src/lib/teact/teactn.tsx | 51 +- src/util/appBadge.ts | 3 + src/util/audioPlayer.ts | 10 +- src/util/deepDiff.ts | 63 + src/util/deepMerge.ts | 34 + src/util/deeplink.ts | 4 +- src/util/environment.ts | 1 + src/util/establishMultitabRole.ts | 183 + src/util/folderManager.ts | 4 +- src/util/handleError.ts | 10 +- src/util/mediaLoader.ts | 3 +- src/util/moduleLoader.ts | 8 +- src/util/multitab.ts | 356 ++ src/util/notifications.ts | 2 +- src/util/passcode.ts | 30 + src/util/routing.ts | 6 + src/util/schedulers.ts | 64 +- src/util/sessions.ts | 25 +- src/util/setupServiceWorker.ts | 2 +- src/util/updateIcon.ts | 12 + src/util/updatePageTitle.ts | 7 + src/util/websync.ts | 6 + 264 files changed, 13529 insertions(+), 5138 deletions(-) create mode 100644 dev/eslint-multitab/.eslintrc.js create mode 100644 dev/eslint-multitab/lib/index.js create mode 100644 dev/eslint-multitab/lib/rules/must-specify-action-handler-return-type.js create mode 100644 dev/eslint-multitab/lib/rules/must-update-global-after-await.js create mode 100644 dev/eslint-multitab/lib/rules/no-getactions-in-actions.js create mode 100644 dev/eslint-multitab/lib/rules/no-immediate-global.js create mode 100644 dev/eslint-multitab/lib/rules/set-global-only-variable.js create mode 100644 dev/eslint-multitab/package-lock.json create mode 100644 dev/eslint-multitab/package.json create mode 100644 src/global/reducers/tabs.ts create mode 100644 src/global/selectors/tabs.ts create mode 100644 src/util/deepDiff.ts create mode 100644 src/util/deepMerge.ts create mode 100644 src/util/establishMultitabRole.ts create mode 100644 src/util/multitab.ts create mode 100644 src/util/updateIcon.ts create mode 100644 src/util/updatePageTitle.ts diff --git a/.eslintrc b/.eslintrc index 01683a87e..ed0ca4ea0 100644 --- a/.eslintrc +++ b/.eslintrc @@ -9,9 +9,15 @@ "jsx-expressions", "no-async-without-await", "teactn", - "no-null" + "no-null", + "eslint-multitab-tt" ], "rules": { + "eslint-multitab-tt/no-immediate-global": "error", + "eslint-multitab-tt/must-update-global-after-await": "off", + "eslint-multitab-tt/set-global-only-variable": "error", + "eslint-multitab-tt/no-getactions-in-actions": "error", + "eslint-multitab-tt/must-specify-action-handler-return-type": "error", "indent": [ "error", 2, diff --git a/dev/eslint-multitab/.eslintrc.js b/dev/eslint-multitab/.eslintrc.js new file mode 100644 index 000000000..2f33f7a70 --- /dev/null +++ b/dev/eslint-multitab/.eslintrc.js @@ -0,0 +1,19 @@ +"use strict"; + +module.exports = { + root: true, + extends: [ + "eslint:recommended", + "plugin:eslint-plugin/recommended", + "plugin:node/recommended", + ], + env: { + node: true, + }, + overrides: [ + { + files: ["tests/**/*.js"], + env: { mocha: true }, + }, + ], +}; diff --git a/dev/eslint-multitab/lib/index.js b/dev/eslint-multitab/lib/index.js new file mode 100644 index 000000000..0d6cca0ef --- /dev/null +++ b/dev/eslint-multitab/lib/index.js @@ -0,0 +1,22 @@ +/** + * @fileoverview eslint-multitab-tt + * @author undrfined + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const requireIndex = require("requireindex"); + +//------------------------------------------------------------------------------ +// Plugin Definition +//------------------------------------------------------------------------------ + + +// import all rules in lib/rules +module.exports.rules = requireIndex(__dirname + "/rules"); + + + diff --git a/dev/eslint-multitab/lib/rules/must-specify-action-handler-return-type.js b/dev/eslint-multitab/lib/rules/must-specify-action-handler-return-type.js new file mode 100644 index 000000000..35484e95a --- /dev/null +++ b/dev/eslint-multitab/lib/rules/must-specify-action-handler-return-type.js @@ -0,0 +1,39 @@ +/** + * @fileoverview Must specify action handler return type + * @author undrfined + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: "problem", + docs: { + description: "Must specify action handler return type", + recommended: false, + url: null, + }, + fixable: null, + schema: [], + messages: { + mustSpecifyActionHandlerReturnType: "Must specify action handler return type", + } + }, + + create(context) { + return { + ArrowFunctionExpression: (node) => { + if(node.parent.type === "CallExpression" && node.parent.callee.name === 'addActionHandler' && !node.returnType) { + context.report({ + node, + messageId: "mustSpecifyActionHandlerReturnType", + }) + } + } + }; + }, +}; diff --git a/dev/eslint-multitab/lib/rules/must-update-global-after-await.js b/dev/eslint-multitab/lib/rules/must-update-global-after-await.js new file mode 100644 index 000000000..53400ec1e --- /dev/null +++ b/dev/eslint-multitab/lib/rules/must-update-global-after-await.js @@ -0,0 +1,108 @@ +/** + * @fileoverview Must update global after await + * @author undrfined + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +// TODO This rule is not working properly +module.exports = { + meta: { + type: "problem", + docs: { + description: "Must update global after await", + recommended: false, + url: null, + }, + fixable: null, + schema: [], + messages: { + mustUpdateGlobalAfterAwait: "Global is outdated because of await here -> {{before}}, use global = getGlobal() to update", + } + }, + + create(context) { + let hasAssignmentOnBlockLevel; + let blocks = 0; + let d; + let hasAwait = false; + let hasAwaitOnBlockLevel; + let assigned; + + //---------------------------------------------------------------------- + // Helpers + //---------------------------------------------------------------------- + function endFunction() { + hasAwait = false; + assigned = undefined; + d = undefined; + hasAssignmentOnBlockLevel = undefined; + hasAwaitOnBlockLevel = undefined; + } + + //---------------------------------------------------------------------- + // Public + //---------------------------------------------------------------------- + + return { + 'FunctionDeclaration:exit': endFunction, + 'FunctionExpression:exit': endFunction, + 'ArrowFunctionExpression:exit': endFunction, + 'AwaitExpression:exit': (node) => { + if(!node) return; + hasAwait = true; + hasAwaitOnBlockLevel = blocks; + d = node; + }, + 'BlockStatement': () => { + blocks += 1; + }, + 'BlockStatement:exit': () => { + blocks -= 1; + if(hasAwaitOnBlockLevel && blocks === hasAwaitOnBlockLevel) { + hasAwaitOnBlockLevel = undefined; + } + }, + 'ReturnStatement:exit': (node) => { + if(hasAwait && hasAwaitOnBlockLevel && blocks === hasAwaitOnBlockLevel && node.parent.type === 'BlockExpression') { + endFunction(); + } + }, + 'AssignmentExpression': (node) => { + if(node.left.type !== "Identifier" || node.left.name !== "global") return; + if(node.right.type !== "CallExpression" || node.right.callee.name !== "getGlobal") return; + + if(hasAwaitOnBlockLevel && blocks === hasAwaitOnBlockLevel) { + hasAwait = false; + hasAwaitOnBlockLevel = undefined; + d = undefined; + } else { + hasAssignmentOnBlockLevel = blocks; + assigned = node; + } + }, + Identifier: (node) => { + if(node.name !== "global") return; + if(node.parent === assigned) return; + if(hasAwait) { + if(hasAssignmentOnBlockLevel !== undefined && hasAssignmentOnBlockLevel <= blocks) { + endFunction(); + return; + } + context.report({ + node, + messageId: "mustUpdateGlobalAfterAwait", + data: { + before: d ? d.loc.start.line + ':' + d.loc.start.column : 'unknown' + }, + }) + } + }, + "Program:exit": endFunction, + }; + }, +}; diff --git a/dev/eslint-multitab/lib/rules/no-getactions-in-actions.js b/dev/eslint-multitab/lib/rules/no-getactions-in-actions.js new file mode 100644 index 000000000..5570232a2 --- /dev/null +++ b/dev/eslint-multitab/lib/rules/no-getactions-in-actions.js @@ -0,0 +1,40 @@ +/** + * @fileoverview Forbid usage of getActions in actions + * @author undrfined + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: "problem", + docs: { + description: "Forbid usage of getActions in action handlers", + recommended: false, + url: null, + }, + fixable: null, + schema: [], + messages: { + noGetActionsInActions: "Do not use getActions inside action handlers, instead use the second argument of the action handler", + } + }, + + create(context) { + return { + CallExpression: (node) => { + if(!context.getPhysicalFilename().substring(context.getCwd().length).startsWith('/src/global')) return; + if(node.callee.name === 'getActions') { + context.report({ + node, + messageId: 'noGetActionsInActions', + }) + } + } + }; + }, +}; diff --git a/dev/eslint-multitab/lib/rules/no-immediate-global.js b/dev/eslint-multitab/lib/rules/no-immediate-global.js new file mode 100644 index 000000000..926dad974 --- /dev/null +++ b/dev/eslint-multitab/lib/rules/no-immediate-global.js @@ -0,0 +1,42 @@ +/** + * @fileoverview No immediate global + * @author undrfined + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: "problem", + docs: { + description: "No immediate global", + recommended: false, + url: null, + }, + fixable: null, + schema: [], + messages: { + noImmediateGlobal: "Only use getGlobal() to assign to global variable", + } + }, + + create(context) { + return { + CallExpression: (node) => { + if(!context.getPhysicalFilename().substring(context.getCwd().length).startsWith('/src/global')) return; + if(node.callee.name === 'getGlobal' + && node.parent.type !== 'AssignmentExpression' + ) { + context.report({ + node, + messageId: "noImmediateGlobal", + }) + } + } + }; + }, +}; diff --git a/dev/eslint-multitab/lib/rules/set-global-only-variable.js b/dev/eslint-multitab/lib/rules/set-global-only-variable.js new file mode 100644 index 000000000..c1ea7cbcc --- /dev/null +++ b/dev/eslint-multitab/lib/rules/set-global-only-variable.js @@ -0,0 +1,53 @@ +/** + * @fileoverview setGlobal must only be used with 'global' variable + * @author undrfined + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: "problem", + docs: { + description: "setGlobal must only be used with 'global' variable", + recommended: false, + url: null, + }, + fixable: null, + schema: [], + hasSuggestions: true, + messages: { + setGlobalOnlyVariable: "setGlobal must only be used with 'global' variable", + } + }, + + create(context) { + return { + CallExpression: (node) => { + if(node.callee.name === 'setGlobal') { + if(node.arguments[0] && node.arguments[0].type !== 'Identifier' || node.arguments[0].name !== 'global') { + context.report({ + node, + messageId: 'setGlobalOnlyVariable', + ...(node.parent.type === 'ExpressionStatement' && { + suggest: [{ + desc: "Move the global assignment before the setGlobal call", + *fix(fixer) { + const sc = context.getSourceCode(); + const parent = node.parent; + yield fixer.insertTextBefore(parent, 'global = ' + sc.getText(node.arguments[0]) + ';\n'); + yield fixer.replaceText(node.arguments[0], 'global'); + }, + }] + }), + }) + } + } + } + }; + }, +}; diff --git a/dev/eslint-multitab/package-lock.json b/dev/eslint-multitab/package-lock.json new file mode 100644 index 000000000..394ebda9c --- /dev/null +++ b/dev/eslint-multitab/package-lock.json @@ -0,0 +1,3647 @@ +{ + "name": "eslint-plugin-eslint-multitab-tt", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "eslint-plugin-eslint-multitab-tt", + "version": "0.0.0", + "license": "ISC", + "dependencies": { + "requireindex": "^1.2.0" + }, + "devDependencies": { + "chalk": "^5.2.0", + "eslint": "^8.19.0", + "eslint-doc-generator": "^1.0.0", + "eslint-plugin-eslint-plugin": "^5.0.0", + "eslint-plugin-node": "^11.1.0", + "mocha": "^10.0.0", + "npm-run-all": "^4.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz", + "integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz", + "integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz", + "integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/visitor-keys": "5.48.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.1.tgz", + "integrity": "sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.48.1", + "@typescript-eslint/types": "5.48.1", + "@typescript-eslint/typescript-estree": "5.48.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.48.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz", + "integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.48.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cosmiconfig": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", + "integrity": "sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-7.2.0.tgz", + "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", + "dev": true, + "dependencies": { + "type-fest": "^2.11.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.31.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz", + "integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-doc-generator": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-doc-generator/-/eslint-doc-generator-1.4.2.tgz", + "integrity": "sha512-axO5W9Nt/n/cppE75nJ8JVoqmzUyinzhNQ42vJFqjSnK8KeQoWE6IWqqT1wZly5uSneMuuWYYlCGicc0UeJp2g==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.38.1", + "ajv": "^8.11.2", + "boolean": "^3.2.0", + "commander": "^9.4.0", + "cosmiconfig": "^8.0.0", + "deepmerge": "^4.2.2", + "dot-prop": "^7.2.0", + "jest-diff": "^29.2.1", + "json-schema-traverse": "^1.0.0", + "markdown-table": "^3.0.3", + "no-case": "^3.0.4", + "type-fest": "^3.0.0" + }, + "bin": { + "eslint-doc-generator": "dist/bin/eslint-doc-generator.js" + }, + "engines": { + "node": "^14.18.0 || ^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": ">= 7" + } + }, + "node_modules/eslint-doc-generator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-doc-generator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-eslint-plugin": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-5.0.7.tgz", + "integrity": "sha512-hcz4Bze1ECwv3Q/Bi/ZMZZNiuvI2YclNuxjnczkblQ0skrlPhdO83rSM7felf5n+7ZJOZi4GS8y8gNiRtvI0hA==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "estraverse": "^5.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-node/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.19.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", + "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jest-diff": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz", + "integrity": "sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.3.1", + "jest-get-type": "^29.2.0", + "pretty-format": "^29.3.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jest-get-type": { + "version": "29.2.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.2.0.tgz", + "integrity": "sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/js-sdsl": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz", + "integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.3.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz", + "integrity": "sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz", + "integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "engines": { + "node": ">=0.10.5" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", + "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.1.tgz", + "integrity": "sha512-70T99cpILFk2fzwuljwWxmazSphFrdOe3gRHbp6bqs71pxFBbJwFqnmkLO2lQL6aLHxHmYAnP/sL+AJWpT70jA==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/dev/eslint-multitab/package.json b/dev/eslint-multitab/package.json new file mode 100644 index 000000000..fbef4171a --- /dev/null +++ b/dev/eslint-multitab/package.json @@ -0,0 +1,39 @@ +{ + "name": "eslint-plugin-eslint-multitab-tt", + "version": "0.0.0", + "description": "eslint-multitab-tt", + "keywords": [ + "eslint", + "eslintplugin", + "eslint-plugin" + ], + "author": "undrfined", + "main": "./lib/index.js", + "exports": "./lib/index.js", + "scripts": { + "lint": "npm-run-all \"lint:*\"", + "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"", + "lint:js": "eslint .", + "test": "mocha tests --recursive", + "update:eslint-docs": "eslint-doc-generator" + }, + "dependencies": { + "requireindex": "^1.2.0" + }, + "devDependencies": { + "chalk": "^5.2.0", + "eslint": "^8.19.0", + "eslint-doc-generator": "^1.0.0", + "eslint-plugin-eslint-plugin": "^5.0.0", + "eslint-plugin-node": "^11.1.0", + "mocha": "^10.0.0", + "npm-run-all": "^4.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + }, + "license": "ISC" +} diff --git a/package-lock.json b/package-lock.json index 6a1ae505a..56973d3fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "telegram-t", "version": "1.58.0", + "hasInstallScript": true, "license": "GPL-3.0-or-later", "dependencies": { "@cryptography/aes": "^0.1.1", @@ -56,6 +57,7 @@ "eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-react-app": "^7.0.1", "eslint-import-resolver-webpack": "^0.13.2", + "eslint-plugin-eslint-multitab-tt": "file:dev/eslint-multitab", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", @@ -101,6 +103,42 @@ "fsevents": "2.3.2" } }, + "dev/eslint-multitab": { + "name": "eslint-plugin-eslint-multitab-tt", + "version": "0.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "requireindex": "^1.2.0" + }, + "devDependencies": { + "chalk": "^5.2.0", + "eslint": "^8.19.0", + "eslint-doc-generator": "^1.0.0", + "eslint-plugin-eslint-plugin": "^5.0.0", + "eslint-plugin-node": "^11.1.0", + "mocha": "^10.0.0", + "npm-run-all": "^4.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7" + } + }, + "dev/eslint-multitab/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/@adobe/css-tools": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", @@ -4594,6 +4632,15 @@ "node": ">=8" } }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -5266,6 +5313,12 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true + }, "node_modules/boxen": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", @@ -5402,6 +5455,12 @@ "node": ">=8" } }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "node_modules/browserlist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/browserlist/-/browserlist-1.0.1.tgz", @@ -6499,6 +6558,15 @@ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "dev": true }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "29.3.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz", @@ -6651,6 +6719,33 @@ "tslib": "^2.0.3" } }, + "node_modules/dot-prop": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-7.2.0.tgz", + "integrity": "sha512-Ol/IPXUARn9CSbkrdV4VJo7uCy1I3VuSiWCaFSg+8BdUOzF9n3jefIpcgAydvUZbTdEBZs2vEiTiS9m61ssiDA==", + "dev": true, + "dependencies": { + "type-fest": "^2.11.2" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dot-prop/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -7090,6 +7185,93 @@ "eslint": "^8.0.0" } }, + "node_modules/eslint-doc-generator": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eslint-doc-generator/-/eslint-doc-generator-1.4.2.tgz", + "integrity": "sha512-axO5W9Nt/n/cppE75nJ8JVoqmzUyinzhNQ42vJFqjSnK8KeQoWE6IWqqT1wZly5uSneMuuWYYlCGicc0UeJp2g==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "^5.38.1", + "ajv": "^8.11.2", + "boolean": "^3.2.0", + "commander": "^9.4.0", + "cosmiconfig": "^8.0.0", + "deepmerge": "^4.2.2", + "dot-prop": "^7.2.0", + "jest-diff": "^29.2.1", + "json-schema-traverse": "^1.0.0", + "markdown-table": "^3.0.3", + "no-case": "^3.0.4", + "type-fest": "^3.0.0" + }, + "bin": { + "eslint-doc-generator": "dist/bin/eslint-doc-generator.js" + }, + "engines": { + "node": "^14.18.0 || ^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": ">= 7" + } + }, + "node_modules/eslint-doc-generator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint-doc-generator/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/eslint-doc-generator/node_modules/cosmiconfig": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.0.0.tgz", + "integrity": "sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ==", + "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/eslint-doc-generator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/eslint-doc-generator/node_modules/type-fest": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.5.2.tgz", + "integrity": "sha512-Ph7S4EhXzWy0sbljEuZo0tTNoLl+K2tPauGrQpcwUWrOVneLePTuhVzcuzVJJ6RU5DsNwQZka+8YtkXXU4z9cA==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", @@ -7179,6 +7361,69 @@ "ms": "^2.1.1" } }, + "node_modules/eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint-plugin-eslint-multitab-tt": { + "resolved": "dev/eslint-multitab", + "link": true + }, + "node_modules/eslint-plugin-eslint-plugin": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-5.0.7.tgz", + "integrity": "sha512-hcz4Bze1ECwv3Q/Bi/ZMZZNiuvI2YclNuxjnczkblQ0skrlPhdO83rSM7felf5n+7ZJOZi4GS8y8gNiRtvI0hA==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "estraverse": "^5.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, "node_modules/eslint-plugin-flowtype": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", @@ -7355,6 +7600,50 @@ "eslint": ">=3.0.0" } }, + "node_modules/eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "dependencies": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "peerDependencies": { + "eslint": ">=5.16.0" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-node/node_modules/eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/eslint-plugin-react": { "version": "7.31.11", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.11.tgz", @@ -8103,6 +8392,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, "node_modules/flat-cache": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", @@ -9457,6 +9755,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-weakmap": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", @@ -11298,6 +11608,12 @@ "node": ">=4" } }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -11703,6 +12019,43 @@ "node": ">=8" } }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/loader-runner": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", @@ -11766,6 +12119,92 @@ "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/log-update": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", @@ -11960,6 +12399,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-table": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/mathml-tag-names": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", @@ -12008,6 +12457,15 @@ "integrity": "sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==", "dev": true }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/meow": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", @@ -12200,6 +12658,220 @@ "node": ">= 6" } }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -12258,6 +12930,12 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -12337,6 +13015,154 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/npm-run-all/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/npm-run-all/node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -12816,6 +13642,15 @@ "node": ">=0.10" } }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -13726,6 +14561,15 @@ "node": ">=0.10.0" } }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -14352,6 +15196,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", @@ -14678,6 +15531,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/string.prototype.padend": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.4.tgz", + "integrity": "sha512-67otBXoksdjsnXXRUq+KMVTdlVRZ2af422Y0aTyTjVaoQkGr3mxl2Bc5emi7dOQ3OGVVQQskmLEWwFXwommpNw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -16241,6 +17111,12 @@ "node": ">=0.10.0" } }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -16426,6 +17302,54 @@ "node": ">=10" } }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs-unparser/node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/yargs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", diff --git a/package.json b/package.json index fa1fdbea5..4859ee0f1 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "test:record": "playwright codegen localhost:1235", "prepare": "husky install", "statoscope:validate": "statoscope validate --input public/build-stats.json", - "statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json" + "statoscope:validate-diff": "statoscope validate --input input.json --reference reference.json", + "postinstall": "(cd dev/eslint-multitab && npm i)" }, "engines": { "node": "^18", @@ -76,6 +77,7 @@ "eslint-config-airbnb-typescript": "^17.0.0", "eslint-config-react-app": "^7.0.1", "eslint-import-resolver-webpack": "^0.13.2", + "eslint-plugin-eslint-multitab-tt": "file:dev/eslint-multitab", "eslint-plugin-flowtype": "^8.0.3", "eslint-plugin-import": "^2.26.0", "eslint-plugin-jsx-a11y": "^6.6.1", diff --git a/src/App.tsx b/src/App.tsx index fc1586096..ba663773c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,13 +6,16 @@ import type { GlobalState } from './global/types'; import type { UiLoaderPage } from './components/common/UiLoader'; import { INACTIVE_MARKER, PAGE_TITLE } from './config'; -import { PLATFORM_ENV } from './util/environment'; +import { IS_MULTITAB_SUPPORTED, PLATFORM_ENV } from './util/environment'; +import { selectTabState } from './global/selectors'; import { updateSizes } from './util/windowSize'; import { addActiveTabChangeListener } from './util/activeTabMonitor'; import { hasStoredSession } from './util/sessions'; import buildClassName from './util/buildClassName'; +import { parseInitialLocationHash } from './util/routing'; import useFlag from './hooks/useFlag'; import usePrevious from './hooks/usePrevious'; +import useAppLayout from './hooks/useAppLayout'; import Auth from './components/auth/Auth'; import Main from './components/main/Main.async'; @@ -20,14 +23,13 @@ import LockScreen from './components/main/LockScreen.async'; import AppInactive from './components/main/AppInactive'; import Transition from './components/ui/Transition'; import UiLoader from './components/common/UiLoader'; -import { parseInitialLocationHash } from './util/routing'; -import useAppLayout from './hooks/useAppLayout'; // import Test from './components/test/TestNoRedundancy'; type StateProps = { authState: GlobalState['authState']; isScreenLocked?: boolean; hasPasscode?: boolean; + isInactiveAuth?: boolean; hasWebAuthTokenFailed?: boolean; }; @@ -43,23 +45,14 @@ const App: FC = ({ isScreenLocked, hasPasscode, hasWebAuthTokenFailed, + isInactiveAuth, }) => { const { disconnect } = getActions(); - const [isInactive, markInactive] = useFlag(false); + const [isInactive, markInactive, unmarkInactive] = useFlag(false); const { isMobile } = useAppLayout(); const isMobileOs = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android'; - useEffect(() => { - updateSizes(); - addActiveTabChangeListener(() => { - disconnect(); - document.title = `${PAGE_TITLE}${INACTIVE_MARKER}`; - - markInactive(); - }); - }, [disconnect, markInactive]); - // Prevent drop on elements that do not accept it useEffect(() => { const body = document.body; @@ -144,13 +137,35 @@ const App: FC = ({ activeKey = AppScreens.main; } + useEffect(() => { + updateSizes(); + + if (IS_MULTITAB_SUPPORTED) return; + + addActiveTabChangeListener(() => { + disconnect(); + document.title = `${PAGE_TITLE}${INACTIVE_MARKER}`; + + markInactive(); + }); + }, [activeKey, disconnect, markInactive]); + + useEffect(() => { + if (isInactiveAuth) { + document.title = `${PAGE_TITLE}${INACTIVE_MARKER}`; + markInactive(); + } else { + unmarkInactive(); + } + }, [isInactiveAuth, markInactive, unmarkInactive]); + const prevActiveKey = usePrevious(activeKey); // eslint-disable-next-line consistent-return - function renderContent(isActive: boolean) { + function renderContent() { switch (activeKey) { case AppScreens.auth: - return ; + return ; case AppScreens.main: return
; case AppScreens.lock: @@ -183,6 +198,7 @@ export default withGlobal( authState: global.authState, isScreenLocked: global.passcode?.isScreenLocked, hasPasscode: global.passcode?.hasPasscode, + isInactiveAuth: selectTabState(global).isInactive, hasWebAuthTokenFailed: global.hasWebAuthTokenFailed || global.hasWebAuthTokenPasswordRequired, }; }, diff --git a/src/api/gramjs/index.ts b/src/api/gramjs/index.ts index 9ea03f841..ba584a0cc 100644 --- a/src/api/gramjs/index.ts +++ b/src/api/gramjs/index.ts @@ -1,2 +1,8 @@ // export { initApi, callApi, cancelApiProgress } from './provider'; -export { initApi, callApi, cancelApiProgress } from './worker/provider'; +export { + initApi, callApi, cancelApiProgress, cancelApiProgressMaster, callApiLocal, + handleMethodCallback, + handleMethodResponse, + updateFullLocalDb, + updateLocalDb, +} from './worker/provider'; diff --git a/src/api/gramjs/localDb.ts b/src/api/gramjs/localDb.ts index 33172a5e5..f372e9c0a 100644 --- a/src/api/gramjs/localDb.ts +++ b/src/api/gramjs/localDb.ts @@ -1,7 +1,15 @@ +import BigInt from 'big-integer'; import type { Api as GramJs } from '../../lib/gramjs'; import type { ApiMessage } from '../types'; +import { omitVirtualClassFields } from './apiBuilders/helpers'; +import { DATA_BROADCAST_CHANNEL_NAME } from '../../config'; +import { constructors } from '../../lib/gramjs/tl'; +import { throttle } from '../../util/schedulers'; -interface LocalDb { +// eslint-disable-next-line no-restricted-globals +const IS_MULTITAB_SUPPORTED = 'BroadcastChannel' in self; + +export interface LocalDb { localMessages: Record; // Used for loading avatars and media through in-memory Gram JS instances. chats: Record; @@ -13,21 +21,99 @@ interface LocalDb { webDocuments: Record; } -const LOCAL_DB_INITIAL = { - localMessages: {}, - chats: {}, - users: {}, - messages: {}, - documents: {}, - stickerSets: {}, - photos: {}, - webDocuments: {}, -}; +const channel = IS_MULTITAB_SUPPORTED ? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) : undefined; -const localDb: LocalDb = LOCAL_DB_INITIAL; +let batchedUpdates: { + name: string; + prop: string; + value: any; +}[] = []; +const throttledLocalDbUpdate = throttle(() => { + channel!.postMessage({ + type: 'localDbUpdate', + batchedUpdates, + }); + batchedUpdates = []; +}, 100); + +function createProxy(name: string, object: any) { + return new Proxy(object, { + get(target, prop: string, value: any) { + return Reflect.get(target, prop, value); + }, + set(target, prop: string, value: any) { + batchedUpdates.push({ name, prop, value }); + throttledLocalDbUpdate(); + return Reflect.set(target, prop, value); + }, + }); +} + +function convertToVirtualClass(value: any): any { + if (value instanceof Uint8Array) return Buffer.from(value); + if (typeof value === 'object' && Object.keys(value).length === 1 && Object.keys(value)[0] === 'value') { + return BigInt(value.value); + } + + if (Array.isArray(value)) { + return value.map(convertToVirtualClass); + } + + if (typeof value !== 'object' || !('CONSTRUCTOR_ID' in value)) { + return value; + } + const path = value.className.split('.'); + const VirtualClass = path.reduce((acc: any, field: string) => { + return acc[field]; + }, constructors); + + const valueOmited = omitVirtualClassFields(value); + const valueConverted = Object.keys(valueOmited).reduce((acc, key) => { + acc[key] = convertToVirtualClass(valueOmited[key]); + return acc; + }, {} as Record); + + return new VirtualClass(valueConverted); +} + +function createLocalDbInitial(initial?: LocalDb): LocalDb { + return [ + 'localMessages', 'chats', 'users', 'messages', 'documents', 'stickerSets', 'photos', 'webDocuments', + ] + .reduce((acc: Record, key) => { + const value = initial?.[key as keyof LocalDb] ?? {}; + const valueVirtualClass = Object.keys(value).reduce((acc2, key2) => { + acc2[key2] = convertToVirtualClass(value[key2]); + return acc2; + }, {} as Record); + + acc[key] = IS_MULTITAB_SUPPORTED + ? createProxy(key, valueVirtualClass) + : valueVirtualClass; + return acc; + }, {} as LocalDb) as LocalDb; +} + +const localDb: LocalDb = createLocalDbInitial(); export default localDb; -export function clearLocalDb() { - Object.assign(localDb, LOCAL_DB_INITIAL); +export function broadcastLocalDbUpdateFull() { + if (!channel) return; + + channel.postMessage({ + type: 'localDbUpdateFull', + localDb: Object.keys(localDb).reduce((acc: Record, key) => { + acc[key] = { ...localDb[key as keyof LocalDb] }; + return acc; + }, {} as Record), + }); +} + +export function updateFullLocalDb(initial: LocalDb) { + Object.assign(localDb, createLocalDbInitial(initial)); +} + +export function clearLocalDb() { + Object.assign(localDb, createLocalDbInitial()); } diff --git a/src/api/gramjs/methods/chats.ts b/src/api/gramjs/methods/chats.ts index 57df01662..bd7237fa3 100644 --- a/src/api/gramjs/methods/chats.ts +++ b/src/api/gramjs/methods/chats.ts @@ -992,7 +992,7 @@ export function updateChatMemberBannedRights({ export function updateChatAdmin({ chat, user, adminRights, customTitle = '', -}: { chat: ApiChat; user: ApiUser; adminRights: ApiChatAdminRights; customTitle: string }) { +}: { chat: ApiChat; user: ApiUser; adminRights: ApiChatAdminRights; customTitle?: string }) { const channel = buildInputEntity(chat.id, chat.accessHash) as GramJs.InputChannel; const userId = buildInputEntity(user.id, user.accessHash) as GramJs.InputUser; diff --git a/src/api/gramjs/methods/client.ts b/src/api/gramjs/methods/client.ts index 236b9ec0e..370a09279 100644 --- a/src/api/gramjs/methods/client.ts +++ b/src/api/gramjs/methods/client.ts @@ -145,12 +145,12 @@ export function setIsPremium({ isPremium }: { isPremium: boolean }) { client.setIsPremium(isPremium); } -export async function destroy(noLogOut = false) { +export async function destroy(noLogOut = false, noClearLocalDb = false) { if (!noLogOut) { await invokeRequest(new GramJs.auth.LogOut()); } - clearLocalDb(); + if (!noClearLocalDb) clearLocalDb(); await client.destroy(); } diff --git a/src/api/gramjs/methods/index.ts b/src/api/gramjs/methods/index.ts index 31b948c1f..371767c9b 100644 --- a/src/api/gramjs/methods/index.ts +++ b/src/api/gramjs/methods/index.ts @@ -100,3 +100,7 @@ export { acceptPhoneCall, confirmPhoneCall, requestPhoneCall, decodePhoneCallData, createPhoneCallState, destroyPhoneCallState, encodePhoneCallData, } from './phoneCallState'; + +export { + broadcastLocalDbUpdateFull, +} from '../localDb'; diff --git a/src/api/gramjs/methods/management.ts b/src/api/gramjs/methods/management.ts index 6cd6251af..8ba75360b 100644 --- a/src/api/gramjs/methods/management.ts +++ b/src/api/gramjs/methods/management.ts @@ -91,7 +91,7 @@ export async function updatePrivateLink({ export async function fetchExportedChatInvites({ peer, admin, limit = 0, isRevoked, -}: { peer: ApiChat; admin: ApiUser; limit: number; isRevoked?: boolean }) { +}: { peer: ApiChat; admin: ApiUser; limit?: number; isRevoked?: boolean }) { const exportedInvites = await invokeRequest(new GramJs.messages.GetExportedChatInvites({ peer: buildInputPeer(peer.id, peer.accessHash), adminId: buildInputEntity(admin.id, admin.accessHash) as GramJs.InputUser, @@ -209,7 +209,7 @@ export async function deleteRevokedExportedChatInvites({ export async function fetchChatInviteImporters({ peer, link, offsetDate = 0, offsetUser, limit = 0, isRequested, }: { - peer: ApiChat; link?: string; offsetDate: number; offsetUser?: ApiUser; limit: number; isRequested?: boolean; + peer: ApiChat; link?: string; offsetDate?: number; offsetUser?: ApiUser; limit?: number; isRequested?: boolean; }) { const result = await invokeRequest(new GramJs.messages.GetChatInviteImporters({ peer: buildInputPeer(peer.id, peer.accessHash), diff --git a/src/api/gramjs/methods/media.ts b/src/api/gramjs/methods/media.ts index 3cff93748..58f11bd08 100644 --- a/src/api/gramjs/methods/media.ts +++ b/src/api/gramjs/methods/media.ts @@ -1,6 +1,6 @@ import type { TelegramClient } from '../../../lib/gramjs'; import { Api as GramJs } from '../../../lib/gramjs'; -import type { ApiOnProgress, ApiParsedMedia, ApiPreparedMedia } from '../../types'; +import type { ApiOnProgress, ApiParsedMedia } from '../../types'; import { ApiMediaFormat, } from '../../types'; @@ -52,11 +52,11 @@ export default async function downloadMedia( void cacheApi.save(cacheName, url, parsed); } - const prepared = mediaFormat === ApiMediaFormat.Progressive ? '' : prepareMedia(parsed as string | Blob); + const dataBlob = mediaFormat === ApiMediaFormat.Progressive ? '' : parsed as string | Blob; const arrayBuffer = mediaFormat === ApiMediaFormat.Progressive ? parsed as ArrayBuffer : undefined; return { - prepared, + dataBlob, arrayBuffer, mimeType, fullSize, @@ -262,14 +262,6 @@ async function parseMedia( return undefined; } -function prepareMedia(mediaData: Exclude): ApiPreparedMedia { - if (mediaData instanceof Blob) { - return URL.createObjectURL(mediaData); - } - - return mediaData; -} - function getMimeType(data: Uint8Array, fallbackMimeType = 'image/jpeg') { if (data.length < 4) { return fallbackMimeType; diff --git a/src/api/gramjs/methods/messages.ts b/src/api/gramjs/methods/messages.ts index 0d4ef883a..ba75d49dc 100644 --- a/src/api/gramjs/methods/messages.ts +++ b/src/api/gramjs/methods/messages.ts @@ -637,7 +637,7 @@ async function uploadMedia(localMessage: ApiMessage, attachment: ApiAttachment, export async function pinMessage({ chat, messageId, isUnpin, isOneSide, isSilent, -}: { chat: ApiChat; messageId: number; isUnpin: boolean; isOneSide: boolean; isSilent: boolean }) { +}: { chat: ApiChat; messageId: number; isUnpin: boolean; isOneSide?: boolean; isSilent?: boolean }) { await invokeRequest(new GramJs.messages.UpdatePinnedMessage({ peer: buildInputPeer(chat.id, chat.accessHash), id: messageId, diff --git a/src/api/gramjs/methods/settings.ts b/src/api/gramjs/methods/settings.ts index a4e494a19..0082f75b0 100644 --- a/src/api/gramjs/methods/settings.ts +++ b/src/api/gramjs/methods/settings.ts @@ -370,8 +370,8 @@ export function updateNotificationSettings(peerType: 'contact' | 'group' | 'broa isSilent, shouldShowPreviews, }: { - isSilent: boolean; - shouldShowPreviews: boolean; + isSilent?: boolean; + shouldShowPreviews?: boolean; }) { let peer: GramJs.TypeInputNotifyPeer; if (peerType === 'contact') { diff --git a/src/api/gramjs/provider.ts b/src/api/gramjs/provider.ts index 231714782..9cc994554 100644 --- a/src/api/gramjs/provider.ts +++ b/src/api/gramjs/provider.ts @@ -5,9 +5,11 @@ import type { ApiOnProgress, } from '../types'; import type { Methods, MethodArgs, MethodResponse } from './methods/types'; +import type { LocalDb } from './localDb'; import { API_THROTTLE_RESET_UPDATES, API_UPDATE_THROTTLE } from '../../config'; import { throttle, throttleWithTickEnd } from '../../util/schedulers'; +import { updateFullLocalDb } from './localDb'; import { init as initUpdater } from './updater'; import { init as initAuth } from './methods/auth'; import { init as initChats } from './methods/chats'; @@ -24,7 +26,7 @@ import * as methods from './methods'; let onUpdate: OnApiUpdate; -export async function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) { +export async function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs, initialLocalDb?: LocalDb) { onUpdate = _onUpdate; initUpdater(handleUpdate); @@ -39,6 +41,8 @@ export async function initApi(_onUpdate: OnApiUpdate, initialArgs: ApiInitialArg initCalls(handleUpdate); initPayments(handleUpdate); + if (initialLocalDb) updateFullLocalDb(initialLocalDb); + await initClient(handleUpdate, initialArgs); } diff --git a/src/api/gramjs/worker/provider.ts b/src/api/gramjs/worker/provider.ts index 30544caa9..f4327d638 100644 --- a/src/api/gramjs/worker/provider.ts +++ b/src/api/gramjs/worker/provider.ts @@ -1,11 +1,15 @@ import type { Api } from '../../../lib/gramjs'; import type { ApiInitialArgs, ApiOnProgress, OnApiUpdate } from '../../types'; import type { Methods, MethodArgs, MethodResponse } from '../methods/types'; -import type { WorkerMessageEvent, OriginRequest } from './types'; +import type { WorkerMessageEvent, OriginRequest, ThenArg } from './types'; +import type { LocalDb } from '../localDb'; +import type { TypedBroadcastChannel } from '../../../util/multitab'; -import { DEBUG } from '../../../config'; +import { IS_MULTITAB_SUPPORTED } from '../../../util/environment'; +import { DATA_BROADCAST_CHANNEL_NAME, DEBUG } from '../../../config'; import generateIdFor from '../../../util/generateIdFor'; import { pause } from '../../../util/schedulers'; +import { getCurrentTabId, subscribeToMasterChange } from '../../../util/establishMultitabRole'; type RequestStates = { messageId: string; @@ -20,10 +24,44 @@ const HEALTH_CHECK_MIN_DELAY = 5 * 1000; // 5 sec let worker: Worker; const requestStates = new Map(); const requestStatesByCallback = new Map(); +const savedLocalDb: LocalDb = { + localMessages: {}, + chats: {}, + users: {}, + messages: {}, + documents: {}, + stickerSets: {}, + photos: {}, + webDocuments: {}, +}; // TODO Re-use `util/WorkerConnector.ts` +let isMasterTab = true; +subscribeToMasterChange((isMasterTabNew) => { + isMasterTab = isMasterTabNew; +}); + +const channel = IS_MULTITAB_SUPPORTED + ? new BroadcastChannel(DATA_BROADCAST_CHANNEL_NAME) as TypedBroadcastChannel + : undefined; + +export function initApiOnMasterTab(initialArgs: ApiInitialArgs) { + if (!channel) return; + + channel.postMessage({ + type: 'initApi', + token: getCurrentTabId(), + initialArgs, + }); +} + export function initApi(onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) { + if (!isMasterTab) { + initApiOnMasterTab(initialArgs); + return Promise.resolve(); + } + if (!worker) { if (DEBUG) { // eslint-disable-next-line no-console @@ -40,11 +78,33 @@ export function initApi(onUpdate: OnApiUpdate, initialArgs: ApiInitialArgs) { return makeRequest({ type: 'initApi', - args: [initialArgs], + args: [initialArgs, savedLocalDb], }); } -export function callApi(fnName: T, ...args: MethodArgs) { +export function updateLocalDb(name: keyof LocalDb, prop: string, value: any) { + savedLocalDb[name][prop] = value; +} + +export function updateFullLocalDb(initial: LocalDb) { + Object.assign(savedLocalDb, initial); +} + +export function callApiOnMasterTab(payload: any) { + if (!channel) return; + + channel.postMessage({ + type: 'callApi', + token: getCurrentTabId(), + ...payload, + }); +} + +/* + * Call a worker method on this tab's worker, without transferring to master tab + * Mostly needed to disconnect worker when re-electing master + */ +export function callApiLocal(fnName: T, ...args: MethodArgs) { if (!worker) { if (DEBUG) { // eslint-disable-next-line no-console @@ -86,6 +146,51 @@ export function callApi(fnName: T, ...args: MethodArgs< return promise as MethodResponse; } +export function callApi(fnName: T, ...args: MethodArgs) { + if (!worker && isMasterTab) { + if (DEBUG) { + // eslint-disable-next-line no-console + console.warn('API is not initialized'); + } + + return undefined; + } + + const promise = isMasterTab ? makeRequest({ + type: 'callMethod', + name: fnName, + args, + }) : makeRequestToMaster({ + name: fnName, + args, + }); + + // Some TypeScript magic to make sure `VirtualClass` is never returned from any method + if (DEBUG) { + (async () => { + try { + type ForbiddenTypes = + Api.VirtualClass + | (Api.VirtualClass | undefined)[]; + type ForbiddenResponses = + ForbiddenTypes + | (AnyLiteral & { [k: string]: ForbiddenTypes }); + + // Unwrap all chained promises + const response = await promise; + // Make sure responses do not include `VirtualClass` instances + const allowedResponse: Exclude = response; + // Suppress "unused variable" constraint + void allowedResponse; + } catch (err) { + // Do noting + } + })(); + } + + return promise as MethodResponse; +} + export function cancelApiProgress(progressCallback: ApiOnProgress) { progressCallback.isCanceled = true; @@ -94,6 +199,20 @@ export function cancelApiProgress(progressCallback: ApiOnProgress) { return; } + if (isMasterTab) { + cancelApiProgressMaster(messageId); + } else { + if (!channel) return; + + channel.postMessage({ + type: 'cancelApiProgress', + token: getCurrentTabId(), + messageId, + }); + } +} + +export function cancelApiProgressMaster(messageId: string) { worker.postMessage({ type: 'cancelProgress', messageId, @@ -105,22 +224,79 @@ function subscribeToWorker(onUpdate: OnApiUpdate) { if (data.type === 'update') { onUpdate(data.update); } else if (data.type === 'methodResponse') { - const requestState = requestStates.get(data.messageId); - if (requestState) { - if (data.error) { - requestState.reject(data.error); - } else { - requestState.resolve(data.response); - } - } + handleMethodResponse(data); } else if (data.type === 'methodCallback') { - requestStates.get(data.messageId)?.callback?.(...data.callbackArgs); + handleMethodCallback(data); } else if (data.type === 'unhandledError') { throw new Error(data.error?.message); } }); } +export function handleMethodResponse(data: { messageId: string; + response?: ThenArg>; + error?: { message: string }; +}) { + const requestState = requestStates.get(data.messageId); + if (requestState) { + if (data.error) { + requestState.reject(data.error); + } else { + requestState.resolve(data.response); + } + } +} + +export function handleMethodCallback(data: { messageId: string; + callbackArgs: any[]; +}) { + requestStates.get(data.messageId)?.callback?.(...data.callbackArgs); +} + +function makeRequestToMaster(message: { + messageId?: string; + name: keyof Methods; + args: MethodArgs; + withCallback?: boolean; +}) { + const messageId = generateIdFor(requestStates); + const payload = { + messageId, + ...message, + }; + + const requestState = { messageId } as RequestStates; + + // Re-wrap type because of `postMessage` + const promise: Promise> = new Promise((resolve, reject) => { + Object.assign(requestState, { resolve, reject }); + }); + + if ('args' in payload && 'name' in payload && typeof payload.args[1] === 'function') { + payload.withCallback = true; + + const callback = payload.args.pop() as AnyToVoidFunction; + requestState.callback = callback; + requestStatesByCallback.set(callback, requestState); + } + + requestStates.set(messageId, requestState); + + promise + .catch(() => undefined) + .finally(() => { + requestStates.delete(messageId); + + if (requestState.callback) { + requestStatesByCallback.delete(requestState.callback); + } + }); + + callApiOnMasterTab(payload); + + return promise; +} + function makeRequest(message: OriginRequest) { const messageId = generateIdFor(requestStates); const payload: OriginRequest = { diff --git a/src/api/gramjs/worker/types.ts b/src/api/gramjs/worker/types.ts index 25e3e5a2b..02a551b8a 100644 --- a/src/api/gramjs/worker/types.ts +++ b/src/api/gramjs/worker/types.ts @@ -1,5 +1,6 @@ import type { ApiInitialArgs, ApiUpdate } from '../../types'; import type { Methods, MethodArgs, MethodResponse } from '../methods/types'; +import type { LocalDb } from '../localDb'; export type ThenArg = T extends Promise ? U : T; @@ -27,7 +28,7 @@ export interface WorkerMessageEvent { export type OriginRequest = { type: 'initApi'; messageId?: string; - args: [ApiInitialArgs]; + args: [ApiInitialArgs, LocalDb]; } | { type: 'callMethod'; messageId?: string; diff --git a/src/api/gramjs/worker/worker.ts b/src/api/gramjs/worker/worker.ts index 8450a476c..447c7119a 100644 --- a/src/api/gramjs/worker/worker.ts +++ b/src/api/gramjs/worker/worker.ts @@ -21,7 +21,7 @@ onmessage = async (message: OriginMessageEvent) => { switch (data.type) { case 'initApi': { - await initApi(onUpdate, data.args[0]); + await initApi(onUpdate, data.args[0], data.args[1]); break; } case 'callMethod': { diff --git a/src/api/types/misc.ts b/src/api/types/misc.ts index 6a98aaf39..372c1b41c 100644 --- a/src/api/types/misc.ts +++ b/src/api/types/misc.ts @@ -1,6 +1,6 @@ import type { ApiDocument, ApiPhoto, ApiReaction } from './messages'; import type { ApiUser } from './users'; -import type { ApiLimitType } from '../../global/types'; +import type { ApiLimitType, CallbackAction } from '../../global/types'; export interface ApiInitialArgs { userAgent: string; @@ -107,7 +107,7 @@ export type ApiNotification = { title?: string; message: string; actionText?: string; - action: VoidFunction; + action?: CallbackAction; className?: string; }; diff --git a/src/bundles/main.ts b/src/bundles/main.ts index 9669ee2b7..13240fcc9 100644 --- a/src/bundles/main.ts +++ b/src/bundles/main.ts @@ -1,5 +1,6 @@ import { getActions, getGlobal } from '../global'; +import { IS_MULTITAB_SUPPORTED } from '../util/environment'; import { DEBUG } from '../config'; // eslint-disable-next-line import/no-cycle @@ -11,7 +12,7 @@ if (DEBUG) { console.log('>>> FINISH LOAD MAIN BUNDLE'); } -const { connectionState, passcode: { isScreenLocked } } = getGlobal(); -if (!connectionState && !isScreenLocked) { +const { passcode: { isScreenLocked }, connectionState } = getGlobal(); +if (!connectionState && !isScreenLocked && !IS_MULTITAB_SUPPORTED) { getActions().initApi(); } diff --git a/src/components/auth/Auth.tsx b/src/components/auth/Auth.tsx index af300cd41..4ab23df37 100644 --- a/src/components/auth/Auth.tsx +++ b/src/components/auth/Auth.tsx @@ -1,11 +1,10 @@ import type { FC } from '../../lib/teact/teact'; -import React, { useEffect, memo } from '../../lib/teact/teact'; +import React, { memo } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import type { GlobalState } from '../../global/types'; import '../../global/actions/initial'; -import { pick } from '../../util/iteratees'; import { PLATFORM_ENV } from '../../util/environment'; import useHistoryBack from '../../hooks/useHistoryBack'; import useCurrentOrPrev from '../../hooks/useCurrentOrPrev'; @@ -19,26 +18,15 @@ import AuthQrCode from './AuthQrCode'; import './Auth.scss'; -type OwnProps = { - isActive: boolean; -}; +type StateProps = Pick; -type StateProps = Pick; - -const Auth: FC = ({ - isActive, authState, hasWebAuthTokenPasswordRequired, +const Auth: FC = ({ + authState, }) => { const { - reset, initApi, returnToAuthPhoneNumber, goToAuthQrCode, + returnToAuthPhoneNumber, goToAuthQrCode, } = getActions(); - useEffect(() => { - if (isActive && !hasWebAuthTokenPasswordRequired) { - reset(); - initApi(); - } - }, [isActive, reset, initApi, hasWebAuthTokenPasswordRequired]); - const isMobile = PLATFORM_ENV === 'iOS' || PLATFORM_ENV === 'Android'; const handleChangeAuthorizationMethod = () => { @@ -102,6 +90,10 @@ const Auth: FC = ({ ); }; -export default memo(withGlobal( - (global): StateProps => pick(global, ['authState', 'hasWebAuthTokenPasswordRequired']), +export default memo(withGlobal( + (global): StateProps => { + return { + authState: global.authState, + }; + }, )(Auth)); diff --git a/src/components/auth/AuthCode.tsx b/src/components/auth/AuthCode.tsx index 69f49e6b0..4ec18fcaf 100644 --- a/src/components/auth/AuthCode.tsx +++ b/src/components/auth/AuthCode.tsx @@ -82,6 +82,10 @@ const AuthCode: FC = ({ } }, [authError, clearAuthError, code, isTracking, setAuthCode]); + function handleReturnToAuthPhoneNumber() { + returnToAuthPhoneNumber(); + } + return (
@@ -95,7 +99,7 @@ const AuthCode: FC = ({ {authPhoneNumber}
= ({ } } + const handleGoToAuthQrCode = useCallback(() => { + goToAuthQrCode(); + }, [goToAuthQrCode]); + const isAuthReady = authState === 'authorizationStateWaitPhoneNumber'; return ( @@ -242,7 +246,7 @@ const AuthPhoneNumber: FC = ({ ) )} {isAuthReady && ( - )} diff --git a/src/components/auth/AuthQrCode.tsx b/src/components/auth/AuthQrCode.tsx index c54a34d84..981ff654e 100644 --- a/src/components/auth/AuthQrCode.tsx +++ b/src/components/auth/AuthQrCode.tsx @@ -129,6 +129,10 @@ const AuthCode: FC = ({ }); }, [markIsLoading, setSettingOption, suggestedLanguage, unmarkIsLoading]); + const habdleReturnToAuthPhoneNumber = useCallback(() => { + returnToAuthPhoneNumber(); + }, [returnToAuthPhoneNumber]); + const isAuthReady = authState === 'authorizationStateWaitQrCode'; return ( @@ -162,7 +166,7 @@ const AuthCode: FC = ({
  • {lang('Login.QR.Help3')}
  • {isAuthReady && ( - + )} {suggestedLanguage && suggestedLanguage !== language && continueText && ( diff --git a/src/components/calls/ActiveCallHeader.tsx b/src/components/calls/ActiveCallHeader.tsx index a6b3df189..8bba9a3a1 100644 --- a/src/components/calls/ActiveCallHeader.tsx +++ b/src/components/calls/ActiveCallHeader.tsx @@ -5,6 +5,7 @@ import { getActions, withGlobal } from '../../global'; import type { ApiGroupCall, ApiUser } from '../../api/types'; import { selectActiveGroupCall, selectPhoneCallUser } from '../../global/selectors/calls'; +import { selectTabState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import useLang from '../../hooks/useLang'; @@ -33,6 +34,10 @@ const ActiveCallHeader: FC = ({ }; }, [isCallPanelVisible]); + function handleToggleGroupCallPanel() { + toggleGroupCallPanel(); + } + if (!groupCall && !phoneCallUser) return undefined; return ( @@ -41,7 +46,7 @@ const ActiveCallHeader: FC = ({ 'ActiveCallHeader', isCallPanelVisible && 'open', )} - onClick={toggleGroupCallPanel} + onClick={handleToggleGroupCallPanel} > {phoneCallUser?.firstName || groupCall?.title || lang('VoipGroupVoiceChat')}
    @@ -50,10 +55,11 @@ const ActiveCallHeader: FC = ({ export default memo(withGlobal( (global): StateProps => { + const tabState = selectTabState(global); return { - groupCall: selectActiveGroupCall(global), - isCallPanelVisible: global.isCallPanelVisible, - phoneCallUser: selectPhoneCallUser(global), + groupCall: tabState.isMasterTab ? selectActiveGroupCall(global) : undefined, + isCallPanelVisible: tabState.isCallPanelVisible, + phoneCallUser: tabState.isMasterTab ? selectPhoneCallUser(global) : undefined, }; }, )(ActiveCallHeader)); diff --git a/src/components/calls/group/GroupCall.tsx b/src/components/calls/group/GroupCall.tsx index dcbde871d..2a17915d7 100644 --- a/src/components/calls/group/GroupCall.tsx +++ b/src/components/calls/group/GroupCall.tsx @@ -25,6 +25,7 @@ import { selectGroupCallParticipant, selectIsAdminInActiveGroupCall, } from '../../../global/selectors/calls'; +import { selectTabState } from '../../../global/selectors'; import useFlag from '../../../hooks/useFlag'; import useLang from '../../../hooks/useLang'; import useAppLayout from '../../../hooks/useAppLayout'; @@ -249,6 +250,10 @@ const GroupCall: FC = ({ } }, [isLeaving, leaveGroupCall, shouldEndGroupCall]); + const handleToggleGroupCallPresentation = useCallback(() => { + toggleGroupCallPresentation(); + }, [toggleGroupCallPresentation]); + return ( = ({ {IS_SCREENSHARE_SUPPORTED && !shouldRaiseHand && ( {lang(hasPresentation ? 'VoipChatStopScreenCapture' : 'VoipChatStartScreenCapture')} @@ -416,7 +421,7 @@ export default memo(withGlobal( isSpeakerEnabled: !isSpeakerDisabled, participantsCount, meParticipant: selectGroupCallParticipant(global, groupCallId, global.currentUserId!), - isCallPanelVisible: Boolean(global.isCallPanelVisible), + isCallPanelVisible: Boolean(selectTabState(global).isCallPanelVisible), isAdmin: selectIsAdminInActiveGroupCall(global), participants, }; diff --git a/src/components/calls/group/GroupCallParticipantList.tsx b/src/components/calls/group/GroupCallParticipantList.tsx index ff5c54bf9..6b3453b75 100644 --- a/src/components/calls/group/GroupCallParticipantList.tsx +++ b/src/components/calls/group/GroupCallParticipantList.tsx @@ -1,6 +1,6 @@ import type { GroupCallParticipant as TypeGroupCallParticipant } from '../../../lib/secret-sauce'; import type { FC } from '../../../lib/teact/teact'; -import React, { memo, useMemo } from '../../../lib/teact/teact'; +import React, { memo, useCallback, useMemo } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; import useLang from '../../../hooks/useLang'; @@ -36,15 +36,23 @@ const GroupCallParticipantList: FC = ({ return Object.keys(participants || {}); }, [participants]); + const handleLoadMoreGroupCallParticipants = useCallback(() => { + loadMoreGroupCallParticipants(); + }, [loadMoreGroupCallParticipants]); + const [viewportIds, getMore] = useInfiniteScroll( - loadMoreGroupCallParticipants, + handleLoadMoreGroupCallParticipants, participantsIds, participantsIds.length >= participantsCount, ); + function handleCreateGroupCallInviteLink() { + createGroupCallInviteLink(); + } + return (
    -
    +
    diff --git a/src/components/calls/group/GroupCallParticipantMenu.tsx b/src/components/calls/group/GroupCallParticipantMenu.tsx index ddc08331e..15d1d5c18 100644 --- a/src/components/calls/group/GroupCallParticipantMenu.tsx +++ b/src/components/calls/group/GroupCallParticipantMenu.tsx @@ -108,7 +108,7 @@ const GroupCallParticipantMenu: FC = ({ } toggleGroupCallMute({ - participantId: id, + participantId: id!, value: isAdmin ? !shouldRaiseHand : !isMutedByMe, }); }, [closeDropdown, toggleGroupCallMute, id, isAdmin, shouldRaiseHand, isMutedByMe]); @@ -131,12 +131,12 @@ const GroupCallParticipantMenu: FC = ({ runThrottled(() => { if (value === VOLUME_ZERO) { toggleGroupCallMute({ - participantId: id, + participantId: id!, value: true, }); } else { setGroupCallParticipantVolume({ - participantId: id, + participantId: id!, volume: Math.floor(value * GROUP_CALL_VOLUME_MULTIPLIER), }); } diff --git a/src/components/calls/group/GroupCallTopPane.tsx b/src/components/calls/group/GroupCallTopPane.tsx index 480f8068f..39c53cfc7 100644 --- a/src/components/calls/group/GroupCallTopPane.tsx +++ b/src/components/calls/group/GroupCallTopPane.tsx @@ -9,7 +9,7 @@ import type { AnimationLevel } from '../../../types'; import { selectChatGroupCall } from '../../../global/selectors/calls'; import buildClassName from '../../../util/buildClassName'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import useLang from '../../../hooks/useLang'; import Button from '../../ui/Button'; @@ -42,17 +42,17 @@ const GroupCallTopPane: FC = ({ animationLevel, }) => { const { - joinGroupCall, + requestMasterAndJoinGroupCall, subscribeToGroupCallUpdates, } = getActions(); const lang = useLang(); const handleJoinGroupCall = useCallback(() => { - joinGroupCall({ + requestMasterAndJoinGroupCall({ chatId, }); - }, [joinGroupCall, chatId]); + }, [requestMasterAndJoinGroupCall, chatId]); const participants = groupCall?.participants; @@ -128,6 +128,7 @@ export default memo(withGlobal( (global, { chatId }) => { const chat = selectChat(global, chatId)!; const groupCall = selectChatGroupCall(global, chatId); + const activeGroupCallId = selectTabState(global).isMasterTab ? global.groupCalls.activeGroupCallId : undefined; return { groupCall, usersById: global.users.byId, @@ -135,7 +136,7 @@ export default memo(withGlobal( activeGroupCallId: global.groupCalls.activeGroupCallId, isActive: ((!groupCall ? (chat && chat.isCallNotEmpty && chat.isCallActive) : (groupCall.participantsCount > 0 && groupCall.isLoaded))) - && (global.groupCalls.activeGroupCallId !== groupCall?.id), + && (activeGroupCallId !== groupCall?.id), animationLevel: global.settings.byKey.animationLevel, }; }, diff --git a/src/components/calls/phone/PhoneCall.tsx b/src/components/calls/phone/PhoneCall.tsx index fae2b5706..8a0d99785 100644 --- a/src/components/calls/phone/PhoneCall.tsx +++ b/src/components/calls/phone/PhoneCall.tsx @@ -14,6 +14,7 @@ import { IS_REQUEST_FULLSCREEN_SUPPORTED, } from '../../../util/environment'; import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets'; +import { selectTabState } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { selectPhoneCallUser } from '../../../global/selectors/calls'; import useLang from '../../../hooks/useLang'; @@ -52,7 +53,7 @@ const PhoneCall: FC = ({ }) => { const lang = useLang(); const { - hangUp, acceptCall, playGroupCallSound, toggleGroupCallPanel, connectToActivePhoneCall, + hangUp, requestMasterAndAcceptCall, playGroupCallSound, toggleGroupCallPanel, connectToActivePhoneCall, } = getActions(); // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); @@ -347,7 +348,7 @@ const PhoneCall: FC = ({ )} {isIncomingRequested && ( = ({ export default memo(withGlobal( (global): StateProps => { const { phoneCall, currentUserId } = global; + const { isCallPanelVisible, isMasterTab } = selectTabState(global); return { - isCallPanelVisible: Boolean(global.isCallPanelVisible), + isCallPanelVisible: Boolean(isCallPanelVisible), user: selectPhoneCallUser(global), isOutgoing: phoneCall?.adminId === currentUserId, - phoneCall, + phoneCall: isMasterTab ? phoneCall : undefined, animationLevel: global.settings.byKey.animationLevel, }; }, diff --git a/src/components/common/ChatExtra.tsx b/src/components/common/ChatExtra.tsx index fa09e7afb..73a8e7bf0 100644 --- a/src/components/common/ChatExtra.tsx +++ b/src/components/common/ChatExtra.tsx @@ -118,7 +118,7 @@ const ChatExtra: FC = ({ isMuted: !newAreNotificationsEnabled, }); } else { - updateChatMutedState({ chatId, isMuted: !newAreNotificationsEnabled }); + updateChatMutedState({ chatId: chatId!, isMuted: !newAreNotificationsEnabled }); } }); diff --git a/src/components/common/DeleteChatModal.tsx b/src/components/common/DeleteChatModal.tsx index af065fa3a..15f0cf3f9 100644 --- a/src/components/common/DeleteChatModal.tsx +++ b/src/components/common/DeleteChatModal.tsx @@ -80,7 +80,7 @@ const DeleteChatModal: FC = ({ const handleDeleteAndStop = useCallback(() => { deleteHistory({ chatId: chat.id, shouldDeleteForAll: true }); - blockContact({ contactId: chat.id, accessHash: chat.accessHash }); + blockContact({ contactId: chat.id, accessHash: chat.accessHash! }); onClose(); }, [deleteHistory, chat.id, chat.accessHash, blockContact, onClose]); @@ -89,7 +89,7 @@ const DeleteChatModal: FC = ({ if (isPrivateChat) { deleteHistory({ chatId: chat.id, shouldDeleteForAll: false }); } else if (isBasicGroup) { - deleteChatUser({ chatId: chat.id, userId: currentUserId }); + deleteChatUser({ chatId: chat.id, userId: currentUserId! }); deleteHistory({ chatId: chat.id, shouldDeleteForAll: false }); } else if ((isChannel || isSuperGroup) && !chat.isCreator) { leaveChannel({ chatId: chat.id }); diff --git a/src/components/common/GroupCallLink.tsx b/src/components/common/GroupCallLink.tsx index 3f2813685..d3fedb317 100644 --- a/src/components/common/GroupCallLink.tsx +++ b/src/components/common/GroupCallLink.tsx @@ -17,13 +17,13 @@ type OwnProps = { const GroupCallLink: FC = ({ className, groupCall, children, }) => { - const { joinGroupCall } = getActions(); + const { requestMasterAndJoinGroupCall } = getActions(); const handleClick = useCallback(() => { if (groupCall) { - joinGroupCall({ id: groupCall.id, accessHash: groupCall.accessHash }); + requestMasterAndJoinGroupCall({ id: groupCall.id, accessHash: groupCall.accessHash }); } - }, [groupCall, joinGroupCall]); + }, [groupCall, requestMasterAndJoinGroupCall]); if (!groupCall) { return children; diff --git a/src/components/common/PinMessageModal.tsx b/src/components/common/PinMessageModal.tsx index 2ac5433d7..8214cff7e 100644 --- a/src/components/common/PinMessageModal.tsx +++ b/src/components/common/PinMessageModal.tsx @@ -37,7 +37,6 @@ type StateProps = { const PinMessageModal: FC = ({ isOpen, messageId, - chatId, isChannel, isGroup, isSuperGroup, @@ -49,17 +48,17 @@ const PinMessageModal: FC = ({ const handlePinMessageForAll = useCallback(() => { pinMessage({ - chatId, messageId, isUnpin: false, + messageId, isUnpin: false, }); onClose(); - }, [pinMessage, chatId, messageId, onClose]); + }, [pinMessage, messageId, onClose]); const handlePinMessage = useCallback(() => { pinMessage({ - chatId, messageId, isUnpin: false, isOneSide: true, isSilent: true, + messageId, isUnpin: false, isOneSide: true, isSilent: true, }); onClose(); - }, [chatId, messageId, onClose, pinMessage]); + }, [messageId, onClose, pinMessage]); const lang = useLang(); diff --git a/src/components/common/ProfileInfo.tsx b/src/components/common/ProfileInfo.tsx index d27737a58..755785555 100644 --- a/src/components/common/ProfileInfo.tsx +++ b/src/components/common/ProfileInfo.tsx @@ -14,6 +14,7 @@ import { MediaViewerOrigin } from '../../types'; import { IS_TOUCH_ENV } from '../../util/environment'; import { MEMO_EMPTY_ARRAY } from '../../util/memo'; import { + selectTabState, selectChat, selectCurrentMessageList, selectThreadMessagesCount, selectUser, selectUserStatus, } from '../../global/selectors'; import { getUserStatus, isChatChannel, isUserOnline } from '../../global/helpers'; @@ -336,7 +337,7 @@ export default memo(withGlobal( const chat = selectChat(global, userId); const isSavedMessages = !forceShowSelf && user && user.isSelf; const { animationLevel } = global.settings.byKey; - const { mediaId, avatarOwnerId } = global.mediaViewer; + const { mediaId, avatarOwnerId } = selectTabState(global).mediaViewer; const isForum = chat?.isForum; const { threadId: currentTopicId } = selectCurrentMessageList(global) || {}; const topic = isForum && currentTopicId ? chat?.topics?.[currentTopicId] : undefined; diff --git a/src/components/common/ReportModal.tsx b/src/components/common/ReportModal.tsx index 020501552..2ad1ba0ac 100644 --- a/src/components/common/ReportModal.tsx +++ b/src/components/common/ReportModal.tsx @@ -47,7 +47,7 @@ const ReportModal: FC = ({ const handleReport = useCallback(() => { switch (subject) { case 'messages': - reportMessages({ messageIds, reason: selectedReason, description }); + reportMessages({ messageIds: messageIds!, reason: selectedReason, description }); exitMessageSelectMode(); break; case 'peer': diff --git a/src/components/common/SeenByModal.tsx b/src/components/common/SeenByModal.tsx index 76fe9e8f9..8ed00aa04 100644 --- a/src/components/common/SeenByModal.tsx +++ b/src/components/common/SeenByModal.tsx @@ -3,7 +3,7 @@ import React, { useCallback, memo } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; import useLang from '../../hooks/useLang'; -import { selectChatMessage } from '../../global/selectors'; +import { selectChatMessage, selectTabState } from '../../global/selectors'; import useCurrentOrPrev from '../../hooks/useCurrentOrPrev'; import Modal from '../ui/Modal'; @@ -40,6 +40,10 @@ const SeenByModal: FC = ({ }, CLOSE_ANIMATION_DURATION); }, [closeSeenByModal, openChat]); + const handleCloseSeenByModal = useCallback(() => { + closeSeenByModal(); + }, [closeSeenByModal]); + const renderingMemberIds = useCurrentOrPrev(memberIds, true); return ( @@ -64,7 +68,7 @@ const SeenByModal: FC = ({ @@ -74,7 +78,7 @@ const SeenByModal: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { chatId, messageId } = global.seenByModal || {}; + const { chatId, messageId } = selectTabState(global).seenByModal || {}; if (!chatId || !messageId) { return {}; } diff --git a/src/components/common/StickerSetModal.tsx b/src/components/common/StickerSetModal.tsx index ba18ec570..e39246c34 100644 --- a/src/components/common/StickerSetModal.tsx +++ b/src/components/common/StickerSetModal.tsx @@ -79,7 +79,7 @@ const StickerSetModal: FC = ({ const prevStickerSet = usePrevious(stickerSet); const renderingStickerSet = stickerSet || prevStickerSet; - const isAdded = !renderingStickerSet?.isArchived && renderingStickerSet?.installedDate; + const isAdded = Boolean(!renderingStickerSet?.isArchived && renderingStickerSet?.installedDate); const isEmoji = renderingStickerSet?.isEmoji; const isButtonLocked = !isAdded && isSetPremium && !isCurrentUserPremium; diff --git a/src/components/common/UiLoader.tsx b/src/components/common/UiLoader.tsx index 570bdbc60..19c51ddff 100644 --- a/src/components/common/UiLoader.tsx +++ b/src/components/common/UiLoader.tsx @@ -2,12 +2,16 @@ import React, { useEffect } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; import { ApiMediaFormat } from '../../api/types'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import type { ThemeKey } from '../../types'; import type { FC } from '../../lib/teact/teact'; import { getChatAvatarHash } from '../../global/helpers/chats'; // Direct import for better module splitting -import { selectIsRightColumnShown, selectTheme } from '../../global/selectors'; +import { + selectIsRightColumnShown, + selectTheme, + selectTabState, +} from '../../global/selectors'; import { DARK_THEME_BG_COLOR, LIGHT_THEME_BG_COLOR } from '../../config'; import useFlag from '../../hooks/useFlag'; import useShowTransition from '../../hooks/useShowTransition'; @@ -41,7 +45,7 @@ type OwnProps = { isMobile?: boolean; }; -type StateProps = Pick & { +type StateProps = Pick & { isRightColumnShown?: boolean; leftColumnWidth?: number; theme: ThemeKey; @@ -74,7 +78,7 @@ function preloadAvatars() { const preloadTasks = { main: () => Promise.all([ - loadModule(Bundles.Main, 'Main') + loadModule(Bundles.Main) .then(preloadFonts), preloadAvatars(), preloadImage(reactionThumbsPath), @@ -176,12 +180,13 @@ const UiLoader: FC = ({ export default withGlobal( (global, { isMobile }): StateProps => { const theme = selectTheme(global); + const tabState = selectTabState(global); return { - shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations, - uiReadyState: global.uiReadyState, + shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations, + uiReadyState: tabState.uiReadyState, isRightColumnShown: selectIsRightColumnShown(global, isMobile), - leftColumnWidth: global.leftColumnWidth, + leftColumnWidth: tabState.leftColumnWidth, theme, }; }, diff --git a/src/components/common/hooks/useAnimatedEmoji.ts b/src/components/common/hooks/useAnimatedEmoji.ts index be46e3581..7e762c0eb 100644 --- a/src/components/common/hooks/useAnimatedEmoji.ts +++ b/src/components/common/hooks/useAnimatedEmoji.ts @@ -52,11 +52,11 @@ export default function useAnimatedEmoji( if (!container) return; sendEmojiInteraction({ - chatId, - messageId, + chatId: chatId!, + messageId: messageId!, localEffect, - emoji, - interactions: interactions.current, + emoji: emoji!, + interactions: interactions.current!, }); startedInteractions.current = undefined; interactions.current = undefined; @@ -91,7 +91,7 @@ export default function useAnimatedEmoji( interactWithAnimatedEmoji({ localEffect, - emoji, + emoji: emoji!, x, y, startSize: size, @@ -131,8 +131,8 @@ export default function useAnimatedEmoji( sendWatchingEmojiInteraction({ id, - chatId, - emoticon: localEffect ? selectLocalAnimatedEmojiEffectByName(localEffect) : emoji, + chatId: chatId!, + emoticon: localEffect ? selectLocalAnimatedEmojiEffectByName(localEffect)! : emoji!, startSize: size, x, y, diff --git a/src/components/left/LeftColumn.tsx b/src/components/left/LeftColumn.tsx index 25af47560..be4a966f2 100644 --- a/src/components/left/LeftColumn.tsx +++ b/src/components/left/LeftColumn.tsx @@ -8,7 +8,7 @@ import { LeftColumnContent, SettingsScreens } from '../../types'; import { IS_MAC_OS, IS_PWA, LAYERS_ANIMATION_NAME } from '../../util/environment'; import captureEscKeyListener from '../../util/captureEscKeyListener'; -import { selectCurrentChat, selectIsForumPanelOpen } from '../../global/selectors'; +import { selectTabState, selectCurrentChat, selectIsForumPanelOpen } from '../../global/selectors'; import useFoldersReducer from '../../hooks/reducers/useFoldersReducer'; import { useResize } from '../../hooks/useResize'; import { useHotkeys } from '../../hooks/useHotkeys'; @@ -113,13 +113,13 @@ const LeftColumn: FC = ({ function fullReset() { setContent(LeftColumnContent.ChatList); setContactsFilter(''); - setGlobalSearchClosing(true); + setGlobalSearchClosing({ isClosing: true }); resetChatCreation(); setTimeout(() => { setGlobalSearchQuery({ query: '' }); setGlobalSearchDate({ date: undefined }); setGlobalSearchChatId({ id: undefined }); - setGlobalSearchClosing(false); + setGlobalSearchClosing({ isClosing: false }); setLastResetTime(Date.now()); }, RESET_TRANSITION_DELAY_MS); } @@ -376,13 +376,15 @@ const LeftColumn: FC = ({ if (nextSettingsScreen !== undefined) { setContent(LeftColumnContent.Settings); setSettingsScreen(nextSettingsScreen); - requestNextSettingsScreen(undefined); + requestNextSettingsScreen({ screen: undefined }); } }, [nextSettingsScreen, requestNextSettingsScreen]); const { initResize, resetResize, handleMouseUp, - } = useResize(resizeRef, setLeftColumnWidth, resetLeftColumnWidth, leftColumnWidth, '--left-column-width'); + } = useResize(resizeRef, (n) => setLeftColumnWidth({ + leftColumnWidth: n, + }), resetLeftColumnWidth, leftColumnWidth, '--left-column-width'); const handleSettingsScreenSelect = useCallback((screen: SettingsScreens) => { setContent(LeftColumnContent.Settings); @@ -479,30 +481,29 @@ const LeftColumn: FC = ({ export default memo(withGlobal( (global): StateProps => { + const tabState = selectTabState(global); const { globalSearch: { query, date, }, - chatFolders: { - activeChatFolder, - }, shouldSkipHistoryAnimations, leftColumnWidth, + activeChatFolder, + nextSettingsScreen, + } = tabState; + const { currentUserId, passcode: { hasPasscode, }, - settings: { - nextScreen: nextSettingsScreen, - }, isUpdateAvailable, } = global; const currentChat = selectCurrentChat(global); const isChatOpen = Boolean(currentChat?.id); const isForumPanelOpen = selectIsForumPanelOpen(global); - const forumPanelChatId = global.forumPanelChatId; + const forumPanelChatId = tabState.forumPanelChatId; return { searchQuery: query, @@ -517,7 +518,7 @@ export default memo(withGlobal( isUpdateAvailable, isForumPanelOpen, forumPanelChatId, - isClosingSearch: global.globalSearch.isClosing, + isClosingSearch: tabState.globalSearch.isClosing, }; }, )(LeftColumn)); diff --git a/src/components/left/main/Chat.tsx b/src/components/left/main/Chat.tsx index a3002c8b4..5586f615c 100644 --- a/src/components/left/main/Chat.tsx +++ b/src/components/left/main/Chat.tsx @@ -34,7 +34,7 @@ import { selectNotifyExceptions, selectUserStatus, selectTopicFromMessage, - selectThreadParam, + selectThreadParam, selectTabState, } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; @@ -323,7 +323,7 @@ export default memo(withGlobal( type: messageListType, } = selectCurrentMessageList(global) || {}; const isSelected = chatId === currentChatId && currentThreadId === MAIN_THREAD_ID; - const isSelectedForum = chatId === global.forumPanelChatId; + const isSelectedForum = chatId === selectTabState(global).forumPanelChatId; const user = privateChatUserId ? selectUser(global, privateChatUserId) : undefined; const userStatus = privateChatUserId ? selectUserStatus(global, privateChatUserId) : undefined; diff --git a/src/components/left/main/ChatFolders.tsx b/src/components/left/main/ChatFolders.tsx index 8b4d144d8..efb3ff7d6 100644 --- a/src/components/left/main/ChatFolders.tsx +++ b/src/components/left/main/ChatFolders.tsx @@ -14,6 +14,7 @@ import { captureEvents, SwipeDirection } from '../../../util/captureEvents'; import buildClassName from '../../../util/buildClassName'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; import { selectCurrentLimit } from '../../../global/selectors/limits'; +import { selectTabState, selectIsForumPanelOpen } from '../../../global/selectors'; import useShowTransition from '../../../hooks/useShowTransition'; import useLang from '../../../hooks/useLang'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -22,7 +23,6 @@ import { useFolderManagerForUnreadCounters } from '../../../hooks/useFolderManag import Transition from '../../ui/Transition'; import TabList from '../../ui/TabList'; import ChatList from './ChatList'; -import { selectIsForumPanelOpen } from '../../../global/selectors'; type OwnProps = { onScreenSelect: (screen: SettingsScreens) => void; @@ -69,10 +69,10 @@ const ChatFolders: FC = ({ const lang = useLang(); useEffect(() => { - if (lastSyncTime) { + if (lastSyncTime && !orderedFolderIds) { loadChatFolders(); } - }, [lastSyncTime, loadChatFolders]); + }, [lastSyncTime, loadChatFolders, orderedFolderIds]); const allChatsFolder = useMemo(() => { return { @@ -117,7 +117,7 @@ const ChatFolders: FC = ({ }, [displayedFolders, folderCountersById, maxFolders]); const handleSwitchTab = useCallback((index: number) => { - setActiveChatFolder(index, { forceOnHeavyAnimation: true }); + setActiveChatFolder({ activeChatFolder: index }, { forceOnHeavyAnimation: true }); }, [setActiveChatFolder]); // Prevent `activeTab` pointing at non-existing folder after update @@ -127,7 +127,7 @@ const ChatFolders: FC = ({ } if (activeChatFolder >= folderTabs.length) { - setActiveChatFolder(FIRST_FOLDER_INDEX); + setActiveChatFolder({ activeChatFolder: FIRST_FOLDER_INDEX }); } }, [activeChatFolder, folderTabs, setActiveChatFolder]); @@ -140,10 +140,13 @@ const ChatFolders: FC = ({ selectorToPreventScroll: '.chat-list', onSwipe: ((e, direction) => { if (direction === SwipeDirection.Left) { - setActiveChatFolder(Math.min(activeChatFolder + 1, folderTabs.length - 1), { forceOnHeavyAnimation: true }); + setActiveChatFolder( + { activeChatFolder: Math.min(activeChatFolder + 1, folderTabs.length - 1) }, + { forceOnHeavyAnimation: true }, + ); return true; } else if (direction === SwipeDirection.Right) { - setActiveChatFolder(Math.max(0, activeChatFolder - 1), { forceOnHeavyAnimation: true }); + setActiveChatFolder({ activeChatFolder: Math.max(0, activeChatFolder - 1) }, { forceOnHeavyAnimation: true }); return true; } @@ -156,13 +159,13 @@ const ChatFolders: FC = ({ isNotInFirstFolderRef.current = !isInFirstFolder; useEffect(() => (isNotInFirstFolderRef.current ? captureEscKeyListener(() => { if (isNotInFirstFolderRef.current) { - setActiveChatFolder(FIRST_FOLDER_INDEX); + setActiveChatFolder({ activeChatFolder: FIRST_FOLDER_INDEX }); } }) : undefined), [activeChatFolder, setActiveChatFolder]); useHistoryBack({ isActive: !isInFirstFolder, - onBack: () => setActiveChatFolder(FIRST_FOLDER_INDEX, { forceOnHeavyAnimation: true }), + onBack: () => setActiveChatFolder({ activeChatFolder: FIRST_FOLDER_INDEX }, { forceOnHeavyAnimation: true }), }); useEffect(() => { @@ -179,7 +182,7 @@ const ChatFolders: FC = ({ const folder = Number(digit) - 1; if (folder > folderTabs.length - 1) return; - setActiveChatFolder(folder, { forceOnHeavyAnimation: true }); + setActiveChatFolder({ activeChatFolder: folder }, { forceOnHeavyAnimation: true }); e.preventDefault(); } }; @@ -245,12 +248,11 @@ export default memo(withGlobal( chatFolders: { byId: chatFoldersById, orderedIds: orderedFolderIds, - activeChatFolder, }, currentUserId, lastSyncTime, - shouldSkipHistoryAnimations, } = global; + const { shouldSkipHistoryAnimations, activeChatFolder } = selectTabState(global); return { chatFoldersById, diff --git a/src/components/left/main/ForumPanel.tsx b/src/components/left/main/ForumPanel.tsx index 402b33a06..fab7c1937 100644 --- a/src/components/left/main/ForumPanel.tsx +++ b/src/components/left/main/ForumPanel.tsx @@ -12,7 +12,9 @@ import { TOPICS_SLICE, TOPIC_HEIGHT_PX, TOPIC_LIST_SENSITIVE_AREA, } from '../../../config'; import { IS_TOUCH_ENV } from '../../../util/environment'; -import { selectChat, selectCurrentMessageList, selectIsForumPanelOpen } from '../../../global/selectors'; +import { + selectChat, selectCurrentMessageList, selectIsForumPanelOpen, selectTabState, +} from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import { getOrderedTopics } from '../../../global/helpers'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; @@ -264,7 +266,7 @@ export default memo(withGlobal( (global, ownProps, detachWhenChanged): StateProps => { detachWhenChanged(selectIsForumPanelOpen(global)); - const chatId = global.forumPanelChatId; + const chatId = selectTabState(global).forumPanelChatId; const chat = chatId ? selectChat(global, chatId) : undefined; const { chatId: currentChatId, diff --git a/src/components/left/main/LeftMainHeader.tsx b/src/components/left/main/LeftMainHeader.tsx index b71ef58bb..381574805 100644 --- a/src/components/left/main/LeftMainHeader.tsx +++ b/src/components/left/main/LeftMainHeader.tsx @@ -7,7 +7,7 @@ import { getActions, withGlobal } from '../../../global'; import type { AnimationLevel, ISettings } from '../../../types'; import { LeftColumnContent, SettingsScreens } from '../../../types'; import type { ApiChat } from '../../../api/types'; -import type { GlobalState } from '../../../global/types'; +import type { TabState, GlobalState } from '../../../global/types'; import { ANIMATION_LEVEL_MAX, @@ -25,7 +25,7 @@ import { formatDateToString } from '../../../util/dateFormat'; import switchTheme from '../../../util/switchTheme'; import { setPermanentWebVersion } from '../../../util/permanentWebVersion'; import { clearWebsync } from '../../../util/websync'; -import { selectCurrentMessageList, selectTheme } from '../../../global/selectors'; +import { selectCurrentMessageList, selectTabState, selectTheme } from '../../../global/selectors'; import { isChatArchived } from '../../../global/helpers'; import useLang from '../../../hooks/useLang'; import useConnectionStatus from '../../../hooks/useConnectionStatus'; @@ -74,7 +74,7 @@ type StateProps = areChatsLoaded?: boolean; hasPasscode?: boolean; } - & Pick; + & Pick & Pick; const ANIMATION_LEVEL_OPTIONS = [0, 1, 2]; const LEGACY_VERSION_URL = 'https://web.telegram.org/?legacy=1'; @@ -153,7 +153,7 @@ const LeftMainHeader: FC = ({ if (hasPasscode) { lockScreen(); } else { - requestNextSettingsScreen(SettingsScreens.PasscodeDisabled); + requestNextSettingsScreen({ screen: SettingsScreens.PasscodeDisabled }); } }, [hasPasscode, lockScreen, requestNextSettingsScreen]); @@ -461,9 +461,10 @@ const LeftMainHeader: FC = ({ export default memo(withGlobal( (global): StateProps => { + const tabState = selectTabState(global); const { query: searchQuery, fetchingStatus, chatId, date, - } = global.globalSearch; + } = tabState.globalSearch; const { currentUserId, connectionState, isSyncing } = global; const { byId: chatsById } = global.chats; const { isConnectionStatusMinimized, animationLevel } = global.settings.byKey; @@ -483,7 +484,7 @@ export default memo(withGlobal( isConnectionStatusMinimized, areChatsLoaded: Boolean(global.chats.listIds.active), hasPasscode: Boolean(global.passcode.hasPasscode), - canInstall: Boolean(global.canInstall), + canInstall: Boolean(tabState.canInstall), }; }, )(LeftMainHeader)); diff --git a/src/components/left/newChat/NewChatStep1.tsx b/src/components/left/newChat/NewChatStep1.tsx index eb8ba47b4..364799b60 100644 --- a/src/components/left/newChat/NewChatStep1.tsx +++ b/src/components/left/newChat/NewChatStep1.tsx @@ -4,6 +4,7 @@ import { getActions, getGlobal, withGlobal } from '../../../global'; import type { ApiChat } from '../../../api/types'; +import { selectTabState } from '../../../global/selectors'; import { unique } from '../../../util/iteratees'; import { filterUsersByName, isUserBot, sortChatIds } from '../../../global/helpers'; import useLang from '../../../hooks/useLang'; @@ -139,7 +140,7 @@ export default memo(withGlobal( fetchingStatus, globalResults, localResults, - } = global.globalSearch; + } = selectTabState(global).globalSearch; const { userIds: globalUserIds } = globalResults || {}; const { userIds: localUserIds } = localResults || {}; diff --git a/src/components/left/newChat/NewChatStep2.tsx b/src/components/left/newChat/NewChatStep2.tsx index 242bff21f..204cd9bb4 100644 --- a/src/components/left/newChat/NewChatStep2.tsx +++ b/src/components/left/newChat/NewChatStep2.tsx @@ -6,6 +6,7 @@ import { getActions, withGlobal } from '../../../global'; import { ChatCreationProgress } from '../../../types'; +import { selectTabState } from '../../../global/selectors'; import useLang from '../../../hooks/useLang'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -198,7 +199,7 @@ export default memo(withGlobal( const { progress: creationProgress, error: creationError, - } = global.chatCreation || {}; + } = selectTabState(global).chatCreation || {}; return { creationProgress, diff --git a/src/components/left/search/AudioResults.tsx b/src/components/left/search/AudioResults.tsx index 0b3d0aebd..7b60a4460 100644 --- a/src/components/left/search/AudioResults.tsx +++ b/src/components/left/search/AudioResults.tsx @@ -30,7 +30,6 @@ const AudioResults: FC = ({ theme, isVoice, searchQuery, - searchChatId, isLoading, chatsById, usersById, @@ -52,12 +51,10 @@ const AudioResults: FC = ({ runThrottled(() => { searchMessagesGlobal({ type: currentType, - query: searchQuery, - chatId: searchChatId, }); }); } - }, [currentType, lastSyncTime, searchMessagesGlobal, searchQuery, searchChatId]); + }, [currentType, lastSyncTime, searchMessagesGlobal]); const foundMessages = useMemo(() => { if (!foundIds || !globalMessagesByChatId) { diff --git a/src/components/left/search/ChatMessageResults.tsx b/src/components/left/search/ChatMessageResults.tsx index f485940c7..7d52896c3 100644 --- a/src/components/left/search/ChatMessageResults.tsx +++ b/src/components/left/search/ChatMessageResults.tsx @@ -5,6 +5,7 @@ import { getActions, withGlobal } from '../../../global'; import type { ApiChat, ApiMessage } from '../../../api/types'; import { LoadMoreDirection } from '../../../types'; +import { selectTabState } from '../../../global/selectors'; import { MEMO_EMPTY_ARRAY } from '../../../util/memo'; import { throttle } from '../../../util/schedulers'; import { renderMessageSummary } from '../../common/helpers/renderMessageText'; @@ -39,7 +40,6 @@ const runThrottled = throttle((cb) => cb(), 500, true); const ChatMessageResults: FC = ({ searchQuery, - currentUserId, dateSearchQuery, foundIds, globalMessagesByChatId, @@ -61,12 +61,10 @@ const ChatMessageResults: FC = ({ runThrottled(() => { searchMessagesGlobal({ type: 'text', - query: searchQuery, - chatId: currentUserId, }); }); } - }, [currentUserId, lastSyncTime, searchMessagesGlobal, searchQuery]); + }, [lastSyncTime, searchMessagesGlobal]); const handleTopicClick = useCallback( (id: number) => { @@ -171,7 +169,7 @@ export default memo(withGlobal( const { currentUserId, messages: { byChatId: globalMessagesByChatId }, lastSyncTime } = global; const { fetchingStatus, resultsByType, foundTopicIds, chatId: searchChatId, - } = global.globalSearch; + } = selectTabState(global).globalSearch; const { foundIds } = (resultsByType?.text) || {}; diff --git a/src/components/left/search/ChatResults.tsx b/src/components/left/search/ChatResults.tsx index 64ff44ae2..035c0c6dd 100644 --- a/src/components/left/search/ChatResults.tsx +++ b/src/components/left/search/ChatResults.tsx @@ -7,6 +7,7 @@ import { getActions, getGlobal, withGlobal } from '../../../global'; import type { ApiChat, ApiMessage } from '../../../api/types'; import { LoadMoreDirection } from '../../../types'; +import { selectTabState } from '../../../global/selectors'; import { unique } from '../../../util/iteratees'; import { sortChatIds, @@ -81,11 +82,10 @@ const ChatResults: FC = ({ runThrottled(() => { searchMessagesGlobal({ type: 'text', - query: searchQuery, }); }); } - }, [lastSyncTime, searchMessagesGlobal, searchQuery]); + }, [lastSyncTime, searchMessagesGlobal]); const handleChatClick = useCallback( (id: string) => { @@ -303,7 +303,7 @@ export default memo(withGlobal( } = global; const { fetchingStatus, globalResults, localResults, resultsByType, - } = global.globalSearch; + } = selectTabState(global).globalSearch; const { chatIds: globalChatIds, userIds: globalUserIds } = globalResults || {}; const { chatIds: localChatIds, userIds: localUserIds } = localResults || {}; const { byChatId: globalMessagesByChatId } = messages; diff --git a/src/components/left/search/FileResults.tsx b/src/components/left/search/FileResults.tsx index 4c59a2246..d8a8e02db 100644 --- a/src/components/left/search/FileResults.tsx +++ b/src/components/left/search/FileResults.tsx @@ -35,7 +35,6 @@ const runThrottled = throttle((cb) => cb(), 500, true); const FileResults: FC = ({ searchQuery, - searchChatId, isLoading, chatsById, usersById, @@ -64,12 +63,10 @@ const FileResults: FC = ({ runThrottled(() => { searchMessagesGlobal({ type: CURRENT_TYPE, - query: searchQuery, - chatId: searchChatId, }); }); } - }, [lastSyncTime, searchMessagesGlobal, searchQuery, searchChatId]); + }, [lastSyncTime, searchMessagesGlobal]); const foundMessages = useMemo(() => { if (!foundIds || !globalMessagesByChatId) { diff --git a/src/components/left/search/LeftSearch.tsx b/src/components/left/search/LeftSearch.tsx index 44a5446e8..d6b22d698 100644 --- a/src/components/left/search/LeftSearch.tsx +++ b/src/components/left/search/LeftSearch.tsx @@ -6,6 +6,7 @@ import { getActions, withGlobal } from '../../../global'; import { GlobalSearchContent } from '../../../types'; +import { selectTabState } from '../../../global/selectors'; import { parseDateString } from '../../../util/dateFormat'; import useKeyboardListNavigation from '../../../hooks/useKeyboardListNavigation'; import useLang from '../../../hooks/useLang'; @@ -148,7 +149,7 @@ const LeftSearch: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { currentContent, chatId } = global.globalSearch; + const { currentContent, chatId } = selectTabState(global).globalSearch; return { currentContent, chatId }; }, diff --git a/src/components/left/search/LinkResults.tsx b/src/components/left/search/LinkResults.tsx index f6d046770..4346482fb 100644 --- a/src/components/left/search/LinkResults.tsx +++ b/src/components/left/search/LinkResults.tsx @@ -33,7 +33,6 @@ const runThrottled = throttle((cb) => cb(), 500, true); const LinkResults: FC = ({ searchQuery, - searchChatId, isLoading, chatsById, usersById, @@ -62,12 +61,10 @@ const LinkResults: FC = ({ runThrottled(() => { searchMessagesGlobal({ type: CURRENT_TYPE, - query: searchQuery, - chatId: searchChatId, }); }); } - }, [lastSyncTime, searchMessagesGlobal, searchQuery, searchChatId]); + }, [lastSyncTime, searchMessagesGlobal]); const foundMessages = useMemo(() => { if (!foundIds || !globalMessagesByChatId) { diff --git a/src/components/left/search/MediaResults.tsx b/src/components/left/search/MediaResults.tsx index fdabe0532..f9ce541fc 100644 --- a/src/components/left/search/MediaResults.tsx +++ b/src/components/left/search/MediaResults.tsx @@ -33,7 +33,6 @@ const runThrottled = throttle((cb) => cb(), 500, true); const MediaResults: FC = ({ searchQuery, - searchChatId, isLoading, globalMessagesByChatId, foundIds, @@ -60,12 +59,10 @@ const MediaResults: FC = ({ runThrottled(() => { searchMessagesGlobal({ type: CURRENT_TYPE, - query: searchQuery, - chatId: searchChatId, }); }); } - }, [lastSyncTime, searchMessagesGlobal, searchQuery, searchChatId]); + }, [lastSyncTime, searchMessagesGlobal]); const foundMessages = useMemo(() => { if (!foundIds || !globalMessagesByChatId) { diff --git a/src/components/left/search/RecentContacts.tsx b/src/components/left/search/RecentContacts.tsx index 7e781f581..b1954093d 100644 --- a/src/components/left/search/RecentContacts.tsx +++ b/src/components/left/search/RecentContacts.tsx @@ -68,6 +68,10 @@ const RecentContacts: FC = ({ }, SEARCH_CLOSE_TIMEOUT_MS); }, [openChat, addRecentlyFoundChatId, onReset]); + const handleClearRecentlyFoundChats = useCallback(() => { + clearRecentlyFoundChats(); + }, [clearRecentlyFoundChats]); + const lang = useLang(); return ( @@ -94,7 +98,7 @@ const RecentContacts: FC = ({ size="smaller" color="translucent" ariaLabel="Clear recent chats" - onClick={clearRecentlyFoundChats} + onClick={handleClearRecentlyFoundChats} isRtl={lang.isRtl} > @@ -116,7 +120,7 @@ export default memo(withGlobal( (global): StateProps => { const { userIds: topUserIds } = global.topPeers; const usersById = global.users.byId; - const { recentlyFoundChatIds } = global.globalSearch; + const { recentlyFoundChatIds } = global; const { animationLevel } = global.settings.byKey; return { diff --git a/src/components/left/search/helpers/createMapStateToProps.ts b/src/components/left/search/helpers/createMapStateToProps.ts index 9c6d7cea1..f310b5240 100644 --- a/src/components/left/search/helpers/createMapStateToProps.ts +++ b/src/components/left/search/helpers/createMapStateToProps.ts @@ -4,7 +4,7 @@ import type { } from '../../../../api/types'; import type { ISettings } from '../../../../types'; -import { selectChat, selectTheme } from '../../../../global/selectors'; +import { selectChat, selectTabState, selectTheme } from '../../../../global/selectors'; export type StateProps = { theme: ISettings['theme']; @@ -21,11 +21,12 @@ export type StateProps = { export function createMapStateToProps(type: ApiGlobalMessageSearchType) { return (global: GlobalState, props: any) => { + const tabState = selectTabState(global); const { byId: chatsById } = global.chats; const { byId: usersById } = global.users; const { fetchingStatus, resultsByType, chatId, - } = global.globalSearch; + } = tabState.globalSearch; // One component is used for two different types of results. // The differences between them are only in the isVoice property. @@ -35,7 +36,7 @@ export function createMapStateToProps(type: ApiGlobalMessageSearchType) { const { byChatId: globalMessagesByChatId } = global.messages; const foundIds = resultsByType?.[currentType]?.foundIds; - const activeDownloads = global.activeDownloads.byChatId; + const activeDownloads = tabState.activeDownloads.byChatId; return { theme: selectTheme(global), diff --git a/src/components/left/settings/BlockUserModal.tsx b/src/components/left/settings/BlockUserModal.tsx index 22ae8968e..68b555ae0 100644 --- a/src/components/left/settings/BlockUserModal.tsx +++ b/src/components/left/settings/BlockUserModal.tsx @@ -6,6 +6,7 @@ import { getActions, withGlobal } from '../../../global'; import type { ApiUser } from '../../../api/types'; +import { selectTabState } from '../../../global/selectors'; import { filterUsersByName, getUserFullName } from '../../../global/helpers'; import { unique } from '../../../util/iteratees'; import useLang from '../../../hooks/useLang'; @@ -102,7 +103,7 @@ export default memo(withGlobal( usersById, blockedIds, contactIds: contactList?.userIds, - localContactIds: global.userSearch.localUserIds, + localContactIds: selectTabState(global).userSearch.localUserIds, currentUserId, }; }, diff --git a/src/components/left/settings/SettingsEditProfile.tsx b/src/components/left/settings/SettingsEditProfile.tsx index 4170e44df..caae161f6 100644 --- a/src/components/left/settings/SettingsEditProfile.tsx +++ b/src/components/left/settings/SettingsEditProfile.tsx @@ -11,7 +11,7 @@ import { ProfileEditProgress } from '../../../types'; import { PURCHASE_USERNAME, TME_LINK_PREFIX, USERNAME_PURCHASE_ERROR } from '../../../config'; import { throttle } from '../../../util/schedulers'; -import { selectUser } from '../../../global/selectors'; +import { selectTabState, selectUser } from '../../../global/selectors'; import { getChatAvatarHash } from '../../../global/helpers'; import { selectCurrentLimit } from '../../../global/selectors/limits'; import renderText from '../../common/helpers/renderText'; @@ -165,6 +165,8 @@ const SettingsEditProfile: FC = ({ const trimmedLastName = lastName.trim(); const trimmedBio = bio.trim(); + if (!editableUsername) return; + if (!trimmedFirstName.length) { setError(ERROR_FIRST_NAME_MISSING); return; @@ -292,7 +294,7 @@ export default memo(withGlobal( const { currentUserId } = global; const { progress, isUsernameAvailable, checkedUsername, error: editUsernameError, - } = global.profileEdit || {}; + } = selectTabState(global).profileEdit || {}; const currentUser = currentUserId ? selectUser(global, currentUserId) : undefined; const maxBioLength = selectCurrentLimit(global, 'aboutLength'); diff --git a/src/components/left/settings/SettingsGeneral.tsx b/src/components/left/settings/SettingsGeneral.tsx index d64b079ec..ab303c2e0 100644 --- a/src/components/left/settings/SettingsGeneral.tsx +++ b/src/components/left/settings/SettingsGeneral.tsx @@ -4,7 +4,7 @@ import React, { } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; -import type { ISettings, TimeFormat } from '../../../types'; +import type { AnimationLevel, ISettings, TimeFormat } from '../../../types'; import { SettingsScreens } from '../../../types'; import { @@ -95,7 +95,7 @@ const SettingsGeneral: FC = ({ document.body.classList.toggle(`animation-level-${i}`, newLevel === i); }); - setSettingOption({ animationLevel: newLevel }); + setSettingOption({ animationLevel: newLevel as AnimationLevel }); }, [setSettingOption]); const handleMessageTextSizeChange = useCallback((newSize: number) => { @@ -120,14 +120,14 @@ const SettingsGeneral: FC = ({ }, [animationLevel, setSettingOption, theme]); const handleTimeFormatChange = useCallback((newTimeFormat: string) => { - setSettingOption({ timeFormat: newTimeFormat }); + setSettingOption({ timeFormat: newTimeFormat as TimeFormat }); setSettingOption({ wasTimeFormatSetManually: true }); setTimeFormat(newTimeFormat as TimeFormat); }, [setSettingOption]); const handleMessageSendComboChange = useCallback((newCombo: string) => { - setSettingOption({ messageSendKeyCombo: newCombo }); + setSettingOption({ messageSendKeyCombo: newCombo as ISettings['messageSendKeyCombo'] }); }, [setSettingOption]); useHistoryBack({ diff --git a/src/components/left/settings/SettingsGeneralBackground.tsx b/src/components/left/settings/SettingsGeneralBackground.tsx index 47a3bfe44..411fa1260 100644 --- a/src/components/left/settings/SettingsGeneralBackground.tsx +++ b/src/components/left/settings/SettingsGeneralBackground.tsx @@ -56,7 +56,7 @@ const SettingsGeneralBackground: FC = ({ setThemeSettings, } = getActions(); - const themeRef = useRef(); + const themeRef = useRef(); themeRef.current = theme; // Due to the parent Transition, this component never gets unmounted, // that's why we use throttled API call on every update. @@ -94,20 +94,20 @@ const SettingsGeneralBackground: FC = ({ }, [setThemeSettings, theme]); const handleWallPaperSelect = useCallback((slug: string) => { - setThemeSettings({ theme: themeRef.current, background: slug }); + setThemeSettings({ theme: themeRef.current!, background: slug }); const currentWallpaper = loadedWallpapers && loadedWallpapers.find((wallpaper) => wallpaper.slug === slug); if (currentWallpaper?.document.thumbnail) { getAverageColor(currentWallpaper.document.thumbnail.dataUri) .then((color) => { const patternColor = getPatternColor(color); const rgbColor = `#${rgb2hex(color)}`; - setThemeSettings({ theme: themeRef.current, backgroundColor: rgbColor, patternColor }); + setThemeSettings({ theme: themeRef.current!, backgroundColor: rgbColor, patternColor }); }); } }, [loadedWallpapers, setThemeSettings]); const handleWallPaperBlurChange = useCallback((e: React.ChangeEvent) => { - setThemeSettings({ theme: themeRef.current, isBlurred: e.target.checked }); + setThemeSettings({ theme: themeRef.current!, isBlurred: e.target.checked }); }, [setThemeSettings]); const lang = useLang(); diff --git a/src/components/left/settings/SettingsGeneralBackgroundColor.tsx b/src/components/left/settings/SettingsGeneralBackgroundColor.tsx index fbf4f4adb..d91611b6a 100644 --- a/src/components/left/settings/SettingsGeneralBackgroundColor.tsx +++ b/src/components/left/settings/SettingsGeneralBackgroundColor.tsx @@ -59,7 +59,7 @@ const SettingsGeneralBackground: FC = ({ }) => { const { setThemeSettings } = getActions(); - const themeRef = useRef(); + const themeRef = useRef(); themeRef.current = theme; // eslint-disable-next-line no-null/no-null const containerRef = useRef(null); @@ -156,7 +156,7 @@ const SettingsGeneralBackground: FC = ({ if (!isFirstRunRef.current) { const patternColor = getPatternColor(rgb); setThemeSettings({ - theme: themeRef.current, + theme: themeRef.current!, background: undefined, backgroundColor: color, patternColor, diff --git a/src/components/left/settings/SettingsHeader.tsx b/src/components/left/settings/SettingsHeader.tsx index ec1a5fe53..4b92bbd5e 100644 --- a/src/components/left/settings/SettingsHeader.tsx +++ b/src/components/left/settings/SettingsHeader.tsx @@ -58,7 +58,7 @@ const SettingsHeader: FC = ({ const handleSignOutMessage = useCallback(() => { closeSignOutConfirmation(); - signOut(); + signOut({ forceInitApi: true }); }, [closeSignOutConfirmation, signOut]); const SettingsMenuButton: FC<{ onTrigger: () => void; isOpen?: boolean }> = useMemo(() => { diff --git a/src/components/left/settings/SettingsLanguage.tsx b/src/components/left/settings/SettingsLanguage.tsx index 24efee8e3..ed6c2ceb3 100644 --- a/src/components/left/settings/SettingsLanguage.tsx +++ b/src/components/left/settings/SettingsLanguage.tsx @@ -48,7 +48,7 @@ const SettingsLanguage: FC = ({ void setLanguage(langCode as LangCode, () => { unmarkIsLoading(); - setSettingOption({ language: langCode }); + setSettingOption({ language: langCode as LangCode }); loadAttachBots(); // Should be refetched every language change }); diff --git a/src/components/left/settings/SettingsPrivacy.tsx b/src/components/left/settings/SettingsPrivacy.tsx index bc39d0237..98cff73e4 100644 --- a/src/components/left/settings/SettingsPrivacy.tsx +++ b/src/components/left/settings/SettingsPrivacy.tsx @@ -108,6 +108,10 @@ const SettingsPrivacy: FC = ({ } }, [isCurrentUserPremium, lang, onScreenSelect, showNotification]); + const handleUpdateContentSettings = useCallback((isChecked: boolean) => { + updateContentSettings(isChecked); + }, [updateContentSettings]); + function getVisibilityValue(setting?: ApiPrivacySettings) { const { visibility } = setting || {}; const blockCount = setting ? setting.blockChatIds.length + setting.blockUserIds.length : 0; @@ -318,7 +322,7 @@ const SettingsPrivacy: FC = ({ subLabel={lang('lng_settings_sensitive_about')} checked={Boolean(isSensitiveEnabled)} disabled={!canChangeSensitive} - onCheck={updateContentSettings} + onCheck={handleUpdateContentSettings} />
    )} diff --git a/src/components/left/settings/SettingsPrivacyVisibility.tsx b/src/components/left/settings/SettingsPrivacyVisibility.tsx index 9a3708046..4f07f7413 100644 --- a/src/components/left/settings/SettingsPrivacyVisibility.tsx +++ b/src/components/left/settings/SettingsPrivacyVisibility.tsx @@ -171,7 +171,7 @@ const SettingsPrivacyVisibility: FC = ({ const handleVisibilityChange = useCallback((value) => { setPrivacyVisibility({ - privacyKey, + privacyKey: privacyKey!, visibility: value, }); }, [privacyKey, setPrivacyVisibility]); diff --git a/src/components/left/settings/SettingsPrivacyVisibilityExceptionList.tsx b/src/components/left/settings/SettingsPrivacyVisibilityExceptionList.tsx index 5c943cba4..d632e73d6 100644 --- a/src/components/left/settings/SettingsPrivacyVisibilityExceptionList.tsx +++ b/src/components/left/settings/SettingsPrivacyVisibilityExceptionList.tsx @@ -86,7 +86,7 @@ const SettingsPrivacyVisibilityExceptionList: FC = ({ const handleSubmit = useCallback(() => { setPrivacySettings({ - privacyKey: getPrivacyKey(screen), + privacyKey: getPrivacyKey(screen)!, isAllowList: Boolean(isAllowList), contactsIds: newSelectedContactIds, }); diff --git a/src/components/left/settings/folders/SettingsFoldersEdit.tsx b/src/components/left/settings/folders/SettingsFoldersEdit.tsx index ed7dcbfb9..f8aaea03d 100644 --- a/src/components/left/settings/folders/SettingsFoldersEdit.tsx +++ b/src/components/left/settings/folders/SettingsFoldersEdit.tsx @@ -4,6 +4,8 @@ import React, { } from '../../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../../global'; +import type { ApiChatFolder } from '../../../../api/types'; + import { STICKER_SIZE_FOLDER_SETTINGS } from '../../../../config'; import { LOCAL_TGS_URLS } from '../../../common/helpers/animatedAssets'; import { findIntersectionWithSet } from '../../../../util/iteratees'; @@ -142,9 +144,9 @@ const SettingsFoldersEdit: FC = ({ dispatch({ type: 'setIsLoading', payload: true }); if (state.mode === 'edit') { - editChatFolder({ id: state.folderId, folderUpdate: state.folder }); + editChatFolder({ id: state.folderId!, folderUpdate: state.folder }); } else { - addChatFolder({ folder: state.folder }); + addChatFolder({ folder: state.folder as ApiChatFolder }); } setTimeout(() => { diff --git a/src/components/left/settings/twoFa/SettingsTwoFa.tsx b/src/components/left/settings/twoFa/SettingsTwoFa.tsx index f806c3d74..ec21ea987 100644 --- a/src/components/left/settings/twoFa/SettingsTwoFa.tsx +++ b/src/components/left/settings/twoFa/SettingsTwoFa.tsx @@ -142,7 +142,7 @@ const SettingsTwoFa: FC = ({ dispatch({ type: 'setEmail', payload: value }); updateRecoveryEmail({ ...state, - email: value, + email: value!, onSuccess: () => { onScreenSelect(SettingsScreens.TwoFaCongratulations); }, diff --git a/src/components/main/AttachBotRecipientPicker.tsx b/src/components/main/AttachBotRecipientPicker.tsx index 0bc0dbc5d..14e032373 100644 --- a/src/components/main/AttachBotRecipientPicker.tsx +++ b/src/components/main/AttachBotRecipientPicker.tsx @@ -2,7 +2,7 @@ import React, { memo, useCallback, useEffect } from '../../lib/teact/teact'; import { getActions } from '../../global'; import type { FC } from '../../lib/teact/teact'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import useLang from '../../hooks/useLang'; import useFlag from '../../hooks/useFlag'; @@ -10,7 +10,7 @@ import useFlag from '../../hooks/useFlag'; import RecipientPicker from '../common/RecipientPicker'; export type OwnProps = { - requestedAttachBotInChat?: GlobalState['requestedAttachBotInChat']; + requestedAttachBotInChat?: TabState['requestedAttachBotInChat']; }; const AttachBotRecipientPicker: FC = ({ diff --git a/src/components/main/ConfettiContainer.tsx b/src/components/main/ConfettiContainer.tsx index ee4cb96aa..be76569c8 100644 --- a/src/components/main/ConfettiContainer.tsx +++ b/src/components/main/ConfettiContainer.tsx @@ -1,11 +1,12 @@ import React, { memo, useRef } from '../../lib/teact/teact'; import { withGlobal } from '../../global'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import type { FC } from '../../lib/teact/teact'; import { pick } from '../../util/iteratees'; import buildStyle from '../../util/buildStyle'; +import { selectTabState } from '../../global/selectors'; import useWindowSize from '../../hooks/useWindowSize'; import useOnChange from '../../hooks/useOnChange'; @@ -15,7 +16,7 @@ import useAppLayout from '../../hooks/useAppLayout'; import styles from './ConfettiContainer.module.scss'; type StateProps = { - confetti?: GlobalState['confetti']; + confetti?: TabState['confetti']; }; interface Confetti { @@ -203,5 +204,5 @@ const ConfettiContainer: FC = ({ confetti }) => { }; export default memo(withGlobal( - (global): StateProps => pick(global, ['confetti']), + (global): StateProps => pick(selectTabState(global), ['confetti']), )(ConfettiContainer)); diff --git a/src/components/main/DeleteFolderDialog.tsx b/src/components/main/DeleteFolderDialog.tsx index 0b784dc7c..3b7d9178b 100644 --- a/src/components/main/DeleteFolderDialog.tsx +++ b/src/components/main/DeleteFolderDialog.tsx @@ -18,7 +18,7 @@ const DeleteFolderDialog: FC = ({ const handleDeleteFolderMessage = useCallback(() => { closeDeleteChatFolderModal(); - deleteChatFolder({ id: deleteFolderDialogId }); + deleteChatFolder({ id: deleteFolderDialogId! }); }, [closeDeleteChatFolderModal, deleteChatFolder, deleteFolderDialogId]); return ( diff --git a/src/components/main/Dialogs.tsx b/src/components/main/Dialogs.tsx index 22e789f7e..86e2f82b1 100644 --- a/src/components/main/Dialogs.tsx +++ b/src/components/main/Dialogs.tsx @@ -7,6 +7,7 @@ import type { } from '../../api/types'; import type { AnimationLevel } from '../../types'; +import { selectTabState } from '../../global/selectors'; import getReadableErrorText from '../../util/getReadableErrorText'; import { pick } from '../../util/iteratees'; import renderText from '../common/helpers/renderText'; @@ -20,7 +21,7 @@ import Avatar from '../common/Avatar'; import './Dialogs.scss'; type StateProps = { - dialogs: (ApiError | ApiInviteInfo)[]; + dialogs: (ApiError | ApiInviteInfo | ApiContact)[]; animationLevel: AnimationLevel; }; @@ -198,7 +199,7 @@ function getErrorHeader(error: ApiError) { export default memo(withGlobal( (global): StateProps => { return { - dialogs: global.dialogs, + dialogs: selectTabState(global).dialogs, animationLevel: global.settings.byKey.animationLevel, }; }, diff --git a/src/components/main/DownloadManager.tsx b/src/components/main/DownloadManager.tsx index 7ddd41e55..ad581b69a 100644 --- a/src/components/main/DownloadManager.tsx +++ b/src/components/main/DownloadManager.tsx @@ -6,6 +6,7 @@ import type { Thread } from '../../global/types'; import type { ApiMessage } from '../../api/types'; import { ApiMediaFormat } from '../../api/types'; +import { selectTabState } from '../../global/selectors'; import { IS_OPFS_SUPPORTED, IS_SERVICE_WORKER_SUPPORTED, MAX_BUFFER_SIZE } from '../../util/environment'; import * as mediaLoader from '../../util/mediaLoader'; import download from '../../util/download'; @@ -113,7 +114,7 @@ const DownloadManager: FC = ({ export default memo(withGlobal( (global): StateProps => { - const activeDownloads = global.activeDownloads.byChatId; + const activeDownloads = selectTabState(global).activeDownloads.byChatId; const messages = global.messages.byChatId; return { activeDownloads, diff --git a/src/components/main/DraftRecipientPicker.tsx b/src/components/main/DraftRecipientPicker.tsx index dc6657fd7..af8993bb4 100644 --- a/src/components/main/DraftRecipientPicker.tsx +++ b/src/components/main/DraftRecipientPicker.tsx @@ -4,7 +4,7 @@ import React, { } from '../../lib/teact/teact'; import { getActions } from '../../global'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import useLang from '../../hooks/useLang'; import useFlag from '../../hooks/useFlag'; @@ -12,7 +12,7 @@ import useFlag from '../../hooks/useFlag'; import RecipientPicker from '../common/RecipientPicker'; export type OwnProps = { - requestedDraft?: GlobalState['requestedDraft']; + requestedDraft?: TabState['requestedDraft']; }; const DraftRecipientPicker: FC = ({ diff --git a/src/components/main/ForwardRecipientPicker.tsx b/src/components/main/ForwardRecipientPicker.tsx index 99dce382d..fc3ac134b 100644 --- a/src/components/main/ForwardRecipientPicker.tsx +++ b/src/components/main/ForwardRecipientPicker.tsx @@ -4,6 +4,7 @@ import React, { } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; +import { selectTabState } from '../../global/selectors'; import useLang from '../../hooks/useLang'; import useFlag from '../../hooks/useFlag'; @@ -74,6 +75,6 @@ const ForwardRecipientPicker: FC = ({ export default memo(withGlobal((global): StateProps => { return { currentUserId: global.currentUserId, - isManyMessages: (global.forwardMessages.messageIds?.length || 0) > 1, + isManyMessages: (selectTabState(global).forwardMessages.messageIds?.length || 0) > 1, }; })(ForwardRecipientPicker)); diff --git a/src/components/main/GameModal.tsx b/src/components/main/GameModal.tsx index d8f774cba..a4c8dbd87 100644 --- a/src/components/main/GameModal.tsx +++ b/src/components/main/GameModal.tsx @@ -2,7 +2,7 @@ import React, { memo, useCallback, useEffect } from '../../lib/teact/teact'; import { getActions } from '../../lib/teact/teactn'; import type { FC } from '../../lib/teact/teact'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import { MAIN_THREAD_ID } from '../../api/types'; import { withGlobal } from '../../global'; @@ -22,7 +22,7 @@ type GameEvents = { eventType: 'share_score' | 'share_game' }; const PLAY_GAME_ACTION_INTERVAL = 5000; type OwnProps = { - openedGame?: GlobalState['openedGame']; + openedGame?: TabState['openedGame']; gameTitle?: string; }; diff --git a/src/components/main/HistoryCalendar.tsx b/src/components/main/HistoryCalendar.tsx index 00481d2b3..b7c735175 100644 --- a/src/components/main/HistoryCalendar.tsx +++ b/src/components/main/HistoryCalendar.tsx @@ -2,6 +2,7 @@ import type { FC } from '../../lib/teact/teact'; import React, { memo, useCallback } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; +import { selectTabState } from '../../global/selectors'; import useLang from '../../hooks/useLang'; import CalendarModal from '../common/CalendarModal'; @@ -40,6 +41,6 @@ const HistoryCalendar: FC = ({ export default memo(withGlobal( (global): StateProps => { - return { selectedAt: global.historyCalendarSelectedAt }; + return { selectedAt: selectTabState(global).historyCalendarSelectedAt }; }, )(HistoryCalendar)); diff --git a/src/components/main/LockScreen.tsx b/src/components/main/LockScreen.tsx index 8baa337d3..a91f89062 100644 --- a/src/components/main/LockScreen.tsx +++ b/src/components/main/LockScreen.tsx @@ -90,7 +90,7 @@ const LockScreen: FC = ({ const handleSignOutMessage = useCallback(() => { closeSignOutConfirmation(); - signOut(); + signOut({ forceInitApi: true }); }, [closeSignOutConfirmation, signOut]); if (!shouldRender) { diff --git a/src/components/main/Main.tsx b/src/components/main/Main.tsx index 992d98bee..7dee6414c 100644 --- a/src/components/main/Main.tsx +++ b/src/components/main/Main.tsx @@ -9,7 +9,7 @@ import type { ApiAttachBot, ApiChat, ApiMessage, ApiUser, } from '../../api/types'; -import type { ApiLimitTypeWithModal, GlobalState } from '../../global/types'; +import type { ApiLimitTypeWithModal, TabState } from '../../global/types'; import '../../global/actions/all'; import { @@ -18,6 +18,7 @@ import { import { IS_ANDROID } from '../../util/environment'; import { selectChatMessage, + selectTabState, selectCurrentMessageList, selectIsForwardModalOpen, selectIsMediaViewerOpen, @@ -28,7 +29,6 @@ import { import buildClassName from '../../util/buildClassName'; import { waitForTransitionEnd } from '../../util/cssAnimationEndListeners'; import { processDeepLink } from '../../util/deeplink'; -import { getAllNotificationsCount } from '../../util/folderManager'; import { parseInitialLocationHash, parseLocationHash } from '../../util/routing'; import { fastRaf } from '../../util/schedulers'; @@ -42,6 +42,8 @@ import useShowTransition from '../../hooks/useShowTransition'; import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck'; import useInterval from '../../hooks/useInterval'; import useAppLayout from '../../hooks/useAppLayout'; +import updatePageTitle from '../../util/updatePageTitle'; +import updateIcon from '../../util/updateIcon'; import StickerSetModal from '../common/StickerSetModal.async'; import UnreadCount from '../common/UnreadCounter'; @@ -84,6 +86,7 @@ export interface OwnProps { } type StateProps = { + isMasterTab?: boolean; chat?: ApiChat; lastSyncTime?: number; isLeftColumnOpen: boolean; @@ -109,29 +112,26 @@ type StateProps = { addedCustomEmojiIds?: string[]; newContactUserId?: string; newContactByPhoneNumber?: boolean; - openedGame?: GlobalState['openedGame']; + openedGame?: TabState['openedGame']; gameTitle?: string; isRatePhoneCallModalOpen?: boolean; - webApp?: GlobalState['webApp']; + webApp?: TabState['webApp']; isPremiumModalOpen?: boolean; - botTrustRequest?: GlobalState['botTrustRequest']; + botTrustRequest?: TabState['botTrustRequest']; botTrustRequestBot?: ApiUser; attachBotToInstall?: ApiAttachBot; - requestedAttachBotInChat?: GlobalState['requestedAttachBotInChat']; - requestedDraft?: GlobalState['requestedDraft']; + requestedAttachBotInChat?: TabState['requestedAttachBotInChat']; + requestedDraft?: TabState['requestedDraft']; currentUser?: ApiUser; - urlAuth?: GlobalState['urlAuth']; + urlAuth?: TabState['urlAuth']; limitReached?: ApiLimitTypeWithModal; deleteFolderDialogId?: number; isPaymentModalOpen?: boolean; isReceiptModalOpen?: boolean; }; -const NOTIFICATION_INTERVAL = 1000; const APP_OUTDATED_TIMEOUT_MS = 5 * 60 * 1000; // 5 min -let notificationInterval: number | undefined; - // eslint-disable-next-line @typescript-eslint/naming-convention let DEBUG_isLogged = false; @@ -177,12 +177,14 @@ const Main: FC = ({ isPaymentModalOpen, isReceiptModalOpen, deleteFolderDialogId, + isMasterTab, }) => { const { loadAnimatedEmojis, loadNotificationSettings, loadNotificationExceptions, updateIsOnline, + onTabFocusChange, loadTopInlineBots, loadEmojiKeywords, loadCountryList, @@ -224,11 +226,11 @@ const Main: FC = ({ } }, [isDesktop, isLeftColumnOpen, isMiddleColumnOpen, toggleLeftColumn]); - useInterval(checkAppVersion, APP_OUTDATED_TIMEOUT_MS, true); + useInterval(checkAppVersion, isMasterTab ? APP_OUTDATED_TIMEOUT_MS : undefined, true); // Initial API calls useEffect(() => { - if (lastSyncTime) { + if (lastSyncTime && isMasterTab) { updateIsOnline(true); loadConfig(); loadAppConfig(); @@ -248,33 +250,33 @@ const Main: FC = ({ }, [ lastSyncTime, loadAnimatedEmojis, loadEmojiKeywords, loadNotificationExceptions, loadNotificationSettings, loadTopInlineBots, updateIsOnline, loadAvailableReactions, loadAppConfig, loadAttachBots, loadContactList, - loadPremiumGifts, checkAppVersion, loadConfig, loadGenericEmojiEffects, loadDefaultTopicIcons, + loadPremiumGifts, checkAppVersion, loadConfig, loadGenericEmojiEffects, loadDefaultTopicIcons, isMasterTab, ]); // Language-based API calls useEffect(() => { - if (lastSyncTime) { + if (lastSyncTime && isMasterTab) { if (language !== BASE_EMOJI_KEYWORD_LANG) { - loadEmojiKeywords({ language }); + loadEmojiKeywords({ language: language! }); } loadCountryList({ langCode: language }); } - }, [language, lastSyncTime, loadCountryList, loadEmojiKeywords]); + }, [language, lastSyncTime, loadCountryList, loadEmojiKeywords, isMasterTab]); // Re-fetch cached saved emoji for `localDb` useEffectWithPrevDeps(([prevLastSyncTime]) => { - if (!prevLastSyncTime && lastSyncTime) { + if (!prevLastSyncTime && lastSyncTime && isMasterTab) { loadCustomEmojis({ ids: Object.keys(getGlobal().customEmojis.byId), ignoreCache: true, }); } - }, [lastSyncTime] as const); + }, [lastSyncTime, isMasterTab] as const); // Sticker sets useEffect(() => { - if (lastSyncTime) { + if (lastSyncTime && isMasterTab) { if (!addedSetIds || !addedCustomEmojiIds) { loadStickerSets(); loadFavoriteStickers(); @@ -284,14 +286,17 @@ const Main: FC = ({ loadAddedStickers(); } } - }, [lastSyncTime, addedSetIds, loadStickerSets, loadFavoriteStickers, loadAddedStickers, addedCustomEmojiIds]); + }, [ + lastSyncTime, addedSetIds, loadStickerSets, loadFavoriteStickers, loadAddedStickers, addedCustomEmojiIds, + isMasterTab, + ]); // Check version when service chat is ready useEffect(() => { - if (lastSyncTime && isServiceChatReady) { + if (lastSyncTime && isServiceChatReady && isMasterTab) { checkVersionNotification(); } - }, [lastSyncTime, isServiceChatReady, checkVersionNotification]); + }, [lastSyncTime, isServiceChatReady, checkVersionNotification, isMasterTab]); // Ensure time format useEffect(() => { @@ -391,45 +396,18 @@ const Main: FC = ({ ); const handleBlur = useCallback(() => { - updateIsOnline(false); - - const initialUnread = getAllNotificationsCount(); - let index = 0; - - clearInterval(notificationInterval); - notificationInterval = window.setInterval(() => { - if (document.title.includes(INACTIVE_MARKER)) { - updateIcon(false); - return; - } - - if (index % 2 === 0) { - const newUnread = getAllNotificationsCount() - initialUnread; - if (newUnread > 0) { - updatePageTitle(`${newUnread} notification${newUnread > 1 ? 's' : ''}`); - updateIcon(true); - } - } else { - updatePageTitle(PAGE_TITLE); - updateIcon(false); - } - - index++; - }, NOTIFICATION_INTERVAL); - }, [updateIsOnline]); + onTabFocusChange({ isBlurred: true }); + }, [onTabFocusChange]); const handleFocus = useCallback(() => { - updateIsOnline(true); - - clearInterval(notificationInterval); - notificationInterval = undefined; + onTabFocusChange({ isBlurred: false }); if (!document.title.includes(INACTIVE_MARKER)) { updatePageTitle(PAGE_TITLE); } updateIcon(false); - }, [updateIsOnline]); + }, [onTabFocusChange]); const handleStickerSetModalClose = useCallback(() => { closeStickerSetModal(); @@ -494,27 +472,6 @@ const Main: FC = ({ ); }; -function updateIcon(asUnread: boolean) { - document.querySelectorAll('link[rel="icon"], link[rel="alternate icon"]') - .forEach((link) => { - if (asUnread) { - if (!link.href.includes('favicon-unread')) { - link.href = link.href.replace('favicon', 'favicon-unread'); - } - } else { - link.href = link.href.replace('favicon-unread', 'favicon'); - } - }); -} - -// For some reason setting `document.title` to the same value -// causes increment of Chrome Dev Tools > Performance Monitor > DOM Nodes counter -function updatePageTitle(nextTitle: string) { - if (document.title !== nextTitle) { - document.title = nextTitle; - } -} - export default memo(withGlobal( (global, { isMobile }): StateProps => { const { @@ -523,6 +480,10 @@ export default memo(withGlobal( animationLevel, language, wasTimeFormatSetManually, }, }, + lastSyncTime, + } = global; + + const { botTrustRequest, requestedAttachBotInstall, requestedAttachBotInChat, @@ -530,16 +491,28 @@ export default memo(withGlobal( urlAuth, webApp, safeLinkModalUrl, - lastSyncTime, openedStickerSetShortName, openedCustomEmojiSetIds, shouldSkipHistoryAnimations, - } = global; - const { chatId: audioChatId, messageId: audioMessageId } = global.audioPlayer; + openedGame, + audioPlayer, + isLeftColumnShown, + historyCalendarSelectedAt, + notifications, + dialogs, + newContact, + ratingPhoneCall, + premiumModal, + isMasterTab, + payment, + limitReachedModal, + deleteFolderDialogModal, + } = selectTabState(global); + + const { chatId: audioChatId, messageId: audioMessageId } = audioPlayer; const audioMessage = audioChatId && audioMessageId ? selectChatMessage(global, audioChatId, audioMessageId) : undefined; - const openedGame = global.openedGame; const gameMessage = openedGame && selectChatMessage(global, openedGame.chatId, openedGame.messageId); const gameTitle = gameMessage?.content.game?.title; const currentUser = global.currentUserId ? selectUser(global, global.currentUserId) : undefined; @@ -547,32 +520,32 @@ export default memo(withGlobal( return { lastSyncTime, - isLeftColumnOpen: global.isLeftColumnShown, + isLeftColumnOpen: isLeftColumnShown, isMiddleColumnOpen: Boolean(chatId), isRightColumnOpen: selectIsRightColumnShown(global, isMobile), isMediaViewerOpen: selectIsMediaViewerOpen(global), isForwardModalOpen: selectIsForwardModalOpen(global), - hasNotifications: Boolean(global.notifications.length), - hasDialogs: Boolean(global.dialogs.length), + hasNotifications: Boolean(notifications.length), + hasDialogs: Boolean(dialogs.length), audioMessage, safeLinkModalUrl, - isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt), + isHistoryCalendarOpen: Boolean(historyCalendarSelectedAt), shouldSkipHistoryAnimations, openedStickerSetShortName, openedCustomEmojiSetIds, isServiceChatReady: selectIsServiceChatReady(global), - activeGroupCallId: global.groupCalls.activeGroupCallId, + activeGroupCallId: isMasterTab ? global.groupCalls.activeGroupCallId : undefined, animationLevel, language, wasTimeFormatSetManually, - isPhoneCallActive: Boolean(global.phoneCall), + isPhoneCallActive: isMasterTab ? Boolean(global.phoneCall) : undefined, addedSetIds: global.stickers.added.setIds, addedCustomEmojiIds: global.customEmojis.added.setIds, - newContactUserId: global.newContact?.userId, - newContactByPhoneNumber: global.newContact?.isByPhoneNumber, + newContactUserId: newContact?.userId, + newContactByPhoneNumber: newContact?.isByPhoneNumber, openedGame, gameTitle, - isRatePhoneCallModalOpen: Boolean(global.ratingPhoneCall), + isRatePhoneCallModalOpen: Boolean(ratingPhoneCall), botTrustRequest, botTrustRequestBot: botTrustRequest && selectUser(global, botTrustRequest.botId), attachBotToInstall: requestedAttachBotInstall?.bot, @@ -580,11 +553,12 @@ export default memo(withGlobal( webApp, currentUser, urlAuth, - isPremiumModalOpen: global.premiumModal?.isOpen, - limitReached: global.limitReachedModal?.limit, - isPaymentModalOpen: global.payment.isPaymentModalOpen, - isReceiptModalOpen: Boolean(global.payment.receipt), - deleteFolderDialogId: global.deleteFolderDialogModal, + isPremiumModalOpen: premiumModal?.isOpen, + limitReached: limitReachedModal?.limit, + isPaymentModalOpen: payment.isPaymentModalOpen, + isReceiptModalOpen: Boolean(payment.receipt), + deleteFolderDialogId: deleteFolderDialogModal, + isMasterTab, requestedDraft, }; }, diff --git a/src/components/main/Notifications.tsx b/src/components/main/Notifications.tsx index a24bf364a..637f090f1 100644 --- a/src/components/main/Notifications.tsx +++ b/src/components/main/Notifications.tsx @@ -4,6 +4,7 @@ import { getActions, withGlobal } from '../../global'; import type { ApiNotification } from '../../api/types'; +import { selectTabState } from '../../global/selectors'; import { pick } from '../../util/iteratees'; import renderText from '../common/helpers/renderText'; @@ -40,5 +41,5 @@ const Notifications: FC = ({ notifications }) => { }; export default memo(withGlobal( - (global): StateProps => pick(global, ['notifications']), + (global): StateProps => pick(selectTabState(global), ['notifications']), )(Notifications)); diff --git a/src/components/main/UrlAuthModal.tsx b/src/components/main/UrlAuthModal.tsx index 74a0bda6a..bd9061d82 100644 --- a/src/components/main/UrlAuthModal.tsx +++ b/src/components/main/UrlAuthModal.tsx @@ -5,7 +5,7 @@ import { getActions, getGlobal } from '../../global'; import type { FC } from '../../lib/teact/teact'; import type { ApiUser } from '../../api/types'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import { ensureProtocol } from '../../util/ensureProtocol'; import renderText from '../common/helpers/renderText'; @@ -20,7 +20,7 @@ import Checkbox from '../ui/Checkbox'; import styles from './UrlAuthModal.module.scss'; export type OwnProps = { - urlAuth?: GlobalState['urlAuth']; + urlAuth?: TabState['urlAuth']; currentUser?: ApiUser; }; diff --git a/src/components/main/WebAppModal.tsx b/src/components/main/WebAppModal.tsx index 16ac23024..de152c173 100644 --- a/src/components/main/WebAppModal.tsx +++ b/src/components/main/WebAppModal.tsx @@ -5,12 +5,14 @@ import { getActions, withGlobal } from '../../global'; import type { FC } from '../../lib/teact/teact'; import type { ApiAttachBot, ApiChat, ApiUser } from '../../api/types'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import type { ThemeKey } from '../../types'; import type { PopupOptions, WebAppInboundEvent } from './hooks/useWebAppFrame'; import { TME_LINK_PREFIX } from '../../config'; -import { selectCurrentChat, selectTheme, selectUser } from '../../global/selectors'; +import { + selectCurrentChat, selectTabState, selectTheme, selectUser, +} from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { extractCurrentThemeParams, validateHexColor } from '../../util/themeStyle'; @@ -41,7 +43,7 @@ type WebAppButton = { }; export type OwnProps = { - webApp?: GlobalState['webApp']; + webApp?: TabState['webApp']; }; type StateProps = { @@ -50,7 +52,7 @@ type StateProps = { attachBot?: ApiAttachBot; theme?: ThemeKey; isPaymentModalOpen?: boolean; - paymentStatus?: GlobalState['payment']['status']; + paymentStatus?: TabState['payment']['status']; }; const NBSP = '\u00A0'; @@ -504,7 +506,7 @@ export default memo(withGlobal( const bot = botId ? selectUser(global, botId) : undefined; const chat = selectCurrentChat(global); const theme = selectTheme(global); - const { isPaymentModalOpen, status } = global.payment; + const { isPaymentModalOpen, status } = selectTabState(global).payment; return { attachBot, diff --git a/src/components/main/premium/GiftPremiumModal.tsx b/src/components/main/premium/GiftPremiumModal.tsx index 779d36d94..2eaea1ce3 100644 --- a/src/components/main/premium/GiftPremiumModal.tsx +++ b/src/components/main/premium/GiftPremiumModal.tsx @@ -10,7 +10,7 @@ import type { AnimationLevel } from '../../../types'; import { formatCurrency } from '../../../util/formatCurrency'; import renderText from '../../common/helpers/renderText'; import { getUserFirstOrLastName } from '../../../global/helpers'; -import { selectUser } from '../../../global/selectors'; +import { selectTabState, selectUser } from '../../../global/selectors'; import useCurrentOrPrev from '../../../hooks/useCurrentOrPrev'; import useLang from '../../../hooks/useLang'; @@ -160,7 +160,7 @@ const GiftPremiumModal: FC = ({ }; export default memo(withGlobal((global): StateProps => { - const { forUserId, monthlyCurrency, monthlyAmount } = global.giftPremiumModal || {}; + const { forUserId, monthlyCurrency, monthlyAmount } = selectTabState(global).giftPremiumModal || {}; const user = forUserId ? selectUser(global, forUserId) : undefined; const gifts = user ? user.fullInfo?.premiumGifts : undefined; diff --git a/src/components/main/premium/PremiumMainModal.tsx b/src/components/main/premium/PremiumMainModal.tsx index 73919357e..577f35eeb 100644 --- a/src/components/main/premium/PremiumMainModal.tsx +++ b/src/components/main/premium/PremiumMainModal.tsx @@ -15,7 +15,7 @@ import PremiumFeatureModal, { import { TME_LINK_PREFIX } from '../../../config'; import { formatCurrency } from '../../../util/formatCurrency'; import buildClassName from '../../../util/buildClassName'; -import { selectIsCurrentUserPremium, selectUser } from '../../../global/selectors'; +import { selectTabState, selectIsCurrentUserPremium, selectUser } from '../../../global/selectors'; import { renderTextWithEntities } from '../../common/helpers/renderTextWithEntities'; import { selectPremiumLimit } from '../../../global/selectors/limits'; import renderText from '../../common/helpers/renderText'; @@ -309,16 +309,19 @@ const PremiumMainModal: FC = ({ }; export default memo(withGlobal((global): StateProps => { + const { + premiumModal, + } = selectTabState(global); return { currentUserId: global.currentUserId, - promo: global.premiumModal?.promo, - isClosing: global.premiumModal?.isClosing, - isSuccess: global.premiumModal?.isSuccess, - isGift: global.premiumModal?.isGift, - monthsAmount: global.premiumModal?.monthsAmount, - fromUser: global.premiumModal?.fromUserId ? selectUser(global, global.premiumModal.fromUserId) : undefined, - toUser: global.premiumModal?.toUserId ? selectUser(global, global.premiumModal.toUserId) : undefined, - initialSection: global.premiumModal?.initialSection, + promo: premiumModal?.promo, + isClosing: premiumModal?.isClosing, + isSuccess: premiumModal?.isSuccess, + isGift: premiumModal?.isGift, + monthsAmount: premiumModal?.monthsAmount, + fromUser: premiumModal?.fromUserId ? selectUser(global, premiumModal.fromUserId) : undefined, + toUser: premiumModal?.toUserId ? selectUser(global, premiumModal.toUserId) : undefined, + initialSection: premiumModal?.initialSection, isPremium: selectIsCurrentUserPremium(global), limitChannels: selectPremiumLimit(global, 'channels'), limitFolders: selectPremiumLimit(global, 'dialogFilters'), diff --git a/src/components/mediaViewer/MediaViewer.tsx b/src/components/mediaViewer/MediaViewer.tsx index 74920da80..944e3f48c 100644 --- a/src/components/mediaViewer/MediaViewer.tsx +++ b/src/components/mediaViewer/MediaViewer.tsx @@ -14,7 +14,7 @@ import { selectChatMessage, selectChatMessages, selectChatScheduledMessages, - selectCurrentMediaSearch, + selectCurrentMediaSearch, selectTabState, selectIsChatWithSelf, selectListedIds, selectOutlyingIds, @@ -218,13 +218,15 @@ const MediaViewer: FC = ({ const handleFooterClick = useCallback(() => { handleClose(); + if (!chatId || !mediaId) return; + if (isMobile) { setTimeout(() => { - toggleChatInfo(false, { forceSyncOnIOs: true }); - focusMessage({ chatId, threadId, mediaId }); + toggleChatInfo({ force: false }, { forceSyncOnIOs: true }); + focusMessage({ chatId, threadId, messageId: mediaId }); }, ANIMATION_DURATION); } else { - focusMessage({ chatId, threadId, mediaId }); + focusMessage({ chatId, threadId, messageId: mediaId }); } }, [handleClose, isMobile, chatId, threadId, focusMessage, toggleChatInfo, mediaId]); @@ -370,6 +372,7 @@ const MediaViewer: FC = ({ export default memo(withGlobal( (global): StateProps => { + const { mediaViewer, shouldSkipHistoryAnimations } = selectTabState(global); const { chatId, threadId, @@ -377,12 +380,12 @@ export default memo(withGlobal( avatarOwnerId, origin, isHidden, - } = global.mediaViewer; + } = mediaViewer; const { animationLevel, } = global.settings.byKey; - const { shouldSkipHistoryAnimations, currentUserId } = global; + const { currentUserId } = global; let isChatWithSelf = !!chatId && selectIsChatWithSelf(global, chatId); if (origin === MediaViewerOrigin.SearchResult) { diff --git a/src/components/mediaViewer/MediaViewerContent.tsx b/src/components/mediaViewer/MediaViewerContent.tsx index 397ac5ca8..e115b5381 100644 --- a/src/components/mediaViewer/MediaViewerContent.tsx +++ b/src/components/mediaViewer/MediaViewerContent.tsx @@ -10,7 +10,7 @@ import { MediaViewerOrigin } from '../../types'; import { IS_TOUCH_ENV } from '../../util/environment'; import { - selectChat, selectChatMessage, selectIsMessageProtected, selectScheduledMessage, selectUser, + selectChat, selectChatMessage, selectTabState, selectIsMessageProtected, selectScheduledMessage, selectUser, } from '../../global/selectors'; import { calculateMediaViewerDimensions } from '../common/helpers/mediaDimensions'; import { renderMessageText } from '../common/helpers/renderMessageText'; @@ -216,7 +216,7 @@ export default memo(withGlobal( isMuted, playbackRate, isHidden, - } = global.mediaViewer; + } = selectTabState(global).mediaViewer; if (origin === MediaViewerOrigin.SearchResult) { if (!(chatId && mediaId)) { diff --git a/src/components/mediaViewer/SenderInfo.tsx b/src/components/mediaViewer/SenderInfo.tsx index b8dfc8746..b2fc17cd8 100644 --- a/src/components/mediaViewer/SenderInfo.tsx +++ b/src/components/mediaViewer/SenderInfo.tsx @@ -56,9 +56,11 @@ const SenderInfo: FC = ({ const handleFocusMessage = useCallback(() => { closeMediaViewer(); + if (!chatId || !messageId) return; + if (isMobile) { setTimeout(() => { - toggleChatInfo(false, { forceSyncOnIOs: true }); + toggleChatInfo({ force: false }, { forceSyncOnIOs: true }); focusMessage({ chatId, messageId }); }, ANIMATION_DURATION); } else { diff --git a/src/components/mediaViewer/VideoPlayer.tsx b/src/components/mediaViewer/VideoPlayer.tsx index 2b4fc1046..4a503bed1 100644 --- a/src/components/mediaViewer/VideoPlayer.tsx +++ b/src/components/mediaViewer/VideoPlayer.tsx @@ -81,12 +81,12 @@ const VideoPlayer: FC = ({ const handleEnterFullscreen = useCallback(() => { // Yandex browser doesn't support PIP when video is hidden if (IS_YA_BROWSER) return; - setMediaViewerHidden(true); + setMediaViewerHidden({ isHidden: true }); }, [setMediaViewerHidden]); const handleLeaveFullscreen = useCallback(() => { if (IS_YA_BROWSER) return; - setMediaViewerHidden(false); + setMediaViewerHidden({ isHidden: false }); }, [setMediaViewerHidden]); const [ diff --git a/src/components/middle/ActionMessage.tsx b/src/components/middle/ActionMessage.tsx index e4536bcbe..ab98d0852 100644 --- a/src/components/middle/ActionMessage.tsx +++ b/src/components/middle/ActionMessage.tsx @@ -15,6 +15,7 @@ import { selectIsMessageFocused, selectChat, selectTopicFromMessage, + selectTabState, } from '../../global/selectors'; import { getMessageHtmlId, isChatChannel } from '../../global/helpers'; import buildClassName from '../../util/buildClassName'; @@ -242,7 +243,10 @@ export default memo(withGlobal( : undefined; const isFocused = selectIsMessageFocused(global, message); - const { direction: focusDirection, noHighlight: noFocusHighlight } = (isFocused && global.focusedMessage) || {}; + const { + direction: focusDirection, + noHighlight: noFocusHighlight, + } = (isFocused && selectTabState(global).focusedMessage) || {}; const chat = selectChat(global, message.chatId); const isChat = chat && (isChatChannel(chat) || userId === message.chatId); diff --git a/src/components/middle/ActionMessageSuggestedAvatar.tsx b/src/components/middle/ActionMessageSuggestedAvatar.tsx index 33a979e90..0f42dc8bd 100644 --- a/src/components/middle/ActionMessageSuggestedAvatar.tsx +++ b/src/components/middle/ActionMessageSuggestedAvatar.tsx @@ -27,7 +27,7 @@ const ActionMessageSuggestedAvatar: FC = ({ content, }) => { const { - openMediaViewer, uploadProfilePhoto, showNotification, requestNextSettingsScreen, + openMediaViewer, uploadProfilePhoto, showNotification, } = getActions(); const { isOutgoing } = message; @@ -42,10 +42,15 @@ const ActionMessageSuggestedAvatar: FC = ({ showNotification({ title: lang('ApplyAvatarHintTitle'), message: lang('ApplyAvatarHint'), - action: () => requestNextSettingsScreen(SettingsScreens.Main), + action: { + action: 'requestNextSettingsScreen', + payload: { + screen: SettingsScreens.Main, + }, + }, actionText: lang('Open'), }); - }, [lang, requestNextSettingsScreen, showNotification]); + }, [lang, showNotification]); const handleSetSuggestedAvatar = useCallback((file: File) => { setCropModalBlob(undefined); diff --git a/src/components/middle/AudioPlayer.tsx b/src/components/middle/AudioPlayer.tsx index 0e1908bbf..54e46fd31 100644 --- a/src/components/middle/AudioPlayer.tsx +++ b/src/components/middle/AudioPlayer.tsx @@ -14,7 +14,7 @@ import * as mediaLoader from '../../util/mediaLoader'; import { getMediaDuration, getMessageContent, getMessageMediaHash, getSenderTitle, isMessageLocal, } from '../../global/helpers'; -import { selectChat, selectSender } from '../../global/selectors'; +import { selectChat, selectTabState, selectSender } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { makeTrackId } from '../../util/audioPlayer'; import { clearMediaSession } from '../../util/mediaSession'; @@ -320,7 +320,7 @@ export default withGlobal( (global, { message }): StateProps => { const sender = selectSender(global, message); const chat = selectChat(global, message.chatId); - const { volume, playbackRate, isMuted } = global.audioPlayer; + const { volume, playbackRate, isMuted } = selectTabState(global).audioPlayer; return { sender, diff --git a/src/components/middle/ChatReportPanel.tsx b/src/components/middle/ChatReportPanel.tsx index 43e31d7b5..a0f439747 100644 --- a/src/components/middle/ChatReportPanel.tsx +++ b/src/components/middle/ChatReportPanel.tsx @@ -58,13 +58,13 @@ const ChatReportPanel: FC = ({ const handleAddContact = useCallback(() => { openAddContactDialog({ userId: chatId }); if (isAutoArchived) { - toggleChatArchived({ chatId }); + toggleChatArchived({ id: chatId }); } }, [openAddContactDialog, isAutoArchived, toggleChatArchived, chatId]); const handleConfirmBlock = useCallback(() => { closeBlockUserModal(); - blockContact({ contactId: chatId, accessHash }); + blockContact({ contactId: chatId, accessHash: accessHash! }); if (canReportSpam && shouldReportSpam) { reportSpam({ chatId }); } @@ -84,7 +84,7 @@ const ChatReportPanel: FC = ({ closeBlockUserModal(); reportSpam({ chatId }); if (isBasicGroup) { - deleteChatUser({ chatId, userId: currentUserId }); + deleteChatUser({ chatId, userId: currentUserId! }); deleteHistory({ chatId, shouldDeleteForAll: false }); } else { leaveChannel({ chatId }); diff --git a/src/components/middle/DeleteSelectedMessageModal.tsx b/src/components/middle/DeleteSelectedMessageModal.tsx index 06439f05f..886fe3ae9 100644 --- a/src/components/middle/DeleteSelectedMessageModal.tsx +++ b/src/components/middle/DeleteSelectedMessageModal.tsx @@ -2,7 +2,9 @@ import type { FC } from '../../lib/teact/teact'; import React, { useCallback, memo, useEffect } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; -import { selectCanDeleteSelectedMessages, selectCurrentChat, selectUser } from '../../global/selectors'; +import { + selectCanDeleteSelectedMessages, selectCurrentChat, selectTabState, selectUser, +} from '../../global/selectors'; import { isUserId, getUserFirstOrLastName, @@ -51,14 +53,14 @@ const DeleteSelectedMessageModal: FC = ({ const handleDeleteMessageForAll = useCallback(() => { onClose(); - deleteMessages({ messageIds: selectedMessageIds, shouldDeleteForAll: true }); + deleteMessages({ messageIds: selectedMessageIds!, shouldDeleteForAll: true }); }, [deleteMessages, selectedMessageIds, onClose]); const handleDeleteMessageForSelf = useCallback(() => { if (isSchedule) { - deleteScheduledMessages({ messageIds: selectedMessageIds }); + deleteScheduledMessages({ messageIds: selectedMessageIds! }); } else { - deleteMessages({ messageIds: selectedMessageIds, shouldDeleteForAll: false }); + deleteMessages({ messageIds: selectedMessageIds!, shouldDeleteForAll: false }); } onClose(); @@ -111,7 +113,7 @@ const DeleteSelectedMessageModal: FC = ({ export default memo(withGlobal( (global, { isSchedule }): StateProps => { - const { messageIds: selectedMessageIds } = global.selectedMessages || {}; + const { messageIds: selectedMessageIds } = selectTabState(global).selectedMessages || {}; const { canDeleteForAll } = selectCanDeleteSelectedMessages(global); const chat = selectCurrentChat(global); const contactName = chat && isUserId(chat.id) diff --git a/src/components/middle/HeaderActions.tsx b/src/components/middle/HeaderActions.tsx index 785575a52..b2f5dc979 100644 --- a/src/components/middle/HeaderActions.tsx +++ b/src/components/middle/HeaderActions.tsx @@ -95,7 +95,7 @@ const HeaderActions: FC = ({ sendBotCommand, openLocalTextSearch, restartBot, - requestCall, + requestMasterAndRequestCall, requestNextManagementScreen, showNotification, openChat, @@ -168,7 +168,7 @@ const HeaderActions: FC = ({ }, [chatId, openChat]); function handleRequestCall() { - requestCall({ userId: chatId }); + requestMasterAndRequestCall({ userId: chatId }); } const handleHotkeySearchClick = useCallback((e: KeyboardEvent) => { diff --git a/src/components/middle/HeaderMenuContainer.tsx b/src/components/middle/HeaderMenuContainer.tsx index 0763f0f66..c667c45b0 100644 --- a/src/components/middle/HeaderMenuContainer.tsx +++ b/src/components/middle/HeaderMenuContainer.tsx @@ -17,7 +17,7 @@ import { selectUser, selectChatBot, selectIsPremiumPurchaseBlocked, - selectCurrentMessageList, + selectCurrentMessageList, selectTabState, } from '../../global/selectors'; import { isUserId, @@ -150,11 +150,11 @@ const HeaderMenuContainer: FC = ({ enterMessageSelectMode, sendBotCommand, restartBot, - joinGroupCall, + requestMasterAndJoinGroupCall, createGroupCall, openLinkedChat, openAddContactDialog, - requestCall, + requestMasterAndRequestCall, toggleStatistics, openGiftPremiumModal, openChatWithInfo, @@ -239,12 +239,12 @@ const HeaderMenuContainer: FC = ({ chatId, }); } else { - joinGroupCall({ + requestMasterAndJoinGroupCall({ chatId, }); } closeMenu(); - }, [closeMenu, canCreateVoiceChat, chatId, joinGroupCall, createGroupCall]); + }, [closeMenu, canCreateVoiceChat, chatId, requestMasterAndJoinGroupCall, createGroupCall]); const handleLinkedChatClick = useCallback(() => { openLinkedChat({ id: chatId }); @@ -267,14 +267,14 @@ const HeaderMenuContainer: FC = ({ }, [closeMenu, onSubscribeChannel]); const handleVideoCall = useCallback(() => { - requestCall({ userId: chatId, isVideo: true }); + requestMasterAndRequestCall({ userId: chatId, isVideo: true }); closeMenu(); - }, [chatId, closeMenu, requestCall]); + }, [chatId, closeMenu, requestMasterAndRequestCall]); const handleCall = useCallback(() => { - requestCall({ userId: chatId }); + requestMasterAndRequestCall({ userId: chatId }); closeMenu(); - }, [chatId, closeMenu, requestCall]); + }, [chatId, closeMenu, requestMasterAndRequestCall]); const handleSearch = useCallback(() => { onSearchClick(); @@ -573,7 +573,8 @@ export default memo(withGlobal( canGiftPremium, hasLinkedChat: Boolean(chat?.fullInfo?.linkedChatId), botCommands: chatBot?.fullInfo?.botInfo?.commands, - isChatInfoShown: global.isChatInfoShown && currentChatId === chatId && currentThreadId === threadId, + isChatInfoShown: selectTabState(global).isChatInfoShown + && currentChatId === chatId && currentThreadId === threadId, canCreateTopic, canEditTopic, }; diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 9137663f2..c7ed50aa2 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -28,7 +28,7 @@ import { selectFirstMessageId, selectChatScheduledMessages, selectCurrentMessageIds, - selectIsCurrentUserPremium, + selectIsCurrentUserPremium, selectLastScrollOffset, } from '../../global/selectors'; import { isChatChannel, @@ -169,7 +169,11 @@ const MessageList: FC = ({ // We update local cached `scrollOffsetRef` when opening chat. // Then we update global version every second on scrolling. - const scrollOffsetRef = useRef((type === 'thread' && selectScrollOffset(getGlobal(), chatId, threadId)) || 0); + const scrollOffsetRef = useRef((type === 'thread' + && selectScrollOffset(getGlobal(), chatId, threadId)) + || selectLastScrollOffset(getGlobal(), chatId, threadId) + || 0); + const anchorIdRef = useRef(); const anchorTopRef = useRef(); const listItemElementsRef = useRef(); diff --git a/src/components/middle/MessageListHistoryHandler.tsx b/src/components/middle/MessageListHistoryHandler.tsx index b8212f085..2f8703458 100644 --- a/src/components/middle/MessageListHistoryHandler.tsx +++ b/src/components/middle/MessageListHistoryHandler.tsx @@ -1,10 +1,12 @@ -import type { FC } from '../../lib/teact/teact'; import React, { memo } from '../../lib/teact/teact'; -import { getActions, withGlobal } from '../../lib/teact/teactn'; +import { getActions, withGlobal } from '../../global'; + +import type { FC } from '../../lib/teact/teact'; +import type { MessageList as GlobalMessageList } from '../../global/types'; import { createLocationHash } from '../../util/routing'; +import { selectTabState } from '../../global/selectors'; import useHistoryBack from '../../hooks/useHistoryBack'; -import type { MessageList as GlobalMessageList } from '../../global/types'; type StateProps = { messageLists?: GlobalMessageList[]; @@ -44,7 +46,7 @@ const MessageListHistoryHandler: FC = ({ messageLists }) => { export default memo(withGlobal( (global): StateProps => { return { - messageLists: global.messages.messageLists, + messageLists: selectTabState(global).messageLists, }; }, )(MessageListHistoryHandler)); diff --git a/src/components/middle/MessageSelectToolbar.tsx b/src/components/middle/MessageSelectToolbar.tsx index 7cf957f3b..527680723 100644 --- a/src/components/middle/MessageSelectToolbar.tsx +++ b/src/components/middle/MessageSelectToolbar.tsx @@ -9,7 +9,7 @@ import { selectCanDownloadSelectedMessages, selectCanForwardMessages, selectCanReportSelectedMessages, - selectCurrentMessageList, + selectCurrentMessageList, selectTabState, selectHasProtectedMessage, selectSelectedMessagesCount, } from '../../global/selectors'; @@ -72,16 +72,21 @@ const MessageSelectToolbar: FC = ({ const [isReportModalOpen, openReportModal, closeReportModal] = useFlag(); useCopySelectedMessages(Boolean(isActive), copySelectedMessages); + + const handleExitMessageSelectMode = useCallback(() => { + exitMessageSelectMode(); + }, [exitMessageSelectMode]); + useEffect(() => { return isActive && !isDeleteModalOpen && !isReportModalOpen && !isAnyModalOpen ? captureKeyboardListeners({ onBackspace: canDeleteMessages ? openDeleteModal : undefined, onDelete: canDeleteMessages ? openDeleteModal : undefined, - onEsc: exitMessageSelectMode, + onEsc: handleExitMessageSelectMode, }) : undefined; }, [ - isActive, isDeleteModalOpen, isReportModalOpen, openDeleteModal, exitMessageSelectMode, isAnyModalOpen, + isActive, isDeleteModalOpen, isReportModalOpen, openDeleteModal, handleExitMessageSelectMode, isAnyModalOpen, canDeleteMessages, ]); @@ -134,7 +139,7 @@ const MessageSelectToolbar: FC = ({ @@ -213,7 +217,7 @@ export default memo(withGlobal( query, totalCount, foundIds, - isHistoryCalendarOpen: Boolean(global.historyCalendarSelectedAt), + isHistoryCalendarOpen: Boolean(selectTabState(global).historyCalendarSelectedAt), }; }, )(MobileSearchFooter)); diff --git a/src/components/middle/ReactorListModal.tsx b/src/components/middle/ReactorListModal.tsx index c38879a7e..fc5ef0cac 100644 --- a/src/components/middle/ReactorListModal.tsx +++ b/src/components/middle/ReactorListModal.tsx @@ -8,7 +8,7 @@ import type { ApiAvailableReaction, ApiMessage, ApiReaction } from '../../api/ty import type { AnimationLevel } from '../../types'; import { LoadMoreDirection } from '../../types'; -import { selectChatMessage } from '../../global/selectors'; +import { selectChatMessage, selectTabState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { formatIntegerCompact } from '../../util/textFormat'; import { unique } from '../../util/iteratees'; @@ -97,8 +97,8 @@ const ReactorListModal: FC = ({ const handleLoadMore = useCallback(() => { loadReactors({ - chatId, - messageId, + chatId: chatId!, + messageId: messageId!, }); }, [chatId, loadReactors, messageId]); @@ -225,7 +225,7 @@ const ReactorListModal: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { chatId, messageId } = global.reactorModal || {}; + const { chatId, messageId } = selectTabState(global).reactorModal || {}; const message = chatId && messageId ? selectChatMessage(global, chatId, messageId) : undefined; return { diff --git a/src/components/middle/composer/BotCommandMenu.tsx b/src/components/middle/composer/BotCommandMenu.tsx index af33b3838..eecc8d88c 100644 --- a/src/components/middle/composer/BotCommandMenu.tsx +++ b/src/components/middle/composer/BotCommandMenu.tsx @@ -30,7 +30,6 @@ const BotCommandMenu: FC = ({ const handleClick = useCallback((botCommand: ApiBotCommand) => { sendBotCommand({ command: `/${botCommand.command}`, - botId: botCommand.botId, }); onClose(); }, [onClose, sendBotCommand]); diff --git a/src/components/middle/composer/BotCommandTooltip.tsx b/src/components/middle/composer/BotCommandTooltip.tsx index d4e60712b..775b7ec70 100644 --- a/src/components/middle/composer/BotCommandTooltip.tsx +++ b/src/components/middle/composer/BotCommandTooltip.tsx @@ -46,7 +46,6 @@ const BotCommandTooltip: FC = ({ const bot = usersById[botId]; sendBotCommand({ command: `/${command}${withUsername && bot ? `@${bot.usernames![0].username}` : ''}`, - botId, }); onClick(); }, [onClick, sendBotCommand, usersById, withUsername]); diff --git a/src/components/middle/composer/Composer.tsx b/src/components/middle/composer/Composer.tsx index e66502105..d151b2984 100644 --- a/src/components/middle/composer/Composer.tsx +++ b/src/components/middle/composer/Composer.tsx @@ -4,7 +4,7 @@ import React, { } from '../../../lib/teact/teact'; import { getActions, withGlobal } from '../../../global'; -import type { GlobalState, MessageListType } from '../../../global/types'; +import type { TabState, MessageListType, GlobalState } from '../../../global/types'; import type { ApiAttachment, ApiBotInlineResult, @@ -53,6 +53,7 @@ import { selectIsCurrentUserPremium, selectChatType, selectRequestedDraftFiles, + selectTabState, } from '../../../global/selectors'; import { getAllowedAttachmentOptions, @@ -148,7 +149,7 @@ type StateProps = isRightColumnShown?: boolean; isSelectModeActive?: boolean; isForwarding?: boolean; - pollModal: GlobalState['pollModal']; + pollModal: TabState['pollModal']; botKeyboardMessageId?: number; botKeyboardPlaceholder?: string; withScheduledButton?: boolean; @@ -160,7 +161,7 @@ type StateProps = currentUserId?: string; recentEmojis: string[]; lastSyncTime?: number; - contentToBeScheduled?: GlobalState['messages']['contentToBeScheduled']; + contentToBeScheduled?: TabState['contentToBeScheduled']; shouldSuggestStickers?: boolean; shouldSuggestCustomEmoji?: boolean; baseEmojiKeywords?: Record; @@ -195,7 +196,7 @@ enum MainButtonState { Schedule = 'schedule', } -type ScheduledMessageArgs = GlobalState['messages']['contentToBeScheduled'] | { +type ScheduledMessageArgs = TabState['contentToBeScheduled'] | { id: string; queryId: string; isSilent?: boolean; }; @@ -283,7 +284,6 @@ const Composer: FC = ({ loadSendAs, resetOpenChatWithDraft, callAttachBot, - openPremiumModal, addRecentCustomEmoji, showNotification, } = getActions(); @@ -578,18 +578,24 @@ const Composer: FC = ({ if (!notificationNumber) { showNotification({ message: lang('UnlockPremiumEmojiHint'), - action: () => openPremiumModal({ initialSection: 'animated_emoji' }), + action: { + action: 'openPremiumModal', + payload: { initialSection: 'animated_emoji' }, + }, actionText: lang('PremiumMore'), }); } else { showNotification({ message: lang('UnlockPremiumEmojiHint2'), - action: () => openChat({ id: currentUserId, shouldReplaceHistory: true }), + action: { + action: 'openChat', + payload: { id: currentUserId, shouldReplaceHistory: true }, + }, actionText: lang('Open'), }); } customEmojiNotificationNumber.current = Number(!notificationNumber); - }, [currentUserId, lang, openChat, openPremiumModal, showNotification]); + }, [currentUserId, lang, showNotification]); const [handleEditComplete, handleEditCancel] = useEditing( htmlRef, @@ -627,7 +633,7 @@ const Composer: FC = ({ data: { message: 'MESSAGE_TOO_LONG_PLEASE_REMOVE_CHARACTERS', textParams: { - '{EXTRA_CHARS_COUNT}': extraLength, + '{EXTRA_CHARS_COUNT}': extraLength.toString(), '{PLURAL_S}': extraLength > 1 ? 's' : '', }, hasErrorKey: true, @@ -1523,6 +1529,8 @@ export default memo(withGlobal( ? selectEditingScheduledDraft(global, chatId) : selectEditingDraft(global, chatId, threadId); + const tabState = selectTabState(global); + return { editingMessage: selectEditingMessage(global, chatId, threadId, messageListType), connectionState: global.connectionState, @@ -1542,22 +1550,22 @@ export default memo(withGlobal( shouldSchedule: messageListType === 'scheduled', botKeyboardMessageId, botKeyboardPlaceholder: keyboardMessage?.keyboardPlaceholder, - isForwarding: chatId === global.forwardMessages.toChatId, - pollModal: global.pollModal, + isForwarding: chatId === tabState.forwardMessages.toChatId, + pollModal: tabState.pollModal, stickersForEmoji: global.stickers.forEmoji.stickers, customEmojiForEmoji: global.customEmojis.forEmoji.stickers, groupChatMembers: chat?.fullInfo?.members, topInlineBotIds: global.topInlineBots?.userIds, currentUserId, lastSyncTime: global.lastSyncTime, - contentToBeScheduled: global.messages.contentToBeScheduled, + contentToBeScheduled: tabState.contentToBeScheduled, shouldSuggestStickers, shouldSuggestCustomEmoji, recentEmojis: global.recentEmojis, baseEmojiKeywords: baseEmojiKeywords?.keywords, emojiKeywords: emojiKeywords?.keywords, - inlineBots: global.inlineBots.byUsername, - isInlineBotLoading: global.inlineBots.isLoading, + inlineBots: tabState.inlineBots.byUsername, + isInlineBotLoading: tabState.inlineBots.isLoading, chatBotCommands: chat?.fullInfo && chat.fullInfo.botCommands, botCommands: chatBot?.fullInfo ? (chatBot.fullInfo.botInfo?.commands || false) : undefined, botMenuButton: chatBot?.fullInfo?.botInfo?.menuButton, diff --git a/src/components/middle/composer/ComposerEmbeddedMessage.tsx b/src/components/middle/composer/ComposerEmbeddedMessage.tsx index c89237bc1..331588049 100644 --- a/src/components/middle/composer/ComposerEmbeddedMessage.tsx +++ b/src/components/middle/composer/ComposerEmbeddedMessage.tsx @@ -19,6 +19,7 @@ import { selectEditingMessage, selectIsChatWithSelf, selectIsCurrentUserPremium, + selectTabState, } from '../../../global/selectors'; import captureEscKeyListener from '../../../util/captureEscKeyListener'; import buildClassName from '../../../util/buildClassName'; @@ -220,7 +221,9 @@ const ComposerEmbeddedMessage: FC = ({ icon={!noAuthors ? 'message-succeeded' : undefined} customIcon={noAuthors ? : undefined} // eslint-disable-next-line react/jsx-no-bind - onClick={() => setForwardNoAuthors(false)} + onClick={() => setForwardNoAuthors({ + noAuthors: false, + })} > {lang(forwardedMessagesCount > 1 ? 'ShowSenderNames' : 'ShowSendersName')} @@ -228,7 +231,9 @@ const ComposerEmbeddedMessage: FC = ({ icon={noAuthors ? 'message-succeeded' : undefined} customIcon={!noAuthors ? : undefined} // eslint-disable-next-line react/jsx-no-bind - onClick={() => setForwardNoAuthors(true)} + onClick={() => setForwardNoAuthors({ + noAuthors: true, + })} > {lang(forwardedMessagesCount > 1 ? 'HideSenderNames' : 'HideSendersName')} @@ -239,7 +244,9 @@ const ComposerEmbeddedMessage: FC = ({ icon={!noCaptions ? 'message-succeeded' : undefined} customIcon={noCaptions ? : undefined} // eslint-disable-next-line react/jsx-no-bind - onClick={() => setForwardNoCaptions(false)} + onClick={() => setForwardNoCaptions({ + noCaptions: false, + })} > {lang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.ShowCaption' : 'ShowCaption')} @@ -247,7 +254,9 @@ const ComposerEmbeddedMessage: FC = ({ icon={noCaptions ? 'message-succeeded' : undefined} customIcon={!noCaptions ? : undefined} // eslint-disable-next-line react/jsx-no-bind - onClick={() => setForwardNoCaptions(true)} + onClick={() => setForwardNoCaptions({ + noCaptions: true, + })} > {lang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.HideCaption' : 'HideCaption')} @@ -275,7 +284,7 @@ export default memo(withGlobal( forwardMessages: { fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions, }, - } = global; + } = selectTabState(global); const replyingToId = selectReplyingToId(global, chatId, threadId); const editingId = messageListType === 'scheduled' diff --git a/src/components/middle/composer/SendAsMenu.tsx b/src/components/middle/composer/SendAsMenu.tsx index b8d26b45b..35e97974c 100644 --- a/src/components/middle/composer/SendAsMenu.tsx +++ b/src/components/middle/composer/SendAsMenu.tsx @@ -38,7 +38,7 @@ const SendAsMenu: FC = ({ isCurrentUserPremium, onClose, }) => { - const { saveDefaultSendAs, showNotification, openPremiumModal } = getActions(); + const { saveDefaultSendAs, showNotification } = getActions(); // No need for expensive global updates on users and chats, so we avoid them const usersById = getGlobal().users.byId; @@ -58,7 +58,7 @@ const SendAsMenu: FC = ({ const handleUserSelect = useCallback((id: string) => { onClose(); - saveDefaultSendAs({ chatId, sendAsId: id }); + saveDefaultSendAs({ chatId: chatId!, sendAsId: id }); }, [chatId, onClose, saveDefaultSendAs]); const selectedSendAsIndex = useKeyboardNavigation({ @@ -106,7 +106,10 @@ const SendAsMenu: FC = ({ showNotification({ message: lang('SelectSendAsPeerPremiumHint'), actionText: lang('Open'), - action: () => openPremiumModal(), + action: { + action: 'openPremiumModal', + payload: {}, + }, }); } }; diff --git a/src/components/middle/composer/SymbolMenu.tsx b/src/components/middle/composer/SymbolMenu.tsx index e784292a7..adb447217 100644 --- a/src/components/middle/composer/SymbolMenu.tsx +++ b/src/components/middle/composer/SymbolMenu.tsx @@ -10,7 +10,7 @@ import type { GlobalActions } from '../../../global/types'; import { IS_TOUCH_ENV } from '../../../util/environment'; import { fastRaf } from '../../../util/schedulers'; import buildClassName from '../../../util/buildClassName'; -import { selectIsCurrentUserPremium } from '../../../global/selectors'; +import { selectTabState, selectIsCurrentUserPremium } from '../../../global/selectors'; import useShowTransition from '../../../hooks/useShowTransition'; import useMouseInside from '../../../hooks/useMouseInside'; @@ -304,7 +304,7 @@ const SymbolMenu: FC = ({ export default memo(withGlobal( (global): StateProps => { return { - isLeftColumnShown: global.isLeftColumnShown, + isLeftColumnShown: selectTabState(global).isLeftColumnShown, isCurrentUserPremium: selectIsCurrentUserPremium(global), lastSyncTime: global.lastSyncTime, }; diff --git a/src/components/middle/composer/WebPagePreview.tsx b/src/components/middle/composer/WebPagePreview.tsx index ba9f8cd3e..d945fa0dc 100644 --- a/src/components/middle/composer/WebPagePreview.tsx +++ b/src/components/middle/composer/WebPagePreview.tsx @@ -7,7 +7,7 @@ import { ApiMessageEntityTypes } from '../../../api/types'; import type { ISettings } from '../../../types'; import { RE_LINK_TEMPLATE } from '../../../config'; -import { selectNoWebPage, selectTheme } from '../../../global/selectors'; +import { selectTabState, selectNoWebPage, selectTheme } from '../../../global/selectors'; import parseMessageInput from '../../../util/parseMessageInput'; import useOnChange from '../../../hooks/useOnChange'; import useShowTransition from '../../../hooks/useShowTransition'; @@ -131,7 +131,7 @@ export default memo(withGlobal( const noWebPage = selectNoWebPage(global, chatId, threadId); return { theme: selectTheme(global), - webPagePreview: global.webPagePreview, + webPagePreview: selectTabState(global).webPagePreview, noWebPage, }; }, diff --git a/src/components/middle/composer/helpers/buildAttachment.ts b/src/components/middle/composer/helpers/buildAttachment.ts index 28ea43446..64533bec6 100644 --- a/src/components/middle/composer/helpers/buildAttachment.ts +++ b/src/components/middle/composer/helpers/buildAttachment.ts @@ -77,7 +77,9 @@ export default async function buildAttachment( }; } -export function prepareAttachmentsToSend(attachments: ApiAttachment[], shouldSendCompressed?: boolean) { +export function prepareAttachmentsToSend( + attachments: ApiAttachment[], shouldSendCompressed?: boolean, +): ApiAttachment[] { return attachments.map((attach) => { if (shouldSendCompressed) { if (attach.compressedBlobUrl) { diff --git a/src/components/middle/composer/hooks/useEditing.ts b/src/components/middle/composer/hooks/useEditing.ts index bf3fe3204..dbefddc7b 100644 --- a/src/components/middle/composer/hooks/useEditing.ts +++ b/src/components/middle/composer/hooks/useEditing.ts @@ -91,7 +91,6 @@ const useEditing = ( } editMessage({ - messageId: editedMessage.id, text, entities, }); diff --git a/src/components/middle/composer/hooks/useInlineBotTooltip.ts b/src/components/middle/composer/hooks/useInlineBotTooltip.ts index dc7406cc9..19ab05332 100644 --- a/src/components/middle/composer/hooks/useInlineBotTooltip.ts +++ b/src/components/middle/composer/hooks/useInlineBotTooltip.ts @@ -49,14 +49,14 @@ export default function useInlineBotTooltip( useEffect(() => { if (isAllowed && usernameLowered && chatId) { - queryInlineBot({ chatId, username: usernameLowered, query }); + queryInlineBot({ chatId, username: usernameLowered, query: query! }); } }, [query, isAllowed, queryInlineBot, chatId, usernameLowered]); const loadMore = useCallback(() => { if (isAllowed && usernameLowered && chatId) { queryInlineBot({ - chatId, username: usernameLowered, query, offset, + chatId, username: usernameLowered, query: query!, offset, }); } }, [isAllowed, usernameLowered, chatId, queryInlineBot, query, offset]); @@ -70,7 +70,7 @@ export default function useInlineBotTooltip( }, [botId, isAllowed, markIsOpen, results, switchPm, unmarkIsOpen]); if (prevUsername !== username) { - resetInlineBot({ username: prevUsername }); + resetInlineBot({ username: prevUsername! }); } return { diff --git a/src/components/middle/message/ContextMenuContainer.tsx b/src/components/middle/message/ContextMenuContainer.tsx index 00c15f91e..b9ba2718e 100644 --- a/src/components/middle/message/ContextMenuContainer.tsx +++ b/src/components/middle/message/ContextMenuContainer.tsx @@ -281,12 +281,12 @@ const ContextMenuContainer: FC = ({ const handleFaveSticker = useCallback(() => { closeMenu(); - faveSticker({ sticker: message.content.sticker }); + faveSticker({ sticker: message.content.sticker! }); }, [closeMenu, message.content.sticker, faveSticker]); const handleUnfaveSticker = useCallback(() => { closeMenu(); - unfaveSticker({ sticker: message.content.sticker }); + unfaveSticker({ sticker: message.content.sticker! }); }, [closeMenu, message.content.sticker, unfaveSticker]); const handleCancelVote = useCallback(() => { @@ -369,7 +369,7 @@ const ContextMenuContainer: FC = ({ const handleSaveGif = useCallback(() => { const video = getMessageVideo(message); - saveGif({ gif: video }); + saveGif({ gif: video! }); closeMenu(); }, [closeMenu, message, saveGif]); diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index 9253e2a63..422a81875 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -63,6 +63,7 @@ import { selectIsCurrentUserPremium, selectIsChatProtected, selectTopicFromMessage, + selectTabState, } from '../../../global/selectors'; import { getMessageContent, @@ -1204,8 +1205,9 @@ const Message: FC = ({ export default memo(withGlobal( (global, ownProps): StateProps => { const { - focusedMessage, forwardMessages, lastSyncTime, - } = global; + focusedMessage, forwardMessages, activeReactions, activeEmojiInteractions, + } = selectTabState(global); + const { lastSyncTime } = global; const { message, album, withSenderName, withAvatar, threadId, messageListType, isLastInDocumentGroup, isFirstInGroup, } = ownProps; @@ -1330,8 +1332,8 @@ export default memo(withGlobal( repliesThreadInfo: actualRepliesThreadInfo, availableReactions: global.availableReactions, defaultReaction: isMessageLocal(message) ? undefined : selectDefaultReaction(global, chatId), - activeReactions: reactionMessage && global.activeReactions[reactionMessage.id], - activeEmojiInteractions: global.activeEmojiInteractions, + activeReactions: reactionMessage && activeReactions[reactionMessage.id], + activeEmojiInteractions, ...(isOutgoing && { outgoingStatus: selectOutgoingStatus(global, message, messageListType === 'scheduled') }), ...(typeof uploadProgress === 'number' && { uploadProgress }), ...(isFocused && { focusDirection, noFocusHighlight, isResizingContainer }), diff --git a/src/components/middle/message/MessagePhoneCall.tsx b/src/components/middle/message/MessagePhoneCall.tsx index 54d94f720..92ee5f22d 100644 --- a/src/components/middle/message/MessagePhoneCall.tsx +++ b/src/components/middle/message/MessagePhoneCall.tsx @@ -24,7 +24,7 @@ const MessagePhoneCall: FC = ({ message, chatId, }) => { - const { requestCall } = getActions(); + const { requestMasterAndRequestCall } = getActions(); const lang = useLang(); const { isOutgoing, isVideo, reason } = phoneCall; @@ -32,8 +32,8 @@ const MessagePhoneCall: FC = ({ const isCancelled = reason === 'busy' && !isOutgoing; const handleCall = useCallback(() => { - requestCall({ isVideo, userId: chatId }); - }, [chatId, isVideo, requestCall]); + requestMasterAndRequestCall({ isVideo, userId: chatId }); + }, [chatId, isVideo, requestMasterAndRequestCall]); const reasonText = useMemo(() => { if (isVideo) { diff --git a/src/components/middle/message/SponsoredMessage.tsx b/src/components/middle/message/SponsoredMessage.tsx index a4881020d..215c6748b 100644 --- a/src/components/middle/message/SponsoredMessage.tsx +++ b/src/components/middle/message/SponsoredMessage.tsx @@ -89,7 +89,7 @@ const SponsoredMessage: FC = ({ if (message.chatInviteHash) { openChatByInvite({ hash: message.chatInviteHash }); } else if (message.channelPostId) { - focusMessage({ chatId: message.chatId, messageId: message.channelPostId }); + focusMessage({ chatId: message.chatId!, messageId: message.channelPostId }); } else { openChat({ id: message.chatId }); diff --git a/src/components/middle/message/Sticker.tsx b/src/components/middle/message/Sticker.tsx index cdcd63dd2..955bb068b 100644 --- a/src/components/middle/message/Sticker.tsx +++ b/src/components/middle/message/Sticker.tsx @@ -89,7 +89,12 @@ const Sticker: FC = ({ if (isPlayingEffect) { showNotification({ message: lang('PremiumStickerTooltip'), - action: openModal, + action: { + action: 'openStickerSet', + payload: { + stickerSetInfo: sticker.stickerSetInfo, + }, + }, actionText: lang('ViewAction'), }); return; @@ -100,7 +105,10 @@ const Sticker: FC = ({ } } openModal(); - }, [hasEffect, isPlayingEffect, lang, onPlayEffect, openModal, showNotification, startPlayingEffect]); + }, [ + hasEffect, isPlayingEffect, lang, onPlayEffect, openModal, showNotification, startPlayingEffect, + sticker.stickerSetInfo, + ]); const isMemojiSticker = 'isMissing' in stickerSetInfo; const { width, height } = getStickerDimensions(sticker, isMobile); diff --git a/src/components/middle/message/hooks/useInnerHandlers.ts b/src/components/middle/message/hooks/useInnerHandlers.ts index fecc69401..b4853ba52 100644 --- a/src/components/middle/message/hooks/useInnerHandlers.ts +++ b/src/components/middle/message/hooks/useInnerHandlers.ts @@ -71,7 +71,7 @@ export default function useInnerHandlers( focusMessage({ chatId: isChatWithRepliesBot && replyToChatId ? replyToChatId : chatId, threadId, - messageId: replyToMessageId, + messageId: replyToMessageId!, replyMessageId: isChatWithRepliesBot && replyToChatId ? undefined : messageId, }); }, [focusMessage, isChatWithRepliesBot, replyToChatId, chatId, threadId, replyToMessageId, messageId]); @@ -132,7 +132,7 @@ export default function useInnerHandlers( const handleFocusForwarded = useCallback(() => { if (isInDocumentGroup) { focusMessage({ - chatId: forwardInfo!.fromChatId, groupedId, groupedChatId: chatId, + chatId: forwardInfo!.fromChatId!, groupedId, groupedChatId: chatId, messageId: forwardInfo!.fromMessageId!, }); return; } @@ -140,12 +140,12 @@ export default function useInnerHandlers( if (isChatWithRepliesBot && replyToChatId) { focusMessageInComments({ chatId: replyToChatId, - threadId: replyToTopMessageId, - messageId: forwardInfo!.fromMessageId, + threadId: replyToTopMessageId!, + messageId: forwardInfo!.fromMessageId!, }); } else { focusMessage({ - chatId: forwardInfo!.fromChatId, messageId: forwardInfo!.fromMessageId, + chatId: forwardInfo!.fromChatId!, messageId: forwardInfo!.fromMessageId!, }); } }, [ diff --git a/src/components/payment/PasswordConfirm.tsx b/src/components/payment/PasswordConfirm.tsx index 753ebdfd7..cbf0d4692 100644 --- a/src/components/payment/PasswordConfirm.tsx +++ b/src/components/payment/PasswordConfirm.tsx @@ -5,6 +5,7 @@ import type { FC } from '../../lib/teact/teact'; import type { ApiPaymentCredentials } from '../../api/types'; import type { FormState } from '../../hooks/reducers/usePaymentReducer'; +import { selectTabState } from '../../global/selectors'; import useLang from '../../hooks/useLang'; import PasswordMonkey from '../common/PasswordMonkey'; @@ -62,9 +63,10 @@ const PasswordConfirm: FC = ({ }; export default memo(withGlobal((global): StateProps => { + const { payment } = selectTabState(global); return { - error: global.payment.error?.message, + error: payment.error?.message, passwordHint: global.twoFaSettings.hint, - savedCredentials: global.payment.savedCredentials, + savedCredentials: payment.savedCredentials, }; })(PasswordConfirm)); diff --git a/src/components/payment/PaymentModal.tsx b/src/components/payment/PaymentModal.tsx index ed95037f5..af8a18eb8 100644 --- a/src/components/payment/PaymentModal.tsx +++ b/src/components/payment/PaymentModal.tsx @@ -4,13 +4,13 @@ import React, { } from '../../lib/teact/teact'; import { getActions, withGlobal } from '../../global'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import type { ApiChat, ApiCountry, ApiPaymentCredentials } from '../../api/types'; import type { Price, ShippingOption } from '../../types'; import type { FormState } from '../../hooks/reducers/usePaymentReducer'; import { PaymentStep } from '../../types'; -import { selectChat } from '../../global/selectors'; +import { selectChat, selectTabState } from '../../global/selectors'; import { formatCurrency } from '../../util/formatCurrency'; import buildClassName from '../../util/buildClassName'; import { detectCardTypeText } from '../common/helpers/detectCardType'; @@ -67,7 +67,7 @@ type StateProps = { isExtendedMedia?: boolean; }; -type GlobalStateProps = Pick; @@ -223,6 +223,10 @@ const PaymentModal: FC = ({ setStep(PaymentStep.PaymentInfo); }, [setStep]); + const handleClearPaymentError = useCallback(() => { + clearPaymentError(); + }, [clearPaymentError]); + function renderError() { if (!error) { return undefined; @@ -237,7 +241,7 @@ const PaymentModal: FC = ({

    {error.description || 'Error'}

    @@ -595,7 +599,7 @@ export default memo(withGlobal( savedCredentials, temporaryPassword, isExtendedMedia, - } = global.payment; + } = selectTabState(global).payment; const chat = inputInvoice && 'chatId' in inputInvoice ? selectChat(global, inputInvoice.chatId) : undefined; const isProviderError = Boolean(invoice && (!nativeProvider || !SUPPORTED_PROVIDERS.has(nativeProvider))); @@ -721,7 +725,19 @@ function getRequestInfo(paymentState: FormState) { }; } -function getCredentials(paymentState: FormState) { +export type ApiCredentials = { + data: { + cardNumber: string; + cardholder: string; + expiryMonth: string; + expiryYear: string; + cvv: string; + country: string; + zip: string; + }; +}; + +function getCredentials(paymentState: FormState): ApiCredentials { const { cardNumber, cardholder, expiry, cvv, billingCountry, billingZip, } = paymentState; diff --git a/src/components/payment/ReceiptModal.tsx b/src/components/payment/ReceiptModal.tsx index bddae5898..a78633b05 100644 --- a/src/components/payment/ReceiptModal.tsx +++ b/src/components/payment/ReceiptModal.tsx @@ -6,6 +6,7 @@ import type { FC } from '../../lib/teact/teact'; import type { Price } from '../../types'; import type { ApiShippingAddress, ApiWebDocument } from '../../api/types'; +import { selectTabState } from '../../global/selectors'; import useLang from '../../hooks/useLang'; import useFlag from '../../hooks/useFlag'; @@ -116,7 +117,7 @@ const ReceiptModal: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { receipt } = global.payment; + const { receipt } = selectTabState(global).payment; const { currency, prices, diff --git a/src/components/right/AddChatMembers.tsx b/src/components/right/AddChatMembers.tsx index 8be2f5df7..e0c8f4ea8 100644 --- a/src/components/right/AddChatMembers.tsx +++ b/src/components/right/AddChatMembers.tsx @@ -10,7 +10,7 @@ import type { import { NewChatMembersProgress } from '../../types'; import { unique } from '../../util/iteratees'; -import { selectChat } from '../../global/selectors'; +import { selectChat, selectTabState } from '../../global/selectors'; import { filterUsersByName, isChatChannel, isUserBot, sortChatIds, } from '../../global/helpers'; @@ -153,7 +153,8 @@ export default memo(withGlobal( const chat = selectChat(global, chatId); const { userIds: localContactIds } = global.contactList || {}; const { byId: chatsById } = global.chats; - const { currentUserId, newChatMembersProgress } = global; + const { newChatMembersProgress } = selectTabState(global); + const { currentUserId } = global; const isChannel = chat && isChatChannel(chat); const { @@ -161,7 +162,7 @@ export default memo(withGlobal( fetchingStatus, globalUserIds, localUserIds, - } = global.userSearch; + } = selectTabState(global).userSearch; return { isChannel, diff --git a/src/components/right/CreateTopic.tsx b/src/components/right/CreateTopic.tsx index 70414272d..c237b5095 100644 --- a/src/components/right/CreateTopic.tsx +++ b/src/components/right/CreateTopic.tsx @@ -5,10 +5,10 @@ import { getActions, withGlobal } from '../../global'; import type { FC } from '../../lib/teact/teact'; import type { ApiChat, ApiSticker } from '../../api/types'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import { DEFAULT_TOPIC_ICON_STICKER_ID } from '../../config'; -import { selectChat, selectIsCurrentUserPremium } from '../../global/selectors'; +import { selectChat, selectIsCurrentUserPremium, selectTabState } from '../../global/selectors'; import { getTopicColors } from '../../util/forumColors'; import cycleRestrict from '../../util/cycleRestrict'; import buildClassName from '../../util/buildClassName'; @@ -35,7 +35,7 @@ export type OwnProps = { type StateProps = { chat?: ApiChat; - createTopicPanel?: GlobalState['createTopicPanel']; + createTopicPanel?: TabState['createTopicPanel']; isCurrentUserPremium?: boolean; }; @@ -159,7 +159,7 @@ const CreateTopic: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { createTopicPanel } = global; + const { createTopicPanel } = selectTabState(global); return { chat: createTopicPanel?.chatId ? selectChat(global, createTopicPanel.chatId) : undefined, createTopicPanel, diff --git a/src/components/right/DeleteMemberModal.tsx b/src/components/right/DeleteMemberModal.tsx index 0cbca71cd..aeb87ccd9 100644 --- a/src/components/right/DeleteMemberModal.tsx +++ b/src/components/right/DeleteMemberModal.tsx @@ -34,7 +34,7 @@ const DeleteMemberModal: FC = ({ const lang = useLang(); const handleDeleteChatMember = useCallback(() => { - deleteChatMember({ chatId: chat!.id, userId }); + deleteChatMember({ chatId: chat!.id, userId: userId! }); onClose(); }, [chat, deleteChatMember, onClose, userId]); diff --git a/src/components/right/EditTopic.tsx b/src/components/right/EditTopic.tsx index c4f866eea..041d6c8f7 100644 --- a/src/components/right/EditTopic.tsx +++ b/src/components/right/EditTopic.tsx @@ -5,10 +5,10 @@ import { getActions, withGlobal } from '../../global'; import type { FC } from '../../lib/teact/teact'; import type { ApiChat, ApiSticker, ApiTopic } from '../../api/types'; -import type { GlobalState } from '../../global/types'; +import type { TabState } from '../../global/types'; import { DEFAULT_TOPIC_ICON_STICKER_ID, GENERAL_TOPIC_ID } from '../../config'; -import { selectChat, selectIsCurrentUserPremium } from '../../global/selectors'; +import { selectChat, selectIsCurrentUserPremium, selectTabState } from '../../global/selectors'; import buildClassName from '../../util/buildClassName'; import { REM } from '../common/helpers/mediaDimensions'; @@ -36,7 +36,7 @@ export type OwnProps = { type StateProps = { chat?: ApiChat; topic?: ApiTopic; - editTopicPanel?: GlobalState['editTopicPanel']; + editTopicPanel?: TabState['editTopicPanel']; isCurrentUserPremium?: boolean; }; @@ -173,7 +173,7 @@ const EditTopic: FC = ({ export default memo(withGlobal( (global): StateProps => { - const { editTopicPanel } = global; + const { editTopicPanel } = selectTabState(global); const chat = editTopicPanel?.chatId ? selectChat(global, editTopicPanel.chatId) : undefined; const topic = editTopicPanel?.topicId ? chat?.topics?.[editTopicPanel?.topicId] : undefined; return { diff --git a/src/components/right/GifSearch.tsx b/src/components/right/GifSearch.tsx index 3713340d2..0efedb079 100644 --- a/src/components/right/GifSearch.tsx +++ b/src/components/right/GifSearch.tsx @@ -88,6 +88,10 @@ const GifSearch: FC = ({ } }, [canSendGifs, requestCalendar, sendMessage, setGifSearchQuery]); + const handleSearchMoreGifs = useCallback(() => { + searchMoreGifs(); + }, [searchMoreGifs]); + const lang = useLang(); useHistoryBack({ @@ -134,7 +138,7 @@ const GifSearch: FC = ({ itemSelector=".GifButton" preloadBackwards={PRELOAD_BACKWARDS} noFastList - onLoadMore={searchMoreGifs} + onLoadMore={handleSearchMoreGifs} > {renderContent()} diff --git a/src/components/right/PollAnswerResults.tsx b/src/components/right/PollAnswerResults.tsx index 3fa535cbd..b2e8c273c 100644 --- a/src/components/right/PollAnswerResults.tsx +++ b/src/components/right/PollAnswerResults.tsx @@ -10,6 +10,7 @@ import type { ApiPollAnswer, ApiPollResult, } from '../../api/types'; +import { selectTabState } from '../../global/selectors'; import usePrevious from '../../hooks/usePrevious'; import useLang from '../../hooks/useLang'; @@ -134,7 +135,7 @@ function getPercentage(value: number, total: number) { export default memo(withGlobal( (global, { answer }: OwnProps): StateProps => { - const { voters, offsets } = global.pollResults; + const { voters, offsets } = selectTabState(global).pollResults; return { voters: voters?.[answer.option], diff --git a/src/components/right/PollResults.tsx b/src/components/right/PollResults.tsx index 434965daa..0cdf857da 100644 --- a/src/components/right/PollResults.tsx +++ b/src/components/right/PollResults.tsx @@ -3,7 +3,7 @@ import React, { memo } from '../../lib/teact/teact'; import { withGlobal } from '../../global'; import type { ApiMessage, ApiChat } from '../../api/types'; -import { selectChat, selectChatMessage } from '../../global/selectors'; +import { selectChat, selectChatMessage, selectTabState } from '../../global/selectors'; import { buildCollectionByKey } from '../../util/iteratees'; import { getMessagePoll } from '../../global/helpers'; import renderText from '../common/helpers/renderText'; @@ -74,8 +74,8 @@ export default memo(withGlobal( (global): StateProps => { const { pollResults: { chatId, messageId }, - lastSyncTime, - } = global; + } = selectTabState(global); + const { lastSyncTime } = global; if (!chatId || !messageId) { return {}; diff --git a/src/components/right/Profile.tsx b/src/components/right/Profile.tsx index 46398fb2b..c86fb4014 100644 --- a/src/components/right/Profile.tsx +++ b/src/components/right/Profile.tsx @@ -218,12 +218,12 @@ const Profile: FC = ({ }, [releaseTransitionFix, resetCacheBuster]); const handleNewMemberDialogOpen = useCallback(() => { - setNewChatMembersDialogState(NewChatMembersProgress.InProgress); + setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.InProgress }); }, [setNewChatMembersDialogState]); // Update search type when switching tabs or forum topics useEffect(() => { - setLocalMediaSearchType({ mediaType: tabType }); + setLocalMediaSearchType({ mediaType: tabType as SharedMediaType }); }, [setLocalMediaSearchType, tabType, topicId]); const profileId = resolvedUserId || chatId; diff --git a/src/components/right/RightColumn.tsx b/src/components/right/RightColumn.tsx index 7ac6428b2..29ed8fd34 100644 --- a/src/components/right/RightColumn.tsx +++ b/src/components/right/RightColumn.tsx @@ -14,7 +14,7 @@ import captureEscKeyListener from '../../util/captureEscKeyListener'; import { selectAreActiveChatsLoaded, selectChat, - selectCurrentMessageList, + selectCurrentMessageList, selectTabState, selectRightColumnContentKey, } from '../../global/selectors'; import useLayoutEffectWithPrevDeps from '../../hooks/useLayoutEffectWithPrevDeps'; @@ -119,14 +119,14 @@ const RightColumn: FC = ({ const close = useCallback((shouldScrollUp = true) => { switch (contentKey) { case RightColumnContent.AddingMembers: - setNewChatMembersDialogState(NewChatMembersProgress.Closed); + setNewChatMembersDialogState({ newChatMembersProgress: NewChatMembersProgress.Closed }); break; case RightColumnContent.ChatInfo: if (isScrolledDown && shouldScrollUp) { setProfileState(ProfileState.Profile); break; } - toggleChatInfo(undefined, { forceSyncOnIOs: true }); + toggleChatInfo({ force: false }, { forceSyncOnIOs: true }); break; case RightColumnContent.Management: { switch (managementScreen) { @@ -162,8 +162,8 @@ const RightColumn: FC = ({ case ManagementScreens.EditInvite: case ManagementScreens.InviteInfo: setManagementScreen(ManagementScreens.Invites); - setOpenedInviteInfo({ invite: undefined }); - setEditingExportedInvite({ chatId, invite: undefined }); + setOpenedInviteInfo({ chatId: chatId!, invite: undefined }); + setEditingExportedInvite({ chatId: chatId!, invite: undefined }); break; } @@ -212,7 +212,7 @@ const RightColumn: FC = ({ }, []); const handleAppendingChatMembers = useCallback((memberIds: string[]) => { - addChatMembers({ chatId, memberIds }); + addChatMembers({ chatId: chatId!, memberIds }); }, [addChatMembers, chatId]); useEffect(() => (isOpen ? captureEscKeyListener(close) : undefined), [isOpen, close]); @@ -369,7 +369,8 @@ export default memo(withGlobal( (global, { isMobile }): StateProps => { const { chatId, threadId } = selectCurrentMessageList(global) || {}; const areActiveChatsLoaded = selectAreActiveChatsLoaded(global); - const nextManagementScreen = chatId ? global.management.byChatId[chatId]?.nextScreen : undefined; + const { management, shouldSkipHistoryAnimations } = selectTabState(global); + const nextManagementScreen = chatId ? management.byChatId[chatId]?.nextScreen : undefined; const isForum = chatId ? selectChat(global, chatId)?.isForum : undefined; const isInsideTopic = isForum && Boolean(threadId && threadId !== MAIN_THREAD_ID); @@ -379,7 +380,7 @@ export default memo(withGlobal( threadId, isInsideTopic, isChatSelected: Boolean(chatId && areActiveChatsLoaded), - shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations, + shouldSkipHistoryAnimations, nextManagementScreen, }; }, diff --git a/src/components/right/RightHeader.tsx b/src/components/right/RightHeader.tsx index 18ecb899f..5c85deecc 100644 --- a/src/components/right/RightHeader.tsx +++ b/src/components/right/RightHeader.tsx @@ -14,7 +14,7 @@ import buildClassName from '../../util/buildClassName'; import { selectChat, selectCurrentGifSearch, - selectCurrentStickerSearch, + selectCurrentStickerSearch, selectTabState, selectCurrentTextSearch, selectIsChatWithSelf, selectUser, @@ -199,6 +199,14 @@ const RightHeader: FC = ({ openEditTopicPanel({ chatId, topicId: threadId }); }, [chatId, openEditTopicPanel, threadId]); + const handleToggleManagement = useCallback(() => { + toggleManagement(); + }, [toggleManagement]); + + const handleToggleStatistics = useCallback(() => { + toggleStatistics(); + }, [toggleStatistics]); + const [shouldSkipTransition, setShouldSkipTransition] = useState(!isColumnOpen); useEffect(() => { @@ -457,7 +465,7 @@ const RightHeader: FC = ({ color="translucent" size="smaller" ariaLabel={lang('Edit')} - onClick={toggleManagement} + onClick={handleToggleManagement} > @@ -479,7 +487,7 @@ const RightHeader: FC = ({ color="translucent" size="smaller" ariaLabel={lang('Statistics')} - onClick={toggleStatistics} + onClick={handleToggleStatistics} > @@ -531,6 +539,7 @@ export default memo(withGlobal( (global, { chatId, isProfile, isManagement, threadId, }): StateProps => { + const tabState = selectTabState(global); const { query: messageSearchQuery } = selectCurrentTextSearch(global) || {}; const { query: stickerSearchQuery } = selectCurrentStickerSearch(global) || {}; const { query: gifSearchQuery } = selectCurrentGifSearch(global) || {}; @@ -552,9 +561,10 @@ export default memo(withGlobal( // chat.isCreator is for Basic Groups && (isUserId(chat.id) || ((isChatAdmin(chat) || chat.isCreator) && !chat.isNotJoined)), ); - const isEditingInvite = Boolean(chatId && global.management.byChatId[chatId]?.editingInvite); + const isEditingInvite = Boolean(chatId && tabState.management.byChatId[chatId]?.editingInvite); const canViewStatistics = !isInsideTopic && chat?.fullInfo?.canViewStatistics; - const currentInviteInfo = chatId ? global.management.byChatId[chatId]?.inviteInfo?.invite : undefined; + const currentInviteInfo = chatId + ? tabState.management.byChatId[chatId]?.inviteInfo?.invite : undefined; return { canManage, @@ -570,7 +580,7 @@ export default memo(withGlobal( gifSearchQuery, isEditingInvite, currentInviteInfo, - shouldSkipHistoryAnimations: global.shouldSkipHistoryAnimations, + shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations, }; }, )(RightHeader)); diff --git a/src/components/right/RightSearch.tsx b/src/components/right/RightSearch.tsx index b4ed7056e..bf315273a 100644 --- a/src/components/right/RightSearch.tsx +++ b/src/components/right/RightSearch.tsx @@ -1,5 +1,5 @@ import React, { - useMemo, memo, useRef, useEffect, + useMemo, memo, useRef, useEffect, useCallback, } from '../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../global'; @@ -84,7 +84,11 @@ const RightSearch: FC = ({ return enableDirectTextInput; }, [isActive]); - const [viewportIds, getMore] = useInfiniteScroll(searchTextMessagesLocal, foundIds); + const handleSearchTextMessagesLocal = useCallback(() => { + searchTextMessagesLocal(); + }, [searchTextMessagesLocal]); + + const [viewportIds, getMore] = useInfiniteScroll(handleSearchTextMessagesLocal, foundIds); const viewportResults = useMemo(() => { if (!query || !viewportIds?.length || !messagesById) { diff --git a/src/components/right/StickerSearch.tsx b/src/components/right/StickerSearch.tsx index 31a94bffc..5890539c1 100644 --- a/src/components/right/StickerSearch.tsx +++ b/src/components/right/StickerSearch.tsx @@ -5,7 +5,7 @@ import React, { import { getActions, withGlobal } from '../../global'; import { throttle } from '../../util/schedulers'; -import { selectCurrentStickerSearch } from '../../global/selectors'; +import { selectCurrentStickerSearch, selectTabState } from '../../global/selectors'; import { useIntersectionObserver } from '../../hooks/useIntersectionObserver'; import useLang from '../../hooks/useLang'; import useHistoryBack from '../../hooks/useHistoryBack'; @@ -114,7 +114,7 @@ export default memo(withGlobal( query, featuredIds: featured.setIds, resultIds, - isModalOpen: Boolean(global.openedStickerSetShortName), + isModalOpen: Boolean(selectTabState(global).openedStickerSetShortName), }; }, )(StickerSearch)); diff --git a/src/components/right/management/ManageChannel.tsx b/src/components/right/management/ManageChannel.tsx index 5a8d0130f..5143cc57b 100644 --- a/src/components/right/management/ManageChannel.tsx +++ b/src/components/right/management/ManageChannel.tsx @@ -12,7 +12,7 @@ import { ApiMediaFormat } from '../../../api/types'; import { getChatAvatarHash, getHasAdminRight, isChatPublic } from '../../../global/helpers'; import useMedia from '../../../hooks/useMedia'; import useLang from '../../../hooks/useLang'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import useFlag from '../../../hooks/useFlag'; import useHistoryBack from '../../../hooks/useHistoryBack'; import { formatInteger } from '../../../util/textFormat'; @@ -360,9 +360,10 @@ const ManageChannel: FC = ({ export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId)!; - const { progress } = global.management; + const { management } = selectTabState(global); + const { progress } = management; const isSignaturesShown = Boolean(chat?.isSignaturesShown); - const { invites } = global.management.byChatId[chatId] || {}; + const { invites } = management.byChatId[chatId] || {}; return { chat, diff --git a/src/components/right/management/ManageChatPrivacyType.tsx b/src/components/right/management/ManageChatPrivacyType.tsx index b8375d489..e1191d2b6 100644 --- a/src/components/right/management/ManageChatPrivacyType.tsx +++ b/src/components/right/management/ManageChatPrivacyType.tsx @@ -9,7 +9,7 @@ import type { ApiChat } from '../../../api/types'; import { ManagementProgress } from '../../../types'; import { PURCHASE_USERNAME, TME_LINK_PREFIX, USERNAME_PURCHASE_ERROR } from '../../../config'; -import { selectChat, selectManagement } from '../../../global/selectors'; +import { selectChat, selectTabState, selectManagement } from '../../../global/selectors'; import { isChatChannel, isChatPublic } from '../../../global/helpers'; import { selectCurrentLimit } from '../../../global/selectors/limits'; @@ -73,7 +73,7 @@ const ManageChatPrivacyType: FC = ({ const [isProfileFieldsTouched, setIsProfileFieldsTouched] = useState(false); const [privacyType, setPrivacyType] = useState(isPublic ? 'public' : 'private'); - const [editableUsername, setEditableUsername] = useState(); + const [editableUsername, setEditableUsername] = useState(); const [isRevokeConfirmDialogOpen, openRevokeConfirmDialog, closeRevokeConfirmDialog] = useFlag(); const [isUsernameLostDialogOpen, openUsernameLostDialog, closeUsernameLostDialog] = useFlag(); @@ -292,7 +292,7 @@ export default memo(withGlobal( return { chat, isChannel: isChatChannel(chat), - progress: global.management.progress, + progress: selectTabState(global).management.progress, error, isUsernameAvailable, checkedUsername, diff --git a/src/components/right/management/ManageDiscussion.tsx b/src/components/right/management/ManageDiscussion.tsx index 1f3d74c96..36266d2ba 100644 --- a/src/components/right/management/ManageDiscussion.tsx +++ b/src/components/right/management/ManageDiscussion.tsx @@ -84,7 +84,7 @@ const ManageDiscussion: FC = ({ const handleUnlinkGroupSessions = useCallback(() => { closeConfirmUnlinkGroupDialog(); - unlinkDiscussionGroup({ channelId: isChannel ? chatId : linkedChatId }); + unlinkDiscussionGroup({ channelId: isChannel ? chatId : linkedChatId! }); if (!isChannel) { onScreenSelect(ManagementScreens.Initial); } diff --git a/src/components/right/management/ManageGroup.tsx b/src/components/right/management/ManageGroup.tsx index 3047612bc..b57bc9860 100644 --- a/src/components/right/management/ManageGroup.tsx +++ b/src/components/right/management/ManageGroup.tsx @@ -21,7 +21,7 @@ import { import useMedia from '../../../hooks/useMedia'; import useLang from '../../../hooks/useLang'; import useFlag from '../../../hooks/useFlag'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import { formatInteger } from '../../../util/textFormat'; import renderText from '../../common/helpers/renderText'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -463,10 +463,11 @@ const ManageGroup: FC = ({ export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId)!; - const { progress } = global.management; + const { management, limitReachedModal } = selectTabState(global); + const { progress } = management; const hasLinkedChannel = Boolean(chat.fullInfo?.linkedChatId); const isBasicGroup = isChatBasicGroup(chat); - const { invites } = global.management.byChatId[chatId] || {}; + const { invites } = management.byChatId[chatId] || {}; const canEditForum = !hasLinkedChannel && isChatSuperGroup(chat) && getHasAdminRight(chat, 'changeInfo'); return { @@ -479,7 +480,7 @@ export default memo(withGlobal( canInvite: isBasicGroup ? chat.isCreator : getHasAdminRight(chat, 'inviteUsers'), exportedInvites: invites, lastSyncTime: global.lastSyncTime, - isChannelsPremiumLimitReached: global.limitReachedModal?.limit === 'channels', + isChannelsPremiumLimitReached: limitReachedModal?.limit === 'channels', availableReactions: global.availableReactions, canEditForum, }; diff --git a/src/components/right/management/ManageGroupMembers.tsx b/src/components/right/management/ManageGroupMembers.tsx index 9cc30cf60..071f0ab59 100644 --- a/src/components/right/management/ManageGroupMembers.tsx +++ b/src/components/right/management/ManageGroupMembers.tsx @@ -8,7 +8,7 @@ import type { ApiChatMember, ApiUserStatus } from '../../../api/types'; import { ManagementScreens } from '../../../types'; import { unique } from '../../../util/iteratees'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import { sortUserIds, isChatChannel, filterUsersByName, sortChatIds, isUserBot, getHasAdminRight, isChatBasicGroup, } from '../../../global/helpers'; @@ -261,7 +261,7 @@ export default memo(withGlobal( fetchingStatus, globalUserIds, localUserIds, - } = global.userSearch; + } = selectTabState(global).userSearch; const canDeleteMembers = chat && (chat.isCreator || getHasAdminRight(chat, 'banUsers')); diff --git a/src/components/right/management/ManageInvite.tsx b/src/components/right/management/ManageInvite.tsx index 9995b56e1..0cc8ede29 100644 --- a/src/components/right/management/ManageInvite.tsx +++ b/src/components/right/management/ManageInvite.tsx @@ -6,6 +6,7 @@ import { getActions, withGlobal } from '../../../global'; import type { ApiExportedInvite } from '../../../api/types'; import { ManagementScreens } from '../../../types'; +import { selectTabState } from '../../../global/selectors'; import useHistoryBack from '../../../hooks/useHistoryBack'; import useLang from '../../../hooks/useLang'; import { formatFullDate, formatTime } from '../../../util/dateFormat'; @@ -111,7 +112,7 @@ const ManageInvite: FC = ({ const handleSaveClick = useCallback(() => { setIsSubmitBlocked(true); - const usageLimit = selectedUsageOption === 'custom' ? customUsageLimit : selectedUsageOption; + const usageLimit = selectedUsageOption === 'custom' ? customUsageLimit : Number(selectedUsageOption); let expireDate; switch (selectedExpireOption) { case 'custom': @@ -265,7 +266,7 @@ const ManageInvite: FC = ({ export default memo(withGlobal( (global, { chatId }): StateProps => { - const { editingInvite } = global.management.byChatId[chatId]; + const { editingInvite } = selectTabState(global).management.byChatId[chatId]; return { editingInvite, diff --git a/src/components/right/management/ManageInviteInfo.tsx b/src/components/right/management/ManageInviteInfo.tsx index e2f6fa94c..115cd9261 100644 --- a/src/components/right/management/ManageInviteInfo.tsx +++ b/src/components/right/management/ManageInviteInfo.tsx @@ -10,7 +10,7 @@ import { copyTextToClipboard } from '../../../util/clipboard'; import { getServerTime } from '../../../util/serverTime'; import { formatFullDate, formatMediaDateTime, formatTime } from '../../../util/dateFormat'; import { isChatChannel } from '../../../global/helpers'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import PrivateChatInfo from '../../common/PrivateChatInfo'; import ListItem from '../../ui/ListItem'; @@ -179,7 +179,7 @@ const ManageInviteInfo: FC = ({ export default memo(withGlobal( (global, { chatId }): StateProps => { - const { inviteInfo } = global.management.byChatId[chatId]; + const { inviteInfo } = selectTabState(global).management.byChatId[chatId]; const { invite, importers, requesters } = inviteInfo || {}; const chat = selectChat(global, chatId); const isChannel = chat && isChatChannel(chat); diff --git a/src/components/right/management/ManageInvites.tsx b/src/components/right/management/ManageInvites.tsx index 28faa253f..3611f14b6 100644 --- a/src/components/right/management/ManageInvites.tsx +++ b/src/components/right/management/ManageInvites.tsx @@ -11,7 +11,7 @@ import { STICKER_SIZE_INVITES, TME_LINK_PREFIX } from '../../../config'; import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets'; import { formatCountdown, MILLISECONDS_IN_DAY } from '../../../util/dateFormat'; import { getMainUsername, isChatChannel } from '../../../global/helpers'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import { copyTextToClipboard } from '../../../util/clipboard'; import { getServerTime } from '../../../util/serverTime'; import useHistoryBack from '../../../hooks/useHistoryBack'; @@ -416,7 +416,7 @@ const ManageInvites: FC = ({ export default memo(withGlobal( (global, { chatId }): StateProps => { - const { invites, revokedInvites } = global.management.byChatId[chatId]; + const { invites, revokedInvites } = selectTabState(global).management.byChatId[chatId]; const chat = selectChat(global, chatId); const isChannel = chat && isChatChannel(chat); diff --git a/src/components/right/management/ManageUser.tsx b/src/components/right/management/ManageUser.tsx index d6d95dee9..a5a76db72 100644 --- a/src/components/right/management/ManageUser.tsx +++ b/src/components/right/management/ManageUser.tsx @@ -10,7 +10,7 @@ import { ManagementProgress } from '../../../types'; import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config'; import { - selectChat, selectNotifyExceptions, selectNotifySettings, selectUser, + selectChat, selectTabState, selectNotifyExceptions, selectNotifySettings, selectUser, } from '../../../global/selectors'; import { isUserBot, selectIsChatMuted } from '../../../global/helpers'; import useFlag from '../../../hooks/useFlag'; @@ -277,7 +277,7 @@ export default memo(withGlobal( (global, { userId }): StateProps => { const user = selectUser(global, userId); const chat = selectChat(global, userId)!; - const { progress } = global.management; + const { progress } = selectTabState(global).management; const isMuted = selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)); return { diff --git a/src/components/right/statistics/MessageStatistics.tsx b/src/components/right/statistics/MessageStatistics.tsx index 43ae06bd7..c773393b6 100644 --- a/src/components/right/statistics/MessageStatistics.tsx +++ b/src/components/right/statistics/MessageStatistics.tsx @@ -6,7 +6,7 @@ import { getActions, withGlobal } from '../../../global'; import { callApi } from '../../../api/gramjs'; import type { ApiMessageStatistics, ApiMessagePublicForward, StatisticsGraph } from '../../../api/types'; -import { selectChat } from '../../../global/selectors'; +import { selectChat, selectTabState } from '../../../global/selectors'; import buildClassName from '../../../util/buildClassName'; import useLang from '../../../hooks/useLang'; @@ -175,8 +175,9 @@ export default memo(withGlobal( (global, { chatId }): StateProps => { const chat = selectChat(global, chatId); const dcId = chat?.fullInfo?.statisticsDcId; - const statistics = global.statistics.currentMessage; - const messageId = global.statistics.currentMessageId; + const tabState = selectTabState(global); + const statistics = tabState.statistics.currentMessage; + const messageId = tabState.statistics.currentMessageId; return { statistics, dcId, messageId }; }, diff --git a/src/components/right/statistics/StatisticsPublicForward.tsx b/src/components/right/statistics/StatisticsPublicForward.tsx index d08f6c6d2..5847fee4e 100644 --- a/src/components/right/statistics/StatisticsPublicForward.tsx +++ b/src/components/right/statistics/StatisticsPublicForward.tsx @@ -22,7 +22,7 @@ const StatisticsPublicForward: FC = ({ data }) => { const username = useMemo(() => getMainUsername(data.chat), [data.chat]); const handleClick = useCallback(() => { - openChatByUsername({ username, messageId: data.messageId }); + openChatByUsername({ username: username!, messageId: data.messageId }); }, [data.messageId, openChatByUsername, username]); return ( diff --git a/src/components/test/TestNoRedundancy.tsx b/src/components/test/TestNoRedundancy.tsx index 8dc4dd8e3..b5b754f84 100644 --- a/src/components/test/TestNoRedundancy.tsx +++ b/src/components/test/TestNoRedundancy.tsx @@ -1,3 +1,4 @@ +/* eslint-disable eslint-multitab-tt/set-global-only-variable */ import type { FC } from '../../lib/teact/teact'; import React from '../../lib/teact/teact'; import { getGlobal, setGlobal, withGlobal } from '../../global'; diff --git a/src/components/ui/Notification.tsx b/src/components/ui/Notification.tsx index 5ab08c9bd..6cc3ef44c 100644 --- a/src/components/ui/Notification.tsx +++ b/src/components/ui/Notification.tsx @@ -5,8 +5,10 @@ import React, { useRef, useState, } from '../../lib/teact/teact'; +import { getActions } from '../../global'; import type { TextPart } from '../../types'; +import type { CallbackAction } from '../../global/types'; import { ANIMATION_END_DELAY } from '../../config'; import useShowTransition from '../../hooks/useShowTransition'; @@ -24,7 +26,7 @@ type OwnProps = { message: TextPart[]; duration?: number; onDismiss: () => void; - action?: VoidFunction; + action?: CallbackAction; actionText?: string; className?: string; }; @@ -37,6 +39,8 @@ const Notification: FC = ({ message, duration = DEFAULT_DURATION, containerId, onDismiss, action, actionText, }) => { + const actions = getActions(); + const [isOpen, setIsOpen] = useState(true); // eslint-disable-next-line no-null/no-null const timerRef = useRef(null); @@ -47,10 +51,13 @@ const Notification: FC = ({ setTimeout(onDismiss, ANIMATION_DURATION + ANIMATION_END_DELAY); }, [onDismiss]); - function handleClick() { - action?.(); + const handleClick = useCallback(() => { + if (action) { + // @ts-ignore + actions[action.action](action.payload); + } closeAndDismiss(); - } + }, [action, actions, closeAndDismiss]); useEffect(() => (isOpen ? captureEscKeyListener(closeAndDismiss) : undefined), [isOpen, closeAndDismiss]); @@ -91,7 +98,7 @@ const Notification: FC = ({ {action && actionText && (