From 756fad517b4ed6d77c51fe6cfe4e27d3042a06ed Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Sat, 30 May 2026 03:18:12 +0300 Subject: [PATCH 01/43] fix: Add questionmark next to secondary components --- .../pages/home/ui/HomeSecondaryComponentsSection.tsx | 5 +++-- src/renderer/pages/home/ui/home.module.scss | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/renderer/pages/home/ui/HomeSecondaryComponentsSection.tsx b/src/renderer/pages/home/ui/HomeSecondaryComponentsSection.tsx index 6a1286e6..9063a2e9 100644 --- a/src/renderer/pages/home/ui/HomeSecondaryComponentsSection.tsx +++ b/src/renderer/pages/home/ui/HomeSecondaryComponentsSection.tsx @@ -1,5 +1,6 @@ import { useTranslation } from 'react-i18next' +import { HiQuestionMarkCircle } from 'react-icons/hi' import { staticAsset } from '@shared/lib/staticAssets' import ButtonV2 from '@shared/ui/buttonV2' import TooltipButton from '@shared/ui/tooltip_button' @@ -51,14 +52,14 @@ export default function HomeSecondaryComponentsSection({ items, isObsInstalled, ) : isMetadataBackedSubcomponent(item.id) ? (
{item.version ? t('pages.home.installed') : t('pages.home.notInstalled')} +
) : ( diff --git a/src/renderer/pages/home/ui/home.module.scss b/src/renderer/pages/home/ui/home.module.scss index 2e7ad327..cb848e13 100644 --- a/src/renderer/pages/home/ui/home.module.scss +++ b/src/renderer/pages/home/ui/home.module.scss @@ -312,6 +312,10 @@ line-height: 110%; color: rgba(244, 246, 251, 0.72); + + display: flex; + align-items: center; + gap: 4px; } .secondaryActionButton { From e8a4822fd7577e4127df6d3354dbdf8f6981a859 Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Sat, 30 May 2026 03:19:27 +0300 Subject: [PATCH 02/43] fix: Remove extra dataSide props --- src/renderer/widgets/layout/ExperimentOverridesDevButton.tsx | 2 +- src/renderer/widgets/layout/NotificationsBell.tsx | 2 +- src/renderer/widgets/layout/SubscriptionGiveawaysButton.tsx | 2 +- src/renderer/widgets/layout/UpdateChannelOverrideButton.tsx | 2 +- src/renderer/widgets/layout/header.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/renderer/widgets/layout/ExperimentOverridesDevButton.tsx b/src/renderer/widgets/layout/ExperimentOverridesDevButton.tsx index a1e8484c..b6a8248f 100644 --- a/src/renderer/widgets/layout/ExperimentOverridesDevButton.tsx +++ b/src/renderer/widgets/layout/ExperimentOverridesDevButton.tsx @@ -14,7 +14,7 @@ const ExperimentOverridesDevButton: React.FC = () => { const label = t('header.devOverrides.open') return ( - + diff --git a/src/renderer/widgets/layout/NotificationsBell.tsx b/src/renderer/widgets/layout/NotificationsBell.tsx index 1b5f74fb..58db3de8 100644 --- a/src/renderer/widgets/layout/NotificationsBell.tsx +++ b/src/renderer/widgets/layout/NotificationsBell.tsx @@ -195,7 +195,7 @@ const NotificationsBell: React.FC = () => { return (
- + diff --git a/src/renderer/widgets/layout/UpdateChannelOverrideButton.tsx b/src/renderer/widgets/layout/UpdateChannelOverrideButton.tsx index 86d84d3e..c047de1c 100644 --- a/src/renderer/widgets/layout/UpdateChannelOverrideButton.tsx +++ b/src/renderer/widgets/layout/UpdateChannelOverrideButton.tsx @@ -55,7 +55,7 @@ const UpdateChannelOverrideButton: React.FC = () => { }, [refreshStatus]) return ( - + From 4dcdd8d79e404db3b4631e7d1ff55b6351f5d198 Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Sat, 30 May 2026 03:41:38 +0300 Subject: [PATCH 03/43] fix: Add user-select: text when necessary --- .../route/extBox/ThemeInfo/ThemeInfo.module.scss | 3 ++- src/renderer/pages/home/ui/home.module.scss | 10 ++-------- .../shared/ui/PSUI/ExtensionCardStore/card.module.scss | 6 ++++++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/renderer/pages/extension/route/extBox/ThemeInfo/ThemeInfo.module.scss b/src/renderer/pages/extension/route/extBox/ThemeInfo/ThemeInfo.module.scss index fdd6a54b..78dc1132 100644 --- a/src/renderer/pages/extension/route/extBox/ThemeInfo/ThemeInfo.module.scss +++ b/src/renderer/pages/extension/route/extBox/ThemeInfo/ThemeInfo.module.scss @@ -101,13 +101,14 @@ text-shadow: 0 2px 8px rgba(8, 10, 18, 0.5), 0 12px 32px rgba(8, 10, 18, 0.38); + user-select: text; } .invisible { background: transparent; height: 266px; width: -webkit-fill-available; - z-index: 1; + z-index: 0; position: relative; } diff --git a/src/renderer/pages/home/ui/home.module.scss b/src/renderer/pages/home/ui/home.module.scss index cb848e13..a637b8f5 100644 --- a/src/renderer/pages/home/ui/home.module.scss +++ b/src/renderer/pages/home/ui/home.module.scss @@ -233,15 +233,8 @@ font-weight: 700; font-size: 18px; line-height: 110%; - /* identical to box height, or 20px */ - color: #FFFFFF; - - - /* Inside auto layout */ - flex: none; - order: 0; - flex-grow: 0; + user-select: text; } @@ -251,6 +244,7 @@ font-weight: 600; line-height: 1.15; color: rgba(241, 244, 251, 0.92); + user-select: text; } .animatedComponentVersion { diff --git a/src/renderer/shared/ui/PSUI/ExtensionCardStore/card.module.scss b/src/renderer/shared/ui/PSUI/ExtensionCardStore/card.module.scss index 2549c001..3fabc73d 100644 --- a/src/renderer/shared/ui/PSUI/ExtensionCardStore/card.module.scss +++ b/src/renderer/shared/ui/PSUI/ExtensionCardStore/card.module.scss @@ -182,6 +182,8 @@ overflow: hidden; text-overflow: ellipsis; text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); + + user-select: text; } .card_title_version { @@ -198,6 +200,8 @@ font-weight: 700; line-height: 1; white-space: nowrap; + + user-select: text; } .card_title_verified { @@ -222,6 +226,8 @@ -webkit-line-clamp: 2; -webkit-box-orient: vertical; text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.5); + + user-select: text; } .card_meta { From bf1b5bc84b7f90aab0a14575bf65862f3bc9d7da Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 1 Jun 2026 03:47:59 +0300 Subject: [PATCH 04/43] feat: notify subscription purchases and expiry --- package.json | 22 +- src/locales/en/renderer.json | 9 + src/locales/ru/renderer.json | 9 + .../app/providers/experiments/constants.ts | 1 + .../providers/notifications/presentation.ts | 38 + .../useNotificationsController.ts | 8 +- src/renderer/features/context_menu/index.tsx | 10 +- .../model/contextMenuSections.tsx | 24 +- yarn.lock | 720 ++++++++++-------- 9 files changed, 501 insertions(+), 340 deletions(-) diff --git a/package.json b/package.json index 4aa07c55..a19777c0 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,8 @@ "keywords": [], "license": "SEE LICENSE IN LICENSE", "devDependencies": { - "@aws-sdk/client-s3": "^3.1053.0", - "@babel/core": "7.29.0", + "@aws-sdk/client-s3": "^3.1057.0", + "@babel/core": "7.29.7", "@electron-forge/cli": "^7.11.2", "@electron-forge/maker-deb": "^7.11.2", "@electron-forge/maker-dmg": "^7.11.2", @@ -59,19 +59,19 @@ "@types/unzipper": "^0.10.11", "@types/uuid": "^11.0.0", "@types/yazl": "^3.3.1", - "@typescript-eslint/eslint-plugin": "^8.59.4", - "@typescript-eslint/parser": "^8.59.4", + "@typescript-eslint/eslint-plugin": "^8.60.0", + "@typescript-eslint/parser": "^8.60.0", "@vitejs/plugin-react": "^6.0.2", "babel-plugin-react-compiler": "^1.0.0", - "electron": "42.2.0", + "electron": "42.3.0", "electron-builder": "^26.8.1", - "eslint": "^10.4.0", + "eslint": "^10.4.1", "eslint-plugin-import": "^2.32.0", "iconv-lite": "^0.7.2", "prettier": "^3.8.3", "sass": "^1.100.0", "ts-node": "^10.9.2", - "tsx": "4.22.3", + "tsx": "4.22.4", "typescript": "^6.0.3", "vite": "^8.0.14", "vite-plugin-svgr": "^5.2.0" @@ -90,7 +90,7 @@ "adm-zip": "^0.5.17", "axios": "^1.16.1", "browserify-fs": "^1.0.0", - "browserify-sign": "^4.2.5", + "browserify-sign": "^4.2.6", "chart.js": "^4.5.1", "crypto-browserify": "^3.12.1", "electron-chrome-web-store": "^0.13.0", @@ -102,7 +102,7 @@ "glob": "^13.0.6", "graphql": "^16.14.0", "graphql-ws": "^6.0.8", - "i18next": "^26.2.0", + "i18next": "^26.3.0", "lodash.debounce": "^4.0.8", "log4js": "^6.9.1", "net": "^1.0.2", @@ -117,7 +117,7 @@ "react-loading-skeleton": "^3.5.0", "react-markdown": "^10.1.0", "react-modal": "^3.16.3", - "react-router-dom": "^7.15.1", + "react-router-dom": "^7.16.0", "react-transition-group": "^4.4.5", "rehype-raw": "^7.0.0", "remark-breaks": "^4.0.0", @@ -130,7 +130,7 @@ "source-map-support": "^0.5.21", "stream-browserify": "^3.0.0", "string-similarity": "^4.0.4", - "systeminformation": "^5.31.6", + "systeminformation": "^5.31.7", "tar": "^7.5.15", "url": "^0.11.4", "uuid": "^14.0.0", diff --git a/src/locales/en/renderer.json b/src/locales/en/renderer.json index 31e58656..de2b92f6 100644 --- a/src/locales/en/renderer.json +++ b/src/locales/en/renderer.json @@ -566,10 +566,19 @@ "giveawayWonPrize": "{{name}} for {{months}} mo", "giveawayFallbackTitle": "Subscription giveaway", "giveawayFallbackPrize": "PulseSync subscription", + "subscriptionPurchaseSucceededTitle": "Subscription purchased", + "subscriptionPurchaseSucceededBody": "{{plan}} is active until {{date}}. Thanks for your support.", + "subscriptionExpiringSoonTitle": "Subscription ends soon", + "subscriptionExpiringSoonBody": "{{plan}} ends on {{date}}. You can extend it early.", + "subscriptionFallbackPlan": "PulseSync subscription", + "subscriptionFallbackDate": "soon", "genericTitle": "New notification", "genericBody": "Open the notification to view details." } }, + "subscription": { + "open": "Buy subscription" + }, "giveaways": { "open": "Open subscription giveaways", "eyebrow": "Subscriptions", diff --git a/src/locales/ru/renderer.json b/src/locales/ru/renderer.json index c4d5d0d3..df3674dd 100644 --- a/src/locales/ru/renderer.json +++ b/src/locales/ru/renderer.json @@ -566,10 +566,19 @@ "giveawayWonPrize": "{{name}} на {{months}} мес.", "giveawayFallbackTitle": "Розыгрыш подписки", "giveawayFallbackPrize": "подписку PulseSync", + "subscriptionPurchaseSucceededTitle": "Подписка оформлена", + "subscriptionPurchaseSucceededBody": "{{plan}} активна до {{date}}. Спасибо за поддержку.", + "subscriptionExpiringSoonTitle": "Подписка скоро закончится", + "subscriptionExpiringSoonBody": "{{plan}} заканчивается {{date}}. Можно продлить её заранее.", + "subscriptionFallbackPlan": "Подписка PulseSync", + "subscriptionFallbackDate": "скоро", "genericTitle": "Новое уведомление", "genericBody": "Открой уведомление, чтобы посмотреть подробности." } }, + "subscription": { + "open": "Купить подписку" + }, "giveaways": { "open": "Открыть розыгрыши подписок", "eyebrow": "Подписки", diff --git a/src/renderer/app/providers/experiments/constants.ts b/src/renderer/app/providers/experiments/constants.ts index c293f7dc..480e52f3 100644 --- a/src/renderer/app/providers/experiments/constants.ts +++ b/src/renderer/app/providers/experiments/constants.ts @@ -8,6 +8,7 @@ export const CLIENT_EXPERIMENTS = { ClientMetricsSending: 'ClientMetricsSending', WebLocalizationContribution: 'WebLocalizationContribution', WebHomeSections: 'WebHomeSections', + WebSubscriptionsPage: 'WebSubscriptionsPage', } as const export const KNOWN_CLIENT_EXPERIMENT_KEYS = Object.values(CLIENT_EXPERIMENTS) diff --git a/src/renderer/app/providers/notifications/presentation.ts b/src/renderer/app/providers/notifications/presentation.ts index feec6928..7bcc597d 100644 --- a/src/renderer/app/providers/notifications/presentation.ts +++ b/src/renderer/app/providers/notifications/presentation.ts @@ -9,6 +9,20 @@ export type NotificationPresentation = { tone: NotificationTone } +function formatPayloadDate(value: unknown): string { + const rawValue = typeof value === 'string' ? value : '' + const date = new Date(rawValue) + if (Number.isNaN(date.getTime())) { + return t('header.notifications.items.subscriptionFallbackDate') + } + + return new Intl.DateTimeFormat(undefined, { + day: '2-digit', + month: 'short', + year: 'numeric', + }).format(date) +} + export function getNotificationPresentation(notification: NotificationItem): NotificationPresentation { switch (notification.type) { case 'addon.review.pending': @@ -88,6 +102,30 @@ export function getNotificationPresentation(notification: NotificationItem): Not } } + case 'subscription.purchase.succeeded': { + const planName = String(notification.payload?.['planName'] || notification.payload?.['subscriptionName'] || t('header.notifications.items.subscriptionFallbackPlan')) + return { + tone: 'success', + title: t('header.notifications.items.subscriptionPurchaseSucceededTitle'), + body: t('header.notifications.items.subscriptionPurchaseSucceededBody', { + plan: planName, + date: formatPayloadDate(notification.payload?.['expireAt']), + }), + } + } + + case 'subscription.expiring.soon': { + const planName = String(notification.payload?.['subscriptionName'] || notification.payload?.['planName'] || t('header.notifications.items.subscriptionFallbackPlan')) + return { + tone: 'warning', + title: t('header.notifications.items.subscriptionExpiringSoonTitle'), + body: t('header.notifications.items.subscriptionExpiringSoonBody', { + plan: planName, + date: formatPayloadDate(notification.payload?.['expireAt']), + }), + } + } + default: return { tone: 'warning', diff --git a/src/renderer/app/providers/notifications/useNotificationsController.ts b/src/renderer/app/providers/notifications/useNotificationsController.ts index 9a0fc155..16ad8737 100644 --- a/src/renderer/app/providers/notifications/useNotificationsController.ts +++ b/src/renderer/app/providers/notifications/useNotificationsController.ts @@ -43,6 +43,12 @@ type NotificationsControllerResult = { } const MAX_NOTIFICATIONS = 20 +const REALTIME_TOAST_NOTIFICATION_TYPES = new Set([ + 'achievement.completed', + 'subscription.giveaway.won', + 'subscription.purchase.succeeded', + 'subscription.expiring.soon', +]) function dedupeNotifications(items: NotificationItem[]): NotificationItem[] { const seen = new Set() @@ -143,7 +149,7 @@ export function useNotificationsController(userId: string): NotificationsControl setNotificationsUnreadCount(data.unreadCount) } - if ((data.notification.type === 'achievement.completed' || data.notification.type === 'subscription.giveaway.won') && !data.notification.read) { + if (REALTIME_TOAST_NOTIFICATION_TYPES.has(data.notification.type) && !data.notification.read) { const presentation = getNotificationPresentation(data.notification) toast.custom(presentation.tone, presentation.title, presentation.body) window.desktopEvents?.send(MainEvents.SHOW_NOTIFICATION, { diff --git a/src/renderer/features/context_menu/index.tsx b/src/renderer/features/context_menu/index.tsx index c7772c17..4c36c603 100644 --- a/src/renderer/features/context_menu/index.tsx +++ b/src/renderer/features/context_menu/index.tsx @@ -8,6 +8,7 @@ import RendererEvents from '@common/types/rendererEvents' import toast from '@shared/ui/toast' import SettingsInterface from '@entities/settings/model/settings.interface' import { useModalContext } from '@app/providers/modal' +import { CLIENT_EXPERIMENTS, useExperiments } from '@app/providers/experiments' import { useTranslation } from 'react-i18next' import { buildContextMenuSections, renderContextMenuSections } from '@features/context_menu/model/contextMenuSections' import config from '@common/appConfig' @@ -26,9 +27,11 @@ const ContextMenu: React.FC = ({ modalRef }) => { const { t, i18n } = useTranslation() const { app, setApp, widgetInstalled, setWidgetInstalled, isAutonomousMode } = useContext(userContext) const { Modals, openModal } = useModalContext() + const { isExperimentEnabled } = useExperiments() const widgetDownloadToastIdRef = useRef(null) const [updateSource, setUpdateSourceState] = React.useState('backend') const [updateStatus, setUpdateStatus] = React.useState('IDLE') + const subscriptionPageEnabled = isExperimentEnabled(CLIENT_EXPERIMENTS.WebSubscriptionsPage, false) const openUpdateModal = () => { modalRef.current?.openUpdateModal() @@ -42,8 +45,8 @@ const ContextMenu: React.FC = ({ modalRef }) => { window.desktopEvents?.send(MainEvents.OPEN_PATH, { action: 'appPath' }) } - const openBoostyUrl = () => { - window.open(config.BOOSTY_URL) + const openSubscriptionPage = () => { + window.desktopEvents?.send(MainEvents.OPEN_EXTERNAL, `${config.WEBSITE_URL}/subscription`) } const canResetAsarPath = window.electron.isLinux() && Boolean(window.electron.store.get('settings.modSavePath')) @@ -394,7 +397,8 @@ const ContextMenu: React.FC = ({ modalRef }) => { downloadObsWidget, isAutonomousMode, openAppDirectory, - openBoostyUrl, + openSubscriptionPage, + subscriptionPageEnabled, openUpdateChannelModal, openModal, openUpdateModal, diff --git a/src/renderer/features/context_menu/model/contextMenuSections.tsx b/src/renderer/features/context_menu/model/contextMenuSections.tsx index 4dc74123..3e344090 100644 --- a/src/renderer/features/context_menu/model/contextMenuSections.tsx +++ b/src/renderer/features/context_menu/model/contextMenuSections.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { SiBoosty } from 'react-icons/si' +import { MdWorkspacePremium } from 'react-icons/md' import MainEvents from '@common/types/mainEvents' import ArrowContext from '@shared/assets/icons/arrowContext.svg' @@ -66,7 +66,8 @@ type Params = { downloadObsWidget: () => void isAutonomousMode: boolean openAppDirectory: () => void - openBoostyUrl: () => void + openSubscriptionPage: () => void + subscriptionPageEnabled: boolean openUpdateChannelModal: () => void openModal: (modal: ModalName) => void openUpdateModal: () => void @@ -96,7 +97,8 @@ export function buildContextMenuSections({ downloadObsWidget, isAutonomousMode, openAppDirectory, - openBoostyUrl, + openSubscriptionPage, + subscriptionPageEnabled, openUpdateChannelModal, openModal, openUpdateModal, @@ -134,11 +136,17 @@ export function buildContextMenuSections({ ] return [ - createContentSection( - , + ...( + subscriptionPageEnabled ? + [ + createContentSection( + , + ), + ] + : [] ), createButtonSection(t('contextMenu.obsWidget.title'), [ { diff --git a/yarn.lock b/yarn.lock index 3a8a969e..60141842 100644 --- a/yarn.lock +++ b/yarn.lock @@ -88,40 +88,40 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-s3@^3.1053.0": - version "3.1053.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.1053.0.tgz#13568ec27b695d01c56c11c3c11afb6d3d96496a" - integrity sha512-/oGxoB6p1Nqs935Blt+v1o+anSCEf2n3RjIrcLz84i4cn2Gr+Z7JpDdUkG5+74r5ctqEPG7k/phTGbJ9fNKnHg== +"@aws-sdk/client-s3@^3.1057.0": + version "3.1057.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.1057.0.tgz#5a783870517f8256334b31027b032c66af06e78d" + integrity sha512-4MV5+ph7WSLEqStKYdWf2EIHIvLpPzV8xN98jWSVJfUpp5j7T8dyN3AROPPsKWvCme8hbx1ybCjtK76ALCZUYg== dependencies: "@aws-crypto/sha1-browser" "5.2.0" "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/credential-provider-node" "^3.972.44" - "@aws-sdk/middleware-bucket-endpoint" "^3.972.15" - "@aws-sdk/middleware-expect-continue" "^3.972.13" - "@aws-sdk/middleware-flexible-checksums" "^3.974.21" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/credential-provider-node" "^3.972.47" + "@aws-sdk/middleware-bucket-endpoint" "^3.972.17" + "@aws-sdk/middleware-expect-continue" "^3.972.14" + "@aws-sdk/middleware-flexible-checksums" "^3.974.23" "@aws-sdk/middleware-location-constraint" "^3.972.11" - "@aws-sdk/middleware-sdk-s3" "^3.972.42" + "@aws-sdk/middleware-sdk-s3" "^3.972.44" "@aws-sdk/middleware-ssec" "^3.972.11" - "@aws-sdk/signature-v4-multi-region" "^3.996.28" + "@aws-sdk/signature-v4-multi-region" "^3.996.30" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/fetch-http-handler" "^5.4.3" - "@smithy/node-http-handler" "^4.7.3" + "@smithy/core" "^3.24.5" + "@smithy/fetch-http-handler" "^5.4.5" + "@smithy/node-http-handler" "^4.7.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/core@^3.974.13": - version "3.974.13" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.974.13.tgz#a785d4a726590f679671d18b36c69e3fc9b6cab5" - integrity sha512-+Y5/4tHki0uYgyx8eun146DegRVQBpdKGK5RbV0FTKJPpaKTchvqVxrrRFK6Wk0JksO4iAZKw3eqxGEIwtO98w== +"@aws-sdk/core@^3.974.15": + version "3.974.15" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.974.15.tgz#841395d805ed33b8e4f30b1b86749922a0c6a058" + integrity sha512-UpA0rTGW/tHGITcCqHisbuuEPraYg9GG+mWmXjY5+RxZBMLGe6aL9oe0ix50LztwAcPIkGZLH0yWdMIkCM10hw== dependencies: "@aws-sdk/types" "^3.973.9" - "@aws-sdk/xml-builder" "^3.972.25" + "@aws-sdk/xml-builder" "^3.972.26" "@aws/lambda-invoke-store" "^0.2.2" - "@smithy/core" "^3.24.3" - "@smithy/signature-v4" "^5.4.2" + "@smithy/core" "^3.24.5" + "@smithy/signature-v4" "^5.4.5" "@smithy/types" "^4.14.2" bowser "^2.11.0" tslib "^2.6.2" @@ -134,147 +134,147 @@ "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-env@^3.972.39": - version "3.972.39" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.39.tgz#538cc859f2ac0e15b141b9e246613a752849ae8c" - integrity sha512-29wX9zpAvEt1vcj0psha+y6ygBHy2V/S72mp6e7q0KARLWXq+pwE/lR6qGkwknQvruh52lXvlqZIga8Hdxkucw== +"@aws-sdk/credential-provider-env@^3.972.41": + version "3.972.41" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.41.tgz#753b584626798be5310cf87976f3c72d410f709a" + integrity sha512-n1EbJ98yvPWWdHZZv8bRBMqqDQJrtgtxyJ4xLy2Uqrh25BCOZQ7nnS1CsFXvuH8r0b0KVHDZEGEH5FxmEMP8jg== dependencies: - "@aws-sdk/core" "^3.974.13" + "@aws-sdk/core" "^3.974.15" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-http@^3.972.41": - version "3.972.41" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.41.tgz#07037e7346881cb8bb8ec1fe9f8ed0104072b63a" - integrity sha512-IA3CQTjtJkb6u1H4mE4936c8OPBMa9Jggtwe8U2Mqw/vvb/tZ5Ebd0mcZcX0uKWQhOyYo/+qNIwkV5Xh+FeJJA== +"@aws-sdk/credential-provider-http@^3.972.43": + version "3.972.43" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.43.tgz#bc9af93cdba81df5ce487a3e46f232d677092c97" + integrity sha512-TT76RN1NkI9WoyZqCNxOw6/WBMF7pYOTJcXbMokNFU+euSG40Kaf/t/FhDACVZWP+43wEM6ZynIPIkzS1wR1iA== dependencies: - "@aws-sdk/core" "^3.974.13" + "@aws-sdk/core" "^3.974.15" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/fetch-http-handler" "^5.4.3" - "@smithy/node-http-handler" "^4.7.3" + "@smithy/core" "^3.24.5" + "@smithy/fetch-http-handler" "^5.4.5" + "@smithy/node-http-handler" "^4.7.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-ini@^3.972.43": - version "3.972.43" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.43.tgz#cb9779beebd45bd242c12ea48a820047c77e1b05" - integrity sha512-4mzII+3mZEVXXE1xzrLQrCJL7/r62A63bA6SVzZoNL5rqCJghpf+xgGltVrIBBs0n+mOZBKrQl2tRREtvZ5l6A== - dependencies: - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/credential-provider-env" "^3.972.39" - "@aws-sdk/credential-provider-http" "^3.972.41" - "@aws-sdk/credential-provider-login" "^3.972.43" - "@aws-sdk/credential-provider-process" "^3.972.39" - "@aws-sdk/credential-provider-sso" "^3.972.43" - "@aws-sdk/credential-provider-web-identity" "^3.972.43" - "@aws-sdk/nested-clients" "^3.997.11" +"@aws-sdk/credential-provider-ini@^3.972.46": + version "3.972.46" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.46.tgz#3fea86be2fb9593ac7d339cb4c6ff78e9f69df30" + integrity sha512-hvcgcwOiS0nb2XFb5Op1Pz/vYaWz5K8kKullziGpdNRuG0NwzRXseuPt2CoBqknHGaSPVesu1aOn2OcctEYdCA== + dependencies: + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/credential-provider-env" "^3.972.41" + "@aws-sdk/credential-provider-http" "^3.972.43" + "@aws-sdk/credential-provider-login" "^3.972.45" + "@aws-sdk/credential-provider-process" "^3.972.41" + "@aws-sdk/credential-provider-sso" "^3.972.45" + "@aws-sdk/credential-provider-web-identity" "^3.972.45" + "@aws-sdk/nested-clients" "^3.997.13" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/credential-provider-imds" "^4.3.2" + "@smithy/core" "^3.24.5" + "@smithy/credential-provider-imds" "^4.3.6" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-login@^3.972.43": - version "3.972.43" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.43.tgz#2d6dd4a7d082b0c54be9c5c5269161c14f7ae717" - integrity sha512-HG7kQCwXtbv3oBV61Ins0oNX8KKyvrMqqRkb6ZiAfQHbMuHaiNaEb2KnpKLPkNpqImSBK82UkVE/kaY6IfWikA== +"@aws-sdk/credential-provider-login@^3.972.45": + version "3.972.45" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.45.tgz#e11744272965d423ace09d3aa2b389248d665699" + integrity sha512-MZQv4SNjByk1iOKmrqmzcUF/uCB05wjvEHyXKxmGQTUANTIVayX6HPUF0bzkWLvtnkH7sAn9kUCfkXbSpj9sDA== dependencies: - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/nested-clients" "^3.997.11" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/nested-clients" "^3.997.13" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-node@^3.972.44": - version "3.972.44" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.44.tgz#af009a773d2e20214edfcc98894d3d0779fbc1c3" - integrity sha512-sDaBIT0yrNNIPfvlsiTCmANm07zKju+ipWODjEXgZlsjMeIJR3LVp7RDyAOzUoAsTbDfYKDWp+i5WrFiQP6rmQ== - dependencies: - "@aws-sdk/credential-provider-env" "^3.972.39" - "@aws-sdk/credential-provider-http" "^3.972.41" - "@aws-sdk/credential-provider-ini" "^3.972.43" - "@aws-sdk/credential-provider-process" "^3.972.39" - "@aws-sdk/credential-provider-sso" "^3.972.43" - "@aws-sdk/credential-provider-web-identity" "^3.972.43" +"@aws-sdk/credential-provider-node@^3.972.47": + version "3.972.47" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.47.tgz#f25a77bce8924688bb581e87f8d7ef1477eab9a0" + integrity sha512-HrId+C0DWA5qDIyLG64/kjUB2RNtPypxmABnIctK+TA1P1kHlOYoE/Wf5T5tKOMKgb08P7k/zNyhvfJ3lh5Oag== + dependencies: + "@aws-sdk/credential-provider-env" "^3.972.41" + "@aws-sdk/credential-provider-http" "^3.972.43" + "@aws-sdk/credential-provider-ini" "^3.972.46" + "@aws-sdk/credential-provider-process" "^3.972.41" + "@aws-sdk/credential-provider-sso" "^3.972.45" + "@aws-sdk/credential-provider-web-identity" "^3.972.45" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/credential-provider-imds" "^4.3.2" + "@smithy/core" "^3.24.5" + "@smithy/credential-provider-imds" "^4.3.6" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-process@^3.972.39": - version "3.972.39" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.39.tgz#236f8822180b297e0e98771ee69aea428280a4a7" - integrity sha512-2k/amBifLd75eXNwgvPw/2lKYSQ3NhvHQgkVKVjfUq13/eJ3JRtHmznuFenn74OK3sSfp4SMy1YB2w+UVXoKqA== +"@aws-sdk/credential-provider-process@^3.972.41": + version "3.972.41" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.41.tgz#68d2a4f46ec15e8cd0da1d1d3e56b8889df9b926" + integrity sha512-7I/n1zkysouLOWvkEhjNEP4vMnD2v4kzzr3/3QBdrripEpn7ap1/I5DF3Hou1SUqkKWo1f3oPGMyFAA1FAMvsQ== dependencies: - "@aws-sdk/core" "^3.974.13" + "@aws-sdk/core" "^3.974.15" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-sso@^3.972.43": - version "3.972.43" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.43.tgz#24546d197ce74a29c89c37b3860952ee28f90c9b" - integrity sha512-LPc3+Y4vhH1T4x6CMqwCM6hk5+SRf/Lwmgm8INm95wxTtIRHcMwQUVkDzWu4Iw/RSncxYM2BC01OrYbxOPZvyg== +"@aws-sdk/credential-provider-sso@^3.972.45": + version "3.972.45" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.45.tgz#6c934ed4905f1d0966f6ada39d07432b166b201a" + integrity sha512-oHgbz/eFD8IKiksqDsz9ZMU4A59BpQq4QwJedBnGD80ZqYcHPPHZBwjBnxLVkB7iRVVHWpDclR8yWdD2PkQIUA== dependencies: - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/nested-clients" "^3.997.11" - "@aws-sdk/token-providers" "3.1052.0" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/nested-clients" "^3.997.13" + "@aws-sdk/token-providers" "3.1056.0" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/credential-provider-web-identity@^3.972.43": - version "3.972.43" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.43.tgz#1d4e99a4cba32c63bab21184f8d5d418c0744224" - integrity sha512-wQtL34lUD/09VXjwAUo2T+I3aEXRDxMB3DKmTJL/Zj0Gi6sLDTrVhae1XVt01yzkquOWajI/sZW72JGDZ1ciTw== +"@aws-sdk/credential-provider-web-identity@^3.972.45": + version "3.972.45" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.45.tgz#a8d189321695bf90a69344165814ef0274959221" + integrity sha512-CDhzKdb2onv5bpnjn/acgdNmJOQthPDLsPizU7rZflsEcgMMp8Mlri+U5hdxf8ldvZJpvM3vLU6D56vfJm5AMQ== dependencies: - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/nested-clients" "^3.997.11" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/nested-clients" "^3.997.13" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/middleware-bucket-endpoint@^3.972.15": - version "3.972.15" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.15.tgz#f1752b8107289df1313b647bf42e8e5f78f44192" - integrity sha512-O2HDANa+MrvbxpaRVQDiH3T13uAa9AkMjKyZmDygwauAmmvqZ5B0iRmKW+fuVGW6NPXuyXurFgIx69lSvmAWGA== +"@aws-sdk/middleware-bucket-endpoint@^3.972.17": + version "3.972.17" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.17.tgz#b5ef456db91873e563d1c0c6b9c2ee8533aa8f25" + integrity sha512-lbDmWuHenc+kiwCNrxz4MyN6nkxCWyTXPIWuspJN0ibziu+8CXci7vI1bK9MAkwy8cwJOEXNu0gBM5S0uTGRIg== dependencies: - "@aws-sdk/core" "^3.974.13" + "@aws-sdk/core" "^3.974.15" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/middleware-expect-continue@^3.972.13": - version "3.972.13" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.13.tgz#d6eac0372151e7aa978985ceb67311ab77b03939" - integrity sha512-sHiqIFg8o2ipT7t40B89Vj0ubSUtY6OSt/+Ee/OXhHch5K4+81zP2+QX8Lkc/nJ2QSmCySxOke7TEbmX69fe2g== +"@aws-sdk/middleware-expect-continue@^3.972.14": + version "3.972.14" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.14.tgz#6bffa547da965dba80ee123ac1f8f1446cc596c2" + integrity sha512-3TNFEVGO4sWZj9TEXOCZLzGEctXHnaO4fk2EQ8KVaboTbwHmEPEQrm17Xb9koImUIXEw0sgi2xtHjg7LuTS3rA== dependencies: "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/middleware-flexible-checksums@^3.974.21": - version "3.974.21" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.21.tgz#efa1acea9921691f8fe80160ebaa6514b2e0839c" - integrity sha512-alAu9heyiBK/OmRNXVxq8mmPTgeW2AQ6EYjRsI38kPZa1MZvt2Jh+BlGq7/GG9OVXOaEgD7DlGj/Lzfy5OmuEg== +"@aws-sdk/middleware-flexible-checksums@^3.974.23": + version "3.974.23" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.23.tgz#2166b7c505de4815d5017acb46e7930f39153159" + integrity sha512-4nPKARo2lfKvQGUt2fPA5NlS/mEohckdxpuC9ecbjVfj7B7NFFYHeTg+Bf5BEQwdn3yRfUIzFiEkPp8Yuaw3wA== dependencies: "@aws-crypto/crc32" "5.2.0" "@aws-crypto/crc32c" "5.2.0" "@aws-crypto/util" "5.2.0" - "@aws-sdk/core" "^3.974.13" + "@aws-sdk/core" "^3.974.15" "@aws-sdk/crc64-nvme" "^3.972.9" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" @@ -287,16 +287,15 @@ "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/middleware-sdk-s3@^3.972.42": - version "3.972.42" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.42.tgz#f4217eea10d2de43b482f6f2d0d9895be061e571" - integrity sha512-/xNqNGXv9LaxZd25L9VV4pnSOw9OdDNO4rAHamM+h3KQBSITljIH9vk3dveGga1I2j36lQd0rdG3gjNEXvtNew== +"@aws-sdk/middleware-sdk-s3@^3.972.44": + version "3.972.44" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.44.tgz#24bd4110d62e90e20fbf475a702302d37e5efe7e" + integrity sha512-8HQsRg1NpX8vR4vNl1E8pyLnqZroq9VSL2vZQVSgBqp6wv6365LzYD08/c9FFh/9FTg7YRc7aTtEmXF0ir/pqg== dependencies: - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/signature-v4-multi-region" "^3.996.28" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/signature-v4-multi-region" "^3.996.30" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/signature-v4" "^5.4.2" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" @@ -309,42 +308,41 @@ "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/nested-clients@^3.997.11": - version "3.997.11" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.997.11.tgz#ed97d5dadc5ee15a31834e8af218e502d986d632" - integrity sha512-nWXXJ1r/r8N2Gw1pWolRgED38/A9A8DHR2ETWIv220zh4PZHcybbR4hUVWWktmNXTRHzDJwRluapHn0rZxuoqA== +"@aws-sdk/nested-clients@^3.997.13": + version "3.997.13" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.997.13.tgz#5566425fadaac7e9a31141eb12710c2c1cf0a184" + integrity sha512-2pA6eyb5nSo/ZD2cayhOTEMoGQYgspq0RI05GDLkzQ3ajZ6isS6waV6E92Am/hz4LIlLUTrbwPLurJ/fuiHvkg== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/signature-v4-multi-region" "^3.996.28" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/signature-v4-multi-region" "^3.996.30" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/fetch-http-handler" "^5.4.3" - "@smithy/node-http-handler" "^4.7.3" + "@smithy/core" "^3.24.5" + "@smithy/fetch-http-handler" "^5.4.5" + "@smithy/node-http-handler" "^4.7.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/signature-v4-multi-region@^3.996.28": - version "3.996.28" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.28.tgz#79c12506d5545953c06fe75956b38050d57902f2" - integrity sha512-qs9z5LqXO/CZC2Lg9SGKpoLU8Rhi+m2pFKZqfO9pytX1clc0katqtsDNupJxFy0xT9wsZSPzM2v1y+/H/zfp5Q== +"@aws-sdk/signature-v4-multi-region@^3.996.30": + version "3.996.30" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.30.tgz#78e324094413c0c1e2e4b8c77d6981d1a2393c9f" + integrity sha512-HULDLMVzkmTSEv6//7kx2kRevp/VYUpm8hJNNFbmhxDn0fUiGTxVcM9yg31TukvTq8nyOBDUN2gH0o5IRbKjdw== dependencies: "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" - "@smithy/signature-v4" "^5.4.2" + "@smithy/signature-v4" "^5.4.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@aws-sdk/token-providers@3.1052.0": - version "3.1052.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.1052.0.tgz#0793c2f58351bf91937e8f83abf39d11937ec8f2" - integrity sha512-QqZNB3so7UIDxZtroc85TQaLVxdZRFm0eWM1CSR2N+b06as9TOrilvrlTZuj3guYlxMs6yLOgGxnklJ5qMYtTw== +"@aws-sdk/token-providers@3.1056.0": + version "3.1056.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.1056.0.tgz#a61372715ebc6527c4849c671539461fee4ca050" + integrity sha512-81duvlltQlsfn5K+o8zILcystBRdbT1G2JJYVCML5NZHBz4CL/zf+sAemCtBh/uh6RQUMyInGeZLQ7/8igZhbA== dependencies: - "@aws-sdk/core" "^3.974.13" - "@aws-sdk/nested-clients" "^3.997.11" + "@aws-sdk/core" "^3.974.15" + "@aws-sdk/nested-clients" "^3.997.13" "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" @@ -371,12 +369,11 @@ dependencies: tslib "^2.6.2" -"@aws-sdk/xml-builder@^3.972.25": - version "3.972.25" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.972.25.tgz#252ed0afef165a247c2dcc5d72e54b8f9e45f2e2" - integrity sha512-GH+Kjz4nPKWKHnsiQpnhP1MJdTGIcK4rAka6tzakgjjUkVgNsmPeEbbRAf09SzS1hjGu6duGHCBsxYke0BhHjQ== +"@aws-sdk/xml-builder@^3.972.26": + version "3.972.26" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.972.26.tgz#949366fe7c195f676f0ab9e002dd95b70942410c" + integrity sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g== dependencies: - "@nodable/entities" "2.1.0" "@smithy/types" "^4.14.2" fast-xml-parser "5.7.3" tslib "^2.6.2" @@ -395,12 +392,12 @@ js-tokens "^4.0.0" picocolors "^1.1.1" -"@babel/code-frame@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" - integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== +"@babel/code-frame@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" + integrity sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw== dependencies: - "@babel/helper-validator-identifier" "^7.28.5" + "@babel/helper-validator-identifier" "^7.29.7" js-tokens "^4.0.0" picocolors "^1.1.1" @@ -409,20 +406,25 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.28.6.tgz#103f466803fa0f059e82ccac271475470570d74c" integrity sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg== -"@babel/core@7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.0.tgz#5286ad785df7f79d656e88ce86e650d16ca5f322" - integrity sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA== - dependencies: - "@babel/code-frame" "^7.29.0" - "@babel/generator" "^7.29.0" - "@babel/helper-compilation-targets" "^7.28.6" - "@babel/helper-module-transforms" "^7.28.6" - "@babel/helpers" "^7.28.6" - "@babel/parser" "^7.29.0" - "@babel/template" "^7.28.6" - "@babel/traverse" "^7.29.0" - "@babel/types" "^7.29.0" +"@babel/compat-data@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.29.7.tgz#6f0237f0f36d2e51c0570a636faed9d2d0efe629" + integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== + +"@babel/core@7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.29.7.tgz#80c10b17248082968b57a857b91640971f2070f7" + integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/generator" "^7.29.7" + "@babel/helper-compilation-targets" "^7.29.7" + "@babel/helper-module-transforms" "^7.29.7" + "@babel/helpers" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/template" "^7.29.7" + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" "@jridgewell/remapping" "^2.3.5" convert-source-map "^2.0.0" debug "^4.1.0" @@ -462,13 +464,13 @@ "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" -"@babel/generator@^7.29.0": - version "7.29.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" - integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== +"@babel/generator@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.7.tgz#cca0b8827e6bcf3ba176788e7f3b180ad6db2fa3" + integrity sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ== dependencies: - "@babel/parser" "^7.29.0" - "@babel/types" "^7.29.0" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" "@jridgewell/gen-mapping" "^0.3.12" "@jridgewell/trace-mapping" "^0.3.28" jsesc "^3.0.2" @@ -484,11 +486,27 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz#7a1def704302401c47f64fa85589e974ae217042" + integrity sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g== + dependencies: + "@babel/compat-data" "^7.29.7" + "@babel/helper-validator-option" "^7.29.7" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-globals@^7.28.0": version "7.28.0" resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz#b9430df2aa4e17bc28665eadeae8aa1d985e6674" integrity sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw== +"@babel/helper-globals@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz#f04a96fbd8473241b1079243f5b3f03a3010ab7b" + integrity sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA== + "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.28.6": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" @@ -497,6 +515,14 @@ "@babel/traverse" "^7.28.6" "@babel/types" "^7.28.6" +"@babel/helper-module-imports@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz#ef25048a518e828d7393fac5882ddd73921d7396" + integrity sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g== + dependencies: + "@babel/traverse" "^7.29.7" + "@babel/types" "^7.29.7" + "@babel/helper-module-transforms@^7.28.6": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz#9312d9d9e56edc35aeb6e95c25d4106b50b9eb1e" @@ -506,21 +532,45 @@ "@babel/helper-validator-identifier" "^7.28.5" "@babel/traverse" "^7.28.6" +"@babel/helper-module-transforms@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz#b062747a5997ba138637201328bbff77960574ae" + integrity sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg== + dependencies: + "@babel/helper-module-imports" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + "@babel/traverse" "^7.29.7" + "@babel/helper-string-parser@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687" integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA== +"@babel/helper-string-parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz#7f0871d99824d23137d60f86fcf6130fd5a1b51f" + integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== + "@babel/helper-validator-identifier@^7.28.5": version "7.28.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz#010b6938fab7cb7df74aa2bbc06aa503b8fe5fb4" integrity sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q== +"@babel/helper-validator-identifier@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz#bd87084ced0c796ec46bda492de6e83d29e89fc2" + integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== + "@babel/helper-validator-option@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz#fa52f5b1e7db1ab049445b421c4471303897702f" integrity sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg== +"@babel/helper-validator-option@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz#cf315be940213b354eb4abcc0bd01ebe3f73bc2a" + integrity sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw== + "@babel/helpers@^7.28.6": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.28.6.tgz#fca903a313ae675617936e8998b814c415cbf5d7" @@ -529,6 +579,14 @@ "@babel/template" "^7.28.6" "@babel/types" "^7.28.6" +"@babel/helpers@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.29.7.tgz#45abfde7548997e34376c3e69feb475cffb4a607" + integrity sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg== + dependencies: + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" + "@babel/parser@^7.28.6": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.28.6.tgz#f01a8885b7fa1e56dd8a155130226cd698ef13fd" @@ -536,12 +594,12 @@ dependencies: "@babel/types" "^7.28.6" -"@babel/parser@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6" - integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== +"@babel/parser@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.7.tgz#837b87387cbf5ec5530cb634b3c622f68edb9334" + integrity sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg== dependencies: - "@babel/types" "^7.29.0" + "@babel/types" "^7.29.7" "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.3", "@babel/runtime@^7.23.9", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7": version "7.28.6" @@ -562,6 +620,15 @@ "@babel/parser" "^7.28.6" "@babel/types" "^7.28.6" +"@babel/template@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.29.7.tgz#4d9d4004f645cdd304de958c725162784ecac700" + integrity sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" + "@babel/traverse@^7.28.6": version "7.28.6" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.28.6.tgz#871ddc79a80599a5030c53b1cc48cbe3a5583c2e" @@ -575,17 +642,17 @@ "@babel/types" "^7.28.6" debug "^4.3.1" -"@babel/traverse@^7.29.0": - version "7.29.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" - integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== - dependencies: - "@babel/code-frame" "^7.29.0" - "@babel/generator" "^7.29.0" - "@babel/helper-globals" "^7.28.0" - "@babel/parser" "^7.29.0" - "@babel/template" "^7.28.6" - "@babel/types" "^7.29.0" +"@babel/traverse@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.7.tgz#c47b07a41b95da0907d026b5dd894d98de7d2f2d" + integrity sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw== + dependencies: + "@babel/code-frame" "^7.29.7" + "@babel/generator" "^7.29.7" + "@babel/helper-globals" "^7.29.7" + "@babel/parser" "^7.29.7" + "@babel/template" "^7.29.7" + "@babel/types" "^7.29.7" debug "^4.3.1" "@babel/types@^7.21.3", "@babel/types@^7.28.6": @@ -596,7 +663,7 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" -"@babel/types@^7.26.0", "@babel/types@^7.29.0": +"@babel/types@^7.26.0": version "7.29.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== @@ -604,6 +671,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" +"@babel/types@^7.29.7": + version "7.29.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92" + integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA== + dependencies: + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -1316,10 +1391,10 @@ resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-3.0.5.tgz#88e9bf4d11d2b19c082e78ebe7ce88724a5eb091" integrity sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw== -"@eslint/plugin-kit@^0.7.1": - version "0.7.1" - resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz#c4125fd015eceeb09b793109fdbcd4dd0a02d346" - integrity sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ== +"@eslint/plugin-kit@^0.7.2": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz#4b0962f3f2c7ce8bc98b3ecfe34525c09d2cb729" + integrity sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A== dependencies: "@eslint/core" "^1.2.1" levn "^0.4.1" @@ -1724,7 +1799,7 @@ dependencies: "@tybys/wasm-util" "^0.10.1" -"@nodable/entities@2.1.0", "@nodable/entities@^2.1.0": +"@nodable/entities@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@nodable/entities/-/entities-2.1.0.tgz#f543e5c6446720d4cf9e498a83019dd159973bc2" integrity sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA== @@ -2133,39 +2208,30 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@smithy/core@^3.24.3": - version "3.24.3" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.24.3.tgz#c9689ce6d64b40eee594a259b4504f1a357f6a54" - integrity sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg== - dependencies: - "@aws-crypto/crc32" "5.2.0" - "@smithy/types" "^4.14.2" - tslib "^2.6.2" - -"@smithy/core@^3.24.4": - version "3.24.4" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.24.4.tgz#aded2ba46962b5cceaaa75f646433ac4813c2e17" - integrity sha512-3UNRKEyQyAgVgM0LGlerCLm+ChZWZ1GPfde+jBEW6bm6bSBGU1p0EbblaUV3unbhwvidjLA5Zs3sOs7mnZwvAw== +"@smithy/core@^3.24.5": + version "3.24.5" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.24.5.tgz#396ca5662afc6d83a8f41b7e492e427c48a0924e" + integrity sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA== dependencies: "@aws-crypto/crc32" "5.2.0" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@smithy/credential-provider-imds@^4.3.2": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz#bead31aad6bebac48f034016bce77f68f8b2e4ab" - integrity sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w== +"@smithy/credential-provider-imds@^4.3.6": + version "4.3.6" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.6.tgz#dc1d06447b3208987489133923bcb6bbdd114cc6" + integrity sha512-tHhdiWZfG1ZIh2YcRfPJmY2gHcBmqbAzqm3ER4TIDFYsSEqTD5tICT7cgQ/kI8LRakxp12myOYyK68XPn7MnHw== dependencies: - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@smithy/fetch-http-handler@^5.4.3": - version "5.4.4" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.4.tgz#df28cfdbdbd192cef9508347b488d8874d0166dd" - integrity sha512-qM7AUKI4G6d7lNgaZD3lA1tWSolh5r6gcixfTZAPstVURfjIbvreVTPz+994M0yC3HbX4YYhDRgr31Xy3XwWOQ== +"@smithy/fetch-http-handler@^5.4.5": + version "5.4.5" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.5.tgz#5087612b4cb22671c13c2b5abe96dfb6518ec9a1" + integrity sha512-SK3VMeH0fibgdTg2QeB+O4p7Yy/2E5HBOHJeC58FshkDdeuX8lOgO7PfjYfLyPLP1ch55j91cQqKBzDS0mRjSQ== dependencies: - "@smithy/core" "^3.24.4" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" @@ -2176,21 +2242,21 @@ dependencies: tslib "^2.6.2" -"@smithy/node-http-handler@^4.7.3": - version "4.7.4" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.7.4.tgz#dfa9634130841cbb0a780c8b4a3ea7ec1c904f0c" - integrity sha512-HIeF+1vrDGzPkkv39Hj2vlHSXHY3p958jd/8ZnePIY6+ZOsQX8coyEUKO5yQu4r0bQIVsbpotVIrXXwyycMStQ== +"@smithy/node-http-handler@^4.7.5": + version "4.7.5" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.7.5.tgz#1fdb6ba04beababb54f20bfc1f74a34370fbdf51" + integrity sha512-3dA9TQ+ybRSZ/m0wnbZhiBy4Dezjgq1Ib/ZZrYTpJDBgpoLLU/SDzZc/g0x0MNAdOJe1wPcM+x2PBRmoOur+Sw== dependencies: - "@smithy/core" "^3.24.4" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" -"@smithy/signature-v4@^5.4.2": - version "5.4.3" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.4.3.tgz#d5bea6e6c32fef6bee0afe6819b9c9551b905103" - integrity sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g== +"@smithy/signature-v4@^5.4.5": + version "5.4.5" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.4.5.tgz#61e369ce381f536f833a4c7c53afbe0175a47556" + integrity sha512-QBJKWGqIknH0dc9LWpfH1mkdokAx6iXYN3UcQ3eY6uIEyScuoQAhfl94ge7ozUy9WgFUdE8xsvwBjaYBbWmPNA== dependencies: - "@smithy/core" "^3.24.3" + "@smithy/core" "^3.24.5" "@smithy/types" "^4.14.2" tslib "^2.6.2" @@ -2788,100 +2854,100 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz#c67bfee32caae9cb587dce1ac59c3bf43b659707" - integrity sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A== +"@typescript-eslint/eslint-plugin@^8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz#8fc1e0a950c43270eaf0212dc060f7edaa42f9cf" + integrity sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.59.4" - "@typescript-eslint/type-utils" "8.59.4" - "@typescript-eslint/utils" "8.59.4" - "@typescript-eslint/visitor-keys" "8.59.4" + "@typescript-eslint/scope-manager" "8.60.0" + "@typescript-eslint/type-utils" "8.60.0" + "@typescript-eslint/utils" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.5.0" -"@typescript-eslint/parser@^8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.59.4.tgz#77d99e3b27663e7a22cf12c3fb769db509e5e93c" - integrity sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ== +"@typescript-eslint/parser@^8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.60.0.tgz#38d611b8e658cb10850d4975e8a175a222fbcd6a" + integrity sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg== dependencies: - "@typescript-eslint/scope-manager" "8.59.4" - "@typescript-eslint/types" "8.59.4" - "@typescript-eslint/typescript-estree" "8.59.4" - "@typescript-eslint/visitor-keys" "8.59.4" + "@typescript-eslint/scope-manager" "8.60.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.59.4.tgz#5830535a0e7a3ae806e2669964f47a74c4bc6b0e" - integrity sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg== +"@typescript-eslint/project-service@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.60.0.tgz#b82ab12e64d005d0c7163d1240c432381f1bde0f" + integrity sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.59.4" - "@typescript-eslint/types" "^8.59.4" + "@typescript-eslint/tsconfig-utils" "^8.60.0" + "@typescript-eslint/types" "^8.60.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz#507d1258c758147dac1adee9517a205a8ac1e046" - integrity sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q== +"@typescript-eslint/scope-manager@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz#7617a4617c043fe235dcf066f9a40f106cfd2fd5" + integrity sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw== dependencies: - "@typescript-eslint/types" "8.59.4" - "@typescript-eslint/visitor-keys" "8.59.4" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" -"@typescript-eslint/tsconfig-utils@8.59.4", "@typescript-eslint/tsconfig-utils@^8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz#218ba229d96dde35212e3a76a7d0a6bc831398be" - integrity sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA== +"@typescript-eslint/tsconfig-utils@8.60.0", "@typescript-eslint/tsconfig-utils@^8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz#3af78c48956227a407dea9626b8db8ca53f130d2" + integrity sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ== -"@typescript-eslint/type-utils@8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz#359fc53ba39a1f1860fddda40ebe5bfe0d87faed" - integrity sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA== +"@typescript-eslint/type-utils@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz#6971a61bc4f3a1b2df45dcc14e26a43a88a4cb6a" + integrity sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg== dependencies: - "@typescript-eslint/types" "8.59.4" - "@typescript-eslint/typescript-estree" "8.59.4" - "@typescript-eslint/utils" "8.59.4" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/utils" "8.60.0" debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@8.59.4", "@typescript-eslint/types@^8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.59.4.tgz#c29d5c21bfbaa8347ddc677d3ac1fcd2db0f848e" - integrity sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q== +"@typescript-eslint/types@8.60.0", "@typescript-eslint/types@^8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.60.0.tgz#e77ad768e933263b1960b2fe79de75fe1cc6e7db" + integrity sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA== -"@typescript-eslint/typescript-estree@8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz#d005e5e1fb425526f39685594bed34a04ad755ea" - integrity sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag== +"@typescript-eslint/typescript-estree@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz#c102196a44414481190041c99eea1d854e66001b" + integrity sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g== dependencies: - "@typescript-eslint/project-service" "8.59.4" - "@typescript-eslint/tsconfig-utils" "8.59.4" - "@typescript-eslint/types" "8.59.4" - "@typescript-eslint/visitor-keys" "8.59.4" + "@typescript-eslint/project-service" "8.60.0" + "@typescript-eslint/tsconfig-utils" "8.60.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/visitor-keys" "8.60.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.59.4.tgz#8ccd2b08aecc72c7efc0d7ac6695631d199d256e" - integrity sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw== +"@typescript-eslint/utils@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.60.0.tgz#6110cddaef87606ae4ca6f8bf81bb5949fc8e098" + integrity sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.59.4" - "@typescript-eslint/types" "8.59.4" - "@typescript-eslint/typescript-estree" "8.59.4" + "@typescript-eslint/scope-manager" "8.60.0" + "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/typescript-estree" "8.60.0" -"@typescript-eslint/visitor-keys@8.59.4": - version "8.59.4" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz#1ac23b747b011f5cbdb449da97769f6c5f3a9355" - integrity sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ== +"@typescript-eslint/visitor-keys@8.60.0": + version "8.60.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz#f2c41eedd3d7b03b808369fb2e3fb40a93783ec2" + integrity sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg== dependencies: - "@typescript-eslint/types" "8.59.4" + "@typescript-eslint/types" "8.60.0" eslint-visitor-keys "^5.0.0" "@ungap/structured-clone@^1.0.0": @@ -3536,6 +3602,11 @@ bn.js@^5.2.1, bn.js@^5.2.2: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.2.tgz#82c09f9ebbb17107cd72cb7fd39bd1f9d0aaa566" integrity sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw== +bn.js@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.3.tgz#16a9e409616b23fef3ccbedb8d42f13bff80295e" + integrity sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w== + boolean@^3.0.1: version "3.2.0" resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" @@ -3636,7 +3707,7 @@ browserify-rsa@^4.0.0, browserify-rsa@^4.1.1: randombytes "^2.1.0" safe-buffer "^5.2.1" -browserify-sign@^4.2.3, browserify-sign@^4.2.5: +browserify-sign@^4.2.3: version "4.2.5" resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.5.tgz#3979269fa8af55ba18aac35deef11b45515cd27d" integrity sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw== @@ -3651,6 +3722,21 @@ browserify-sign@^4.2.3, browserify-sign@^4.2.5: readable-stream "^2.3.8" safe-buffer "^5.2.1" +browserify-sign@^4.2.6: + version "4.2.6" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.6.tgz#a8c9fd9a701a3600c7fea3a82c14ab82cad6f451" + integrity sha512-sd+Q65fjlWCYWtZKXiKfrUc8d+4jtp/8f0W2NkwzLtoW4bI6UDnWusLWIurHnmurW0XShIRxpwiOX4EoPtXUAg== + dependencies: + bn.js "^5.2.3" + browserify-rsa "^4.1.1" + create-hash "^1.2.0" + create-hmac "^1.1.7" + elliptic "^6.6.1" + inherits "^2.0.4" + parse-asn1 "^5.1.9" + readable-stream "^2.3.8" + safe-buffer "^5.2.1" + browserslist@^4.24.0, browserslist@^4.28.1: version "4.28.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.28.1.tgz#7f534594628c53c63101079e27e40de490456a95" @@ -4659,10 +4745,10 @@ electron@*: "@types/node" "^24.9.0" extract-zip "^2.0.1" -electron@42.2.0: - version "42.2.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-42.2.0.tgz#513ac2d86c2a3217c17d25f24809bc46d470e7c2" - integrity sha512-b2Tc7sIKiZEl0tBVwFM5GJ+FT5KYhmy9QJHjx8BGVZPVW2SctXWEvrE959ElB56qw7H05dBkhlikDA1DmpaAMw== +electron@42.3.0: + version "42.3.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-42.3.0.tgz#028d2f64bf69182f36fc74b72f59b48fc143f681" + integrity sha512-9ZiLdRXk+WDxW1OgIUz8J2rIQ5TYU9o629gCOjU48Q3dQiOmym7osWsH5Ubs/Jh4uuFLn6m6SBD2rmRXLAPz9g== dependencies: "@electron/get" "^5.0.0" "@types/node" "^24.9.0" @@ -5025,17 +5111,17 @@ eslint-visitor-keys@^5.0.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== -eslint@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.4.0.tgz#d86b6c405de0f19f3318c47139b8cb6771b3f592" - integrity sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ== +eslint@^10.4.1: + version "10.4.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.4.1.tgz#f6640b176e0912246d9ddbf8fcfa5e8b7f02445a" + integrity sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.2" "@eslint/config-array" "^0.23.5" "@eslint/config-helpers" "^0.6.0" "@eslint/core" "^1.2.1" - "@eslint/plugin-kit" "^0.7.1" + "@eslint/plugin-kit" "^0.7.2" "@humanfs/node" "^0.16.6" "@humanwhocodes/module-importer" "^1.0.1" "@humanwhocodes/retry" "^0.4.2" @@ -5969,10 +6055,10 @@ https-proxy-agent@^7.0.0: agent-base "^7.1.2" debug "4" -i18next@^26.2.0: - version "26.2.0" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-26.2.0.tgz#4dc31d5ada1495f6884851f7c7a3fb6a36f23794" - integrity sha512-zwBHldHdTmwN7r6UNc7lC6GWNN+YYg3DrRSeHR5PRRBf5QnJZcYHrQc0uaU26qZeYxR7iFZD+Y315dPnKP47wA== +i18next@^26.3.0: + version "26.3.0" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-26.3.0.tgz#057c1165589b3f312714ea2a0c3f368c2c7cd84f" + integrity sha512-gHSgGpUXVmuqE2El1W61DmxeyeTlFfZgdJRWMo9jScAn5pu7TuTuiccb1zh3E2J9hEBVGJ23+96x0ieBhfuIHA== iconv-corefoundation@^1.1.7: version "1.1.7" @@ -8365,17 +8451,17 @@ react-modal@^3.16.3: "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.4.0" -react-router-dom@^7.15.1: - version "7.15.1" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.15.1.tgz#cf5aee65a44e407a17a2e9718d5350482b1e281f" - integrity sha512-AzF62gjY6U9rkMq4RfP/r2EVtQ7DMfNMjyOp/flLTCrtRylLiK4wT4pSq6O8rOXZ2eXdZYJPEYe+ifomiv+Igg== +react-router-dom@^7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.16.0.tgz#284a7cd021052aa7d0a9240dca4a02eec24eceb5" + integrity sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA== dependencies: - react-router "7.15.1" + react-router "7.16.0" -react-router@7.15.1: - version "7.15.1" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.15.1.tgz#0a12fece05887a47c54970480745385c793bcaac" - integrity sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A== +react-router@7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.16.0.tgz#fb41536aef2ccc2c7be12ea6be819a1e56eb6343" + integrity sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A== dependencies: cookie "^1.0.1" set-cookie-parser "^2.6.0" @@ -9352,10 +9438,10 @@ systeminformation@*: resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.30.5.tgz#0b8840ff697b8f036901bf4f8586c9278c7c9e88" integrity sha512-DpWmpCckhwR3hG+6udb6/aQB7PpiqVnvSljrjbKxNSvTRsGsg7NVE3/vouoYf96xgwMxXFKcS4Ux+cnkFwYM7A== -systeminformation@^5.31.6: - version "5.31.6" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.31.6.tgz#2da4979a7262974fd068a3a306ded30aed6127c0" - integrity sha512-Uv2b2uGGM6ns+26czgW2cYRabYdnswM0ddSOOlryHOaelzsmDSet1iM/NT7VOYxW8x/BW+HkY+b1Ve2pLTSGSA== +systeminformation@^5.31.7: + version "5.31.7" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.31.7.tgz#32009a8790af048299ea46d01e9f306adc1abb45" + integrity sha512-/8NC53e5nP9nmhn42/ncdOkyJnOoue/Vy+tJOyUGd1Yv66G069wK4rrziwhrqDETgk78CudTQupw5z19S5uoZw== tagged-tag@^1.0.0: version "1.0.0" @@ -9616,10 +9702,10 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6 resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== -tsx@4.22.3: - version "4.22.3" - resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.22.3.tgz#7ca7cb34028e3e247f1fad300c157e42a90a1f50" - integrity sha512-mdoNxBC/cSQObGGVQ5Bpn5i+yv7j68gk3Nfm3wFjcJg3Z0Mix9jzAFfP12prmm5eVGmDKtp0yyArrs0Q+8gZHg== +tsx@4.22.4: + version "4.22.4" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.22.4.tgz#0ab3b7fb4ec7feeee74e5b1f26337caa71e44700" + integrity sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg== dependencies: esbuild "~0.28.0" optionalDependencies: From d4a29606f855d26d09bb535544ee25dead7b8388 Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 1 Jun 2026 04:28:09 +0300 Subject: [PATCH 05/43] fix: context menu top action Took 6 minutes --- package.json | 2 +- src/renderer/features/context_menu/index.tsx | 5 +++++ .../model/contextMenuSections.tsx | 22 ++++++++++--------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index a19777c0..5e90862d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "author": "PulseSync ", "description": "PulseSync app for desktops", "homepage": "https://pulsesync.dev", - "version": "2.16.0-beta", + "version": "2.17.0-beta", "main": ".vite/main/index.cjs", "scripts": { "start": "electron-forge start", diff --git a/src/renderer/features/context_menu/index.tsx b/src/renderer/features/context_menu/index.tsx index 4c36c603..5405ee7c 100644 --- a/src/renderer/features/context_menu/index.tsx +++ b/src/renderer/features/context_menu/index.tsx @@ -49,6 +49,10 @@ const ContextMenu: React.FC = ({ modalRef }) => { window.desktopEvents?.send(MainEvents.OPEN_EXTERNAL, `${config.WEBSITE_URL}/subscription`) } + const openBoostyUrl = () => { + window.desktopEvents?.send(MainEvents.OPEN_EXTERNAL, config.BOOSTY_URL) + } + const canResetAsarPath = window.electron.isLinux() && Boolean(window.electron.store.get('settings.modSavePath')) const updateSourceSwitchBlocked = updateStatus === 'CHECKING' || updateStatus === 'DOWNLOADING' @@ -397,6 +401,7 @@ const ContextMenu: React.FC = ({ modalRef }) => { downloadObsWidget, isAutonomousMode, openAppDirectory, + openBoostyUrl, openSubscriptionPage, subscriptionPageEnabled, openUpdateChannelModal, diff --git a/src/renderer/features/context_menu/model/contextMenuSections.tsx b/src/renderer/features/context_menu/model/contextMenuSections.tsx index 3e344090..0d88fa01 100644 --- a/src/renderer/features/context_menu/model/contextMenuSections.tsx +++ b/src/renderer/features/context_menu/model/contextMenuSections.tsx @@ -1,5 +1,6 @@ import React from 'react' import { MdWorkspacePremium } from 'react-icons/md' +import { SiBoosty } from 'react-icons/si' import MainEvents from '@common/types/mainEvents' import ArrowContext from '@shared/assets/icons/arrowContext.svg' @@ -66,6 +67,7 @@ type Params = { downloadObsWidget: () => void isAutonomousMode: boolean openAppDirectory: () => void + openBoostyUrl: () => void openSubscriptionPage: () => void subscriptionPageEnabled: boolean openUpdateChannelModal: () => void @@ -97,6 +99,7 @@ export function buildContextMenuSections({ downloadObsWidget, isAutonomousMode, openAppDirectory, + openBoostyUrl, openSubscriptionPage, subscriptionPageEnabled, openUpdateChannelModal, @@ -136,17 +139,16 @@ export function buildContextMenuSections({ ] return [ - ...( + createContentSection( subscriptionPageEnabled ? - [ - createContentSection( - , - ), - ] - : [] + + : , ), createButtonSection(t('contextMenu.obsWidget.title'), [ { From 71180f38fc2db18af964e8ee399ce0074a214d08 Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Tue, 2 Jun 2026 15:27:24 +0300 Subject: [PATCH 06/43] fix: Fix news padding --- src/renderer/pages/home/ui/home.module.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer/pages/home/ui/home.module.scss b/src/renderer/pages/home/ui/home.module.scss index a637b8f5..22c07af9 100644 --- a/src/renderer/pages/home/ui/home.module.scss +++ b/src/renderer/pages/home/ui/home.module.scss @@ -46,7 +46,8 @@ min-height: 0; width: 100%; background: #434962; - padding-inline: 25px; + padding-inline-start: 25px; + padding-inline-end: 10px; padding-block-start: 15px; position: relative; overflow: hidden; @@ -58,6 +59,7 @@ justify-content: space-between; gap: 16px; margin-bottom: 14px; + margin-right: 18px; } .panelTitle { From 84356ad82e92b25d60cdaffccf30cf38f4e877d6 Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Tue, 2 Jun 2026 15:27:47 +0300 Subject: [PATCH 07/43] fix: Remove settings button --- src/renderer/widgets/layout/header.module.scss | 1 + src/renderer/widgets/layout/header.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/renderer/widgets/layout/header.module.scss b/src/renderer/widgets/layout/header.module.scss index 3b675dc1..69deef90 100644 --- a/src/renderer/widgets/layout/header.module.scss +++ b/src/renderer/widgets/layout/header.module.scss @@ -55,6 +55,7 @@ .logoplace { padding: 10px 20px; + margin-left: 63px; // Заместить пропавшый блок настроек. border-radius: 10px; display: flex; align-items: center; diff --git a/src/renderer/widgets/layout/header.tsx b/src/renderer/widgets/layout/header.tsx index 41d4699b..1b388317 100644 --- a/src/renderer/widgets/layout/header.tsx +++ b/src/renderer/widgets/layout/header.tsx @@ -397,11 +397,11 @@ const Header: React.FC

= () => {

- - - + {/**/} + {/* */} + {/**/}
-
+ {!isMac &&
@@ -497,7 +498,7 @@ const Header: React.FC

= () => { > -

+
}
From b676281938dcf4a3c9baa614538a48c8cb0cc35c Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Tue, 2 Jun 2026 16:59:44 +0300 Subject: [PATCH 09/43] fix: fix trafficLight position on macOS --- src/main/modules/createWindow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/modules/createWindow.ts b/src/main/modules/createWindow.ts index 21c4c428..e2136a0a 100644 --- a/src/main/modules/createWindow.ts +++ b/src/main/modules/createWindow.ts @@ -154,7 +154,7 @@ export async function createWindow(): Promise { minWidth: minMain.width, minHeight: minMain.height, titleBarStyle: 'hidden', - trafficLightPosition: { x: 16, y: 10 }, + trafficLightPosition: { x: 14, y: 24 }, icon, webPreferences: { preload: path.join(__dirname, 'mainWindowPreload.cjs'), From e6609e31e9dc248a397d3fa18d5b8c5cf125a5cf Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Tue, 2 Jun 2026 17:08:22 +0300 Subject: [PATCH 10/43] fix: fix trafficLight position on macOS --- src/main/modules/createWindow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/modules/createWindow.ts b/src/main/modules/createWindow.ts index e2136a0a..c2a93531 100644 --- a/src/main/modules/createWindow.ts +++ b/src/main/modules/createWindow.ts @@ -154,7 +154,7 @@ export async function createWindow(): Promise { minWidth: minMain.width, minHeight: minMain.height, titleBarStyle: 'hidden', - trafficLightPosition: { x: 14, y: 24 }, + trafficLightPosition: { x: 15, y: 20 }, icon, webPreferences: { preload: path.join(__dirname, 'mainWindowPreload.cjs'), From 0082cc86b4b53cbf378ef526366f84cf669dd3af Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Sat, 6 Jun 2026 10:57:19 +0300 Subject: [PATCH 11/43] fix: Change style of searchContainer --- src/renderer/pages/users/users.module.scss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderer/pages/users/users.module.scss b/src/renderer/pages/users/users.module.scss index 34de90d1..0c0e777c 100644 --- a/src/renderer/pages/users/users.module.scss +++ b/src/renderer/pages/users/users.module.scss @@ -124,8 +124,9 @@ height: 48px; max-width: 400px; width: 100%; - background-color: rgb(111 120 153 / 65%); - border-radius: 12px; + background-color: #12151fb8; + border: 1px solid #69739452; + border-radius: 10px; cursor: text; position: relative; transition: border-color 0.2s; From ff7b9481a6994b0c3199db34a2a99853971a20cc Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Sat, 6 Jun 2026 10:58:25 +0300 Subject: [PATCH 12/43] =?UTF-8?q?fix:=20Rename=20"=D0=9A=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=20=D1=80=D0=B0=D1=81=D1=88=D0=B8=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B9"=20to=20"=D0=9A=D0=B0=D1=82=D0=B0=D0=BB?= =?UTF-8?q?=D0=BE=D0=B3=20=D0=B0=D0=B4=D0=B4=D0=BE=D0=BD=D0=BE=D0=B2"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/locales/ru/renderer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/ru/renderer.json b/src/locales/ru/renderer.json index df3674dd..4be9b63a 100644 --- a/src/locales/ru/renderer.json +++ b/src/locales/ru/renderer.json @@ -819,7 +819,7 @@ "nav": { "addonsBeta": "Аддоны", "development": "Development", - "extensionsStore": "Каталог расширений", + "extensionsStore": "Каталог аддонов", "home": "Главная", "trackInfo": "Информация о треке", "users": "Пользователи", @@ -996,7 +996,7 @@ "volumeControl": "Контроль громкости" }, "headerSubtitle": "Обновляйте интерфейс, открывайте новые возможности", - "headerTitle": "Каталог расширений", + "headerTitle": "Каталог аддонов", "title": "Store" } }, From da1a489dab59e07a0fa34037930abea43e2f98c9 Mon Sep 17 00:00:00 2001 From: TheKingOfTime Date: Sat, 6 Jun 2026 11:32:31 +0300 Subject: [PATCH 13/43] feat: Update profile layout --- .../variants/ProfileShimmer.module.scss | 21 ++- .../PSUI/Shimmer/variants/ProfileShimmer.tsx | 43 ++--- .../tabs/ProfileTab/AchievementsSection.tsx | 8 +- .../tabs/ProfileTab/FriendButton.tsx | 2 +- .../tabs/ProfileTab/ProfileHeader.tsx | 167 +++++++++--------- .../userProfileModal.module.scss | 22 ++- 6 files changed, 145 insertions(+), 118 deletions(-) diff --git a/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.module.scss b/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.module.scss index 0eb034f0..55b4d60a 100644 --- a/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.module.scss +++ b/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.module.scss @@ -41,11 +41,20 @@ gap: 15px; } +.profileMetaContainer { + display: flex; + width: 100%; + flex-direction: row; + gap: 15px; + align-items: center; + justify-content: space-between; +} + .identity { display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; + flex-direction: row; + align-items: center; + justify-content: start; gap: 15px; position: relative; z-index: 1; @@ -120,12 +129,12 @@ position: relative; z-index: 1; width: 100%; - margin-top: 4px; + justify-content: end; } .buttonFull { @include shimmer.surface; - width: 100%; + width: 200px; height: 40px; border-radius: 8px; } @@ -133,7 +142,7 @@ .section { @include shimmer.surface(#292c36); min-height: 240px; - padding: 20px 40px; + padding: 10px 40px; display: flex; flex-direction: column; gap: 15px; diff --git a/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.tsx b/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.tsx index 0ea659ee..24a32f25 100644 --- a/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.tsx +++ b/src/renderer/shared/ui/PSUI/Shimmer/variants/ProfileShimmer.tsx @@ -10,34 +10,30 @@ export default function ProfileShimmer() {
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- -
-
+
+
+
-
-
-
-
-
@@ -52,6 +48,11 @@ export default function ProfileShimmer() {
+
+
+
+
+
{Array.from({ length: 3 }, (_, index) => ( diff --git a/src/renderer/widgets/userProfileModal/tabs/ProfileTab/AchievementsSection.tsx b/src/renderer/widgets/userProfileModal/tabs/ProfileTab/AchievementsSection.tsx index 3608669c..bea7e038 100644 --- a/src/renderer/widgets/userProfileModal/tabs/ProfileTab/AchievementsSection.tsx +++ b/src/renderer/widgets/userProfileModal/tabs/ProfileTab/AchievementsSection.tsx @@ -94,16 +94,16 @@ const AchievementsSection: React.FC = ({ userProfile,
)}
-
-
{t('profile.achievements.title')}
-
{t('profile.achievements.subtitle')}
-
+
+
{t('profile.achievements.title')}
+
{t('profile.achievements.subtitle')}
+
{userProfile.allAchievements && userProfile.allAchievements.length > 0 ? ( <>
diff --git a/src/renderer/widgets/userProfileModal/tabs/ProfileTab/FriendButton.tsx b/src/renderer/widgets/userProfileModal/tabs/ProfileTab/FriendButton.tsx index 56f23aee..981ffdb0 100644 --- a/src/renderer/widgets/userProfileModal/tabs/ProfileTab/FriendButton.tsx +++ b/src/renderer/widgets/userProfileModal/tabs/ProfileTab/FriendButton.tsx @@ -60,7 +60,7 @@ const FriendButton: React.FC = ({ userProfile, user, profileN } let buttonTextNormal = t('profile.friendButton.addFriend') - let buttonTextHover = t('profile.friendButton.follow') + let buttonTextHover = t('profile.friendButton.addFriend') let normalIcon = let hoverIcon = let buttonClass = styles.buttonAddFriendWhite diff --git a/src/renderer/widgets/userProfileModal/tabs/ProfileTab/ProfileHeader.tsx b/src/renderer/widgets/userProfileModal/tabs/ProfileTab/ProfileHeader.tsx index 327325e1..8ee84f50 100644 --- a/src/renderer/widgets/userProfileModal/tabs/ProfileTab/ProfileHeader.tsx +++ b/src/renderer/widgets/userProfileModal/tabs/ProfileTab/ProfileHeader.tsx @@ -45,7 +45,7 @@ const ProfileHeader: React.FC = ({ userProfile, user, childr const updateAllowAnimate = () => { const scrollTop = useWindowScroll ? window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0 - : scrollContainer?.scrollTop ?? 0 + : (scrollContainer?.scrollTop ?? 0) setAllowAnimate(scrollTop < threshold) } @@ -76,91 +76,98 @@ const ProfileHeader: React.FC = ({ userProfile, user, childr allowAnimate={allowAnimate} />
-
- -
-
+
+
+ +
- {new Date(userProfile.createdAt) <= new Date(2025, 0, 17) ? ( - - {new Date(userProfile.createdAt).toLocaleString(i18n.language, { - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - })} -
- } - side="top" - > - {t('profile.sinceBeginning')} - - ) : ( - - {new Date(userProfile.createdAt).toLocaleString(i18n.language, { - year: 'numeric', - month: 'long', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', - second: '2-digit', - })} -
- } - side="top" - > - {t('profile.registrationDate')}{' '} - {new Date(userProfile.createdAt).toLocaleDateString(i18n.language, { - month: 'long', - year: 'numeric', - })} - - )} +
+ {new Date(userProfile.createdAt) <= new Date(2025, 0, 17) ? ( + + {new Date(userProfile.createdAt).toLocaleString(i18n.language, { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + })} +
+ } + side="top" + > + {t('profile.sinceBeginning')} + + ) : ( + + {new Date(userProfile.createdAt).toLocaleString(i18n.language, { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + })} +
+ } + side="top" + > + {t('profile.registrationDate')}{' '} + {new Date(userProfile.createdAt).toLocaleDateString(i18n.language, { + month: 'long', + year: 'numeric', + })} + + )} +
-
-
- {userProfile.nickname || t('profile.noNickname')} -
- - - - {visibleBadges.length > 0 && - visibleBadges - .sort((a: any, b: any) => (b.level ?? 0) - (a.level ?? 0)) - .map((badge: any) => ( - - {badge.type} - - ))} +
+ {userProfile.nickname || t('profile.noNickname')} +
+ + + + {visibleBadges.length > 0 && + visibleBadges + .sort((a: any, b: any) => (b.level ?? 0) - (a.level ?? 0)) + .map((badge: any) => ( + + {badge.type} + + ))} +
+
@{userProfile.username}
-
@{userProfile.username}
+
{children}
-
{children}
) } diff --git a/src/renderer/widgets/userProfileModal/userProfileModal.module.scss b/src/renderer/widgets/userProfileModal/userProfileModal.module.scss index ee73dab0..556db7a7 100644 --- a/src/renderer/widgets/userProfileModal/userProfileModal.module.scss +++ b/src/renderer/widgets/userProfileModal/userProfileModal.module.scss @@ -123,6 +123,15 @@ padding: 0; } +.profileMetaContainer { + width: 100%; + + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + .bannerBackground { width: 100%; height: 350px; @@ -203,9 +212,9 @@ .userImage { display: flex; - flex-direction: column; - justify-content: center; - align-items: flex-start; + flex-direction: row; + justify-content: start; + align-items: center; gap: 15px; width: 100%; position: relative; @@ -250,6 +259,7 @@ display: flex; flex-direction: row; align-items: center; + justify-content: end; gap: 10px; width: 100%; position: relative; @@ -268,7 +278,7 @@ border-radius: 8px; padding: 10px 15px; gap: 8px; - width: -webkit-fill-available; + width: fit-content; svg { margin-right: 5px; @@ -324,7 +334,7 @@ align-items: flex-start; padding: 10px 15px; gap: 10px; - width: 100%; + width: fit-content; background: #ffffff; border-radius: 8px; font-weight: 700; @@ -404,7 +414,7 @@ display: flex; flex-direction: column; align-items: flex-start; - padding: 20px 40px; + padding: 10px 40px; gap: 15px; width: 100%; background: #292c36; From 51559cdc682e3a58641aa900642a63634eeb102f Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Fri, 12 Jun 2026 20:36:47 +0300 Subject: [PATCH 14/43] refactor: add worker to mod downloader Took 2 hours 12 minutes --- forge.config.ts | 6 +- package.json | 52 +- src/main/modules/mod/download.helpers.ts | 99 +- src/main/modules/mod/installModUpdateFrom.ts | 2 +- src/main/modules/mod/mod-files.ts | 19 +- src/main/modules/mod/mod-manager.helpers.ts | 73 +- src/main/modules/mod/modManager.ts | 23 +- .../modules/mod/network/artifactWorker.ts | 377 ++++ .../mod/network/artifactWorker.types.ts | 80 + .../mod/network/artifactWorkerClient.ts | 163 ++ src/main/modules/mod/network/helpers.ts | 187 +- src/main/modules/mod/network/index.ts | 342 +++- src/main/modules/network/systemProxy.ts | 21 +- .../app/providers/experiments/constants.ts | 2 - tsconfig.node.json | 1 + vite.worker.config.ts | 30 + yarn.lock | 1580 ++++++++--------- 17 files changed, 1942 insertions(+), 1115 deletions(-) create mode 100644 src/main/modules/mod/network/artifactWorker.ts create mode 100644 src/main/modules/mod/network/artifactWorker.types.ts create mode 100644 src/main/modules/mod/network/artifactWorkerClient.ts create mode 100644 vite.worker.config.ts diff --git a/forge.config.ts b/forge.config.ts index 74edf4e5..9843f700 100644 --- a/forge.config.ts +++ b/forge.config.ts @@ -59,7 +59,7 @@ const forgeConfig: ForgeConfig = { executableName: process.platform === 'linux' ? 'pulsesync' : 'PulseSync', appCopyright: `Copyright (C) ${new Date().getFullYear()} Матвиенко Артём Евгеньевич`, asar: { - unpack: '**/.vite/renderer/**/static/assets/icon/**', + unpack: '{**/.vite/renderer/**/static/assets/icon/**,**/.vite/worker/**}', }, win32metadata: { CompanyName: 'Матвиенко Артём Евгеньевич', @@ -83,6 +83,10 @@ const forgeConfig: ForgeConfig = { config: 'vite.preload.config.ts', target: 'preload', }, + { + entry: 'src/main/modules/mod/network/artifactWorker.ts', + config: 'vite.worker.config.ts', + }, ], renderer: [ { diff --git a/package.json b/package.json index 5e90862d..5d244aa5 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "keywords": [], "license": "SEE LICENSE IN LICENSE", "devDependencies": { - "@aws-sdk/client-s3": "^3.1057.0", + "@aws-sdk/client-s3": "^3.1067.0", "@babel/core": "7.29.7", "@electron-forge/cli": "^7.11.2", "@electron-forge/maker-deb": "^7.11.2", @@ -43,9 +43,9 @@ "@types/fs-extra": "^11.0.4", "@types/glob": "^9.0.0", "@types/lodash.debounce": "^4.0.9", - "@types/node": "^25.9.1", + "@types/node": "^25.9.3", "@types/plist": "^3.0.5", - "@types/react": "^19.2.15", + "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "@types/react-modal": "^3.16.3", "@types/react-transition-group": "^4.4.12", @@ -59,79 +59,79 @@ "@types/unzipper": "^0.10.11", "@types/uuid": "^11.0.0", "@types/yazl": "^3.3.1", - "@typescript-eslint/eslint-plugin": "^8.60.0", - "@typescript-eslint/parser": "^8.60.0", + "@typescript-eslint/eslint-plugin": "^8.61.0", + "@typescript-eslint/parser": "^8.61.0", "@vitejs/plugin-react": "^6.0.2", "babel-plugin-react-compiler": "^1.0.0", - "electron": "42.3.0", - "electron-builder": "^26.8.1", + "electron": "42.4.0", + "electron-builder": "^26.15.3", "eslint": "^10.4.1", "eslint-plugin-import": "^2.32.0", "iconv-lite": "^0.7.2", - "prettier": "^3.8.3", - "sass": "^1.100.0", + "prettier": "^3.8.4", + "sass": "^1.101.0", "ts-node": "^10.9.2", "tsx": "4.22.4", "typescript": "^6.0.3", - "vite": "^8.0.14", + "vite": "^8.0.16", "vite-plugin-svgr": "^5.2.0" }, "dependencies": { - "@apollo/client": "^4.2.0", + "@apollo/client": "^4.2.3", "@dr.pogodin/react-helmet": "^3.2.2", "@electron-forge/plugin-fuses": "^7.11.2", - "@electron/fuses": "2.1.1", + "@electron/fuses": "2.1.2", "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.1", "@mui/joy": "^5.0.0-beta.52", - "@radix-ui/react-tooltip": "^1.2.8", - "acorn": "^8.16.0", + "@radix-ui/react-tooltip": "^1.2.9", + "acorn": "^8.17.0", "acorn-walk": "^8.3.5", "adm-zip": "^0.5.17", - "axios": "^1.16.1", + "axios": "^1.17.0", "browserify-fs": "^1.0.0", "browserify-sign": "^4.2.6", "chart.js": "^4.5.1", "crypto-browserify": "^3.12.1", "electron-chrome-web-store": "^0.13.0", "electron-store": "11.0.2", - "electron-updater": "^6.8.3", + "electron-updater": "^6.8.9", "formik": "^2.4.9", "framer-motion": "^12.40.0", "fs-extra": "^11.3.5", "glob": "^13.0.6", - "graphql": "^16.14.0", + "graphql": "^16.14.2", "graphql-ws": "^6.0.8", - "i18next": "^26.3.0", + "i18next": "^26.3.1", "lodash.debounce": "^4.0.8", "log4js": "^6.9.1", "net": "^1.0.2", "path-browserify": "^1.0.1", "plist": "^5.0.0", - "react": "^19.2.6", + "react": "^19.2.7", "react-chartjs-2": "^5.3.1", - "react-dom": "^19.2.6", + "react-dom": "^19.2.7", "react-hot-toast": "^2.6.0", "react-i18next": "^17.0.8", "react-icons": "^5.6.0", "react-loading-skeleton": "^3.5.0", "react-markdown": "^10.1.0", "react-modal": "^3.16.3", - "react-router-dom": "^7.16.0", + "react-router-dom": "^7.17.0", "react-transition-group": "^4.4.5", "rehype-raw": "^7.0.0", "remark-breaks": "^4.0.0", "remark-gfm": "^4.0.1", "resedit": "^3.0.2", "rxjs": "^7.8.2", - "semver": "^7.8.1", + "semver": "^7.8.4", "socket.io": "^4.8.3", "socket.io-client": "^4.8.3", "source-map-support": "^0.5.21", "stream-browserify": "^3.0.0", "string-similarity": "^4.0.4", "systeminformation": "^5.31.7", - "tar": "^7.5.15", + "tar": "^7.5.16", "url": "^0.11.4", "uuid": "^14.0.0", "yaml": "^2.9.0", @@ -147,9 +147,9 @@ "forge": "./forge.config.ts" }, "buildInfo": { - "VERSION": "2.16.0-beta", - "BRANCH": "4cc7ca30", - "BUILD_TIME": "17.05.2026, 23:38:32" + "VERSION": "2.17.0-beta", + "BRANCH": "da1a489d", + "BUILD_TIME": "12.06.2026, 20:29:39" }, "optionalDependencies": { "bufferutil": "^4.1.0", diff --git a/src/main/modules/mod/download.helpers.ts b/src/main/modules/mod/download.helpers.ts index c30ffde3..c3a2abfb 100644 --- a/src/main/modules/mod/download.helpers.ts +++ b/src/main/modules/mod/download.helpers.ts @@ -3,12 +3,15 @@ import * as fs from 'original-fs' import axios from 'axios' import * as https from 'https' import crypto from 'crypto' -import { Transform, pipeline as nodePipeline } from 'stream' +import { Readable, Transform, pipeline as nodePipeline } from 'stream' import { promisify } from 'util' import RendererEvents, { RendererEvent } from '../../../common/types/rendererEvents' import { t } from '../../i18n' +import logger from '../logger' const pipeline = promisify(nodePipeline) +const DOWNLOAD_REQUEST_TIMEOUT_MS = 30_000 +const DOWNLOAD_INACTIVITY_TIMEOUT_MS = 30_000 export function sendToRenderer(window: BrowserWindow | null | undefined, channel: RendererEvent, payload: any) { window?.webContents.send(channel, payload) @@ -42,7 +45,7 @@ export function unlinkIfExists(p: string) { export function restoreBackupIfExists(savePath: string, backupPath: string) { try { - if (fs.existsSync(backupPath)) fs.renameSync(backupPath, savePath) + if (fs.existsSync(backupPath)) fs.copyFileSync(backupPath, savePath) } catch {} } @@ -67,44 +70,90 @@ export async function downloadToTempWithProgress(args: { name: string }): Promise<{ totalBytes: number; computedHash?: string }> { const { window, url, tempFilePath, expectedChecksum, progressScale = 0.6, progressBase = 0, userAgent, rejectUnauthorized = true, name } = args + const startedAt = Date.now() + const downloadHost = (() => { + try { + return new URL(url).host + } catch { + return 'unknown-host' + } + })() const headers: Record = { 'User-Agent': userAgent, Accept: 'application/octet-stream', } const httpsAgent = new https.Agent({ rejectUnauthorized }) - - const response = await axios.get(url, { httpsAgent, responseType: 'stream', headers }) - - const total = Number(response.headers['content-length'] || 0) let downloaded = 0 - const hasher = expectedChecksum ? crypto.createHash('sha256') : null + let inactivityTimer: NodeJS.Timeout | null = null + let responseStream: Readable | null = null - const progressTap = new Transform({ - transform(chunk, _enc, cb) { - downloaded += chunk.length - if (total > 0) { - const frac = downloaded / total - const scaled = Math.min(frac * progressScale, progressScale) - const combined = Math.min(progressBase + scaled, progressBase + progressScale) - setProgress(window, combined) - sendProgress(window, Math.round(Math.min(progressBase + Math.min(frac, 1) * progressScale, 1) * 100), name) - } - if (hasher) hasher.update(chunk) - this.push(chunk) - cb() - }, - }) + const clearInactivityTimer = () => { + if (!inactivityTimer) return + clearTimeout(inactivityTimer) + inactivityTimer = null + } + + const resetInactivityTimer = () => { + clearInactivityTimer() + inactivityTimer = setTimeout(() => { + const error = new DownloadError(t('main.modDownload.networkError'), 'network') + responseStream?.destroy(error) + }, DOWNLOAD_INACTIVITY_TIMEOUT_MS) + } - const writer = fs.createWriteStream(tempFilePath) + logger.modManager.info(`Starting ${name} download from ${downloadHost}`) try { + const response = await axios.get(url, { + httpsAgent, + responseType: 'stream', + headers, + timeout: DOWNLOAD_REQUEST_TIMEOUT_MS, + }) + + const total = Number(response.headers['content-length'] || 0) + responseStream = response.data as Readable + + const progressTap = new Transform({ + transform(chunk, _enc, cb) { + resetInactivityTimer() + downloaded += chunk.length + if (total > 0) { + const frac = downloaded / total + const scaled = Math.min(frac * progressScale, progressScale) + const combined = Math.min(progressBase + scaled, progressBase + progressScale) + setProgress(window, combined) + sendProgress(window, Math.round(Math.min(progressBase + Math.min(frac, 1) * progressScale, 1) * 100), name) + } + if (hasher) hasher.update(chunk) + this.push(chunk) + cb() + }, + }) + + const writer = fs.createWriteStream(tempFilePath) + resetInactivityTimer() await pipeline(response.data, progressTap, writer) - } catch (e: any) { - throw new DownloadError(e?.message || t('main.modDownload.networkError'), 'network') + } catch (error: any) { + logger.modManager.warn(`${name} download failed from ${downloadHost}`, { + bytes: downloaded, + durationMs: Date.now() - startedAt, + code: error?.code, + message: error?.message, + }) + if (error instanceof DownloadError) throw error + throw new DownloadError(error?.message || t('main.modDownload.networkError'), 'network') + } finally { + clearInactivityTimer() } + logger.modManager.info(`Completed ${name} download from ${downloadHost}`, { + bytes: downloaded, + durationMs: Date.now() - startedAt, + }) + let digest: string | undefined if (expectedChecksum && hasher) { digest = hasher.digest('hex') diff --git a/src/main/modules/mod/installModUpdateFrom.ts b/src/main/modules/mod/installModUpdateFrom.ts index c4e5d8b0..baf0bf1e 100644 --- a/src/main/modules/mod/installModUpdateFrom.ts +++ b/src/main/modules/mod/installModUpdateFrom.ts @@ -198,7 +198,7 @@ export const installModUpdateFromAsar = async ( return { success: false, type: 'patch_error', error } } - const checksum = readChecksum(paths.modAsar) + const checksum = await readChecksum(paths.modAsar) const resolvedChecksum = checksum ?? incomingChecksum await persistInstalledModState(paths, matchedMod, resolvedChecksum) diff --git a/src/main/modules/mod/mod-files.ts b/src/main/modules/mod/mod-files.ts index ad80e42e..c11e8abe 100644 --- a/src/main/modules/mod/mod-files.ts +++ b/src/main/modules/mod/mod-files.ts @@ -101,7 +101,24 @@ export async function writePatchedAsarAndPatchBundle( ok = false } if (!ok) { - if (fs.existsSync(backupPath)) fs.renameSync(backupPath, savePath) + if (fs.existsSync(backupPath)) await copyFile(backupPath, savePath) + return false + } + return true +} + +export async function installPreparedAsarAndPatchBundle(savePath: string, preparedAsarPath: string, backupPath: string): Promise { + await copyFile(preparedAsarPath, savePath) + + const patcher = new AsarPatcher(path.resolve(path.dirname(savePath), '..', '..')) + let ok: boolean + try { + ok = await patcher.patch(() => {}) + } catch { + ok = false + } + if (!ok) { + if (fs.existsSync(backupPath)) await copyFile(backupPath, savePath) return false } return true diff --git a/src/main/modules/mod/mod-manager.helpers.ts b/src/main/modules/mod/mod-manager.helpers.ts index 5e7ac9ea..6de9af6b 100644 --- a/src/main/modules/mod/mod-manager.helpers.ts +++ b/src/main/modules/mod/mod-manager.helpers.ts @@ -2,25 +2,18 @@ import { app, BrowserWindow } from 'electron' import * as path from 'path' import * as fs from 'original-fs' import os from 'os' -import crypto from 'crypto' import RendererEvents, { RendererEvent } from '../../../common/types/rendererEvents' import { getState } from '../state' import logger from '../logger' -import { - closeYandexMusic, - copyFile, - getInstalledYmMetadata, - getYandexMusicProcesses, - isYandexMusicRunning, - launchYandexMusic, -} from '../../utils/appUtils' -import { Paths, writePatchedAsarAndPatchBundle } from './mod-files' -import { downloadAndUpdateFile } from './network' +import { closeYandexMusic, getInstalledYmMetadata, getYandexMusicProcesses, isYandexMusicRunning, launchYandexMusic } from '../../utils/appUtils' +import { Paths } from './mod-files' +import { downloadAndUpdateFile, prepareAndInstallAsarArtifact } from './network' import { nativeDeleteFile, nativeFileExists } from '../nativeModules' import { resetProgress, sendProgress, sendToRenderer, setProgress } from './download.helpers' import { CACHE_DIR } from '../../constants/paths' import { t } from '../../i18n' import type { RemoteModInfo } from './network/modCatalog' +import { hashArtifactInWorker } from './network/artifactWorkerClient' const State = getState() @@ -71,10 +64,17 @@ export async function tryUseCacheOrDownload( sendToRenderer(window, RendererEvents.UPDATE_MESSAGE, { message: t('main.modManager.usingCache') }) try { logger.modManager.info(`Using cached app.asar from ${cacheFile}`) - await copyFile(cacheFile, tempFilePath) - const fileBuffer = fs.readFileSync(tempFilePath) - const ok = await writePatchedAsarAndPatchBundle(paths.modAsar, fileBuffer, link, paths.backupAsar, checksum) + const progressBase = progress?.base ?? 0 + const progressScale = progress?.scale ?? 1 + const processingProgress = progressBase + progressScale * 0.85 + setProgress(window, processingProgress) + sendProgress(window, Math.round(processingProgress * 100), 'app.asar') + const preparedFilePath = `${tempFilePath}.prepared.${process.pid}.${Date.now()}.asar` + const ok = await prepareAndInstallAsarArtifact(cacheFile, preparedFilePath, link, paths.modAsar, paths.backupAsar, checksum) if (ok) { + const completedProgress = progressBase + progressScale + setProgress(window, completedProgress) + sendProgress(window, Math.round(completedProgress * 100), 'app.asar') logger.modManager.info('Successfully restored app.asar from cache') return true } @@ -84,13 +84,30 @@ export async function tryUseCacheOrDownload( resetProgress(window) } } - return await downloadAndUpdateFile(window, link, tempFilePath, paths.modAsar, paths.backupAsar, checksum, cacheDir, progress, 'app.asar', onFailure) + return await downloadAndUpdateFile( + window, + link, + tempFilePath, + paths.modAsar, + paths.backupAsar, + checksum, + cacheDir, + progress, + 'app.asar', + onFailure, + ) } -export function readChecksum(filePath: string): string | null { +export async function readChecksum(filePath: string): Promise { try { - const buf = fs.readFileSync(filePath) - return crypto.createHash('sha256').update(buf).digest('hex') + const startedAt = Date.now() + const result = await hashArtifactInWorker({ filePath }) + logger.modManager.info('Hashed artifact in worker', { + totalMs: Date.now() - startedAt, + workerThreadId: result.workerThreadId, + checksum: result.durationMs, + }) + return result.checksum } catch (err: any) { logger.modManager.warn('Failed to verify existing file:', err) return null @@ -165,11 +182,19 @@ export async function sendSuccessAfterLaunch( channel: RendererEvent, payload: { success: true }, ): Promise { - if (!(await isYandexMusicRunning()) && wasClosed) { - await launchYandexMusic() - setTimeout(() => sendToRenderer(window, channel, payload), 1500) - return true - } sendToRenderer(window, channel, payload) - return false + resetProgress(window) + + if (!wasClosed) return false + + void (async () => { + try { + if (!(await isYandexMusicRunning())) { + await launchYandexMusic() + } + } catch (error) { + logger.modManager.warn('Failed to relaunch Yandex Music after mod operation:', error) + } + })() + return true } diff --git a/src/main/modules/mod/modManager.ts b/src/main/modules/mod/modManager.ts index 9c0d6748..b17659f0 100644 --- a/src/main/modules/mod/modManager.ts +++ b/src/main/modules/mod/modManager.ts @@ -9,7 +9,7 @@ import { copyFile, downloadYandexMusic, getInstalledYmMetadata, isLinux, isMac, import { ensureBackup, ensureLinuxModPath, resolveBasePaths, restoreMacIntegrity, restoreWindowsIntegrity } from './mod-files' import { downloadAndExtractUnpacked, downloadAndUpdateFile } from './network' import { nativeRenameFile } from '../nativeModules' -import { resetProgress, sendFailure, sendToRenderer } from './download.helpers' +import { sendFailure, sendToRenderer } from './download.helpers' import { CACHE_DIR, TEMP_DIR } from '../../constants/paths' import { t } from '../../i18n' import { formatPkexecError, grantLinuxOwnershipWithPkexec, isLinuxAccessError } from '../../utils/appUtils/elevation' @@ -28,9 +28,9 @@ import { getGithubModRelease } from './network/releaseCatalog' import type { ModDownloadFailure } from './network/types' const State = getState() -const PROGRESS_ASAR_ONLY = { base: 0, scale: 1, resetOnComplete: true } -const PROGRESS_ASAR_WITH_UNPACKED = { base: 0, scale: 0.6, resetOnComplete: false } -const PROGRESS_UNPACKED = { base: 0.6, scale: 0.4, resetOnComplete: true } +const PROGRESS_ASAR_ONLY = { base: 0, scale: 0.95, resetOnComplete: false } +const PROGRESS_ASAR_WITH_UNPACKED = { base: 0, scale: 0.58, resetOnComplete: false } +const PROGRESS_UNPACKED = { base: 0.6, scale: 0.39, resetOnComplete: false } const MOD_DOWNLOAD_FALLBACK_TYPES = new Set(['download_error', 'download_unpacked_error', 'checksum_mismatch']) const isFallbackEligibleDownloadFailure = (failure: ModDownloadFailure | null): failure is ModDownloadFailure => @@ -81,6 +81,7 @@ export const modManager = (window: BrowserWindow): void => { const ymMetadata = await getInstalledYmMetadata() const resolvedMusicVersion = ymMetadata?.version ?? musicVersion + let finalProgressName = 'app.asar' if (isMac()) { try { @@ -124,6 +125,7 @@ export const modManager = (window: BrowserWindow): void => { ): Promise => { const tempFilePath = path.join(TEMP_DIR, 'app.asar.download') const hasUnpacked = Boolean(releaseData.unpackLink) + finalProgressName = hasUnpacked ? 'app.asar.unpacked' : 'app.asar' const asarProgress = hasUnpacked ? PROGRESS_ASAR_WITH_UNPACKED : PROGRESS_ASAR_ONLY const unpackedProgress = hasUnpacked ? PROGRESS_UNPACKED : undefined @@ -133,14 +135,14 @@ export const modManager = (window: BrowserWindow): void => { logger.modManager.warn('Failed to create cache dir:', err) }) - const currentHash = fileExists(paths.modAsar) ? readChecksum(paths.modAsar) : null + const currentHash = fileExists(paths.modAsar) ? await readChecksum(paths.modAsar) : null if (currentHash === releaseData.checksum) { logger.modManager.info('app.asar hash matches, skipping download') sendToRenderer(window, RendererEvents.UPDATE_MESSAGE, { message: t('main.modManager.modAlreadyInstalled') }) if (hasUnpacked) { setProgressPercent(window, PROGRESS_UNPACKED.base, 'app.asar.unpacked') } else { - resetProgress(window) + setProgressPercent(window, PROGRESS_ASAR_ONLY.scale, 'app.asar') } } else if ( !(await tryUseCacheOrDownload( @@ -177,11 +179,15 @@ export const modManager = (window: BrowserWindow): void => { } if (releaseData.unpackLink) { + const unpackedBoundaryStartedAt = Date.now() setProgressPercent(window, PROGRESS_UNPACKED.base, 'app.asar.unpacked') + logger.modManager.info('Starting app.asar.unpacked stage', { + progressUpdateMs: Date.now() - unpackedBoundaryStartedAt, + }) const unpackName = path.basename(new URL(releaseData.unpackLink).pathname) const tempUnpackedArchive = path.join(TEMP_DIR, unpackName || 'app.asar.unpacked') - const tempUnpackedDir = path.join(TEMP_DIR, 'app.asar.unpacked') + const tempUnpackedDir = path.join(TEMP_DIR, `pulsesync-unpacked-${process.pid}-${Date.now()}`) const targetUnpackedDir = path.join(path.dirname(paths.modAsar), 'app.asar.unpacked') const unpackedOk = await downloadAndExtractUnpacked( @@ -198,7 +204,7 @@ export const modManager = (window: BrowserWindow): void => { if (!unpackedOk) return false } - const actualAsarChecksum = readChecksum(paths.modAsar) ?? releaseData.checksum + const actualAsarChecksum = (await readChecksum(paths.modAsar)) ?? releaseData.checksum if (actualAsarChecksum) { logger.modManager.info('Calculated actual asar checksum:', actualAsarChecksum) } @@ -292,6 +298,7 @@ export const modManager = (window: BrowserWindow): void => { logger.modManager.warn('Skipping version.bin update because no Yandex Music version was resolved') } + setProgressPercent(window, 1, finalProgressName) if (await sendSuccessAfterLaunch(window, wasClosed, RendererEvents.DOWNLOAD_SUCCESS, { success: true })) return } catch (error: any) { logger.modManager.error('Unexpected error:', error) diff --git a/src/main/modules/mod/network/artifactWorker.ts b/src/main/modules/mod/network/artifactWorker.ts new file mode 100644 index 00000000..774be33f --- /dev/null +++ b/src/main/modules/mod/network/artifactWorker.ts @@ -0,0 +1,377 @@ +import * as fs from 'original-fs' +import * as path from 'node:path' +import * as crypto from 'node:crypto' +import * as zlib from 'node:zlib' +import { parentPort, threadId, workerData } from 'node:worker_threads' +import AdmZip from 'adm-zip' +import type { + ArtifactWorkerFailure, + ArtifactWorkerRequest, + ArtifactWorkerRequestMessage, + ArtifactWorkerResponse, + ArtifactWorkerResponseMessage, + ArtifactWorkerStage, + ArtifactWorkerWarning, + HashArtifactRequest, + InstallUnpackedArtifactRequest, + PrepareAsarArtifactRequest, +} from './artifactWorker.types' + +const RECOVERABLE_CODES = new Set(['EXDEV', 'EPERM', 'EACCES', 'EBUSY', 'ENOTEMPTY', 'EEXIST']) +const FILE_HASH_BUFFER_SIZE = 1024 * 1024 + +async function decompressZstdStable(buffer: Buffer): Promise { + const { ZstdCodec } = await import('zstd-codec') + + return new Promise((resolve, reject) => { + try { + ZstdCodec.run((zstd: any) => { + try { + const decompressed = new zstd.Streaming().decompress(buffer) + if (!decompressed) throw new Error('Failed to decompress zstd archive') + resolve(Buffer.from(decompressed)) + } catch (error) { + reject(error) + } + }) + } catch (error) { + reject(error) + } + }) +} + +function decompressZstdNative(buffer: Buffer): Buffer { + return zlib.zstdDecompressSync(buffer) +} + +type ArchivePreparationRequest = { + archivePath: string + archiveExtension: string + expectedChecksum?: string +} + +class StageError extends Error { + constructor( + public readonly stage: ArtifactWorkerStage, + cause: unknown, + ) { + super(cause instanceof Error ? cause.message : String(cause), { cause }) + } +} + +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) + +async function runStage(stage: ArtifactWorkerStage, operation: () => T | Promise): Promise { + try { + return await operation() + } catch (error) { + throw new StageError(stage, error) + } +} + +async function retryFilesystemOperation(operation: () => void): Promise { + const maxAttempts = process.platform === 'win32' ? 6 : 2 + let lastError: any + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + try { + operation() + return + } catch (error: any) { + lastError = error + if (!RECOVERABLE_CODES.has(error?.code) || attempt === maxAttempts) break + await sleep(150 * attempt) + } + } + + throw lastError +} + +function isZipBuffer(buffer: Buffer): boolean { + return ( + buffer.length >= 4 && + buffer[0] === 0x50 && + buffer[1] === 0x4b && + ((buffer[2] === 0x03 && buffer[3] === 0x04) || (buffer[2] === 0x05 && buffer[3] === 0x06) || (buffer[2] === 0x07 && buffer[3] === 0x08)) + ) +} + +function resolveExtractedRoot(stagingPath: string, targetPath: string): string { + const entries = fs.readdirSync(stagingPath, { withFileTypes: true }).filter(entry => entry.name !== '__MACOSX' && entry.name !== '.DS_Store') + if (entries.length !== 1 || !entries[0].isDirectory() || entries[0].name !== path.basename(targetPath)) return stagingPath + return path.join(stagingPath, entries[0].name) +} + +async function readAndPrepareArchive( + request: ArchivePreparationRequest, + durations: Record, + zstdDecoder: 'native' | 'stable', +): Promise { + const measure = async (name: string, operation: () => Promise | T): Promise => { + const startedAt = Date.now() + try { + return await operation() + } finally { + durations[name] = Date.now() - startedAt + } + } + + const archive = await measure('read', () => runStage('read', () => fs.readFileSync(request.archivePath))) + + if (request.expectedChecksum) { + await measure('checksum', () => + runStage('checksum', () => { + const actualChecksum = crypto.createHash('sha256').update(archive).digest('hex') + if (actualChecksum !== request.expectedChecksum) { + const error: NodeJS.ErrnoException = new Error('Archive checksum mismatch') + error.code = 'CHECKSUM_MISMATCH' + throw error + } + }), + ) + } + + return await measure('decompress', () => + runStage('decompress', async () => { + if (request.archiveExtension === '.gz') return zlib.gunzipSync(archive) + if (request.archiveExtension === '.zst' || request.archiveExtension === '.zstd') { + if (zstdDecoder === 'native') return decompressZstdNative(archive) + + try { + return await decompressZstdStable(archive) + } catch { + return decompressZstdNative(archive) + } + } + return archive + }), + ) +} + +async function prepareAsarArtifact(request: PrepareAsarArtifactRequest): Promise { + const durations: Record = {} + const warnings: ArtifactWorkerWarning[] = [] + const measure = async (name: string, operation: () => Promise | T): Promise => { + const startedAt = Date.now() + try { + return await operation() + } finally { + durations[name] = Date.now() - startedAt + } + } + + try { + const asarBuffer = await readAndPrepareArchive(request, durations, 'native') + await measure('write', () => runStage('write', () => fs.writeFileSync(request.outputPath, asarBuffer))) + return { ok: true, mode: 'prepareAsar', durations, preparedPath: request.outputPath, warnings } + } catch (error: any) { + try { + fs.rmSync(request.outputPath, { force: true }) + } catch {} + const cause = error instanceof StageError ? error.cause : error + return { + ok: false, + stage: error instanceof StageError ? error.stage : 'install', + code: typeof (cause as any)?.code === 'string' ? (cause as any).code : undefined, + message: error instanceof Error ? error.message : String(error), + } + } +} + +async function installUnpackedArtifact(request: InstallUnpackedArtifactRequest): Promise { + const durations: Record = {} + const warnings: ArtifactWorkerWarning[] = [] + const measure = async (name: string, operation: () => Promise | T): Promise => { + const startedAt = Date.now() + try { + return await operation() + } finally { + durations[name] = Date.now() - startedAt + } + } + let backupPath: string | null = null + + try { + let extractedRoot: string + if (request.sourceKind === 'directory') { + await measure('clone', () => + runStage('extract', () => { + fs.rmSync(request.stagingPath, { recursive: true, force: true }) + fs.cpSync(request.archivePath, request.stagingPath, { recursive: true, force: true }) + }), + ) + extractedRoot = request.stagingPath + } else { + const zipBuffer = await readAndPrepareArchive(request, durations, 'stable') + + await measure('extract', () => + runStage('extract', () => { + if (!isZipBuffer(zipBuffer)) throw new Error('Expected ZIP archive') + fs.rmSync(request.stagingPath, { recursive: true, force: true }) + fs.mkdirSync(request.stagingPath, { recursive: true }) + new AdmZip(zipBuffer).extractAllTo(request.stagingPath, true) + }), + ) + + extractedRoot = resolveExtractedRoot(request.stagingPath, request.targetPath) + } + + if (request.preparedDirectoryPath && request.preparedDirectoryMarker) { + const preparedDirectoryPath = request.preparedDirectoryPath + const marker = request.preparedDirectoryMarker + await measure('cacheWrite', () => { + const tempCachePath = `${preparedDirectoryPath}.tmp-${process.pid}-${Date.now()}` + try { + fs.rmSync(tempCachePath, { recursive: true, force: true }) + fs.mkdirSync(path.dirname(preparedDirectoryPath), { recursive: true }) + fs.cpSync(extractedRoot, tempCachePath, { recursive: true, force: true }) + fs.writeFileSync(path.join(tempCachePath, marker.fileName), `${marker.value}\n`, 'utf8') + fs.renameSync(tempCachePath, preparedDirectoryPath) + } catch (error: any) { + warnings.push({ + stage: 'cache', + code: typeof error?.code === 'string' ? error.code : undefined, + message: error instanceof Error ? error.message : String(error), + }) + } finally { + try { + fs.rmSync(tempCachePath, { recursive: true, force: true }) + } catch {} + } + }) + } + + fs.mkdirSync(path.dirname(request.targetPath), { recursive: true }) + + if (fs.existsSync(request.targetPath)) { + backupPath = `${request.targetPath}.pulsesync-backup-${process.pid}-${Date.now()}` + await measure('backup', () => + runStage('backup', () => retryFilesystemOperation(() => fs.renameSync(request.targetPath, backupPath as string))), + ) + } + + await measure('install', () => + runStage('install', async () => { + try { + await retryFilesystemOperation(() => fs.renameSync(extractedRoot, request.targetPath)) + } catch (error: any) { + if (error?.code !== 'EXDEV') throw error + await retryFilesystemOperation(() => fs.cpSync(extractedRoot, request.targetPath, { recursive: true, force: true })) + } + }), + ) + + await measure('cleanup', () => { + for (const cleanupPath of [request.stagingPath, backupPath]) { + if (!cleanupPath) continue + try { + fs.rmSync(cleanupPath, { recursive: true, force: true }) + } catch (error: any) { + warnings.push({ + stage: 'cleanup', + code: typeof error?.code === 'string' ? error.code : undefined, + message: error instanceof Error ? error.message : String(error), + }) + } + } + }) + + return { ok: true, mode: 'installUnpacked', durations, warnings } + } catch (error: any) { + if (backupPath && fs.existsSync(backupPath)) { + try { + fs.rmSync(request.targetPath, { recursive: true, force: true }) + await retryFilesystemOperation(() => fs.renameSync(backupPath as string, request.targetPath)) + } catch (restoreError) { + error = new StageError('restore', restoreError) + } + } + + try { + fs.rmSync(request.stagingPath, { recursive: true, force: true }) + } catch {} + + const cause = error instanceof StageError ? error.cause : error + const failure: ArtifactWorkerFailure = { + ok: false, + stage: error instanceof StageError ? error.stage : 'install', + code: typeof (cause as any)?.code === 'string' ? (cause as any).code : undefined, + message: error instanceof Error ? error.message : String(error), + } + return failure + } +} + +async function hashArtifact(request: HashArtifactRequest): Promise { + const startedAt = Date.now() + let descriptor: number | null = null + try { + const hasher = crypto.createHash('sha256') + const buffer = Buffer.allocUnsafe(FILE_HASH_BUFFER_SIZE) + descriptor = fs.openSync(request.filePath, 'r') + let bytesRead = 0 + do { + bytesRead = fs.readSync(descriptor, buffer, 0, buffer.length, null) + if (bytesRead > 0) hasher.update(buffer.subarray(0, bytesRead)) + } while (bytesRead > 0) + fs.closeSync(descriptor) + descriptor = null + const checksum = hasher.digest('hex') + return { + ok: true, + mode: 'hashFile', + checksum, + durations: { checksum: Date.now() - startedAt }, + warnings: [], + } + } catch (error: any) { + return { + ok: false, + stage: 'checksum', + code: typeof error?.code === 'string' ? error.code : undefined, + message: error instanceof Error ? error.message : String(error), + } + } finally { + if (descriptor !== null) { + try { + fs.closeSync(descriptor) + } catch {} + } + } +} + +async function processArtifact(request: ArtifactWorkerRequest): Promise { + if (request.mode === 'prepareAsar') return await prepareAsarArtifact(request) + if (request.mode === 'hashFile') return await hashArtifact(request) + return await installUnpackedArtifact(request) +} + +async function processArtifactSafely(request: ArtifactWorkerRequest): Promise { + try { + return await processArtifact(request) + } catch (error: any) { + return { + ok: false, + stage: 'install', + code: typeof error?.code === 'string' ? error.code : undefined, + message: error instanceof Error ? error.message : String(error), + } + } +} + +if (workerData !== undefined) { + void processArtifactSafely(workerData as ArtifactWorkerRequest).then(response => parentPort?.postMessage(response)) +} else { + let requestQueue = Promise.resolve() + parentPort?.on('message', (message: ArtifactWorkerRequestMessage) => { + requestQueue = requestQueue.then(async () => { + const response = await processArtifactSafely(message.request) + const responseMessage: ArtifactWorkerResponseMessage = { + id: message.id, + response, + workerThreadId: threadId, + } + parentPort?.postMessage(responseMessage) + }) + }) +} diff --git a/src/main/modules/mod/network/artifactWorker.types.ts b/src/main/modules/mod/network/artifactWorker.types.ts new file mode 100644 index 00000000..0bc4cb26 --- /dev/null +++ b/src/main/modules/mod/network/artifactWorker.types.ts @@ -0,0 +1,80 @@ +export type ArtifactWorkerStage = 'read' | 'checksum' | 'decompress' | 'write' | 'extract' | 'backup' | 'install' | 'cleanup' | 'restore' + +export type InstallUnpackedArtifactRequest = { + mode: 'installUnpacked' + sourceKind?: 'archive' | 'directory' + archivePath: string + archiveExtension: string + expectedChecksum?: string + preparedDirectoryPath?: string + preparedDirectoryMarker?: { + fileName: string + value: string + } + stagingPath: string + targetPath: string +} + +export type PrepareAsarArtifactRequest = { + mode: 'prepareAsar' + archivePath: string + archiveExtension: string + expectedChecksum?: string + outputPath: string +} + +export type HashArtifactRequest = { + mode: 'hashFile' + filePath: string +} + +export type ArtifactWorkerRequest = InstallUnpackedArtifactRequest | PrepareAsarArtifactRequest | HashArtifactRequest + +export type ArtifactWorkerRequestMessage = { + id: number + request: ArtifactWorkerRequest +} + +export type InstallUnpackedArtifactSuccess = { + ok: true + mode: 'installUnpacked' + durations: Record + warnings: ArtifactWorkerWarning[] +} + +export type PrepareAsarArtifactSuccess = { + ok: true + mode: 'prepareAsar' + durations: Record + preparedPath: string + warnings: ArtifactWorkerWarning[] +} + +export type HashArtifactSuccess = { + ok: true + mode: 'hashFile' + checksum: string + durations: Record + warnings: ArtifactWorkerWarning[] +} + +export type ArtifactWorkerWarning = { + stage: 'cache' | 'cleanup' + code?: string + message: string +} + +export type ArtifactWorkerFailure = { + ok: false + stage: ArtifactWorkerStage + code?: string + message: string +} + +export type ArtifactWorkerResponse = InstallUnpackedArtifactSuccess | PrepareAsarArtifactSuccess | HashArtifactSuccess | ArtifactWorkerFailure + +export type ArtifactWorkerResponseMessage = { + id: number + response: ArtifactWorkerResponse + workerThreadId: number +} diff --git a/src/main/modules/mod/network/artifactWorkerClient.ts b/src/main/modules/mod/network/artifactWorkerClient.ts new file mode 100644 index 00000000..9ab3f891 --- /dev/null +++ b/src/main/modules/mod/network/artifactWorkerClient.ts @@ -0,0 +1,163 @@ +import * as path from 'node:path' +import { Worker } from 'node:worker_threads' +import isAppDev from '../../../utils/isAppDev' +import type { + ArtifactWorkerRequest, + ArtifactWorkerRequestMessage, + ArtifactWorkerResponse, + ArtifactWorkerResponseMessage, + ArtifactWorkerStage, + HashArtifactRequest, + InstallUnpackedArtifactRequest, + PrepareAsarArtifactRequest, +} from './artifactWorker.types' + +const ARTIFACT_WORKER_TIMEOUT_MS = 5 * 60 * 1000 +const ARTIFACT_WORKER_IDLE_TIMEOUT_MS = 15 * 1000 + +type PendingRequest = { + resolve: (result: ArtifactWorkerResult) => void + reject: (error: Error) => void + timeout: NodeJS.Timeout +} + +type ArtifactWorkerResult = { + response: ArtifactWorkerResponse + workerThreadId: number +} + +let activeSession: ArtifactWorkerSession | null = null +let nextRequestId = 1 + +export class ArtifactWorkerError extends Error { + constructor( + message: string, + public readonly stage: ArtifactWorkerStage, + public readonly code?: string, + ) { + super(message) + } +} + +function resolveArtifactWorkerPath(): string { + return isAppDev + ? path.resolve(__dirname, '..', 'worker', 'artifactWorker.cjs') + : path.join(process.resourcesPath, 'app.asar.unpacked', '.vite', 'worker', 'artifactWorker.cjs') +} + +class ArtifactWorkerSession { + private readonly worker = new Worker(resolveArtifactWorkerPath()) + private readonly pending = new Map() + private idleTimer: NodeJS.Timeout | null = null + private closed = false + + constructor() { + this.worker.on('message', (message: ArtifactWorkerResponseMessage) => this.handleMessage(message)) + this.worker.once('error', error => this.close(error instanceof Error ? error : new Error(String(error)))) + this.worker.once('exit', code => { + if (!this.closed) this.close(new Error(`Artifact worker exited before returning all results (code ${code})`), false) + }) + this.worker.unref() + } + + request(request: ArtifactWorkerRequest): Promise { + if (this.closed) return Promise.reject(new Error('Artifact worker session is closed')) + + this.clearIdleTimer() + this.worker.ref() + const id = nextRequestId++ + + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + this.close(new Error(`Artifact worker timed out after ${ARTIFACT_WORKER_TIMEOUT_MS / 1000} seconds`)) + }, ARTIFACT_WORKER_TIMEOUT_MS) + const pendingRequest: PendingRequest = { resolve, reject, timeout } + this.pending.set(id, pendingRequest) + + const message: ArtifactWorkerRequestMessage = { id, request } + try { + this.worker.postMessage(message) + } catch (error) { + this.close(error instanceof Error ? error : new Error(String(error))) + } + }) + } + + private handleMessage(message: ArtifactWorkerResponseMessage): void { + const pendingRequest = this.pending.get(message.id) + if (!pendingRequest) return + + this.pending.delete(message.id) + clearTimeout(pendingRequest.timeout) + if (message.response.ok) { + pendingRequest.resolve({ response: message.response, workerThreadId: message.workerThreadId }) + } else { + pendingRequest.reject(new ArtifactWorkerError(message.response.message, message.response.stage, message.response.code)) + } + this.scheduleIdleTermination() + } + + private scheduleIdleTermination(): void { + if (this.closed || this.pending.size > 0) return + this.worker.unref() + this.idleTimer = setTimeout(() => this.close(undefined), ARTIFACT_WORKER_IDLE_TIMEOUT_MS) + this.idleTimer.unref() + } + + private clearIdleTimer(): void { + if (!this.idleTimer) return + clearTimeout(this.idleTimer) + this.idleTimer = null + } + + private close(error?: Error, terminate = true): void { + if (this.closed) return + this.closed = true + this.clearIdleTimer() + if (activeSession === this) activeSession = null + + const closeError = error ?? new Error('Artifact worker session closed') + for (const pendingRequest of this.pending.values()) { + clearTimeout(pendingRequest.timeout) + pendingRequest.reject(closeError) + } + this.pending.clear() + if (terminate) void this.worker.terminate() + } +} + +function getArtifactWorkerSession(): ArtifactWorkerSession { + if (!activeSession) activeSession = new ArtifactWorkerSession() + return activeSession +} + +async function runArtifactWorker(request: ArtifactWorkerRequest): Promise { + return await getArtifactWorkerSession().request(request) +} + +const formatWarnings = (response: Extract): string[] => + response.warnings.map(warning => `${warning.code ? `${warning.code}: ` : ''}${warning.message}`) + +export async function installUnpackedArtifactInWorker( + request: Omit, +): Promise<{ durations: Record; warnings: string[]; workerThreadId: number }> { + const { response, workerThreadId } = await runArtifactWorker({ ...request, mode: 'installUnpacked' }) + if (!response.ok || response.mode !== 'installUnpacked') throw new Error('Artifact worker returned an unexpected response') + return { durations: response.durations, warnings: formatWarnings(response), workerThreadId } +} + +export async function prepareAsarArtifactInWorker( + request: Omit, +): Promise<{ durations: Record; preparedPath: string; warnings: string[]; workerThreadId: number }> { + const { response, workerThreadId } = await runArtifactWorker({ ...request, mode: 'prepareAsar' }) + if (!response.ok || response.mode !== 'prepareAsar') throw new Error('Artifact worker returned an unexpected response') + return { durations: response.durations, preparedPath: response.preparedPath, warnings: formatWarnings(response), workerThreadId } +} + +export async function hashArtifactInWorker( + request: Omit, +): Promise<{ checksum: string; durationMs: number; workerThreadId: number }> { + const { response, workerThreadId } = await runArtifactWorker({ ...request, mode: 'hashFile' }) + if (!response.ok || response.mode !== 'hashFile') throw new Error('Artifact worker returned an unexpected response') + return { checksum: response.checksum, durationMs: response.durations.checksum ?? 0, workerThreadId } +} diff --git a/src/main/modules/mod/network/helpers.ts b/src/main/modules/mod/network/helpers.ts index e167578c..a539fe8b 100644 --- a/src/main/modules/mod/network/helpers.ts +++ b/src/main/modules/mod/network/helpers.ts @@ -1,21 +1,22 @@ import * as fs from 'original-fs' import * as path from 'path' import crypto from 'crypto' -import AdmZip from 'adm-zip' import logger from '../../logger' -import { gunzipAsync, zstdDecompressAsync } from '../mod-files' -import type { ReplaceDirFailure, ReplaceDirResult, RetryStageFailure, RetryStageResult } from './types' export const UNPACKED_MARKER_FILE = '.pulsesync_unpacked_checksum' -const REPLACE_RECOVERABLE_CODES = new Set(['EXDEV', 'EPERM', 'EACCES', 'EBUSY', 'ENOTEMPTY', 'EEXIST']) - -const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) - export function sha256Hex(buf: Buffer): string { return crypto.createHash('sha256').update(buf).digest('hex') } +export async function sha256File(filePath: string): Promise { + const hasher = crypto.createHash('sha256') + for await (const chunk of fs.createReadStream(filePath)) { + hasher.update(chunk) + } + return hasher.digest('hex') +} + export async function ensureDir(dir: string): Promise { try { await fs.promises.mkdir(dir, { recursive: true }) @@ -42,91 +43,43 @@ export async function pruneCacheFiles(cacheDir: string, keepFile: string, matche } } -export function readCachedArchive(cacheFile: string, checksum?: string): Buffer | null { - if (!fs.existsSync(cacheFile)) return null +export async function pruneCacheDirectories(cacheDir: string, keepDirectory: string, matcher: (directory: string) => boolean, warnLabel: string) { try { - const cached = fs.readFileSync(cacheFile) + const entries = await fs.promises.readdir(cacheDir, { withFileTypes: true }) + const keepName = path.basename(keepDirectory) + for (const entry of entries) { + if (!entry.isDirectory() || entry.name === keepName || !matcher(entry.name)) continue + try { + await fs.promises.rm(path.join(cacheDir, entry.name), { recursive: true, force: true }) + } catch (e) { + logger.modManager.warn(warnLabel, entry.name, e) + } + } + } catch (e) { + logger.modManager.warn('Failed to cleanup cache:', e) + } +} + +export async function isCachedArchiveValid(cacheFile: string, checksum?: string): Promise { + try { + await fs.promises.access(cacheFile, fs.constants.R_OK) if (checksum) { - const cachedHash = sha256Hex(cached) + const cachedHash = await sha256File(cacheFile) if (cachedHash !== checksum) { logger.modManager.warn('Cached archive hash mismatch, redownloading') try { - fs.rmSync(cacheFile, { force: true }) + await fs.promises.rm(cacheFile, { force: true }) } catch {} - return null + return false } } - return cached + return true } catch (e) { - logger.modManager.warn('Failed to read cached archive, redownloading:', e) - return null - } -} - -export async function decompressArchive(rawArchive: Buffer, extLower: string): Promise { - if (extLower === '.zst' || extLower === '.zstd') { - return (await zstdDecompressAsync(rawArchive as any)) as Buffer - } - if (extLower === '.gz') { - return await gunzipAsync(rawArchive) - } - return rawArchive -} - -function isZipBuffer(buf: Buffer): boolean { - return ( - !!buf && - buf.length >= 4 && - buf[0] === 0x50 && - buf[1] === 0x4b && - ((buf[2] === 0x03 && buf[3] === 0x04) || (buf[2] === 0x05 && buf[3] === 0x06) || (buf[2] === 0x07 && buf[3] === 0x08)) - ) -} - -export function extractZipBuffer(zipBuffer: Buffer, destination: string): void { - fs.rmSync(destination, { recursive: true, force: true }) - fs.mkdirSync(destination, { recursive: true }) - - if (!zipBuffer || zipBuffer.length < 4) { - throw new Error('Invalid ZIP buffer') - } - - if (!isZipBuffer(zipBuffer)) { - throw new Error('Expected ZIP archive') - } - - try { - const zip = new AdmZip(zipBuffer) - zip.extractAllTo(destination, true) - } catch { - throw new Error('Failed to extract ZIP archive') - } -} - -export function resolveExtractedRoot(extractDir: string, targetPath: string): string { - const expectedRootName = path.basename(targetPath) - - let entries: fs.Dirent[] - try { - entries = fs.readdirSync(extractDir, { withFileTypes: true }) - } catch { - return extractDir + if ((e as NodeJS.ErrnoException)?.code !== 'ENOENT') { + logger.modManager.warn('Failed to validate cached archive, redownloading:', e) + } + return false } - - const meaningful = entries.filter(e => { - const n = e.name - if (!n) return false - if (n === '__MACOSX') return false - return n !== '.DS_Store' - }) - - if (meaningful.length !== 1) return extractDir - - const only = meaningful[0] - if (!only.isDirectory()) return extractDir - if (only.name !== expectedRootName) return extractDir - - return path.join(extractDir, only.name) } export function readUnpackedMarker(targetPath: string): string | null { @@ -148,73 +101,3 @@ export function writeUnpackedMarker(targetPath: string, checksum: string): void logger.modManager.warn('Failed to write unpacked marker:', e) } } - -function cleanupTempExtractPath(sourceDir: string, tempExtractPath: string): void { - if (sourceDir === tempExtractPath) return - fs.rmSync(tempExtractPath, { recursive: true, force: true }) -} - -function isRetryStageFailure(result: RetryStageResult): result is RetryStageFailure { - return result.success === false -} - -export function isReplaceDirFailure(result: ReplaceDirResult): result is ReplaceDirFailure { - return result.ok === false -} - -async function runReplaceStageWithRetries(runStage: () => void, maxAttempts: number, retryDelayStepMs: number): Promise { - let lastErr: any - for (let attempt = 1; attempt <= maxAttempts; attempt++) { - try { - runStage() - return { success: true } - } catch (err: any) { - lastErr = err - const recoverable = REPLACE_RECOVERABLE_CODES.has(err?.code) - if (!recoverable) { - return { success: false, error: err, recoverable: false } - } - if (attempt < maxAttempts) { - await sleep(retryDelayStepMs * attempt) - } - } - } - return { success: false, error: lastErr, recoverable: true } -} - -export async function tryReplaceDir(sourceDir: string, targetDir: string, tempExtractPath: string): Promise { - const maxAttempts = process.platform === 'win32' ? 5 : 2 - - const moveResult = await runReplaceStageWithRetries( - () => { - fs.rmSync(targetDir, { recursive: true, force: true }) - fs.renameSync(sourceDir, targetDir) - }, - maxAttempts, - 120, - ) - - if (!isRetryStageFailure(moveResult)) { - cleanupTempExtractPath(sourceDir, tempExtractPath) - return { ok: true } - } - if (!moveResult.recoverable) { - return { ok: false, error: moveResult.error, stage: 'move' } - } - - const copyResult = await runReplaceStageWithRetries( - () => { - fs.rmSync(targetDir, { recursive: true, force: true }) - fs.cpSync(sourceDir, targetDir, { recursive: true, force: true }) - }, - maxAttempts, - 150, - ) - - if (!isRetryStageFailure(copyResult)) { - fs.rmSync(tempExtractPath, { recursive: true, force: true }) - return { ok: true } - } - - return { ok: false, error: copyResult.error, stage: 'copy' } -} diff --git a/src/main/modules/mod/network/index.ts b/src/main/modules/mod/network/index.ts index 90a21173..a8669171 100644 --- a/src/main/modules/mod/network/index.ts +++ b/src/main/modules/mod/network/index.ts @@ -3,9 +3,8 @@ import axios from 'axios' import * as fs from 'original-fs' import * as path from 'path' import logger from '../../logger' -import RendererEvents from '../../../../common/types/rendererEvents' import { HandleErrorsElectron } from '../../handlers/handleErrorsElectron' -import { isCompressedArchiveLink, writePatchedAsarAndPatchBundle } from '../mod-files' +import { installPreparedAsarAndPatchBundle, isCompressedArchiveLink } from '../mod-files' import { t } from '../../../i18n' import { copyFile } from '../../../utils/appUtils' import { @@ -16,24 +15,43 @@ import { restoreBackupIfExists, downloadToTempWithProgress, DownloadError, + sendProgress, + setProgress, } from '../download.helpers' import { isLinuxAccessError } from '../../../utils/appUtils/elevation' import type { DownloadProgress, ModDownloadFailure } from './types' import { - decompressArchive, ensureDir, - extractZipBuffer, - isReplaceDirFailure, + isCachedArchiveValid, + pruneCacheDirectories, pruneCacheFiles, - readCachedArchive, readUnpackedMarker, - resolveExtractedRoot, - sha256Hex, - tryReplaceDir, + sha256File, + UNPACKED_MARKER_FILE, writeUnpackedMarker, } from './helpers' +import { ArtifactWorkerError, hashArtifactInWorker, installUnpackedArtifactInWorker, prepareAsarArtifactInWorker } from './artifactWorkerClient' const USER_AGENT = () => `Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) PulseSync/${app.getVersion()} Chrome/142.0.7444.59 Electron/39.1.1 Safari/537.36` +const NETWORK_PROGRESS_RATIO = 0.85 +const DERIVED_UNPACKED_DIRECTORY_SUFFIX = '.unpacked-dir' +const LEGACY_PREPARED_UNPACKED_SUFFIX = '.unpacked.zip' +const DERIVED_CACHE_RECOVERY_STAGES = new Set(['read', 'decompress', 'extract']) + +function reportArtifactProgress(window: BrowserWindow, fraction: number, name: string): void { + const boundedFraction = Math.min(Math.max(fraction, 0), 1) + setProgress(window, boundedFraction) + sendProgress(window, Math.round(boundedFraction * 100), name) +} + +async function isCachedUnpackedDirectoryValid(directoryPath: string, checksum: string): Promise { + try { + const stats = await fs.promises.stat(directoryPath) + return stats.isDirectory() && readUnpackedMarker(directoryPath) === checksum + } catch { + return false + } +} function reportFailure(window: BrowserWindow, failure: ModDownloadFailure, onFailure?: (failure: ModDownloadFailure) => void) { if (onFailure) { @@ -57,17 +75,18 @@ export async function downloadAndUpdateFile( name?: string, onFailure?: (failure: ModDownloadFailure) => void, ): Promise { + const preparedFilePath = `${tempFilePath}.prepared.${process.pid}.${Date.now()}.asar` + const progressBase = progress?.base ?? 0 + const progressScale = progress?.scale ?? 1 + const networkProgressScale = progressScale * NETWORK_PROGRESS_RATIO + const completedProgress = progressBase + progressScale + const artifactName = name ?? 'app.asar' try { if (checksum && fs.existsSync(savePath) && !isCompressedArchiveLink(link)) { - const buf = fs.readFileSync(savePath) - const currentHash = sha256Hex(buf) + const currentHash = (await hashArtifactInWorker({ filePath: savePath })).checksum if (currentHash === checksum) { logger.modManager.info('app.asar hash matches, skipping download') - sendToRenderer(window, RendererEvents.DOWNLOAD_SUCCESS, { - success: true, - message: t('main.modManager.modAlreadyInstalled'), - }) - resetProgress(window) + reportArtifactProgress(window, completedProgress, artifactName) return true } } @@ -78,14 +97,14 @@ export async function downloadAndUpdateFile( tempFilePath, expectedChecksum: checksum, userAgent: USER_AGENT(), - progressScale: progress?.scale ?? 1, - progressBase: progress?.base ?? 0, + progressScale: networkProgressScale, + progressBase, rejectUnauthorized: false, - name: name ?? 'app.asar', + name: artifactName, }) - const fileBuffer = fs.readFileSync(tempFilePath) - const ok = await writePatchedAsarAndPatchBundle(savePath, fileBuffer, link, backupPath, checksum) + reportArtifactProgress(window, progressBase + networkProgressScale, artifactName) + const ok = await prepareAndInstallAsarArtifact(tempFilePath, preparedFilePath, link, savePath, backupPath, checksum) if (checksum && cacheDir) { try { const cacheFile = path.join(cacheDir, `${checksum}.asar`) @@ -104,12 +123,14 @@ export async function downloadAndUpdateFile( return false } + reportArtifactProgress(window, completedProgress, artifactName) if (progress?.resetOnComplete ?? true) { resetProgress(window) } return true } catch (err: any) { unlinkIfExists(tempFilePath) + unlinkIfExists(preparedFilePath) restoreBackupIfExists(savePath, backupPath) logger.modManager.error('File download/install error:', err) logger.modManager.error('Error details:', { @@ -124,7 +145,10 @@ export async function downloadAndUpdateFile( return false } - if (err instanceof DownloadError && err.code === 'checksum_mismatch') { + if ( + (err instanceof DownloadError && err.code === 'checksum_mismatch') || + (err instanceof ArtifactWorkerError && err.code === 'CHECKSUM_MISMATCH') + ) { reportFailure(window, { error: t('main.modNetwork.integrityError'), type: 'checksum_mismatch' }, onFailure) } else { reportFailure(window, { error: err?.message || t('main.modDownload.networkError'), type: 'download_error' }, onFailure) @@ -133,6 +157,38 @@ export async function downloadAndUpdateFile( } } +export async function prepareAndInstallAsarArtifact( + archivePath: string, + preparedFilePath: string, + link: string, + savePath: string, + backupPath: string, + checksum?: string, +): Promise { + const extension = path.extname(new URL(link).pathname).toLowerCase() + const startedAt = Date.now() + + try { + const workerResult = await prepareAsarArtifactInWorker({ + archivePath, + archiveExtension: extension, + expectedChecksum: checksum, + outputPath: preparedFilePath, + }) + logger.modManager.info('Prepared app.asar in worker', { + totalMs: Date.now() - startedAt, + workerThreadId: workerResult.workerThreadId, + ...workerResult.durations, + }) + for (const warning of workerResult.warnings) { + logger.modManager.warn('ASAR worker warning:', warning) + } + return await installPreparedAsarAndPatchBundle(savePath, workerResult.preparedPath, backupPath) + } finally { + await fs.promises.rm(preparedFilePath, { force: true }).catch(() => {}) + } +} + export async function downloadAndExtractUnpacked( window: BrowserWindow, link: string, @@ -144,11 +200,19 @@ export async function downloadAndExtractUnpacked( progress?: DownloadProgress, onFailure?: (failure: ModDownloadFailure) => void, ): Promise { + const progressBase = progress?.base ?? 0 + const progressScale = progress?.scale ?? 1 + const networkProgressScale = progressScale * NETWORK_PROGRESS_RATIO + const completedProgress = progressBase + progressScale + const artifactName = 'app.asar.unpacked' + const preflightStartedAt = Date.now() try { + const markerStartedAt = Date.now() if (checksum && fs.existsSync(targetPath)) { const installed = readUnpackedMarker(targetPath) if (installed && installed === checksum) { logger.modManager.info('app.asar.unpacked hash matches, skipping') + reportArtifactProgress(window, completedProgress, artifactName) if (progress?.resetOnComplete ?? true) { resetProgress(window) } @@ -156,91 +220,228 @@ export async function downloadAndExtractUnpacked( } if (installed && installed !== checksum) { logger.modManager.info(`app.asar.unpacked hash mismatch, reinstalling`) - try { - fs.rmSync(targetPath, { recursive: true, force: true }) - } catch (e) { - logger.modManager.warn('Failed to remove old unpacked dir:', e) - } } } + const markerMs = Date.now() - markerStartedAt + const tempCleanupStartedAt = Date.now() unlinkIfExists(tempArchivePath) - fs.rmSync(tempExtractPath, { recursive: true, force: true }) + const tempCleanupMs = Date.now() - tempCleanupStartedAt const pathname = new URL(link).pathname const ext = path.extname(pathname) || '.zip' const extLower = ext.toLowerCase() - let rawArchive: Buffer | null = null - let cacheFile: string | null = null + let preparedCacheDirectory: string | null = null + let legacyPreparedCacheFile: string | null = null + let sourceKind: 'archive' | 'directory' = 'archive' + let archivePath = tempArchivePath + let archiveExtension = extLower + let archiveChecksum = checksum + let downloaded = false if (cacheDir) { + const cacheDirStartedAt = Date.now() await ensureDir(cacheDir) + const cacheDirMs = Date.now() - cacheDirStartedAt if (checksum) { cacheFile = path.join(cacheDir, `${checksum}${ext}`) - const cached = readCachedArchive(cacheFile, checksum) - if (cached) { - logger.modManager.info('Using cached unpacked archive') - rawArchive = cached + if (extLower !== '.zip') { + preparedCacheDirectory = path.join(cacheDir, `${checksum}${DERIVED_UNPACKED_DIRECTORY_SUFFIX}`) + legacyPreparedCacheFile = path.join(cacheDir, `${checksum}${LEGACY_PREPARED_UNPACKED_SUFFIX}`) + } + const cacheValidationStartedAt = Date.now() + const validPreparedCacheDirectory = + preparedCacheDirectory && (await isCachedUnpackedDirectoryValid(preparedCacheDirectory, checksum)) ? preparedCacheDirectory : null + if (validPreparedCacheDirectory) { + logger.modManager.info('Using cached extracted app.asar.unpacked', { + preflightMs: Date.now() - preflightStartedAt, + markerMs, + tempCleanupMs, + cacheDirMs, + cacheValidationMs: Date.now() - cacheValidationStartedAt, + }) + sourceKind = 'directory' + archivePath = validPreparedCacheDirectory + archiveExtension = '' + archiveChecksum = undefined + } else { + if (preparedCacheDirectory) { + await fs.promises.rm(preparedCacheDirectory, { recursive: true, force: true }).catch(() => {}) + } + + if (legacyPreparedCacheFile && (await isCachedArchiveValid(legacyPreparedCacheFile))) { + logger.modManager.info('Using legacy prepared unpacked ZIP', { + preflightMs: Date.now() - preflightStartedAt, + markerMs, + tempCleanupMs, + cacheDirMs, + cacheValidationMs: Date.now() - cacheValidationStartedAt, + }) + archivePath = legacyPreparedCacheFile + archiveExtension = '.zip' + archiveChecksum = undefined + } else if (await isCachedArchiveValid(cacheFile)) { + logger.modManager.info('Using cached unpacked archive', { + preflightMs: Date.now() - preflightStartedAt, + markerMs, + tempCleanupMs, + cacheDirMs, + cacheValidationMs: Date.now() - cacheValidationStartedAt, + }) + archivePath = cacheFile + } } } } - if (!rawArchive) { + const downloadArchive = async (): Promise => { await downloadToTempWithProgress({ window, url: link, tempFilePath: tempArchivePath, userAgent: USER_AGENT(), - progressScale: progress?.scale ?? 1, - progressBase: progress?.base ?? 0, + progressScale: networkProgressScale, + progressBase, rejectUnauthorized: false, expectedChecksum: checksum, - name: 'app.asar.unpacked', + name: artifactName, }) + downloaded = true + sourceKind = 'archive' + archivePath = tempArchivePath + archiveExtension = extLower + archiveChecksum = checksum + } - rawArchive = fs.readFileSync(tempArchivePath) - - if (cacheDir) { - try { - if (!cacheFile) { - const fileHash = sha256Hex(rawArchive) - cacheFile = path.join(cacheDir, `${fileHash}${ext}`) - } - - await copyFile(tempArchivePath, cacheFile) - await pruneCacheFiles(cacheDir, cacheFile, file => file.toLowerCase().endsWith(extLower), 'Failed to remove old unpacked cache:') - } catch (e: any) { - logger.modManager.warn('Failed to cache unpacked archive:', e) - } - } + if (archivePath === tempArchivePath) { + await downloadArchive() } - const zipBuffer = await decompressArchive(rawArchive as Buffer, extLower) + reportArtifactProgress(window, progressBase + networkProgressScale, artifactName) + const processArchive = () => + installUnpackedArtifactInWorker({ + sourceKind, + archivePath, + archiveExtension, + expectedChecksum: archiveChecksum, + preparedDirectoryPath: preparedCacheDirectory && sourceKind === 'archive' ? preparedCacheDirectory : undefined, + preparedDirectoryMarker: + preparedCacheDirectory && sourceKind === 'archive' && checksum ? { fileName: UNPACKED_MARKER_FILE, value: checksum } : undefined, + stagingPath: tempExtractPath, + targetPath, + }) - extractZipBuffer(zipBuffer, tempExtractPath) + let workerStartedAt = Date.now() + let workerResult + for (let attempt = 0; ; attempt++) { + try { + workerResult = await processArchive() + break + } catch (error) { + const canRecoverDirectoryCache = + attempt === 0 && + preparedCacheDirectory !== null && + sourceKind === 'directory' && + archivePath === preparedCacheDirectory && + error instanceof ArtifactWorkerError && + error.stage === 'extract' + if (canRecoverDirectoryCache) { + const invalidPreparedCacheDirectory = preparedCacheDirectory as string + logger.modManager.warn('Extracted unpacked cache is invalid, rebuilding:', error) + await fs.promises.rm(invalidPreparedCacheDirectory, { recursive: true, force: true }).catch(() => {}) + sourceKind = 'archive' + if (legacyPreparedCacheFile && (await isCachedArchiveValid(legacyPreparedCacheFile))) { + archivePath = legacyPreparedCacheFile + archiveExtension = '.zip' + archiveChecksum = undefined + } else if (cacheFile && (await isCachedArchiveValid(cacheFile))) { + archivePath = cacheFile + archiveExtension = extLower + archiveChecksum = checksum + } else { + await downloadArchive() + } + workerStartedAt = Date.now() + continue + } - const extractedRoot = resolveExtractedRoot(tempExtractPath, targetPath) + const canRecoverLegacyPreparedCache = + attempt <= 1 && + legacyPreparedCacheFile !== null && + archivePath === legacyPreparedCacheFile && + error instanceof ArtifactWorkerError && + DERIVED_CACHE_RECOVERY_STAGES.has(error.stage) + if (canRecoverLegacyPreparedCache) { + const invalidLegacyPreparedCacheFile = legacyPreparedCacheFile as string + logger.modManager.warn('Legacy prepared unpacked ZIP is invalid, rebuilding:', error) + await fs.promises.rm(invalidLegacyPreparedCacheFile, { force: true }).catch(() => {}) + if (cacheFile && (await isCachedArchiveValid(cacheFile))) { + sourceKind = 'archive' + archivePath = cacheFile + archiveExtension = extLower + archiveChecksum = checksum + } else { + await downloadArchive() + } + workerStartedAt = Date.now() + continue + } - fs.mkdirSync(path.dirname(targetPath), { recursive: true }) + const canRecoverSourceCache = + attempt <= 1 && + cacheFile !== null && + archivePath === cacheFile && + error instanceof ArtifactWorkerError && + error.code === 'CHECKSUM_MISMATCH' + if (!canRecoverSourceCache) throw error - const moved = await tryReplaceDir(extractedRoot, targetPath, tempExtractPath) - if (isReplaceDirFailure(moved)) { - if (isLinuxAccessError(moved.error)) { - reportFailure(window, { error: t('main.modManager.linuxPermissionsRequired'), type: 'linux_permissions_required' }, onFailure) - return false + const invalidSourceCacheFile = cacheFile as string + logger.modManager.warn('Cached unpacked archive hash mismatch, redownloading') + await fs.promises.rm(invalidSourceCacheFile, { force: true }).catch(() => {}) + await downloadArchive() + workerStartedAt = Date.now() } - const messageKey = moved.stage === 'copy' ? 'main.modNetwork.unpackedCopyError' : 'main.modNetwork.unpackedMoveError' - logger.modManager.error('Failed to replace unpacked dir:', moved.error) - reportFailure(window, { error: moved.error?.message || t(messageKey), type: 'download_unpacked_error' }, onFailure) - return false + } + logger.modManager.info('Processed app.asar.unpacked in worker', { + totalMs: Date.now() - workerStartedAt, + workerThreadId: workerResult.workerThreadId, + ...workerResult.durations, + }) + for (const warning of workerResult.warnings) { + logger.modManager.warn('Artifact worker warning:', warning) } if (checksum) { writeUnpackedMarker(targetPath, checksum) } + if (downloaded && cacheDir) { + try { + if (!cacheFile) { + cacheFile = path.join(cacheDir, `${await sha256File(tempArchivePath)}${ext}`) + } + await copyFile(tempArchivePath, cacheFile) + await pruneCacheFiles(cacheDir, cacheFile, file => file.toLowerCase().endsWith(extLower), 'Failed to remove old unpacked cache:') + } catch (e: any) { + logger.modManager.warn('Failed to cache unpacked archive:', e) + } + } + + if (preparedCacheDirectory && cacheDir && checksum && (await isCachedUnpackedDirectoryValid(preparedCacheDirectory, checksum))) { + await pruneCacheDirectories( + cacheDir, + preparedCacheDirectory, + directory => directory.toLowerCase().endsWith(DERIVED_UNPACKED_DIRECTORY_SUFFIX), + 'Failed to remove old extracted unpacked cache:', + ) + if (legacyPreparedCacheFile) { + await fs.promises.rm(legacyPreparedCacheFile, { force: true }).catch(() => {}) + } + } + + reportArtifactProgress(window, completedProgress, artifactName) if (progress?.resetOnComplete ?? true) { resetProgress(window) } @@ -251,10 +452,17 @@ export async function downloadAndExtractUnpacked( reportFailure(window, { error: t('main.modManager.linuxPermissionsRequired'), type: 'linux_permissions_required' }, onFailure) return false } + if (err instanceof ArtifactWorkerError) { + logger.modManager.error('Artifact worker failed:', { + stage: err.stage, + code: err.code, + message: err.message, + }) + } reportFailure(window, { error: err?.message || t('main.modNetwork.unpackedDownloadError'), type: 'download_unpacked_error' }, onFailure) return false } finally { unlinkIfExists(tempArchivePath) - fs.rmSync(tempExtractPath, { recursive: true, force: true }) + await fs.promises.rm(tempExtractPath, { recursive: true, force: true }).catch(() => {}) } } diff --git a/src/main/modules/network/systemProxy.ts b/src/main/modules/network/systemProxy.ts index 2a8951fd..8dfcf69c 100644 --- a/src/main/modules/network/systemProxy.ts +++ b/src/main/modules/network/systemProxy.ts @@ -118,7 +118,13 @@ function rejectForStatus(response: TResponse, status: number, config: return response } - throw new AxiosError(`Request failed with status code ${status}`, status >= 500 ? AxiosError.ERR_BAD_RESPONSE : AxiosError.ERR_BAD_REQUEST, config, null, response as any) + throw new AxiosError( + `Request failed with status code ${status}`, + status >= 500 ? AxiosError.ERR_BAD_RESPONSE : AxiosError.ERR_BAD_REQUEST, + config, + null, + response as any, + ) } async function requestBuffer(options: ElectronRequestOptions): Promise { @@ -195,10 +201,21 @@ async function requestStream(options: ElectronRequestOptions): Promise stream.write(chunk)) - response.on('end', () => stream.end()) + response.on('end', () => { + responseEnded = true + stream.end() + }) response.on('aborted', () => stream.destroy(new Error('Response aborted'))) response.on('error', error => stream.destroy(error)) + stream.on('close', () => { + if (!responseEnded) { + try { + request.abort() + } catch {} + } + }) resolve({ data: stream, diff --git a/src/renderer/app/providers/experiments/constants.ts b/src/renderer/app/providers/experiments/constants.ts index 480e52f3..86a333ce 100644 --- a/src/renderer/app/providers/experiments/constants.ts +++ b/src/renderer/app/providers/experiments/constants.ts @@ -6,8 +6,6 @@ export const CLIENT_EXPERIMENTS = { ClientUsersPageAccess: 'ClientUsersPageAccess', ClientTrackSending: 'ClientTrackSending', ClientMetricsSending: 'ClientMetricsSending', - WebLocalizationContribution: 'WebLocalizationContribution', - WebHomeSections: 'WebHomeSections', WebSubscriptionsPage: 'WebSubscriptionsPage', } as const diff --git a/tsconfig.node.json b/tsconfig.node.json index 7c9cdc4e..df863e8e 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -13,6 +13,7 @@ "declarations.d.ts", "vite.main.config.ts", "vite.preload.config.ts", + "vite.worker.config.ts", "vite.renderer.config.ts", "forge.config.ts", "scripts/**/*.ts" diff --git a/vite.worker.config.ts b/vite.worker.config.ts new file mode 100644 index 00000000..2fbb2677 --- /dev/null +++ b/vite.worker.config.ts @@ -0,0 +1,30 @@ +import { defineConfig, type UserConfig } from 'vite' +import path from 'path' +import { fileURLToPath } from 'node:url' + +const __dirname = path.dirname(fileURLToPath(import.meta.url)) + +export default defineConfig(({ mode, forgeConfigSelf }: any): UserConfig => { + const isDevMode = mode === 'development' + const entry = forgeConfigSelf?.entry ?? 'src/main/modules/mod/network/artifactWorker.ts' + + return { + ssr: { + noExternal: ['adm-zip', 'zstd-codec'], + }, + build: { + sourcemap: isDevMode, + target: 'node24.14', + outDir: path.resolve(__dirname, '.vite/worker'), + ssr: entry, + rolldownOptions: { + external: ['original-fs'], + output: { + format: 'cjs' as const, + entryFileNames: 'artifactWorker.cjs', + preserveModules: false, + }, + }, + }, + } +}) diff --git a/yarn.lock b/yarn.lock index 60141842..12460be3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,15 +2,10 @@ # yarn lockfile v1 -"7zip-bin@~5.2.0": - version "5.2.0" - resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.2.0.tgz#7a03314684dd6572b7dfa89e68ce31d60286854d" - integrity sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A== - -"@apollo/client@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.2.0.tgz#cd5d40f2fd04eabdda77c2e873a9e4c59ac0759f" - integrity sha512-uZAiXwIRidDqQKZRcL88O01IVZjY6IhLio6g+XzX4N4++Ue9pVK9WCoZvBm1dvx+x2mH0oGfVUZvTvacCW4WcQ== +"@apollo/client@^4.2.3": + version "4.2.3" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-4.2.3.tgz#04177a6a170e3733346f9ae15103161abd50cdce" + integrity sha512-+auRYBXow2v7cT+wKzvjyMyyEojq+G7Sf80vIR57rtEPcxRFuMXuU9IKjwxZ3muclUgdGKwZXNeuki+g0GabgQ== dependencies: "@graphql-typed-document-node/core" "^3.1.1" "@wry/caches" "^1.0.0" @@ -88,262 +83,218 @@ "@smithy/util-utf8" "^2.0.0" tslib "^2.6.2" -"@aws-sdk/client-s3@^3.1057.0": - version "3.1057.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.1057.0.tgz#5a783870517f8256334b31027b032c66af06e78d" - integrity sha512-4MV5+ph7WSLEqStKYdWf2EIHIvLpPzV8xN98jWSVJfUpp5j7T8dyN3AROPPsKWvCme8hbx1ybCjtK76ALCZUYg== +"@aws-sdk/checksums@^3.1000.5": + version "3.1000.5" + resolved "https://registry.yarnpkg.com/@aws-sdk/checksums/-/checksums-3.1000.5.tgz#fbb4451375873dfd7a4581fa6f9cf9331b12c0fb" + integrity sha512-zOXUUnilC6lgCsQtp77p/QNPmRlTES9Xi6tlDwbR6kfC/kz5PCzZckgHWm5z+8DskdwuMAbFDq61x3zr10GEEQ== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-crypto/util" "5.2.0" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.1067.0": + version "3.1067.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.1067.0.tgz#9b0f3d553cc62c63b3b073e3edf42a36b47bad01" + integrity sha512-3f64o9YWzwJ9WzMIC4JlUQiMOm7R/EtkIDyFdj8yaQXuh8SR9ezz2R32UMpvTlVMtpoPan3Uj8oveAHr2UeExw== dependencies: "@aws-crypto/sha1-browser" "5.2.0" "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/credential-provider-node" "^3.972.47" - "@aws-sdk/middleware-bucket-endpoint" "^3.972.17" - "@aws-sdk/middleware-expect-continue" "^3.972.14" - "@aws-sdk/middleware-flexible-checksums" "^3.974.23" - "@aws-sdk/middleware-location-constraint" "^3.972.11" - "@aws-sdk/middleware-sdk-s3" "^3.972.44" - "@aws-sdk/middleware-ssec" "^3.972.11" - "@aws-sdk/signature-v4-multi-region" "^3.996.30" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/fetch-http-handler" "^5.4.5" - "@smithy/node-http-handler" "^4.7.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/credential-provider-node" "^3.972.55" + "@aws-sdk/middleware-flexible-checksums" "^3.974.30" + "@aws-sdk/middleware-sdk-s3" "^3.972.51" + "@aws-sdk/signature-v4-multi-region" "^3.996.34" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/fetch-http-handler" "^5.4.6" + "@smithy/node-http-handler" "^4.7.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/core@^3.974.15": - version "3.974.15" - resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.974.15.tgz#841395d805ed33b8e4f30b1b86749922a0c6a058" - integrity sha512-UpA0rTGW/tHGITcCqHisbuuEPraYg9GG+mWmXjY5+RxZBMLGe6aL9oe0ix50LztwAcPIkGZLH0yWdMIkCM10hw== +"@aws-sdk/core@^3.974.20": + version "3.974.20" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.974.20.tgz#fc9727897662e148fad2276995298f56b587c3ab" + integrity sha512-7sDi2B2N3mc3nf1nz6FyEx/FCrJ1N1QnBmraHHQNabFaeAh2IaOOLml48/rHOD1bICHgTRkbBgNTvUzEr5Z35g== dependencies: - "@aws-sdk/types" "^3.973.9" - "@aws-sdk/xml-builder" "^3.972.26" + "@aws-sdk/types" "^3.973.12" + "@aws-sdk/xml-builder" "^3.972.29" "@aws/lambda-invoke-store" "^0.2.2" - "@smithy/core" "^3.24.5" - "@smithy/signature-v4" "^5.4.5" - "@smithy/types" "^4.14.2" + "@smithy/core" "^3.24.6" + "@smithy/signature-v4" "^5.4.6" + "@smithy/types" "^4.14.3" bowser "^2.11.0" tslib "^2.6.2" -"@aws-sdk/crc64-nvme@^3.972.9": - version "3.972.9" - resolved "https://registry.yarnpkg.com/@aws-sdk/crc64-nvme/-/crc64-nvme-3.972.9.tgz#4ea4d574d473e25e59973fcbab101ca1b64fab91" - integrity sha512-P+QGozmXn2mZZI7sDgk+aUm+RTI61MPSFB+Ir2vjEjEbEsE4e7hYtzrDvAUxZy9ko81h53e11+F/GYlvwDkaOQ== - dependencies: - "@smithy/types" "^4.14.2" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-env@^3.972.41": - version "3.972.41" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.41.tgz#753b584626798be5310cf87976f3c72d410f709a" - integrity sha512-n1EbJ98yvPWWdHZZv8bRBMqqDQJrtgtxyJ4xLy2Uqrh25BCOZQ7nnS1CsFXvuH8r0b0KVHDZEGEH5FxmEMP8jg== - dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-http@^3.972.43": - version "3.972.43" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.43.tgz#bc9af93cdba81df5ce487a3e46f232d677092c97" - integrity sha512-TT76RN1NkI9WoyZqCNxOw6/WBMF7pYOTJcXbMokNFU+euSG40Kaf/t/FhDACVZWP+43wEM6ZynIPIkzS1wR1iA== - dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/fetch-http-handler" "^5.4.5" - "@smithy/node-http-handler" "^4.7.5" - "@smithy/types" "^4.14.2" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-ini@^3.972.46": +"@aws-sdk/credential-provider-env@^3.972.46": version "3.972.46" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.46.tgz#3fea86be2fb9593ac7d339cb4c6ff78e9f69df30" - integrity sha512-hvcgcwOiS0nb2XFb5Op1Pz/vYaWz5K8kKullziGpdNRuG0NwzRXseuPt2CoBqknHGaSPVesu1aOn2OcctEYdCA== - dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/credential-provider-env" "^3.972.41" - "@aws-sdk/credential-provider-http" "^3.972.43" - "@aws-sdk/credential-provider-login" "^3.972.45" - "@aws-sdk/credential-provider-process" "^3.972.41" - "@aws-sdk/credential-provider-sso" "^3.972.45" - "@aws-sdk/credential-provider-web-identity" "^3.972.45" - "@aws-sdk/nested-clients" "^3.997.13" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/credential-provider-imds" "^4.3.6" - "@smithy/types" "^4.14.2" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-login@^3.972.45": - version "3.972.45" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.45.tgz#e11744272965d423ace09d3aa2b389248d665699" - integrity sha512-MZQv4SNjByk1iOKmrqmzcUF/uCB05wjvEHyXKxmGQTUANTIVayX6HPUF0bzkWLvtnkH7sAn9kUCfkXbSpj9sDA== + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.46.tgz#590695585036566535b9d9f28d90658a725a123b" + integrity sha512-+GPXVS2srMOlH74S+SmC1gVuP2TvUZ0siuC0onKO93q+udP+M72dmY8wJfVQ5CX9z/9X5A1HHwz5yRIGBtskvQ== dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/nested-clients" "^3.997.13" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/credential-provider-node@^3.972.47": - version "3.972.47" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.47.tgz#f25a77bce8924688bb581e87f8d7ef1477eab9a0" - integrity sha512-HrId+C0DWA5qDIyLG64/kjUB2RNtPypxmABnIctK+TA1P1kHlOYoE/Wf5T5tKOMKgb08P7k/zNyhvfJ3lh5Oag== - dependencies: - "@aws-sdk/credential-provider-env" "^3.972.41" - "@aws-sdk/credential-provider-http" "^3.972.43" - "@aws-sdk/credential-provider-ini" "^3.972.46" - "@aws-sdk/credential-provider-process" "^3.972.41" - "@aws-sdk/credential-provider-sso" "^3.972.45" - "@aws-sdk/credential-provider-web-identity" "^3.972.45" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/credential-provider-imds" "^4.3.6" - "@smithy/types" "^4.14.2" +"@aws-sdk/credential-provider-http@^3.972.48": + version "3.972.48" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.48.tgz#074fe92aa255f0c42441955da6cfeb2bbc57a263" + integrity sha512-fA5loSdlocacRxyUXtpoHSMuk5rsIKRDzQYVMnMxjcmFeZshaJlJ8lymy/hYKji6sne/UmNGj5pxuEs6kq/Qcg== + dependencies: + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/fetch-http-handler" "^5.4.6" + "@smithy/node-http-handler" "^4.7.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/credential-provider-process@^3.972.41": - version "3.972.41" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.41.tgz#68d2a4f46ec15e8cd0da1d1d3e56b8889df9b926" - integrity sha512-7I/n1zkysouLOWvkEhjNEP4vMnD2v4kzzr3/3QBdrripEpn7ap1/I5DF3Hou1SUqkKWo1f3oPGMyFAA1FAMvsQ== - dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" +"@aws-sdk/credential-provider-ini@^3.972.53": + version "3.972.53" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.53.tgz#d17c2ebfab9c3a38c45f857567535bf4dd9290a0" + integrity sha512-ZfdhIOR41q8TcWEnUac+gCOb+O2LBWdHLmjedXpXz4IEFW2ppNuFcm6p0sMTavpM+zD5TYfpH5Gp7guRyqSgsQ== + dependencies: + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/credential-provider-env" "^3.972.46" + "@aws-sdk/credential-provider-http" "^3.972.48" + "@aws-sdk/credential-provider-login" "^3.972.52" + "@aws-sdk/credential-provider-process" "^3.972.46" + "@aws-sdk/credential-provider-sso" "^3.972.52" + "@aws-sdk/credential-provider-web-identity" "^3.972.52" + "@aws-sdk/nested-clients" "^3.997.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/credential-provider-imds" "^4.3.7" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/credential-provider-sso@^3.972.45": - version "3.972.45" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.45.tgz#6c934ed4905f1d0966f6ada39d07432b166b201a" - integrity sha512-oHgbz/eFD8IKiksqDsz9ZMU4A59BpQq4QwJedBnGD80ZqYcHPPHZBwjBnxLVkB7iRVVHWpDclR8yWdD2PkQIUA== - dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/nested-clients" "^3.997.13" - "@aws-sdk/token-providers" "3.1056.0" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" - tslib "^2.6.2" - -"@aws-sdk/credential-provider-web-identity@^3.972.45": - version "3.972.45" - resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.45.tgz#a8d189321695bf90a69344165814ef0274959221" - integrity sha512-CDhzKdb2onv5bpnjn/acgdNmJOQthPDLsPizU7rZflsEcgMMp8Mlri+U5hdxf8ldvZJpvM3vLU6D56vfJm5AMQ== +"@aws-sdk/credential-provider-login@^3.972.52": + version "3.972.52" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.52.tgz#43f2db767db000a1c5317ba35ecd2124cb0a883e" + integrity sha512-9hu2oR0qH7Fst5Tzdx+UWxm+w5zCXtErTLtOOW5hwwQc170CLwOeniRxyFY6s9mHfGEfC5zFukNBdKBwJR8mhQ== dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/nested-clients" "^3.997.13" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/nested-clients" "^3.997.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/middleware-bucket-endpoint@^3.972.17": - version "3.972.17" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.972.17.tgz#b5ef456db91873e563d1c0c6b9c2ee8533aa8f25" - integrity sha512-lbDmWuHenc+kiwCNrxz4MyN6nkxCWyTXPIWuspJN0ibziu+8CXci7vI1bK9MAkwy8cwJOEXNu0gBM5S0uTGRIg== - dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" +"@aws-sdk/credential-provider-node@^3.972.55": + version "3.972.55" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.55.tgz#117eaec668dde8e3366bdb750d4c5c247a59a31f" + integrity sha512-zMGLa/dhESVqmCD7mmIFFKSwSFrJGScvCXcjvBZEVOOMauFS5JRQvLTMukFpMEFWiV6dTAlsen2ATDBulLPtbg== + dependencies: + "@aws-sdk/credential-provider-env" "^3.972.46" + "@aws-sdk/credential-provider-http" "^3.972.48" + "@aws-sdk/credential-provider-ini" "^3.972.53" + "@aws-sdk/credential-provider-process" "^3.972.46" + "@aws-sdk/credential-provider-sso" "^3.972.52" + "@aws-sdk/credential-provider-web-identity" "^3.972.52" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/credential-provider-imds" "^4.3.7" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/middleware-expect-continue@^3.972.14": - version "3.972.14" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.972.14.tgz#6bffa547da965dba80ee123ac1f8f1446cc596c2" - integrity sha512-3TNFEVGO4sWZj9TEXOCZLzGEctXHnaO4fk2EQ8KVaboTbwHmEPEQrm17Xb9koImUIXEw0sgi2xtHjg7LuTS3rA== +"@aws-sdk/credential-provider-process@^3.972.46": + version "3.972.46" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.46.tgz#b8d967fa7ea65bdb51a0d60609b3947f01ad1390" + integrity sha512-VUoNFBIjWrUN8NbFiQiuxQEgFjvziAlBRPK+ddh27aj65gk0BYu6bLZnrdrNZwpW6vAihtSUtEMQ1PUJ32QRPA== dependencies: - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/middleware-flexible-checksums@^3.974.23": - version "3.974.23" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.23.tgz#2166b7c505de4815d5017acb46e7930f39153159" - integrity sha512-4nPKARo2lfKvQGUt2fPA5NlS/mEohckdxpuC9ecbjVfj7B7NFFYHeTg+Bf5BEQwdn3yRfUIzFiEkPp8Yuaw3wA== - dependencies: - "@aws-crypto/crc32" "5.2.0" - "@aws-crypto/crc32c" "5.2.0" - "@aws-crypto/util" "5.2.0" - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/crc64-nvme" "^3.972.9" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" +"@aws-sdk/credential-provider-sso@^3.972.52": + version "3.972.52" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.52.tgz#d3f43251c0a02378d9278508385f511dcc105fb8" + integrity sha512-nb2/n4o/HQf+FVpVbZe9vCTFngmuDoIsltMgLAtjixaKzvzhB4J8WSDFyWgnErgLHk55ctWH+I4PU+LIHhyffg== + dependencies: + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/nested-clients" "^3.997.20" + "@aws-sdk/token-providers" "3.1066.0" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/middleware-location-constraint@^3.972.11": - version "3.972.11" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.972.11.tgz#272507843738acd4a5644842911a2016f7dfb0e1" - integrity sha512-hkfspNUP4criAH6ton6BGKgnm5dZx+7bUOy1YqlTfejDeUPAM23D81q/IX+hdlS3KUsfwGz5ADTqZWKBEUpf4A== +"@aws-sdk/credential-provider-web-identity@^3.972.52": + version "3.972.52" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.52.tgz#28201a0e787f83aac75b80922d699cbe489a3d94" + integrity sha512-lKj6aRSGbqLmpYmM24bY7a1Xmfcq2vkE3hv8CSPYfc1yCu0BPu/XEJ1L4Fm61MsU6ULLNSG8UGsffNoFUBjESA== dependencies: - "@aws-sdk/types" "^3.973.9" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/nested-clients" "^3.997.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/middleware-sdk-s3@^3.972.44": - version "3.972.44" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.44.tgz#24bd4110d62e90e20fbf475a702302d37e5efe7e" - integrity sha512-8HQsRg1NpX8vR4vNl1E8pyLnqZroq9VSL2vZQVSgBqp6wv6365LzYD08/c9FFh/9FTg7YRc7aTtEmXF0ir/pqg== +"@aws-sdk/middleware-flexible-checksums@^3.974.30": + version "3.974.30" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.974.30.tgz#c3941178513037812b0eff2764589ce1631762b3" + integrity sha512-OaIhub+3yTgfFWPzKO8OzOZFIMUoJaiS5v67y3spQg7SoULGoMx4jKVBbE+uhnzkiZXQ+rEDS0RqrK4/aD1yJw== dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/signature-v4-multi-region" "^3.996.30" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/checksums" "^3.1000.5" tslib "^2.6.2" -"@aws-sdk/middleware-ssec@^3.972.11": - version "3.972.11" - resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.972.11.tgz#b5d5ddde7d54239137949f63b3d5dee6331628ea" - integrity sha512-7PQvGNhtveKlvVqNahqWx5yrwxP7ecwAoB1dYBf8eKwfo2tzzCbNnW+q2nO3N066ktQaB4iBQbDRWtizm+amoQ== +"@aws-sdk/middleware-sdk-s3@^3.972.51": + version "3.972.51" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.972.51.tgz#746c869d5e8a5980946315612d0deefdfd40d54f" + integrity sha512-keQgcIUTcHL0Qn7guhsuLaxQU36r9norCrxgaPH4DNCwon4TPtXdI/UdYuycl9vj3Dlwc3YR1dfL3U+6iIwJ6w== dependencies: - "@aws-sdk/types" "^3.973.9" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/signature-v4-multi-region" "^3.996.34" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/nested-clients@^3.997.13": - version "3.997.13" - resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.997.13.tgz#5566425fadaac7e9a31141eb12710c2c1cf0a184" - integrity sha512-2pA6eyb5nSo/ZD2cayhOTEMoGQYgspq0RI05GDLkzQ3ajZ6isS6waV6E92Am/hz4LIlLUTrbwPLurJ/fuiHvkg== +"@aws-sdk/nested-clients@^3.997.20": + version "3.997.20" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.997.20.tgz#00607f294f0109c1ee96cccab411106b8fa55625" + integrity sha512-IYJuLpXp2DEILVQpQOy0PMpkftv0AHEOCn52o0atyOaumA0CdWQ3klPyXdViGYLbNpESsVFMVybvHUeZAuiGxA== dependencies: "@aws-crypto/sha256-browser" "5.2.0" "@aws-crypto/sha256-js" "5.2.0" - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/signature-v4-multi-region" "^3.996.30" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/fetch-http-handler" "^5.4.5" - "@smithy/node-http-handler" "^4.7.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/signature-v4-multi-region" "^3.996.34" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/fetch-http-handler" "^5.4.6" + "@smithy/node-http-handler" "^4.7.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/signature-v4-multi-region@^3.996.30": - version "3.996.30" - resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.30.tgz#78e324094413c0c1e2e4b8c77d6981d1a2393c9f" - integrity sha512-HULDLMVzkmTSEv6//7kx2kRevp/VYUpm8hJNNFbmhxDn0fUiGTxVcM9yg31TukvTq8nyOBDUN2gH0o5IRbKjdw== +"@aws-sdk/signature-v4-multi-region@^3.996.34": + version "3.996.34" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.34.tgz#99749163def9dfc720eef85f7fe2d14bb4b763fb" + integrity sha512-mx1L5qlumSOt/nKM3BFaHE2HVkWwz0i4Bw0pyYO42FfX/FeLlo8YI6csC0gSPprEk6fTIqI+CZN9RwUwKd5krQ== dependencies: - "@aws-sdk/types" "^3.973.9" - "@smithy/signature-v4" "^5.4.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/types" "^3.973.12" + "@smithy/signature-v4" "^5.4.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@aws-sdk/token-providers@3.1056.0": - version "3.1056.0" - resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.1056.0.tgz#a61372715ebc6527c4849c671539461fee4ca050" - integrity sha512-81duvlltQlsfn5K+o8zILcystBRdbT1G2JJYVCML5NZHBz4CL/zf+sAemCtBh/uh6RQUMyInGeZLQ7/8igZhbA== +"@aws-sdk/token-providers@3.1066.0": + version "3.1066.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.1066.0.tgz#1253c27ab45284655ab935411a0fbbf91b2c7c7f" + integrity sha512-UqEUJq7dqa44hneLDUcX7UJy95cg8YqEWyakRpvIPnrNS3Mq+UlQHgCDGu5pvwAPtlIW4qcYbvW6reG6++FyvA== dependencies: - "@aws-sdk/core" "^3.974.15" - "@aws-sdk/nested-clients" "^3.997.13" - "@aws-sdk/types" "^3.973.9" - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@aws-sdk/core" "^3.974.20" + "@aws-sdk/nested-clients" "^3.997.20" + "@aws-sdk/types" "^3.973.12" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" "@aws-sdk/types@^3.222.0": @@ -354,12 +305,12 @@ "@smithy/types" "^4.12.0" tslib "^2.6.2" -"@aws-sdk/types@^3.973.9": - version "3.973.9" - resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.973.9.tgz#7d1c08cc6e82ec2ac2f2da102a7dd55806592f7f" - integrity sha512-kuBfgQVdcz5Bmapc4A13YbpVw/pXkesfhetcFYwbntqas8sF41OHyd4o28+/TG2ZQdHBsv90Lsu5y6oitvYCdg== +"@aws-sdk/types@^3.973.12": + version "3.973.12" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.973.12.tgz#a3ae50d325644e4e890030d187febbeef2d238ef" + integrity sha512-43ajd1NF0RMgX5k0hxCNUyEdrtFUsb2aHT2QvpktSC/2Eyb2Jr/JPVqdp0XIoaHWikZJq5tNWSLO6kB5q2eMCA== dependencies: - "@smithy/types" "^4.14.2" + "@smithy/types" "^4.14.3" tslib "^2.6.2" "@aws-sdk/util-locate-window@^3.0.0": @@ -369,12 +320,12 @@ dependencies: tslib "^2.6.2" -"@aws-sdk/xml-builder@^3.972.26": - version "3.972.26" - resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.972.26.tgz#949366fe7c195f676f0ab9e002dd95b70942410c" - integrity sha512-cDbrqvDS73whl6YAPSPq0U6whzG6UWI9PuWh0wrUuGoZexhWEqhdunbukV7iBoaWnFV1AODutM5hOD6rtn439g== +"@aws-sdk/xml-builder@^3.972.29": + version "3.972.29" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.972.29.tgz#1fffe1dd3fbb84c034ff2f8008de8a6926a4a672" + integrity sha512-fk0niuGFxfi8yIJuMVM4mhwObkiQSuwZFj3tAPrLVx64Pk3BkrEIpqjzHKY4hKoEBUD6Jg/S74Zj9jy+5F3DnQ== dependencies: - "@smithy/types" "^4.14.2" + "@smithy/types" "^4.14.3" fast-xml-parser "5.7.3" tslib "^2.6.2" @@ -686,14 +637,6 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@develar/schema-utils@~2.6.5": - version "2.6.5" - resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz#3ece22c5838402419a6e0425f85742b961d9b6c6" - integrity sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig== - dependencies: - ajv "^6.12.0" - ajv-keywords "^3.4.1" - "@dr.pogodin/react-helmet@^3.2.2": version "3.2.2" resolved "https://registry.yarnpkg.com/@dr.pogodin/react-helmet/-/react-helmet-3.2.2.tgz#adb8c7f0862e88d2e0d3bdf5c00e11fa75b8d5f1" @@ -941,6 +884,11 @@ dependencies: chrome-trace-event "^1.0.3" +"@electron-internal/extract-zip@^1.0.1": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@electron-internal/extract-zip/-/extract-zip-1.0.2.tgz#c612e3db45f78261a1e16d577cea9eac8663defc" + integrity sha512-VJuNETNPEhrmQEZezeTZO5TZMV+dobBRyJ7zHjGJWIhMS7m7W1UeClt69u4hkUxv9ZZVxuli/E9Yvc4gDNHGsg== + "@electron/asar@3.4.1", "@electron/asar@^3.2.1", "@electron/asar@^3.2.13", "@electron/asar@^3.2.5", "@electron/asar@^3.3.1": version "3.4.1" resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.4.1.tgz#4e9196a4b54fba18c56cd8d5cac67c5bdc588065" @@ -950,10 +898,10 @@ glob "^7.1.6" minimatch "^3.0.4" -"@electron/fuses@2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@electron/fuses/-/fuses-2.1.1.tgz#8b15eb91dcde51e20a2b3cefb5fb11adb43d237a" - integrity sha512-38ho27/mtUV/LpsZ1LCDJUomKBBSUZDk/qBH4FNNtoN5fmnkmWDcIp5pm1Kv3InqhRjKZKs7Jzx+wWZNMArHrA== +"@electron/fuses@2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@electron/fuses/-/fuses-2.1.2.tgz#258e34b51f86dd94005d57afd7c29813aef2b68d" + integrity sha512-x083OSf4xO9CKUKmJFYW+Egua9y/v2XOCcCIuWnM4RlpMj8xHwKICezlVN6vZyI2cZTr/ysZXRU5AKtYPJivBQ== "@electron/fuses@^1.8.0": version "1.8.0" @@ -1056,7 +1004,7 @@ semver "^7.1.3" yargs-parser "^21.1.1" -"@electron/rebuild@4.0.4", "@electron/rebuild@^3.7.0", "@electron/rebuild@^4.0.3": +"@electron/rebuild@4.0.4", "@electron/rebuild@^3.7.0", "@electron/rebuild@^4.0.4": version "4.0.4" resolved "https://registry.yarnpkg.com/@electron/rebuild/-/rebuild-4.0.4.tgz#a61331d9ae3b8e2c7eddca8e446fcd7fcd60e4ce" integrity sha512-Rzc39XPdk/+/wBG8MfwAHohXflep0ITUfulb6Rgz3R0NeSB1noE+E9/M/cb8ftCAiyDD9PPhLuuWgE1GaInbKg== @@ -1799,6 +1747,16 @@ dependencies: "@tybys/wasm-util" "^0.10.1" +"@noble/hashes@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/hashes@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.2.0.tgz#22da1d16a469954fce877055d559900a6c73b63b" + integrity sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg== + "@nodable/entities@^2.1.0": version "2.1.0" resolved "https://registry.yarnpkg.com/@nodable/entities/-/entities-2.1.0.tgz#f543e5c6446720d4cf9e498a83019dd159973bc2" @@ -1825,10 +1783,10 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@oxc-project/types@=0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.132.0.tgz#d77243df4fe1a0a1e60e12ac6240fa898d2363ff" - integrity sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ== +"@oxc-project/types@=0.133.0": + version "0.133.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.133.0.tgz#2e282ef9e1d26e06b68ccd14b73f310a3b2cf7f8" + integrity sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA== "@parcel/watcher-android-arm64@2.5.6": version "2.5.6" @@ -1919,172 +1877,205 @@ "@parcel/watcher-win32-ia32" "2.5.6" "@parcel/watcher-win32-x64" "2.5.6" +"@peculiar/asn1-schema@^2.7.0": + version "2.7.0" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.7.0.tgz#f2dcb25995ce7cac8687ba1039f043e5eff43820" + integrity sha512-W8ZfWzLmQnrcky+eh3tni4IozMdqBDiHWU0N+vve/UGjMaUs8c0L7A2oEdkBXS8rTpWDpK/aoI3DG/L/hxmxPg== + dependencies: + "@peculiar/utils" "^2.0.2" + asn1js "^3.0.6" + tslib "^2.8.1" + +"@peculiar/json-schema@^1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" + integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== + dependencies: + tslib "^2.0.0" + +"@peculiar/utils@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@peculiar/utils/-/utils-2.0.3.tgz#a27ca4c4b73652e110f19a7d16d664f458a5528e" + integrity sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ== + dependencies: + tslib "^2.8.1" + +"@peculiar/webcrypto@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.7.1.tgz#ef8cfae878ca1f1a44ef0c82cf3fa6f3cbe9c26a" + integrity sha512-ODOov0sGMJMf3jPonOkgGqPknTsu+DdQ7kD++gz8aI+aFMOMHFbWAA2taqXXVTdP+OTOQR/znGvSpmkeI0WTYQ== + dependencies: + "@peculiar/asn1-schema" "^2.7.0" + "@peculiar/json-schema" "^1.1.12" + "@peculiar/utils" "^2.0.2" + tslib "^2.8.1" + webcrypto-core "^1.9.2" + "@popperjs/core@^2.11.8": version "2.11.8" resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== -"@radix-ui/primitive@1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.3.tgz#e2dbc13bdc5e4168f4334f75832d7bdd3e2de5ba" - integrity sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg== +"@radix-ui/primitive@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.4.tgz#47ef0f6cff4a1a1c09ebbf6d79159b7f01b967cf" + integrity sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ== -"@radix-ui/react-arrow@1.1.7": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz#e14a2657c81d961598c5e72b73dd6098acc04f09" - integrity sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w== +"@radix-ui/react-arrow@1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.9.tgz#fac41c884c6e154fc0d01fda16b659a2d6bcf7c0" + integrity sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig== dependencies: - "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-primitive" "2.1.5" -"@radix-ui/react-compose-refs@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz#a2c4c47af6337048ee78ff6dc0d090b390d2bb30" - integrity sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg== +"@radix-ui/react-compose-refs@1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz#5f1e61e1a5f52800d31e7f8affa6d046e38f50d1" + integrity sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA== -"@radix-ui/react-context@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.2.tgz#61628ef269a433382c364f6f1e3788a6dc213a36" - integrity sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA== +"@radix-ui/react-context@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.4.tgz#5e39f26ebbefed27836e46e763e8f71e09999ccd" + integrity sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg== -"@radix-ui/react-dismissable-layer@1.1.11": - version "1.1.11" - resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz#e33ab6f6bdaa00f8f7327c408d9f631376b88b37" - integrity sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg== +"@radix-ui/react-dismissable-layer@1.1.12": + version "1.1.12" + resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.12.tgz#f06a8f796e6fb7fa7a102cbb3c754024b43ef7f6" + integrity sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg== dependencies: - "@radix-ui/primitive" "1.1.3" - "@radix-ui/react-compose-refs" "1.1.2" - "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-use-callback-ref" "1.1.1" - "@radix-ui/react-use-escape-keydown" "1.1.1" + "@radix-ui/primitive" "1.1.4" + "@radix-ui/react-compose-refs" "1.1.3" + "@radix-ui/react-primitive" "2.1.5" + "@radix-ui/react-use-callback-ref" "1.1.2" + "@radix-ui/react-use-escape-keydown" "1.1.2" -"@radix-ui/react-id@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.1.tgz#1404002e79a03fe062b7e3864aa01e24bd1471f7" - integrity sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg== +"@radix-ui/react-id@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.2.tgz#6fe97e7289c7133b44f8c9c61fdddf2a6be1421d" + integrity sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA== dependencies: - "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.2" -"@radix-ui/react-popper@1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.2.8.tgz#a79f39cdd2b09ab9fb50bf95250918422c4d9602" - integrity sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw== +"@radix-ui/react-popper@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.3.0.tgz#c50631d3cb7e903b7c62944ac7a2f197588aab4b" + integrity sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ== dependencies: "@floating-ui/react-dom" "^2.0.0" - "@radix-ui/react-arrow" "1.1.7" - "@radix-ui/react-compose-refs" "1.1.2" - "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-use-callback-ref" "1.1.1" - "@radix-ui/react-use-layout-effect" "1.1.1" - "@radix-ui/react-use-rect" "1.1.1" - "@radix-ui/react-use-size" "1.1.1" - "@radix-ui/rect" "1.1.1" - -"@radix-ui/react-portal@1.1.9": - version "1.1.9" - resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.9.tgz#14c3649fe48ec474ac51ed9f2b9f5da4d91c4472" - integrity sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ== + "@radix-ui/react-arrow" "1.1.9" + "@radix-ui/react-compose-refs" "1.1.3" + "@radix-ui/react-context" "1.1.4" + "@radix-ui/react-primitive" "2.1.5" + "@radix-ui/react-use-callback-ref" "1.1.2" + "@radix-ui/react-use-layout-effect" "1.1.2" + "@radix-ui/react-use-rect" "1.1.2" + "@radix-ui/react-use-size" "1.1.2" + "@radix-ui/rect" "1.1.2" + +"@radix-ui/react-portal@1.1.11": + version "1.1.11" + resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.1.11.tgz#19b2e380268fe7b78bbcfeb953f049f32b0a11af" + integrity sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw== dependencies: - "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-primitive" "2.1.5" + "@radix-ui/react-use-layout-effect" "1.1.2" -"@radix-ui/react-presence@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.5.tgz#5d8f28ac316c32f078afce2996839250c10693db" - integrity sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ== +"@radix-ui/react-presence@1.1.6": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.6.tgz#f0edff4f119dbc8ef81611e539a8f58d9afb33c3" + integrity sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ== dependencies: - "@radix-ui/react-compose-refs" "1.1.2" - "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.2" -"@radix-ui/react-primitive@2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz#db9b8bcff49e01be510ad79893fb0e4cda50f1bc" - integrity sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ== - dependencies: - "@radix-ui/react-slot" "1.2.3" - -"@radix-ui/react-slot@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.3.tgz#502d6e354fc847d4169c3bc5f189de777f68cfe1" - integrity sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A== +"@radix-ui/react-primitive@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz#4582608da1cde691c8c9a7642666c6f648ee601f" + integrity sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA== dependencies: - "@radix-ui/react-compose-refs" "1.1.2" + "@radix-ui/react-slot" "1.2.5" -"@radix-ui/react-tooltip@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz#3f50267e25bccfc9e20bb3036bfd9ab4c2c30c2c" - integrity sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg== - dependencies: - "@radix-ui/primitive" "1.1.3" - "@radix-ui/react-compose-refs" "1.1.2" - "@radix-ui/react-context" "1.1.2" - "@radix-ui/react-dismissable-layer" "1.1.11" - "@radix-ui/react-id" "1.1.1" - "@radix-ui/react-popper" "1.2.8" - "@radix-ui/react-portal" "1.1.9" - "@radix-ui/react-presence" "1.1.5" - "@radix-ui/react-primitive" "2.1.3" - "@radix-ui/react-slot" "1.2.3" - "@radix-ui/react-use-controllable-state" "1.2.2" - "@radix-ui/react-visually-hidden" "1.2.3" - -"@radix-ui/react-use-callback-ref@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz#62a4dba8b3255fdc5cc7787faeac1c6e4cc58d40" - integrity sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg== +"@radix-ui/react-slot@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.2.5.tgz#159bf186ee6e6f07240941c2b1a228097f0c69fa" + integrity sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg== + dependencies: + "@radix-ui/react-compose-refs" "1.1.3" + +"@radix-ui/react-tooltip@^1.2.9": + version "1.2.9" + resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.9.tgz#9e74e97a50242eabce6f77884bc7a960e2cb6381" + integrity sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA== + dependencies: + "@radix-ui/primitive" "1.1.4" + "@radix-ui/react-compose-refs" "1.1.3" + "@radix-ui/react-context" "1.1.4" + "@radix-ui/react-dismissable-layer" "1.1.12" + "@radix-ui/react-id" "1.1.2" + "@radix-ui/react-popper" "1.3.0" + "@radix-ui/react-portal" "1.1.11" + "@radix-ui/react-presence" "1.1.6" + "@radix-ui/react-primitive" "2.1.5" + "@radix-ui/react-slot" "1.2.5" + "@radix-ui/react-use-controllable-state" "1.2.3" + "@radix-ui/react-visually-hidden" "1.2.5" + +"@radix-ui/react-use-callback-ref@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz#ddc0bc1381ff3b62368c248808efc45a098bafde" + integrity sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw== -"@radix-ui/react-use-controllable-state@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz#905793405de57d61a439f4afebbb17d0645f3190" - integrity sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg== +"@radix-ui/react-use-controllable-state@1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz#516996f6443207546aa15a59bc71cdf5b54e01d1" + integrity sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA== dependencies: - "@radix-ui/react-use-effect-event" "0.0.2" - "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-effect-event" "0.0.3" + "@radix-ui/react-use-layout-effect" "1.1.2" -"@radix-ui/react-use-effect-event@0.0.2": - version "0.0.2" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz#090cf30d00a4c7632a15548512e9152217593907" - integrity sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA== +"@radix-ui/react-use-effect-event@0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz#e8f45e8e6ef64ce5bea7b5a9effc373f067e3530" + integrity sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA== dependencies: - "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.2" -"@radix-ui/react-use-escape-keydown@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz#b3fed9bbea366a118f40427ac40500aa1423cc29" - integrity sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g== +"@radix-ui/react-use-escape-keydown@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz#d63d64956da411192ef15d8c274b94d7d0adb038" + integrity sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw== dependencies: - "@radix-ui/react-use-callback-ref" "1.1.1" + "@radix-ui/react-use-callback-ref" "1.1.2" -"@radix-ui/react-use-layout-effect@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz#0c4230a9eed49d4589c967e2d9c0d9d60a23971e" - integrity sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ== +"@radix-ui/react-use-layout-effect@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz#c882e66497174d061f250e65251974b699c65b65" + integrity sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA== -"@radix-ui/react-use-rect@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz#01443ca8ed071d33023c1113e5173b5ed8769152" - integrity sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w== +"@radix-ui/react-use-rect@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.2.tgz#83b9de1ea8f6abd1425eb79f2930e00047cb8d19" + integrity sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw== dependencies: - "@radix-ui/rect" "1.1.1" + "@radix-ui/rect" "1.1.2" -"@radix-ui/react-use-size@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz#6de276ffbc389a537ffe4316f5b0f24129405b37" - integrity sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ== +"@radix-ui/react-use-size@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.1.2.tgz#33eb275755424d7dda33ffa32c23ea85ca23be40" + integrity sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w== dependencies: - "@radix-ui/react-use-layout-effect" "1.1.1" + "@radix-ui/react-use-layout-effect" "1.1.2" -"@radix-ui/react-visually-hidden@1.2.3": - version "1.2.3" - resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz#a8c38c8607735dc9f05c32f87ab0f9c2b109efbf" - integrity sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug== +"@radix-ui/react-visually-hidden@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.5.tgz#4f1992a66138209bd1cf4ae2bb4001cacc9938b6" + integrity sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg== dependencies: - "@radix-ui/react-primitive" "2.1.3" + "@radix-ui/react-primitive" "2.1.5" -"@radix-ui/rect@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.1.tgz#78244efe12930c56fd255d7923865857c41ac8cb" - integrity sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw== +"@radix-ui/rect@1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.1.2.tgz#0761a82af55c7e302d5b509eaf1c97ea1fc5feea" + integrity sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA== "@reduxjs/toolkit@1.x.x || 2.x.x": version "2.11.2" @@ -2098,84 +2089,84 @@ redux-thunk "^3.1.0" reselect "^5.1.0" -"@rolldown/binding-android-arm64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz#ebe1264e43ba5bb224c58c85e0ac238f87e5ad14" - integrity sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ== +"@rolldown/binding-android-arm64@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz#54ce8f8382213f4a314a0c2f7ba83f81ffeae592" + integrity sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw== -"@rolldown/binding-darwin-arm64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz#f972fb71bc03840629bee923babbb7048d14a5d0" - integrity sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w== +"@rolldown/binding-darwin-arm64@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz#388fca1566c14c00c4b446fc3928630e7f0d95fc" + integrity sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA== -"@rolldown/binding-darwin-x64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz#da1c062c135e1e50067084be5ab8055cc1e05b29" - integrity sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA== +"@rolldown/binding-darwin-x64@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz#53f57de1f599ecf1db13823cfc88c18fb80954ad" + integrity sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg== -"@rolldown/binding-freebsd-x64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz#a4c8532072705ce597c0d75418513709b75b3f1d" - integrity sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA== +"@rolldown/binding-freebsd-x64@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz#6f3fdda1b7aeaac9d268a526804b4fb96e4e35f1" + integrity sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g== -"@rolldown/binding-linux-arm-gnueabihf@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz#78f3bd274a1bab07356017d728b70289f04011c1" - integrity sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w== +"@rolldown/binding-linux-arm-gnueabihf@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz#d87a454bf585cc9676849377e91d6e375297326f" + integrity sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw== -"@rolldown/binding-linux-arm64-gnu@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz#4dc530aed0b554762ffc1a5e4f016b8be495eea0" - integrity sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig== +"@rolldown/binding-linux-arm64-gnu@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz#419fd6bf612cf348f10528cbcd94ebab9607d8d1" + integrity sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw== -"@rolldown/binding-linux-arm64-musl@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz#56043cdf4768ea3184bb08a3f45b24f183003877" - integrity sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw== +"@rolldown/binding-linux-arm64-musl@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz#fcc6918696bb76844877e1e4930a18fd0d374069" + integrity sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q== -"@rolldown/binding-linux-ppc64-gnu@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz#92eba998d5cd9e4cfc8b95407b4b80f46dacadf4" - integrity sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA== +"@rolldown/binding-linux-ppc64-gnu@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz#32aecb7c8dae5d4f2a8cde57a058ec86991542f8" + integrity sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg== -"@rolldown/binding-linux-s390x-gnu@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz#50b0e95dc3c22af1ecd1669ad7e6030e6860819e" - integrity sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ== +"@rolldown/binding-linux-s390x-gnu@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz#bed9346ea81e6bb8b93cf11f5d88b77db890b763" + integrity sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg== -"@rolldown/binding-linux-x64-gnu@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz#d9fd5af004a373a2d26a09ce8fb66bd0927cbc0b" - integrity sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ== +"@rolldown/binding-linux-x64-gnu@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz#64c2d26f75dffd9b5a1f97557a00ae77250c8cb7" + integrity sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg== -"@rolldown/binding-linux-x64-musl@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz#c0ad80adf4d4df430d0ec309e97924db85065c3c" - integrity sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw== +"@rolldown/binding-linux-x64-musl@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz#5a45132e8a47659eeaaf3b540c2954a97c860ff3" + integrity sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow== -"@rolldown/binding-openharmony-arm64@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz#341fc254ef7759f865bb219beb5cea6f5c9a44f6" - integrity sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w== +"@rolldown/binding-openharmony-arm64@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz#290513068c55e849dc8457a32afee1d7b0acb309" + integrity sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg== -"@rolldown/binding-wasm32-wasi@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz#65f9389f74168fd54e67b12d0630fb90348051f0" - integrity sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ== +"@rolldown/binding-wasm32-wasi@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz#3d9972dbf1a953d3c7afaa4a0f20ef2b2e39f31b" + integrity sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg== dependencies: "@emnapi/core" "1.10.0" "@emnapi/runtime" "1.10.0" "@napi-rs/wasm-runtime" "^1.1.4" -"@rolldown/binding-win32-arm64-msvc@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz#17e80f3402308f12c76ff4172768d51715b522ac" - integrity sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A== +"@rolldown/binding-win32-arm64-msvc@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz#a004ab607a16d6f03bcb555728ff888af75773ad" + integrity sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g== -"@rolldown/binding-win32-x64-msvc@1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz#875cfa37f26d11692dbe28b8331499c97aa99d1f" - integrity sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ== +"@rolldown/binding-win32-x64-msvc@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz#e2a25b34691a1cc8a1209d7de709063026dd0cdb" + integrity sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA== "@rolldown/plugin-babel@^0.2.3": version "0.2.3" @@ -2208,31 +2199,31 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== -"@smithy/core@^3.24.5": - version "3.24.5" - resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.24.5.tgz#396ca5662afc6d83a8f41b7e492e427c48a0924e" - integrity sha512-Kt8phUg45M15EjhYAbZ+fFikYneijLu9Liugz8ZsYz2i8j0hzGv27LWKpEHYRfvj+LyCOSijpcR/2i8RouV+cA== +"@smithy/core@^3.24.6": + version "3.24.6" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.24.6.tgz#72891bad85d577b2e43f30a8fc67adf36d577798" + integrity sha512-wBXDRup6UU97VKyaiRo8AssnfStPtG0oAAfpq/bC0a1YYau8pM86YB4kM6ccoVi1mS8l/UHbn9oDM+7uozr/ug== dependencies: "@aws-crypto/crc32" "5.2.0" - "@smithy/types" "^4.14.2" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@smithy/credential-provider-imds@^4.3.6": - version "4.3.6" - resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.6.tgz#dc1d06447b3208987489133923bcb6bbdd114cc6" - integrity sha512-tHhdiWZfG1ZIh2YcRfPJmY2gHcBmqbAzqm3ER4TIDFYsSEqTD5tICT7cgQ/kI8LRakxp12myOYyK68XPn7MnHw== +"@smithy/credential-provider-imds@^4.3.7": + version "4.3.7" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.7.tgz#f81a2208fc42e134ead1fed4a6c676cc8b89689b" + integrity sha512-xj8gq/bjFABAh6qWPSDCYcY3kzQIm4b561C+YnHH4zGq8rOgzQ3Shk+JGlpUxSd41UGiO6FkLdUCtNX1FAeHgg== dependencies: - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@smithy/fetch-http-handler@^5.4.5": - version "5.4.5" - resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.5.tgz#5087612b4cb22671c13c2b5abe96dfb6518ec9a1" - integrity sha512-SK3VMeH0fibgdTg2QeB+O4p7Yy/2E5HBOHJeC58FshkDdeuX8lOgO7PfjYfLyPLP1ch55j91cQqKBzDS0mRjSQ== +"@smithy/fetch-http-handler@^5.4.6": + version "5.4.6" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.6.tgz#745cdf8b6c333632672f8f48360bde04b8955b47" + integrity sha512-FEwEYJ1jlBKdhe9TPzfghEi1bP55ZeEImlDkEa62bBBYzUcnB6RUCyuiS2mqKt6ZVjUbBgcNhzfIctH+Hevx9g== dependencies: - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" "@smithy/is-array-buffer@^2.2.0": @@ -2242,22 +2233,22 @@ dependencies: tslib "^2.6.2" -"@smithy/node-http-handler@^4.7.5": - version "4.7.5" - resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.7.5.tgz#1fdb6ba04beababb54f20bfc1f74a34370fbdf51" - integrity sha512-3dA9TQ+ybRSZ/m0wnbZhiBy4Dezjgq1Ib/ZZrYTpJDBgpoLLU/SDzZc/g0x0MNAdOJe1wPcM+x2PBRmoOur+Sw== +"@smithy/node-http-handler@^4.7.6": + version "4.7.6" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.7.6.tgz#bc53f75d34106a0cdd867f4e652999ba8a4926fd" + integrity sha512-3fya8i7GrJilQouk4cZJKdy5k8MWQBpjfXrRNaXDedH8r779tr0jcxyH3+yoTmsluc2+vF4S343yFbnvu8ExDQ== dependencies: - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" -"@smithy/signature-v4@^5.4.5": - version "5.4.5" - resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.4.5.tgz#61e369ce381f536f833a4c7c53afbe0175a47556" - integrity sha512-QBJKWGqIknH0dc9LWpfH1mkdokAx6iXYN3UcQ3eY6uIEyScuoQAhfl94ge7ozUy9WgFUdE8xsvwBjaYBbWmPNA== +"@smithy/signature-v4@^5.4.6": + version "5.4.6" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.4.6.tgz#5d2b98aa10e629b6aef36f2289226df81ba4c98e" + integrity sha512-Ojg4B6oIDlIr1R86xCDJt1zJWnYa0VINmqdjfe9qxWjdRivHalZ3iSlQgVqYbW0MdpFOC5XfHEWsnbmdnpIILQ== dependencies: - "@smithy/core" "^3.24.5" - "@smithy/types" "^4.14.2" + "@smithy/core" "^3.24.6" + "@smithy/types" "^4.14.3" tslib "^2.6.2" "@smithy/types@^4.12.0": @@ -2267,10 +2258,10 @@ dependencies: tslib "^2.6.2" -"@smithy/types@^4.14.2": - version "4.14.2" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.14.2.tgz#6034ff1e0e52bfb7d744ac371b651a8bf21f30f1" - integrity sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw== +"@smithy/types@^4.14.3": + version "4.14.3" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.14.3.tgz#784e6d556231645744edf3fea85daaac9054eb40" + integrity sha512-YupL0ZWmFtJexUN2cHzkvvF/b9pKrtAIfT1o7/oY/Ppu8IYeZ+lDPM5vZdQJaSeA132dJCqojjGC9NhXeF71VQ== dependencies: tslib "^2.6.2" @@ -2693,10 +2684,10 @@ dependencies: undici-types "~7.16.0" -"@types/node@^25.9.1": - version "25.9.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.1.tgz#3bda556db500ae4319c08e7fc9ab94f19013ba0b" - integrity sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg== +"@types/node@^25.9.3": + version "25.9.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-25.9.3.tgz#11dfe7a33e68fa5c560f0aa76cc5595621ef26b9" + integrity sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg== dependencies: undici-types ">=7.24.0 <7.24.7" @@ -2705,7 +2696,7 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.2.tgz#5950e50960793055845e956c427fc2b0d70c5239" integrity sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw== -"@types/plist@^3.0.1", "@types/plist@^3.0.5": +"@types/plist@^3.0.5": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/plist/-/plist-3.0.5.tgz#9a0c49c0f9886c8c8696a7904dd703f6284036e0" integrity sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA== @@ -2742,10 +2733,10 @@ dependencies: csstype "^3.2.2" -"@types/react@^19.2.15": - version "19.2.15" - resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.15.tgz#9e2c6a4251a290f5c525c3143caa872fa6e01e0d" - integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q== +"@types/react@^19.2.17": + version "19.2.17" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.2.17.tgz#dccac365baa0f1734ec270ff4b51c89465e8dc7f" + integrity sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw== dependencies: csstype "^3.2.2" @@ -2830,11 +2821,6 @@ dependencies: uuid "*" -"@types/verror@^1.10.3": - version "1.10.11" - resolved "https://registry.yarnpkg.com/@types/verror/-/verror-1.10.11.tgz#d3d6b418978c8aa202d41e5bb3483227b6ecc1bb" - integrity sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg== - "@types/wrap-ansi@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz#18b97a972f94f60a679fd5c796d96421b9abb9fd" @@ -2854,100 +2840,100 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.0.tgz#8fc1e0a950c43270eaf0212dc060f7edaa42f9cf" - integrity sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw== +"@typescript-eslint/eslint-plugin@^8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.0.tgz#db20271974b94a3a54d3b9544e5f5b3481448400" + integrity sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw== dependencies: "@eslint-community/regexpp" "^4.12.2" - "@typescript-eslint/scope-manager" "8.60.0" - "@typescript-eslint/type-utils" "8.60.0" - "@typescript-eslint/utils" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/scope-manager" "8.61.0" + "@typescript-eslint/type-utils" "8.61.0" + "@typescript-eslint/utils" "8.61.0" + "@typescript-eslint/visitor-keys" "8.61.0" ignore "^7.0.5" natural-compare "^1.4.0" ts-api-utils "^2.5.0" -"@typescript-eslint/parser@^8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.60.0.tgz#38d611b8e658cb10850d4975e8a175a222fbcd6a" - integrity sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg== +"@typescript-eslint/parser@^8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.61.0.tgz#1afe73c9ccce16b7a26d6b95f9400b0ccc34af87" + integrity sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w== dependencies: - "@typescript-eslint/scope-manager" "8.60.0" - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/scope-manager" "8.61.0" + "@typescript-eslint/types" "8.61.0" + "@typescript-eslint/typescript-estree" "8.61.0" + "@typescript-eslint/visitor-keys" "8.61.0" debug "^4.4.3" -"@typescript-eslint/project-service@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.60.0.tgz#b82ab12e64d005d0c7163d1240c432381f1bde0f" - integrity sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg== +"@typescript-eslint/project-service@8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/project-service/-/project-service-8.61.0.tgz#417a2feac32e8ebd336d63f068c3b42b736ea1ac" + integrity sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA== dependencies: - "@typescript-eslint/tsconfig-utils" "^8.60.0" - "@typescript-eslint/types" "^8.60.0" + "@typescript-eslint/tsconfig-utils" "^8.61.0" + "@typescript-eslint/types" "^8.61.0" debug "^4.4.3" -"@typescript-eslint/scope-manager@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.60.0.tgz#7617a4617c043fe235dcf066f9a40f106cfd2fd5" - integrity sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw== +"@typescript-eslint/scope-manager@8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.61.0.tgz#93c2520d05653fe65eb9ee98efc74fd0134a7852" + integrity sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA== dependencies: - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/types" "8.61.0" + "@typescript-eslint/visitor-keys" "8.61.0" -"@typescript-eslint/tsconfig-utils@8.60.0", "@typescript-eslint/tsconfig-utils@^8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.0.tgz#3af78c48956227a407dea9626b8db8ca53f130d2" - integrity sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ== +"@typescript-eslint/tsconfig-utils@8.61.0", "@typescript-eslint/tsconfig-utils@^8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.0.tgz#05d6e3ff20001674ebcd22d03dac29ee448043ba" + integrity sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ== -"@typescript-eslint/type-utils@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.60.0.tgz#6971a61bc4f3a1b2df45dcc14e26a43a88a4cb6a" - integrity sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg== +"@typescript-eslint/type-utils@8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.61.0.tgz#50219b57e6b89cecfb1a15f093b15ec9ee019974" + integrity sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A== dependencies: - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" - "@typescript-eslint/utils" "8.60.0" + "@typescript-eslint/types" "8.61.0" + "@typescript-eslint/typescript-estree" "8.61.0" + "@typescript-eslint/utils" "8.61.0" debug "^4.4.3" ts-api-utils "^2.5.0" -"@typescript-eslint/types@8.60.0", "@typescript-eslint/types@^8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.60.0.tgz#e77ad768e933263b1960b2fe79de75fe1cc6e7db" - integrity sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA== +"@typescript-eslint/types@8.61.0", "@typescript-eslint/types@^8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.61.0.tgz#0ddb46e012a4288292950bdd253db42f278ce64d" + integrity sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg== -"@typescript-eslint/typescript-estree@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.0.tgz#c102196a44414481190041c99eea1d854e66001b" - integrity sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g== +"@typescript-eslint/typescript-estree@8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.0.tgz#98ca47260bbf627fc28f018b3a0abf00e3090690" + integrity sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA== dependencies: - "@typescript-eslint/project-service" "8.60.0" - "@typescript-eslint/tsconfig-utils" "8.60.0" - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/visitor-keys" "8.60.0" + "@typescript-eslint/project-service" "8.61.0" + "@typescript-eslint/tsconfig-utils" "8.61.0" + "@typescript-eslint/types" "8.61.0" + "@typescript-eslint/visitor-keys" "8.61.0" debug "^4.4.3" minimatch "^10.2.2" semver "^7.7.3" tinyglobby "^0.2.15" ts-api-utils "^2.5.0" -"@typescript-eslint/utils@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.60.0.tgz#6110cddaef87606ae4ca6f8bf81bb5949fc8e098" - integrity sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA== +"@typescript-eslint/utils@8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.61.0.tgz#ed3546a052787e84ea6c5064d0919fc5eea8522f" + integrity sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA== dependencies: "@eslint-community/eslint-utils" "^4.9.1" - "@typescript-eslint/scope-manager" "8.60.0" - "@typescript-eslint/types" "8.60.0" - "@typescript-eslint/typescript-estree" "8.60.0" + "@typescript-eslint/scope-manager" "8.61.0" + "@typescript-eslint/types" "8.61.0" + "@typescript-eslint/typescript-estree" "8.61.0" -"@typescript-eslint/visitor-keys@8.60.0": - version "8.60.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.0.tgz#f2c41eedd3d7b03b808369fb2e3fb40a93783ec2" - integrity sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg== +"@typescript-eslint/visitor-keys@8.61.0": + version "8.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.0.tgz#39b4e1ab8936d23bea973d39fd092f9aa21f275e" + integrity sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ== dependencies: - "@typescript-eslint/types" "8.60.0" + "@typescript-eslint/types" "8.61.0" eslint-visitor-keys "^5.0.0" "@ungap/structured-clone@^1.0.0": @@ -3190,6 +3176,11 @@ acorn@^8.16.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== +acorn@^8.17.0: + version "8.17.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.17.0.tgz#1785adb84faf8d8add10369b93826fc2bd08f1fe" + integrity sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg== + adm-zip@^0.5.16: version "0.5.16" resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.16.tgz#0b5e4c779f07dedea5805cdccb1147071d94a909" @@ -3226,11 +3217,6 @@ ajv-formats@^3.0.1: dependencies: ajv "^8.0.0" -ajv-keywords@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - ajv-keywords@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" @@ -3238,16 +3224,6 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.0: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - 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" - ajv@^6.14.0: version "6.14.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.14.0.tgz#fd067713e228210636ebb08c60bd3765d6dbe73a" @@ -3268,6 +3244,16 @@ ajv@^8.0.0, ajv@^8.17.1, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" +ajv@^8.18.0: + version "8.20.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.20.0.tgz#304b3636add88ba7d936760dd50ece006dea95f9" + integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-escapes@^4.3.2: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -3304,36 +3290,34 @@ ansi-styles@^6.0.0, ansi-styles@^6.1.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== -app-builder-bin@5.0.0-alpha.12: - version "5.0.0-alpha.12" - resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-5.0.0-alpha.12.tgz#2daf82f8badc698e0adcc95ba36af4ff0650dc80" - integrity sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w== - -app-builder-lib@26.8.1: - version "26.8.1" - resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-26.8.1.tgz#315c893bf1f5882cc6cd174cfcd00535dbb76786" - integrity sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw== +app-builder-lib@26.15.3: + version "26.15.3" + resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-26.15.3.tgz#0fa6c965e307bb6d86226407c750a45b3fd6b1bf" + integrity sha512-2VnyWkqsP5v5XbBhL3tD5Syx8iNPBYsoU7kY4S2fz7wg8Rj/nztWKCUzGKaFRTv0Xwf3/H058CR1Kvtd/3lRow== dependencies: - "@develar/schema-utils" "~2.6.5" "@electron/asar" "3.4.1" "@electron/fuses" "^1.8.0" "@electron/get" "^3.0.0" "@electron/notarize" "2.5.0" "@electron/osx-sign" "1.3.3" - "@electron/rebuild" "^4.0.3" + "@electron/rebuild" "^4.0.4" "@electron/universal" "2.0.3" "@malept/flatpak-bundler" "^0.4.0" + "@noble/hashes" "^2.2.0" + "@peculiar/webcrypto" "^1.7.1" "@types/fs-extra" "9.0.13" + ajv "^8.18.0" + asn1js "^3.0.10" async-exit-hook "^2.0.1" - builder-util "26.8.1" - builder-util-runtime "9.5.1" + builder-util "26.15.3" + builder-util-runtime "9.7.0" chromium-pickle-js "^0.2.0" ci-info "4.3.1" debug "^4.3.4" dotenv "^16.4.5" dotenv-expand "^11.0.6" ejs "^3.1.8" - electron-publish "26.8.1" + electron-publish "26.15.3" fs-extra "^10.1.0" hosted-git-info "^4.1.0" isbinaryfile "^5.0.0" @@ -3341,7 +3325,8 @@ app-builder-lib@26.8.1: js-yaml "^4.1.0" json5 "^2.2.3" lazy-val "^1.0.5" - minimatch "^10.0.3" + minimatch "^10.2.5" + pkijs "^3.4.0" plist "3.1.0" proper-lockfile "^4.1.2" resedit "^1.7.0" @@ -3349,6 +3334,7 @@ app-builder-lib@26.8.1: tar "^7.5.7" temp-file "^3.4.0" tiny-async-pool "1.3.0" + unzipper "^0.12.3" which "^5.0.0" appdmg@^0.6.4: @@ -3455,15 +3441,14 @@ asn1.js@^4.10.1: inherits "^2.0.1" minimalistic-assert "^1.0.0" -assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== +asn1js@^3.0.10, asn1js@^3.0.6: + version "3.0.10" + resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.10.tgz#df26c874c8a8b41ca605efea47b2ad07551013dd" + integrity sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg== + dependencies: + pvtsutils "^1.3.6" + pvutils "^1.1.5" + tslib "^2.8.1" async-exit-hook@^2.0.1: version "2.0.1" @@ -3515,10 +3500,15 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" -axios@^1.16.1: - version "1.16.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.16.1.tgz#517e29291d19d6e8cf919ff264f4fe157261ba12" - integrity sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A== +aws4@^1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.13.2.tgz#0aa167216965ac9474ccfa83892cfb6b3e1e52ef" + integrity sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw== + +axios@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.17.0.tgz#ae5a1164a4f719942cd73c67e6a3f62d3ccb8f2b" + integrity sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw== dependencies: follow-redirects "^1.16.0" form-data "^4.0.5" @@ -3565,7 +3555,7 @@ balanced-match@^4.0.2: dependencies: to-data-view "^1.1.0" -base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -3587,7 +3577,7 @@ bl@~0.8.1: dependencies: readable-stream "~1.0.26" -bluebird@^3.1.1: +bluebird@^3.1.1, bluebird@~3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -3646,6 +3636,13 @@ brace-expansion@^5.0.2: dependencies: balanced-match "^4.0.2" +brace-expansion@^5.0.5: + version "5.0.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.6.tgz#ec68fe0a641a29d8711579caf641d05bae1f2285" + integrity sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g== + dependencies: + balanced-match "^4.0.2" + braces@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -3763,14 +3760,6 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== -buffer@^5.1.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" - integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.1.13" - bufferutil@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.1.0.tgz#a4623541dd23867626bb08a051ec0d2ec0b70294" @@ -3778,23 +3767,21 @@ bufferutil@^4.1.0: dependencies: node-gyp-build "^4.3.0" -builder-util-runtime@9.5.1: - version "9.5.1" - resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz#74125fb374d1ecbf472ae1787485485ff7619702" - integrity sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ== +builder-util-runtime@9.7.0: + version "9.7.0" + resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-9.7.0.tgz#c86fba303684e877daee15c29eede81987166fef" + integrity sha512-g/kR520giAFYkSXTzcmF3kqQq7wi8F6N6SzeDgZrqTBN+VHdmgWOyTdD1yD7AATDId/yXLvuP34CxW46/BwCdw== dependencies: debug "^4.3.4" sax "^1.2.4" -builder-util@26.8.1: - version "26.8.1" - resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-26.8.1.tgz#50fdfc2d4ffeb6f739af363b5bd60c49c95d4170" - integrity sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw== +builder-util@26.15.3: + version "26.15.3" + resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-26.15.3.tgz#1eeab37f0d4a6421fe18ce9db38ced58881d9519" + integrity sha512-q2hn7Mbo2nFNkVekPiHFx6Nfo3hURmES3tfBn+k5Pqxl2RkmP3QGqZUhH/q9Pch/4G05NRhPjDlVj1O8q4Txvw== dependencies: - "7zip-bin" "~5.2.0" "@types/debug" "^4.1.6" - app-builder-bin "5.0.0-alpha.12" - builder-util-runtime "9.5.1" + builder-util-runtime "9.7.0" chalk "^4.1.2" cross-spawn "^7.0.6" debug "^4.3.4" @@ -3808,6 +3795,11 @@ builder-util@26.8.1: temp-file "^3.4.0" tiny-async-pool "1.3.0" +bytestreamjs@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/bytestreamjs/-/bytestreamjs-2.0.1.tgz#a32947c7ce389a6fa11a09a9a563d0a45889535e" + integrity sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ== + cacheable-lookup@^5.0.3: version "5.0.4" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005" @@ -3955,14 +3947,6 @@ cli-cursor@^4.0.0: dependencies: restore-cursor "^4.0.0" -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - cli-truncate@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-3.1.0.tgz#3f23ab12535e3d73e839bb43e73c9de487db1389" @@ -4120,11 +4104,6 @@ cookie@~0.7.2: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" @@ -4159,13 +4138,6 @@ cosmiconfig@^8.1.3: parse-json "^5.2.0" path-type "^4.0.0" -crc@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/crc/-/crc-3.8.0.tgz#ad60269c2c856f8c299e2c4cc0de4556914056c6" - integrity sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ== - dependencies: - buffer "^5.1.0" - create-ecdh@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -4502,32 +4474,15 @@ dir-compare@^4.2.0: minimatch "^3.0.5" p-limit "^3.1.0 " -dmg-builder@26.8.1: - version "26.8.1" - resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-26.8.1.tgz#df99aa790676ac2a2ac0333bbadbef3b6076cb03" - integrity sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg== +dmg-builder@26.15.3: + version "26.15.3" + resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-26.15.3.tgz#d884362e223551ee0dbc96bbb7187b42905378f8" + integrity sha512-O3zJUFUYHJKgzPqioHxfxzBzlSC1eXCSr79gMSBKBP5AgjjpmrydMsMLotEg9fAJF36vdUncb+4ndRNxoPdlSQ== dependencies: - app-builder-lib "26.8.1" - builder-util "26.8.1" + app-builder-lib "26.15.3" + builder-util "26.15.3" fs-extra "^10.1.0" - iconv-lite "^0.6.2" js-yaml "^4.1.0" - optionalDependencies: - dmg-license "^1.0.11" - -dmg-license@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/dmg-license/-/dmg-license-1.0.11.tgz#7b3bc3745d1b52be7506b4ee80cb61df6e4cd79a" - integrity sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q== - dependencies: - "@types/plist" "^3.0.1" - "@types/verror" "^1.10.3" - ajv "^6.10.0" - crc "^3.8.0" - iconv-corefoundation "^1.1.7" - plist "^3.0.4" - smart-buffer "^4.0.2" - verror "^1.10.0" doctrine@^2.1.0: version "2.1.0" @@ -4589,6 +4544,13 @@ dunder-proto@^1.0.0, dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" +duplexer2@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" + integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA== + dependencies: + readable-stream "^2.0.2" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -4601,17 +4563,17 @@ ejs@^3.1.8: dependencies: jake "^10.8.5" -electron-builder@^26.8.1: - version "26.8.1" - resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-26.8.1.tgz#d49056b2fe5d37f0f94aa2eb0e1db38f261fc8c0" - integrity sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw== +electron-builder@^26.15.3: + version "26.15.3" + resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-26.15.3.tgz#0e82828f6829c0b2596338365209170ac0acbcd5" + integrity sha512-a1KM5heqS3gQCZzizXEI8RjJy3QVogULPdeSknt76uLDpBIW/HDGsMg/XgP0riP6PI9COsRvFITKKGDqA8fJxA== dependencies: - app-builder-lib "26.8.1" - builder-util "26.8.1" - builder-util-runtime "9.5.1" + app-builder-lib "26.15.3" + builder-util "26.15.3" + builder-util-runtime "9.7.0" chalk "^4.1.2" ci-info "^4.2.0" - dmg-builder "26.8.1" + dmg-builder "26.15.3" fs-extra "^10.1.0" lazy-val "^1.0.5" simple-update-notifier "2.0.0" @@ -4682,14 +4644,15 @@ electron-installer-redhat@^3.2.0: word-wrap "^1.2.3" yargs "^16.0.2" -electron-publish@26.8.1: - version "26.8.1" - resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.8.1.tgz#6a32fa8eed0d41971dda53072bea06b9932be583" - integrity sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w== +electron-publish@26.15.3: + version "26.15.3" + resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.15.3.tgz#db65642070bf60c7bd26d607441506891d3ad054" + integrity sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q== dependencies: "@types/fs-extra" "^9.0.11" - builder-util "26.8.1" - builder-util-runtime "9.5.1" + aws4 "^1.13.2" + builder-util "26.15.3" + builder-util-runtime "9.7.0" chalk "^4.1.2" form-data "^4.0.5" fs-extra "^10.1.0" @@ -4709,12 +4672,12 @@ electron-to-chromium@^1.5.263: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz#5d84f2df8cdb6bfe7e873706bb21bd4bfb574dc7" integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw== -electron-updater@^6.8.3: - version "6.8.3" - resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.8.3.tgz#bb0c8ef6509e5c67663f6481a729244d1bce21fb" - integrity sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ== +electron-updater@^6.8.9: + version "6.8.9" + resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-6.8.9.tgz#21e3e2400ee3a58b7496f0a023d95d7ab1dae019" + integrity sha512-ZhVxM9iGONUpZGI1FxdMRgJjUFXi7AYGVa5PwKlO1tV1/4zDxQmfKpXOHVztKrd6L9rLcFjERvi1Mf2vxyTkig== dependencies: - builder-util-runtime "9.5.1" + builder-util-runtime "9.7.0" fs-extra "^10.1.0" js-yaml "^4.1.0" lazy-val "^1.0.5" @@ -4745,14 +4708,14 @@ electron@*: "@types/node" "^24.9.0" extract-zip "^2.0.1" -electron@42.3.0: - version "42.3.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-42.3.0.tgz#028d2f64bf69182f36fc74b72f59b48fc143f681" - integrity sha512-9ZiLdRXk+WDxW1OgIUz8J2rIQ5TYU9o629gCOjU48Q3dQiOmym7osWsH5Ubs/Jh4uuFLn6m6SBD2rmRXLAPz9g== +electron@42.4.0: + version "42.4.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-42.4.0.tgz#921e266705600ba499f24d233db57f2cf935e516" + integrity sha512-OXXqh9LD9KxXPv2Fe25EfU9N9AvWTuV6V81sfhQaNvTAXCd9ONA+Q4OWvMe+CmYD6xIwjFxGGtG/ZphDYYC5OQ== dependencies: + "@electron-internal/extract-zip" "^1.0.1" "@electron/get" "^5.0.0" "@types/node" "^24.9.0" - extract-zip "^2.0.1" elliptic@^6.5.3, elliptic@^6.6.1: version "6.6.1" @@ -5266,11 +5229,6 @@ extract-zip@^2.0.0, extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -5491,7 +5449,7 @@ fs-extra@^11.1.0, fs-extra@^11.1.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^11.3.5: +fs-extra@^11.2.0, fs-extra@^11.3.5: version "11.3.5" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.3.5.tgz#07a44eff40bea53e719909a532f91a23bf0769ff" integrity sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg== @@ -5790,7 +5748,7 @@ got@^11.8.5: p-cancelable "^2.0.0" responselike "^2.0.0" -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.6: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -5807,10 +5765,10 @@ graphql-ws@^6.0.8: resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-6.0.8.tgz#6712bb9da8462937e92343e3146726f89424df68" integrity sha512-m3EOaNsUBXwAnkBWbzPfe0Nq8pXUfxsWnolC54sru3FzHvhTZL0Ouf/BoQsaGAXqM+YPerXOJ47BUnmgmoupCw== -graphql@^16.14.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.14.0.tgz#f1128a74b16a34d1245c8436bb07b488d87b83e1" - integrity sha512-BBvQ/406p+4CZbTpCbVPSxfzrZrbnuWSP1ELYgyS6B+hNeKzgrdB4JczCa5VZUBQrDa9hUngm0KnexY6pJRN5Q== +graphql@^16.14.2: + version "16.14.2" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.14.2.tgz#83faf25869e3df727cc855161db5da85b0e5b2c0" + integrity sha512-Chq1s4CY7jmh8gO2qvLIJyfCDIN+EHLFW/9iShnp1z8FjBQMoodWP1kDC36VAMXXIvAjj4ARa7ntfAV2BrjsbA== has-bigints@^1.0.2: version "1.1.0" @@ -6055,18 +6013,10 @@ https-proxy-agent@^7.0.0: agent-base "^7.1.2" debug "4" -i18next@^26.3.0: - version "26.3.0" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-26.3.0.tgz#057c1165589b3f312714ea2a0c3f368c2c7cd84f" - integrity sha512-gHSgGpUXVmuqE2El1W61DmxeyeTlFfZgdJRWMo9jScAn5pu7TuTuiccb1zh3E2J9hEBVGJ23+96x0ieBhfuIHA== - -iconv-corefoundation@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz#31065e6ab2c9272154c8b0821151e2c88f1b002a" - integrity sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ== - dependencies: - cli-truncate "^2.1.0" - node-addon-api "^1.6.3" +i18next@^26.3.1: + version "26.3.1" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-26.3.1.tgz#94762857f5aae0c6282d87f8f0039cbbc9f7b42d" + integrity sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ== iconv-lite@^0.4.24: version "0.4.24" @@ -6075,13 +6025,6 @@ iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - iconv-lite@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.2.tgz#d0bdeac3f12b4835b7359c2ad89c422a4d1cc72e" @@ -6094,11 +6037,6 @@ idb-wrapper@^1.5.0: resolved "https://registry.yarnpkg.com/idb-wrapper/-/idb-wrapper-1.7.2.tgz#8251afd5e77fe95568b1c16152eb44b396767ea2" integrity sha512-zfNREywMuf0NzDo9mVsL0yegjsirJxHpKHvWcyRozIqQy89g0a3U+oBPOCN4cc0oCiOuYgZHimzaW/R46G1Mpg== -ieee754@^1.1.13: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - ignore@^5.2.0: version "5.3.2" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" @@ -7543,7 +7481,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@^10.0.3, minimatch@^10.1.1: +minimatch@^10.1.1: version "10.1.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.1.tgz#e6e61b9b0c1dcab116b5a7d1458e8b6ae9e73a55" integrity sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ== @@ -7564,6 +7502,13 @@ minimatch@^10.2.4: dependencies: brace-expansion "^5.0.2" +minimatch@^10.2.5: + version "10.2.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.5.tgz#bd48687a0be38ed2961399105600f832095861d1" + integrity sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg== + dependencies: + brace-expansion "^5.0.5" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -7700,11 +7645,6 @@ node-abi@^4.2.0: dependencies: semver "^7.6.3" -node-addon-api@^1.6.3: - version "1.7.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" - integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== - node-addon-api@^7.0.0: version "7.1.1" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" @@ -7745,6 +7685,11 @@ node-gyp@12.3.0, node-gyp@^12.2.0: undici "^6.25.0" which "^6.0.0" +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" + integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== + node-releases@^2.0.27: version "2.0.27" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.27.tgz#eedca519205cf20f650f61d56b070db111231e4e" @@ -8158,7 +8103,19 @@ pify@^2.0.0: resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== -plist@3.1.0, plist@^3.0.0, plist@^3.0.4, plist@^3.0.5, plist@^3.1.0: +pkijs@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/pkijs/-/pkijs-3.4.0.tgz#d9164def30ff6d97be2d88966d5e36192499ca9c" + integrity sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw== + dependencies: + "@noble/hashes" "1.4.0" + asn1js "^3.0.6" + bytestreamjs "^2.0.1" + pvtsutils "^1.3.6" + pvutils "^1.1.3" + tslib "^2.8.1" + +plist@3.1.0, plist@^3.0.0, plist@^3.0.5, plist@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== @@ -8206,10 +8163,10 @@ prettier@^3.4.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== -prettier@^3.8.3: - version "3.8.3" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.3.tgz#560f2de55bf01b4c0503bc629d5df99b9a1d09b0" - integrity sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw== +prettier@^3.8.4: + version "3.8.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.4.tgz#f334f013ac04a96676f24dabc23c1c4ae1bae411" + integrity sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q== proc-log@^6.0.0: version "6.1.0" @@ -8312,6 +8269,18 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +pvtsutils@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.6.tgz#ec46e34db7422b9e4fdc5490578c1883657d6001" + integrity sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg== + dependencies: + tslib "^2.8.1" + +pvutils@^1.1.3, pvutils@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.5.tgz#84b0dea4a5d670249aa9800511804ee0b7c2809c" + integrity sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA== + qs@^6.12.3: version "6.14.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159" @@ -8362,10 +8331,10 @@ react-compiler-runtime@^1.0.0: resolved "https://registry.yarnpkg.com/react-compiler-runtime/-/react-compiler-runtime-1.0.0.tgz#ee565c2cd3437a41e7fc31d8ab6c662f6b568fc0" integrity sha512-rRfjYv66HlG8896yPUDONgKzG5BxZD1nV9U6rkm+7VCuvQc903C4MjcoZR4zPw53IKSOX9wMQVpA1IAbRtzQ7w== -react-dom@^19.2.6: - version "19.2.6" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.6.tgz#44a81b0bcca22da814c00847d09d01c8615529b7" - integrity sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g== +react-dom@^19.2.7: + version "19.2.7" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-19.2.7.tgz#0450dc9ae9ddbff76ef196401cd8b8c7fb466ccc" + integrity sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ== dependencies: scheduler "^0.27.0" @@ -8451,17 +8420,17 @@ react-modal@^3.16.3: "@types/use-sync-external-store" "^0.0.6" use-sync-external-store "^1.4.0" -react-router-dom@^7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.16.0.tgz#284a7cd021052aa7d0a9240dca4a02eec24eceb5" - integrity sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA== +react-router-dom@^7.17.0: + version "7.17.0" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-7.17.0.tgz#e77527b4b7862f7b47ff26dd5b9315fb897b82a7" + integrity sha512-fyU2yjGups/hE6Xz0I5ZYbVL8Gx29eCjgpHaRaTaVU+OOAdfRX05KsvyRm0GO8YQwOkhpU3MurW1jyMUJn+zSw== dependencies: - react-router "7.16.0" + react-router "7.17.0" -react-router@7.16.0: - version "7.16.0" - resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.16.0.tgz#fb41536aef2ccc2c7be12ea6be819a1e56eb6343" - integrity sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A== +react-router@7.17.0: + version "7.17.0" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-7.17.0.tgz#88bbe817c6e37ab36faf140623b5d4678bf81e41" + integrity sha512-FDELK7rTMlCHO5+reyXsPlmfr7N1F91lPHsWYfMEGQm/KQ+F4JFM8jGoeQDmDvdTs93Fw9aSilH+uKRb4/jXvQ== dependencies: cookie "^1.0.1" set-cookie-parser "^2.6.0" @@ -8476,10 +8445,10 @@ react-transition-group@^4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^19.2.6: - version "19.2.6" - resolved "https://registry.yarnpkg.com/react/-/react-19.2.6.tgz#3dadb8e12b2a7934c1d5317973e5dce1301f9a4d" - integrity sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q== +react@^19.2.7: + version "19.2.7" + resolved "https://registry.yarnpkg.com/react/-/react-19.2.7.tgz#1f47a1bfc06f8ec885752c6f4af14369a9f8260b" + integrity sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ== read-binary-file-arch@^1.0.6: version "1.0.6" @@ -8515,7 +8484,7 @@ readable-stream@^1.0.26-4: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^2.2.2, readable-stream@^2.3.8: +readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -8796,29 +8765,29 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" -rolldown@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.2.tgz#ea2c786c5f063d08fd22b49e51997f15ec532bbd" - integrity sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g== +rolldown@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/rolldown/-/rolldown-1.0.3.tgz#db88a3008fb0e28230a00423727ce75ba32121ac" + integrity sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g== dependencies: - "@oxc-project/types" "=0.132.0" + "@oxc-project/types" "=0.133.0" "@rolldown/pluginutils" "^1.0.0" optionalDependencies: - "@rolldown/binding-android-arm64" "1.0.2" - "@rolldown/binding-darwin-arm64" "1.0.2" - "@rolldown/binding-darwin-x64" "1.0.2" - "@rolldown/binding-freebsd-x64" "1.0.2" - "@rolldown/binding-linux-arm-gnueabihf" "1.0.2" - "@rolldown/binding-linux-arm64-gnu" "1.0.2" - "@rolldown/binding-linux-arm64-musl" "1.0.2" - "@rolldown/binding-linux-ppc64-gnu" "1.0.2" - "@rolldown/binding-linux-s390x-gnu" "1.0.2" - "@rolldown/binding-linux-x64-gnu" "1.0.2" - "@rolldown/binding-linux-x64-musl" "1.0.2" - "@rolldown/binding-openharmony-arm64" "1.0.2" - "@rolldown/binding-wasm32-wasi" "1.0.2" - "@rolldown/binding-win32-arm64-msvc" "1.0.2" - "@rolldown/binding-win32-x64-msvc" "1.0.2" + "@rolldown/binding-android-arm64" "1.0.3" + "@rolldown/binding-darwin-arm64" "1.0.3" + "@rolldown/binding-darwin-x64" "1.0.3" + "@rolldown/binding-freebsd-x64" "1.0.3" + "@rolldown/binding-linux-arm-gnueabihf" "1.0.3" + "@rolldown/binding-linux-arm64-gnu" "1.0.3" + "@rolldown/binding-linux-arm64-musl" "1.0.3" + "@rolldown/binding-linux-ppc64-gnu" "1.0.3" + "@rolldown/binding-linux-s390x-gnu" "1.0.3" + "@rolldown/binding-linux-x64-gnu" "1.0.3" + "@rolldown/binding-linux-x64-musl" "1.0.3" + "@rolldown/binding-openharmony-arm64" "1.0.3" + "@rolldown/binding-wasm32-wasi" "1.0.3" + "@rolldown/binding-win32-arm64-msvc" "1.0.3" + "@rolldown/binding-win32-x64-msvc" "1.0.3" run-parallel@^1.1.9: version "1.2.0" @@ -8884,10 +8853,10 @@ sanitize-filename@^1.6.3: dependencies: truncate-utf8-bytes "^1.0.0" -sass@^1.100.0: - version "1.100.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.100.0.tgz#b4cab1bed286fe22ac6c879c514f71cd36aa06c8" - integrity sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ== +sass@^1.101.0: + version "1.101.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.101.0.tgz#c2db5bbf2f956be7277f6223b899d0d4be3c899b" + integrity sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw== dependencies: chokidar "^5.0.0" immutable "^5.1.5" @@ -8935,10 +8904,10 @@ semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semve resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== -semver@^7.8.1: - version "7.8.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.1.tgz#bf4970b5e70fda0686363cc18bfe8805d5ed957e" - integrity sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg== +semver@^7.8.4: + version "7.8.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.4.tgz#c73eceebae0616934be8dff28a7fd70757c8e696" + integrity sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA== semver@~2.3.1: version "2.3.2" @@ -9085,15 +9054,6 @@ simple-update-notifier@2.0.0: dependencies: semver "^7.5.3" -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - slice-ansi@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-5.0.0.tgz#b73063c57aa96f9cd881654b15294d95d285c42a" @@ -9102,11 +9062,6 @@ slice-ansi@^5.0.0: ansi-styles "^6.0.0" is-fullwidth-code-point "^4.0.0" -smart-buffer@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" - integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== - snake-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" @@ -9464,10 +9419,10 @@ tar@*: minizlib "^3.1.0" yallist "^5.0.0" -tar@^7.5.15: - version "7.5.15" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.15.tgz#afe6d1316cddf614a566e3813e42fe01aed46fee" - integrity sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ== +tar@^7.5.16: + version "7.5.16" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.16.tgz#f11e063afed4554f758049d082909e37d6b53ced" + integrity sha512-56adEpPMouktRlBLXiaYFFzZ/3+JXa8P9n7WbR+ibIjtviN55mEaOkiysCnPnWm+7kkui1Dn8J9l+g6zV8731w== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" @@ -9574,10 +9529,10 @@ tinyglobby@^0.2.12, tinyglobby@^0.2.15: fdir "^6.5.0" picomatch "^4.0.3" -tinyglobby@^0.2.16: - version "0.2.16" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.16.tgz#1c3b7eb953fce42b226bc5a1ee06428281aff3d6" - integrity sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg== +tinyglobby@^0.2.17: + version "0.2.17" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.17.tgz#562a9a6c9eb2b3b123d39719f9af5bb44fcd7631" + integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g== dependencies: fdir "^6.5.0" picomatch "^4.0.4" @@ -9697,7 +9652,7 @@ tsconfig-paths@^3.15.0: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.2: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0, tslib@^2.6.2, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -9916,6 +9871,17 @@ unorm@^1.4.1: resolved "https://registry.yarnpkg.com/unorm/-/unorm-1.6.0.tgz#029b289661fba714f1a9af439eb51d9b16c205af" integrity sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA== +unzipper@^0.12.3: + version "0.12.3" + resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.12.3.tgz#31958f5eed7368ed8f57deae547e5a673e984f87" + integrity sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA== + dependencies: + bluebird "~3.7.2" + duplexer2 "~0.1.4" + fs-extra "^11.2.0" + graceful-fs "^4.2.2" + node-int64 "^0.4.0" + update-browserslist-db@^1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz#64d76db58713136acbeb4c49114366cc6cc2e80d" @@ -9997,15 +9963,6 @@ vary@^1: resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -verror@^1.10.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.1.tgz#4bf09eeccf4563b109ed4b3d458380c972b0cdeb" - integrity sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - vfile-location@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-5.0.3.tgz#cb9eacd20f2b6426d19451e0eafa3d0a846225c3" @@ -10059,16 +10016,16 @@ vite-plugin-svgr@^5.2.0: "@svgr/core" "^8.1.0" "@svgr/plugin-jsx" "^8.1.0" -vite@^8.0.14: - version "8.0.14" - resolved "https://registry.yarnpkg.com/vite/-/vite-8.0.14.tgz#da5d8d1f63dbd106385cbe9c211acbc7a7a5b192" - integrity sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw== +vite@^8.0.16: + version "8.0.16" + resolved "https://registry.yarnpkg.com/vite/-/vite-8.0.16.tgz#ae073866c06563d6634a90169a496e11bd84f1a6" + integrity sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw== dependencies: lightningcss "^1.32.0" picomatch "^4.0.4" postcss "^8.5.15" - rolldown "1.0.2" - tinyglobby "^0.2.16" + rolldown "1.0.3" + tinyglobby "^0.2.17" optionalDependencies: fsevents "~2.3.3" @@ -10097,6 +10054,17 @@ web-namespaces@^2.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-2.0.1.tgz#1010ff7c650eccb2592cebeeaf9a1b253fd40692" integrity sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ== +webcrypto-core@^1.9.2: + version "1.9.2" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.9.2.tgz#22dd6a0fb55217b1301eab7151d01d5c68e52ccb" + integrity sha512-gsXecm82UQNlTBURJGuqOWy1Ww08S3kZUcr3aOJS02Pk0xLtkfeUAVC0u0xhgdonFme80edSJUIJyuvL/7250Q== + dependencies: + "@peculiar/asn1-schema" "^2.7.0" + "@peculiar/json-schema" "^1.1.12" + "@peculiar/utils" "^2.0.2" + asn1js "^3.0.10" + tslib "^2.8.1" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" From a79c4f6a3d3c7f03545e36932ca6987d126ce5ec Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Sat, 13 Jun 2026 16:44:34 +0300 Subject: [PATCH 15/43] feat: notify users about active giveaways Took 20 minutes --- package.json | 4 +- src/common/appConfig.ts | 2 +- src/locales/en/renderer.json | 2 + src/locales/ru/renderer.json | 2 + .../providers/notifications/presentation.ts | 24 +++++- .../useNotificationsController.ts | 1 + .../shared/api/subscriptionGiveaways.ts | 83 +++++++++++++++++++ .../widgets/layout/NotificationsBell.tsx | 38 +++++---- .../layout/SubscriptionGiveawaysButton.tsx | 68 ++++++++++++++- .../widgets/layout/header.module.scss | 21 ++++- .../modals/SubscriptionGiveawaysModal.tsx | 59 ++++++------- yarn.lock | 16 ++-- 12 files changed, 260 insertions(+), 60 deletions(-) create mode 100644 src/renderer/shared/api/subscriptionGiveaways.ts diff --git a/package.json b/package.json index 5d244aa5..65fd69d5 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "keywords": [], "license": "SEE LICENSE IN LICENSE", "devDependencies": { - "@aws-sdk/client-s3": "^3.1067.0", + "@aws-sdk/client-s3": "^3.1068.0", "@babel/core": "7.29.7", "@electron-forge/cli": "^7.11.2", "@electron-forge/maker-deb": "^7.11.2", @@ -65,7 +65,7 @@ "babel-plugin-react-compiler": "^1.0.0", "electron": "42.4.0", "electron-builder": "^26.15.3", - "eslint": "^10.4.1", + "eslint": "^10.5.0", "eslint-plugin-import": "^2.32.0", "iconv-lite": "^0.7.2", "prettier": "^3.8.4", diff --git a/src/common/appConfig.ts b/src/common/appConfig.ts index 4675ce24..23d23e28 100644 --- a/src/common/appConfig.ts +++ b/src/common/appConfig.ts @@ -4,7 +4,7 @@ export const branch = "beta" const PORT = '2007' const MAIN_PORT = 2007 -const AUTONOMOUS_MUSIC_VERSION = '5.90.0' +const AUTONOMOUS_MUSIC_VERSION = '5.105.3' const config = { PORT, diff --git a/src/locales/en/renderer.json b/src/locales/en/renderer.json index de2b92f6..868c544d 100644 --- a/src/locales/en/renderer.json +++ b/src/locales/en/renderer.json @@ -561,6 +561,8 @@ "localizationApprovedBody": "Your changes passed review and are already published.", "localizationRejectedTitle": "Localization suggestion rejected", "localizationRejectedBody": "Open the notification to review the feedback.", + "giveawayStartedTitle": "A new giveaway has started", + "giveawayStartedBody": "The \"{{giveaway}}\" giveaway is live. You can enter until {{date}}.", "giveawayWonTitle": "You won a giveaway", "giveawayWonBody": "You won \"{{prize}}\" in \"{{giveaway}}\".", "giveawayWonPrize": "{{name}} for {{months}} mo", diff --git a/src/locales/ru/renderer.json b/src/locales/ru/renderer.json index 4be9b63a..a231e2b1 100644 --- a/src/locales/ru/renderer.json +++ b/src/locales/ru/renderer.json @@ -561,6 +561,8 @@ "localizationApprovedBody": "Изменения прошли проверку и уже опубликованы.", "localizationRejectedTitle": "Предложение по локализации отклонено", "localizationRejectedBody": "Открой уведомление, чтобы посмотреть детали ревью.", + "giveawayStartedTitle": "Начался новый розыгрыш", + "giveawayStartedBody": "Розыгрыш «{{giveaway}}» уже идёт. Участвовать можно до {{date}}.", "giveawayWonTitle": "Вы выиграли розыгрыш", "giveawayWonBody": "Вы выиграли «{{prize}}» в розыгрыше «{{giveaway}}».", "giveawayWonPrize": "{{name}} на {{months}} мес.", diff --git a/src/renderer/app/providers/notifications/presentation.ts b/src/renderer/app/providers/notifications/presentation.ts index 7bcc597d..50fafce1 100644 --- a/src/renderer/app/providers/notifications/presentation.ts +++ b/src/renderer/app/providers/notifications/presentation.ts @@ -102,8 +102,24 @@ export function getNotificationPresentation(notification: NotificationItem): Not } } + case 'subscription.giveaway.started': { + const giveawayTitle = String(notification.payload?.['giveawayTitle'] || t('header.notifications.items.giveawayFallbackTitle')) + return { + tone: 'success', + title: t('header.notifications.items.giveawayStartedTitle'), + body: t('header.notifications.items.giveawayStartedBody', { + giveaway: giveawayTitle, + date: formatPayloadDate(notification.payload?.['endsAt']), + }), + } + } + case 'subscription.purchase.succeeded': { - const planName = String(notification.payload?.['planName'] || notification.payload?.['subscriptionName'] || t('header.notifications.items.subscriptionFallbackPlan')) + const planName = String( + notification.payload?.['planName'] || + notification.payload?.['subscriptionName'] || + t('header.notifications.items.subscriptionFallbackPlan'), + ) return { tone: 'success', title: t('header.notifications.items.subscriptionPurchaseSucceededTitle'), @@ -115,7 +131,11 @@ export function getNotificationPresentation(notification: NotificationItem): Not } case 'subscription.expiring.soon': { - const planName = String(notification.payload?.['subscriptionName'] || notification.payload?.['planName'] || t('header.notifications.items.subscriptionFallbackPlan')) + const planName = String( + notification.payload?.['subscriptionName'] || + notification.payload?.['planName'] || + t('header.notifications.items.subscriptionFallbackPlan'), + ) return { tone: 'warning', title: t('header.notifications.items.subscriptionExpiringSoonTitle'), diff --git a/src/renderer/app/providers/notifications/useNotificationsController.ts b/src/renderer/app/providers/notifications/useNotificationsController.ts index 16ad8737..8594b0e2 100644 --- a/src/renderer/app/providers/notifications/useNotificationsController.ts +++ b/src/renderer/app/providers/notifications/useNotificationsController.ts @@ -45,6 +45,7 @@ type NotificationsControllerResult = { const MAX_NOTIFICATIONS = 20 const REALTIME_TOAST_NOTIFICATION_TYPES = new Set([ 'achievement.completed', + 'subscription.giveaway.started', 'subscription.giveaway.won', 'subscription.purchase.succeeded', 'subscription.expiring.soon', diff --git a/src/renderer/shared/api/subscriptionGiveaways.ts b/src/renderer/shared/api/subscriptionGiveaways.ts new file mode 100644 index 00000000..b12901bf --- /dev/null +++ b/src/renderer/shared/api/subscriptionGiveaways.ts @@ -0,0 +1,83 @@ +import rendererHttpClient from '@shared/api/http/client' +import getUserToken from '@shared/lib/auth/getUserToken' + +export type SubscriptionGiveaway = { + uuid: string + title: string + description?: string | null + planCode: string + durationMonths?: number | null + winnersCount: number + status: string + startsAt: string + endsAt: string +} + +type SubscriptionGiveawaysResponse = { + giveaways?: SubscriptionGiveaway[] + ok?: boolean +} + +type EnteredSubscriptionGiveawaysResponse = { + giveawayIds?: string[] + ok?: boolean +} + +export type SubscriptionGiveawaysSnapshot = { + enteredIds: Set + giveaways: SubscriptionGiveaway[] +} + +const CACHE_TTL_MS = 5_000 + +let cachedSnapshot: { authKey: string; fetchedAt: number; value: SubscriptionGiveawaysSnapshot } | null = null +let snapshotRequest: { authKey: string; promise: Promise } | null = null + +export async function loadSubscriptionGiveawaysSnapshot(options?: { force?: boolean }): Promise { + const authKey = getUserToken() ?? '' + const currentRequest = snapshotRequest + if (currentRequest && currentRequest.authKey === authKey) { + return currentRequest.promise + } + + const currentSnapshot = cachedSnapshot + if (!options?.force && currentSnapshot && currentSnapshot.authKey === authKey && Date.now() - currentSnapshot.fetchedAt < CACHE_TTL_MS) { + return currentSnapshot.value + } + + const request = Promise.all([ + rendererHttpClient.get('/subscription/giveaways'), + rendererHttpClient.get('/subscription/giveaways/entered', { auth: true }), + ]).then(([giveawaysResponse, enteredResponse]) => { + if (!giveawaysResponse.ok || !giveawaysResponse.data?.ok || !Array.isArray(giveawaysResponse.data.giveaways)) { + throw new Error('Failed to load subscription giveaways') + } + if (!enteredResponse.ok || !enteredResponse.data?.ok || !Array.isArray(enteredResponse.data.giveawayIds)) { + throw new Error('Failed to load entered subscription giveaways') + } + + const value = { + giveaways: giveawaysResponse.data.giveaways, + enteredIds: new Set(enteredResponse.data.giveawayIds), + } + cachedSnapshot = { + authKey, + fetchedAt: Date.now(), + value, + } + return value + }) + snapshotRequest = { authKey, promise: request } + + try { + return await request + } finally { + if (snapshotRequest?.promise === request) { + snapshotRequest = null + } + } +} + +export function invalidateSubscriptionGiveawaysSnapshot(): void { + cachedSnapshot = null +} diff --git a/src/renderer/widgets/layout/NotificationsBell.tsx b/src/renderer/widgets/layout/NotificationsBell.tsx index 58db3de8..8f19e528 100644 --- a/src/renderer/widgets/layout/NotificationsBell.tsx +++ b/src/renderer/widgets/layout/NotificationsBell.tsx @@ -6,6 +6,7 @@ import config from '@common/appConfig' import { useTranslation } from 'react-i18next' import { useNavigate } from 'react-router-dom' import { useNotifications } from '@app/providers/notifications' +import { useModalContext } from '@app/providers/modal' import { getNotificationPresentation, NotificationTone } from '@app/providers/notifications/presentation' import type { NotificationItem } from '@app/providers/notifications/types' import Loader from '@shared/ui/PSUI/Loader' @@ -56,6 +57,7 @@ const NotificationsBell: React.FC = () => { const { t } = useTranslation() const navigate = useNavigate() const notificationsContext = useNotifications() + const { Modals, openModal } = useModalContext() const [isOpen, setOpen] = useState(false) const rootRef = useRef(null) const notificationItems = notificationsContext.notifications @@ -128,22 +130,30 @@ const NotificationsBell: React.FC = () => { return read ? styles.notificationItemWarning : styles.notificationItemUnreadWarning }, []) - const openNotificationTarget = useCallback((notification: NotificationItem) => { - const internalPath = getInternalNotificationPath(notification.link) - if (internalPath) { - void navigate(internalPath) - return - } + const openNotificationTarget = useCallback( + (notification: NotificationItem) => { + if (notification.type === 'subscription.giveaway.started') { + openModal(Modals.SUBSCRIPTION_GIVEAWAYS) + return + } - const rawLink = notification.link?.trim() - const externalUrl = !rawLink - ? `${config.WEBSITE_URL}/contribute/localization` - : /^https?:\/\//i.test(rawLink) - ? rawLink - : `${config.WEBSITE_URL}${rawLink.startsWith('/') ? rawLink : `/${rawLink}`}` + const internalPath = getInternalNotificationPath(notification.link) + if (internalPath) { + void navigate(internalPath) + return + } - window.desktopEvents?.send(MainEvents.OPEN_EXTERNAL, externalUrl) - }, [navigate]) + const rawLink = notification.link?.trim() + const externalUrl = !rawLink + ? `${config.WEBSITE_URL}/contribute/localization` + : /^https?:\/\//i.test(rawLink) + ? rawLink + : `${config.WEBSITE_URL}${rawLink.startsWith('/') ? rawLink : `/${rawLink}`}` + + window.desktopEvents?.send(MainEvents.OPEN_EXTERNAL, externalUrl) + }, + [Modals.SUBSCRIPTION_GIVEAWAYS, navigate, openModal], + ) const handleNotificationClick = useCallback( async (notification: NotificationItem) => { diff --git a/src/renderer/widgets/layout/SubscriptionGiveawaysButton.tsx b/src/renderer/widgets/layout/SubscriptionGiveawaysButton.tsx index b3c30d97..c6730f17 100644 --- a/src/renderer/widgets/layout/SubscriptionGiveawaysButton.tsx +++ b/src/renderer/widgets/layout/SubscriptionGiveawaysButton.tsx @@ -1,19 +1,81 @@ -import React from 'react' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { MdRedeem } from 'react-icons/md' import { useTranslation } from 'react-i18next' import { useModalContext } from '@app/providers/modal' +import { useNotifications } from '@app/providers/notifications' +import { loadSubscriptionGiveawaysSnapshot } from '@shared/api/subscriptionGiveaways' import TooltipButton from '@shared/ui/tooltip_button' import * as styles from '@widgets/layout/header.module.scss' +const REFRESH_INTERVAL_MS = 60_000 + const SubscriptionGiveawaysButton: React.FC = () => { const { t } = useTranslation() - const { Modals, openModal } = useModalContext() + const { Modals, isModalOpen, openModal } = useModalContext() + const { notifications } = useNotifications() + const [activeCount, setActiveCount] = useState(0) + const giveawaysModalOpen = isModalOpen(Modals.SUBSCRIPTION_GIVEAWAYS) + const wasGiveawaysModalOpen = useRef(giveawaysModalOpen) + const latestStartedNotificationId = useMemo( + () => notifications.find(notification => notification.type === 'subscription.giveaway.started')?.id ?? null, + [notifications], + ) + + const refreshActiveCount = useCallback(async () => { + try { + const snapshot = await loadSubscriptionGiveawaysSnapshot() + const now = Date.now() + const count = snapshot.giveaways.filter(giveaway => { + const startsAt = new Date(giveaway.startsAt).getTime() + const endsAt = new Date(giveaway.endsAt).getTime() + return ( + !snapshot.enteredIds.has(giveaway.uuid) && + giveaway.status === 'open' && + Number.isFinite(startsAt) && + Number.isFinite(endsAt) && + startsAt <= now && + endsAt > now + ) + }).length + + setActiveCount(count) + } catch (error) { + console.error('Failed to refresh active subscription giveaway count:', error) + setActiveCount(0) + } + }, []) + + useEffect(() => { + void refreshActiveCount() + const intervalId = window.setInterval(() => void refreshActiveCount(), REFRESH_INTERVAL_MS) + return () => window.clearInterval(intervalId) + }, [refreshActiveCount]) + + useEffect(() => { + if (latestStartedNotificationId) { + void refreshActiveCount() + } + }, [latestStartedNotificationId, refreshActiveCount]) + + useEffect(() => { + const wasOpen = wasGiveawaysModalOpen.current + wasGiveawaysModalOpen.current = giveawaysModalOpen + if (wasOpen && !giveawaysModalOpen) { + void refreshActiveCount() + } + }, [giveawaysModalOpen, refreshActiveCount]) return ( - + {activeCount > 0 && {activeCount > 99 ? '99+' : activeCount}} ) } diff --git a/src/renderer/widgets/layout/header.module.scss b/src/renderer/widgets/layout/header.module.scss index e03bbf66..c600a1bd 100644 --- a/src/renderer/widgets/layout/header.module.scss +++ b/src/renderer/widgets/layout/header.module.scss @@ -641,7 +641,6 @@ background-color: red; } } - } } } @@ -757,3 +756,23 @@ .devOverridesBadge { background: #4c79ff; } + +.giveawaysBadge { + position: absolute; + top: -5px; + right: -5px; + min-width: 18px; + height: 18px; + padding: 0 5px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 999px; + background: #ff5b6e; + color: #fff; + font-size: 10px; + font-weight: 900; + line-height: 1; + box-shadow: 0 0 0 2px #16181f; + pointer-events: none; +} diff --git a/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.tsx b/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.tsx index dd312995..84bc17ef 100644 --- a/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.tsx +++ b/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.tsx @@ -8,27 +8,15 @@ import remarkGfm from 'remark-gfm' import { useTranslation } from 'react-i18next' import { useModalContext } from '@app/providers/modal' import rendererHttpClient from '@shared/api/http/client' +import { + invalidateSubscriptionGiveawaysSnapshot, + loadSubscriptionGiveawaysSnapshot, + type SubscriptionGiveaway, +} from '@shared/api/subscriptionGiveaways' import CustomModalPS from '@shared/ui/PSUI/CustomModalPS' import toast from '@shared/ui/toast' import * as styles from '@widgets/modalContainer/modals/SubscriptionGiveawaysModal.module.scss' -type SubscriptionGiveaway = { - uuid: string - title: string - description?: string | null - planCode: string - durationMonths?: number | null - winnersCount: number - status: string - startsAt: string - endsAt: string -} - -type SubscriptionGiveawaysResponse = { - ok?: boolean - giveaways?: SubscriptionGiveaway[] -} - type SubscriptionGiveawayEntryResponse = { ok?: boolean } @@ -137,12 +125,9 @@ const SubscriptionGiveawaysModal: React.FC = () => { setError(null) try { - const response = await rendererHttpClient.get('/subscription/giveaways') - if (!response.ok || !response.data?.ok || !Array.isArray(response.data.giveaways)) { - throw new Error('Failed to load subscription giveaways') - } - - setGiveaways(response.data.giveaways) + const snapshot = await loadSubscriptionGiveawaysSnapshot({ force: true }) + setGiveaways(snapshot.giveaways) + setEnteredIds(new Set(snapshot.enteredIds)) } catch (loadError) { console.error('Failed to load subscription giveaways:', loadError) setError(t('header.giveaways.loadError')) @@ -217,7 +202,13 @@ const SubscriptionGiveawaysModal: React.FC = () => { hasEntered, remainingLabel: formatRemaining(endsAtTime), endsAtLabel: formatDate(giveaway.endsAt), - actionLabel: hasEntered ? t('header.giveaways.entered') : hasEnded ? t('header.giveaways.ended') : !hasStarted ? t('header.giveaways.notStarted') : t('header.giveaways.join'), + actionLabel: hasEntered + ? t('header.giveaways.entered') + : hasEnded + ? t('header.giveaways.ended') + : !hasStarted + ? t('header.giveaways.notStarted') + : t('header.giveaways.join'), } }) .sort((a, b) => a.endsAtTime - b.endsAtTime) @@ -240,6 +231,7 @@ const SubscriptionGiveawaysModal: React.FC = () => { throw new Error('Failed to enter subscription giveaway') } + invalidateSubscriptionGiveawaysSnapshot() setEnteredIds(current => new Set(current).add(giveaway.uuid)) toast.custom('success', t('header.giveaways.enteredTitle'), t('header.giveaways.enteredText', { title: giveaway.title })) } catch (enterError) { @@ -306,13 +298,16 @@ const SubscriptionGiveawaysModal: React.FC = () => {
{item.isEnterable || isJoining ? ( - ) : ( - - {item.actionLabel} - + {item.actionLabel} )}
@@ -337,7 +332,13 @@ const SubscriptionGiveawaysModal: React.FC = () => {
- - : , + + ), ), createButtonSection(t('contextMenu.obsWidget.title'), [ { diff --git a/src/renderer/features/context_menu_themes/sectionConfig.tsx b/src/renderer/features/context_menu_themes/sectionConfig.tsx index 5e573527..07a41ebb 100644 --- a/src/renderer/features/context_menu_themes/sectionConfig.tsx +++ b/src/renderer/features/context_menu_themes/sectionConfig.tsx @@ -101,7 +101,10 @@ export const createContextMenuActions = ( const themeDirPath = currentAddon.path window.desktopEvents .invoke(MainEvents.DELETE_ADDON_DIRECTORY, themeDirPath) - .then(() => { + .then(result => { + if (!result?.success) { + throw new Error(result?.reason || 'DELETE_FAILED') + } window.refreshAddons() console.log(t('contextMenuThemes.deleteSuccess', { name: currentAddon.name })) }) @@ -109,7 +112,7 @@ export const createContextMenuActions = ( console.error(t('contextMenuThemes.deleteError', { name: currentAddon.name }), error) }) }, - isOpen: true + isOpen: true, }) }, show: actionVisibility.showDelete ?? false, diff --git a/src/renderer/pages/extension/index.tsx b/src/renderer/pages/extension/index.tsx index 869ab577..14cfddaf 100644 --- a/src/renderer/pages/extension/index.tsx +++ b/src/renderer/pages/extension/index.tsx @@ -33,8 +33,6 @@ import EnableAddonModal from '@pages/extension/ui/EnableAddonModal' import ThemeNotFound from '@pages/extension/ui/ThemeNotFound' import * as extensionStylesV2 from '@pages/extension/extension.module.scss' -import addonInitials from '@entities/addon/model/addon.initials' - import MainEvents from '@common/types/mainEvents' import { staticAsset } from '@shared/lib/staticAssets' import apolloClient from '@shared/api/apolloClient' @@ -378,52 +376,58 @@ export default function ExtensionPage() { [installedRelationAddons, relationLabels], ) - const getActiveConflictLabels = useCallback((addon: Addon, availableAddons: Addon[] = addons): string[] => { - const addonConflictIds = new Set( - Array.isArray(addon.conflictsWith) ? addon.conflictsWith.map(value => String(value || '').trim()).filter(Boolean) : [], - ) - const addonIdentifiers = new Set([addon.id, addon.storeAddonId].map(value => String(value || '').trim()).filter(Boolean)) - - return availableAddons - .filter(candidate => { - if (!candidate.enabled || candidate.directoryName === addon.directoryName) { - return false - } - - const candidateIdentifiers = [candidate.id, candidate.storeAddonId].map(value => String(value || '').trim()).filter(Boolean) - const candidateConflictIds = Array.isArray(candidate.conflictsWith) ? - candidate.conflictsWith.map(value => String(value || '').trim()).filter(Boolean) - : [] + const getActiveConflictLabels = useCallback( + (addon: Addon, availableAddons: Addon[] = addons): string[] => { + const addonConflictIds = new Set( + Array.isArray(addon.conflictsWith) ? addon.conflictsWith.map(value => String(value || '').trim()).filter(Boolean) : [], + ) + const addonIdentifiers = new Set([addon.id, addon.storeAddonId].map(value => String(value || '').trim()).filter(Boolean)) - return ( - candidateIdentifiers.some(identifier => addonConflictIds.has(identifier)) || - candidateConflictIds.some(conflictId => addonIdentifiers.has(conflictId)) - ) - }) - .map(candidate => candidate.name) - }, [addons]) + return availableAddons + .filter(candidate => { + if (!candidate.enabled || candidate.directoryName === addon.directoryName) { + return false + } - const getDependentAddonLabels = useCallback((addon: Addon, availableAddons: Addon[] = addons): string[] => { - const addonIdentifiers = new Set( - [addon.directoryName, addon.name, addon.id, addon.storeAddonId] - .map(value => String(value || '').trim()) - .filter(Boolean) - .flatMap(value => [value, value.toLowerCase()]), - ) + const candidateIdentifiers = [candidate.id, candidate.storeAddonId].map(value => String(value || '').trim()).filter(Boolean) + const candidateConflictIds = Array.isArray(candidate.conflictsWith) + ? candidate.conflictsWith.map(value => String(value || '').trim()).filter(Boolean) + : [] - return availableAddons - .filter(candidate => { - if (!candidate.enabled || candidate.directoryName === addon.directoryName) { - return false - } + return ( + candidateIdentifiers.some(identifier => addonConflictIds.has(identifier)) || + candidateConflictIds.some(conflictId => addonIdentifiers.has(conflictId)) + ) + }) + .map(candidate => candidate.name) + }, + [addons], + ) - return (candidate.dependencies || []) + const getDependentAddonLabels = useCallback( + (addon: Addon, availableAddons: Addon[] = addons): string[] => { + const addonIdentifiers = new Set( + [addon.directoryName, addon.name, addon.id, addon.storeAddonId] .map(value => String(value || '').trim()) .filter(Boolean) - .some(dependencyId => addonIdentifiers.has(dependencyId) || addonIdentifiers.has(dependencyId.toLowerCase())) - }) - .map(candidate => candidate.name) - }, [addons]) + .flatMap(value => [value, value.toLowerCase()]), + ) + + return availableAddons + .filter(candidate => { + if (!candidate.enabled || candidate.directoryName === addon.directoryName) { + return false + } + + return (candidate.dependencies || []) + .map(value => String(value || '').trim()) + .filter(Boolean) + .some(dependencyId => addonIdentifiers.has(dependencyId) || addonIdentifiers.has(dependencyId.toLowerCase())) + }) + .map(candidate => candidate.name) + }, + [addons], + ) const handleCheckboxChange = useCallback( async (addon: Addon, newChecked: boolean, showToast: boolean = true) => { @@ -431,30 +435,28 @@ export default function ExtensionPage() { const previousScripts = [...enabledScripts] const previousEnabledKeys = buildEnabledAddonKeys(previousTheme, previousScripts) - if (addon.type === 'theme') { - if (newChecked) { - window.electron.store.set('addons.theme', addon.directoryName) - } else { - window.electron.store.set('addons.theme', 'Default') - } - } else { - const updated = newChecked ? [...enabledScripts, addon.directoryName] : enabledScripts.filter(name => name !== addon.directoryName) - window.electron.store.set('addons.scripts', updated) + const result = await window.desktopEvents?.invoke(MainEvents.SET_ADDON_ENABLED, { + directoryName: addon.directoryName, + enabled: newChecked, + }) + if (!result?.success) { + throw new Error(result?.reason || 'SET_ADDON_ENABLED_FAILED') } - window.desktopEvents?.send(MainEvents.REFRESH_EXTENSIONS) - const refreshedAddons = await loadAddons(true) - const nextTheme = safeStoreGet('addons.theme', 'Default') || 'Default' - const nextEnabledScripts = readEnabledScriptsState() + const refreshedAddons = Array.isArray(result.addons) + ? (result.addons as Addon[]).filter(a => a.name !== 'Default') + : await loadAddons(true) + setAddons(refreshedAddons) + + const nextTheme = String(result.theme || 'Default') + const nextEnabledScripts = Array.isArray(result.scripts) + ? result.scripts.map((script: unknown) => String(script || '').trim()).filter(Boolean) + : readEnabledScriptsState() + setCurrentTheme(nextTheme) + setEnabledScripts(nextEnabledScripts) const nextEnabledKeys = buildEnabledAddonKeys(nextTheme, nextEnabledScripts) - const resolvedEnabled = - addon.type === 'theme' ? nextTheme === addon.directoryName : nextEnabledScripts.includes(addon.directoryName) - const nextThemeAddon = - nextTheme === 'Default' ? addonInitials[0] : refreshedAddons.find(item => item.type === 'theme' && item.directoryName === nextTheme) || addonInitials[0] + const resolvedEnabled = addon.type === 'theme' ? nextTheme === addon.directoryName : nextEnabledScripts.includes(addon.directoryName) - if (previousTheme !== nextTheme) { - window.desktopEvents?.send(MainEvents.THEME_CHANGED, nextThemeAddon) - } sendStoreAddonMetrics(nextTheme, nextEnabledScripts) if (showToast) { @@ -467,75 +469,73 @@ export default function ExtensionPage() { .flatMap(value => [value, value.toLowerCase()]), ) const autoDisabledAddons = addons.filter(candidate => autoDisabled.includes(candidate.directoryName)) - const dependentAutoDisabledAddons = !newChecked ? - autoDisabledAddons.filter(candidate => - (candidate.dependencies || []) - .map(value => String(value || '').trim()) - .filter(Boolean) - .some(dependencyId => disabledAddonIdentifiers.has(dependencyId) || disabledAddonIdentifiers.has(dependencyId.toLowerCase())), - ) - : [] + const dependentAutoDisabledAddons = !newChecked + ? autoDisabledAddons.filter(candidate => + (candidate.dependencies || []) + .map(value => String(value || '').trim()) + .filter(Boolean) + .some( + dependencyId => + disabledAddonIdentifiers.has(dependencyId) || disabledAddonIdentifiers.has(dependencyId.toLowerCase()), + ), + ) + : [] const dependentAutoDisabledKeys = new Set(dependentAutoDisabledAddons.map(candidate => candidate.directoryName)) const remainingAutoDisabledLabels = autoDisabled .filter(key => !dependentAutoDisabledKeys.has(key)) .map(key => relationLabels[key] || key) - const relationMessages = - addonRelationsEnabled ? - [ - autoEnabled.length ? - t('extensions.relations.autoEnabled', { + const relationMessages = addonRelationsEnabled + ? [ + autoEnabled.length + ? t('extensions.relations.autoEnabled', { value: autoEnabled.map(key => relationLabels[key] || key).join(', '), }) - : '', - dependentAutoDisabledAddons.length ? - t('extensions.relations.autoDisabledDependents', { + : '', + dependentAutoDisabledAddons.length + ? t('extensions.relations.autoDisabledDependents', { dependency: addon.name, value: dependentAutoDisabledAddons.map(item => item.name).join(', '), }) - : '', - remainingAutoDisabledLabels.length ? - t('extensions.relations.autoDisabled', { + : '', + remainingAutoDisabledLabels.length + ? t('extensions.relations.autoDisabled', { value: remainingAutoDisabledLabels.join(', '), }) - : '', - ].filter(Boolean) - : [] + : '', + ].filter(Boolean) + : [] const toastId = `addon-toggle:${addon.directoryName}:${newChecked ? 'enable' : 'disable'}` if (newChecked && !resolvedEnabled) { const missingDependencyLabels = addonRelationsEnabled ? getMissingDependencyLabels(addon) : [] const activeConflictLabels = addonRelationsEnabled ? getActiveConflictLabels(addon, refreshedAddons) : [] const blockingMessages = [ - missingDependencyLabels.length ? - t('extensions.relations.blockedByDependencies', { - value: missingDependencyLabels.join(', '), - }) - : '', - activeConflictLabels.length ? - t('extensions.relations.blockedByConflicts', { - value: activeConflictLabels.join(', '), - }) - : '', + missingDependencyLabels.length + ? t('extensions.relations.blockedByDependencies', { + value: missingDependencyLabels.join(', '), + }) + : '', + activeConflictLabels.length + ? t('extensions.relations.blockedByConflicts', { + value: activeConflictLabels.join(', '), + }) + : '', ].filter(Boolean) toast.custom( 'error', t('common.errorTitle'), - [ - t('extensions.relations.enableBlockedResolved', { name: addon.name }), - ...blockingMessages, - ...relationMessages, - ].join('\n'), + [t('extensions.relations.enableBlockedResolved', { name: addon.name }), ...blockingMessages, ...relationMessages].join('\n'), { id: toastId }, ) } else if (!newChecked && resolvedEnabled) { const dependentAddonLabels = addonRelationsEnabled ? getDependentAddonLabels(addon, refreshedAddons) : [] const blockingMessages = [ - dependentAddonLabels.length ? - t('extensions.relations.disableBlockedByDependents', { - value: dependentAddonLabels.join(', '), - }) - : t('extensions.relations.disableBlockedResolved', { name: addon.name }), + dependentAddonLabels.length + ? t('extensions.relations.disableBlockedByDependents', { + value: dependentAddonLabels.join(', '), + }) + : t('extensions.relations.disableBlockedResolved', { name: addon.name }), ...relationMessages, ].filter(Boolean) @@ -555,9 +555,9 @@ export default function ExtensionPage() { newChecked ? 'success' : 'info', newChecked ? t('extensions.scriptEnabled') : t('extensions.scriptDisabled'), [ - newChecked ? - t('extensions.scriptEnabledMessage', { name: addon.name }) - : t('extensions.scriptDisabledMessage', { name: addon.name }), + newChecked + ? t('extensions.scriptEnabledMessage', { name: addon.name }) + : t('extensions.scriptDisabledMessage', { name: addon.name }), ...relationMessages, ].join('\n'), { id: toastId }, @@ -565,7 +565,19 @@ export default function ExtensionPage() { } } }, - [addonRelationsEnabled, currentTheme, enabledScripts, getActiveConflictLabels, getDependentAddonLabels, getMissingDependencyLabels, loadAddons, relationLabels, sendStoreAddonMetrics, t], + [ + addonRelationsEnabled, + currentTheme, + enabledScripts, + getActiveConflictLabels, + getDependentAddonLabels, + getMissingDependencyLabels, + loadAddons, + relationLabels, + sendStoreAddonMetrics, + setAddons, + t, + ], ) const handleSearchChange = useCallback((e: React.ChangeEvent) => { @@ -724,11 +736,11 @@ export default function ExtensionPage() { const selectedAddonEnableBlockedReason = useMemo( () => - selectedAddonMissingDependencies.length ? - t('extensions.relations.enableBlockedHintMissing', { - value: selectedAddonMissingDependencies.join(', '), - }) - : null, + selectedAddonMissingDependencies.length + ? t('extensions.relations.enableBlockedHintMissing', { + value: selectedAddonMissingDependencies.join(', '), + }) + : null, [selectedAddonMissingDependencies, t], ) @@ -1109,7 +1121,16 @@ export default function ExtensionPage() { true, ) }, - [addonRelationsEnabled, currentTheme, enabledScripts, getMissingDependencyLabels, handleCheckboxChange, isAddonVersionSupported, musicVersion, t], + [ + addonRelationsEnabled, + currentTheme, + enabledScripts, + getMissingDependencyLabels, + handleCheckboxChange, + isAddonVersionSupported, + musicVersion, + t, + ], ) const shouldShowUntrustedAddonWarning = useCallback( @@ -1143,7 +1164,14 @@ export default function ExtensionPage() { continueEnableAddon(addon) }, - [Modals.UNTRUSTED_LOCAL_ADDON_MODAL, addonRelationsEnabled, continueEnableAddon, getMissingDependencyLabels, openModal, shouldShowUntrustedAddonWarning], + [ + Modals.UNTRUSTED_LOCAL_ADDON_MODAL, + addonRelationsEnabled, + continueEnableAddon, + getMissingDependencyLabels, + openModal, + shouldShowUntrustedAddonWarning, + ], ) return ( @@ -1216,37 +1244,37 @@ export default function ExtensionPage() { : enabledScripts.includes(selectedAddon.directoryName) return ( - { - void handleStoreAddonUpdate() - }} - publication={selectedPublication} - publicationReleases={visiblePublicationReleases} - publicationChangelogText={publicationChangelogText} - publicationGithubUrlText={publicationGithubUrlText} - canManagePublication={canManagePublication} - publicationBusy={publicationBusy} - onPublicationChangelogChange={setPublicationChangelogText} - onPublicationGithubUrlChange={setPublicationGithubUrlText} - onPublishAddon={handlePublishAddon} - onUpdateAddon={handleUpdateAddon} - onToggleEnabled={enabled => { - if (enabled) { - handleEnableAddon(selectedAddon) - } else { - handleCheckboxChange(selectedAddon, false, true) - } - }} - setSelectedTags={setSelectedTags} - setShowFilters={setShowFilters} - /> + { + void handleStoreAddonUpdate() + }} + publication={selectedPublication} + publicationReleases={visiblePublicationReleases} + publicationChangelogText={publicationChangelogText} + publicationGithubUrlText={publicationGithubUrlText} + canManagePublication={canManagePublication} + publicationBusy={publicationBusy} + onPublicationChangelogChange={setPublicationChangelogText} + onPublicationGithubUrlChange={setPublicationGithubUrlText} + onPublishAddon={handlePublishAddon} + onUpdateAddon={handleUpdateAddon} + onToggleEnabled={enabled => { + if (enabled) { + handleEnableAddon(selectedAddon) + } else { + handleCheckboxChange(selectedAddon, false, true) + } + }} + setSelectedTags={setSelectedTags} + setShowFilters={setShowFilters} + /> ) })() ) : ( diff --git a/src/renderer/pages/extension/model/addonCatalog.ts b/src/renderer/pages/extension/model/addonCatalog.ts index 9fec288b..40e218a4 100644 --- a/src/renderer/pages/extension/model/addonCatalog.ts +++ b/src/renderer/pages/extension/model/addonCatalog.ts @@ -166,8 +166,8 @@ export function filterAndSortAddons({ }) case 'date': return result.slice().sort((a, b) => { - const dateA = parseFloat(a.lastModified || '0') || 0 - const dateB = parseFloat(b.lastModified || '0') || 0 + const dateA = Number(a.lastModifiedAt) || 0 + const dateB = Number(b.lastModifiedAt) || 0 return sortOrder === 'asc' ? dateA - dateB : dateB - dateA }) case 'size': diff --git a/src/renderer/pages/extension/route/extBox/AddonRelationsPanel.tsx b/src/renderer/pages/extension/route/extBox/AddonRelationsPanel.tsx index 89b483a3..bfb6c28d 100644 --- a/src/renderer/pages/extension/route/extBox/AddonRelationsPanel.tsx +++ b/src/renderer/pages/extension/route/extBox/AddonRelationsPanel.tsx @@ -59,7 +59,10 @@ const AddonRelationsPanel: React.FC = ({ addon, relationLabels = {} }) => } }) - const dependencyItems = useMemo(() => buildRelationItems(addon.dependencies, 'dependency'), [addon.dependencies, installedAddonMap, relationLabels]) + const dependencyItems = useMemo( + () => buildRelationItems(addon.dependencies, 'dependency'), + [addon.dependencies, installedAddonMap, relationLabels], + ) const conflictItems = useMemo(() => buildRelationItems(addon.conflictsWith, 'conflict'), [addon.conflictsWith, installedAddonMap, relationLabels]) if (!dependencyItems.length && !conflictItems.length) { @@ -69,40 +72,41 @@ const AddonRelationsPanel: React.FC = ({ addon, relationLabels = {} }) => const renderCard = (item: RelationItem) => { const isConflictActive = item.kind === 'conflict' && item.isEnabled const statusText = - item.kind === 'dependency' ? - item.isEnabled ? - t('extensions.relations.statusEnabled') - : item.isInstalled ? - t('extensions.relations.statusInstalled') - : t('extensions.relations.statusMissing') - : item.isEnabled ? - t('extensions.relations.statusConflictActive') - : item.isInstalled ? - t('extensions.relations.statusInstalled') - : t('extensions.relations.statusNotInstalled') + item.kind === 'dependency' + ? item.isEnabled + ? t('extensions.relations.statusEnabled') + : item.isInstalled + ? t('extensions.relations.statusInstalled') + : t('extensions.relations.statusMissing') + : item.isEnabled + ? t('extensions.relations.statusConflictActive') + : item.isInstalled + ? t('extensions.relations.statusInstalled') + : t('extensions.relations.statusNotInstalled') const statusToneClass = - item.kind === 'dependency' ? - item.isEnabled ? - s.relationCardStatusSuccess - : item.isInstalled ? - s.relationCardStatusNeutral - : s.relationCardStatusMuted - : isConflictActive ? s.relationCardStatusDanger - : item.isInstalled ? s.relationCardStatusNeutral - : s.relationCardStatusMuted - - const sourceText = - item.isInstalled ? - item.installSource === 'store' ? - t('extensions.source.store') - : t('extensions.source.local') - : item.id + item.kind === 'dependency' + ? item.isEnabled + ? s.relationCardStatusSuccess + : item.isInstalled + ? s.relationCardStatusNeutral + : s.relationCardStatusMuted + : isConflictActive + ? s.relationCardStatusDanger + : item.isInstalled + ? s.relationCardStatusNeutral + : s.relationCardStatusMuted + + const sourceText = item.isInstalled ? (item.installSource === 'store' ? t('extensions.source.store') : t('extensions.source.local')) : item.id const icon = - item.addonType === 'theme' ? - : item.addonType === 'script' ? - : + item.addonType === 'theme' ? ( + + ) : item.addonType === 'script' ? ( + + ) : ( + + ) const isInteractive = !!item.directoryName diff --git a/src/renderer/pages/extension/route/extBox/MetadataEditor.tsx b/src/renderer/pages/extension/route/extBox/MetadataEditor.tsx index bd81ca77..172f3b4d 100644 --- a/src/renderer/pages/extension/route/extBox/MetadataEditor.tsx +++ b/src/renderer/pages/extension/route/extBox/MetadataEditor.tsx @@ -451,7 +451,9 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) = const foundUser = response.data?.findUserByName const normalizedInput = normalizeComparableText(trimmedValue) - const exactMatchExists = [foundUser?.username, foundUser?.nickname].some(candidate => normalizeComparableText(candidate) === normalizedInput) + const exactMatchExists = [foundUser?.username, foundUser?.nickname].some( + candidate => normalizeComparableText(candidate) === normalizedInput, + ) const canonicalAuthor = getProfileSlug(foundUser) if (!exactMatchExists || !canonicalAuthor) { @@ -513,23 +515,17 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) = .filter(addon => !(draft.storeAddonId && addon.id === draft.storeAddonId)) .filter(addon => !(draft.id && addon.id === draft.id)) - const normalizedNameCounts = relationCandidates.reduce( - (acc, addon) => { - const key = addon.name.trim().toLowerCase() - acc.set(key, (acc.get(key) || 0) + 1) - return acc - }, - new Map(), - ) + const normalizedNameCounts = relationCandidates.reduce((acc, addon) => { + const key = addon.name.trim().toLowerCase() + acc.set(key, (acc.get(key) || 0) + 1) + return acc + }, new Map()) return relationCandidates .map(addon => { const normalizedName = addon.name.trim().toLowerCase() const hasDuplicateName = (normalizedNameCounts.get(normalizedName) || 0) > 1 - const suffixParts = [ - hasDuplicateName ? addon.type : '', - addon.currentRelease?.version?.trim() || '', - ].filter(Boolean) + const suffixParts = [hasDuplicateName ? addon.type : '', addon.currentRelease?.version?.trim() || ''].filter(Boolean) const label = suffixParts.length ? `${addon.name} (${suffixParts.join(' • ')})` : addon.name return { @@ -541,7 +537,10 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) = .sort((left, right) => left.label.localeCompare(right.label)) }, [availableAddons, draft.id, draft.storeAddonId]) - const relationOptions = useMemo(() => relationOptionRecords.map(({ value, label, searchText }) => ({ value, label, searchText })), [relationOptionRecords]) + const relationOptions = useMemo( + () => relationOptionRecords.map(({ value, label, searchText }) => ({ value, label, searchText })), + [relationOptionRecords], + ) const relationLabelMap = useMemo(() => { const entries = new Map() @@ -648,13 +647,19 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) = setter(prev => prev.filter(entry => entry !== value)) }, []) - const removeDependency = useCallback((value: string) => { - removeRelation(value, setModalDependenciesDraft) - }, [removeRelation]) + const removeDependency = useCallback( + (value: string) => { + removeRelation(value, setModalDependenciesDraft) + }, + [removeRelation], + ) - const removeConflict = useCallback((value: string) => { - removeRelation(value, setModalConflictsDraft) - }, [removeRelation]) + const removeConflict = useCallback( + (value: string) => { + removeRelation(value, setModalConflictsDraft) + }, + [removeRelation], + ) const removeAllowedUrl = useCallback((value: string) => { setModalAllowedUrlsDraft(prev => prev.filter(entry => entry !== value)) @@ -782,24 +787,27 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) = [resolvePulseAuthor], ) - const removeAuthor = useCallback((value: string) => { - if (normalizeComparableText(value) === normalizeComparableText(selfAuthorSlug)) { - toast.custom('info', t('common.attentionTitle'), t('metadata.authorsEditor.selfRemovalBlocked')) - return - } + const removeAuthor = useCallback( + (value: string) => { + if (normalizeComparableText(value) === normalizeComparableText(selfAuthorSlug)) { + toast.custom('info', t('common.attentionTitle'), t('metadata.authorsEditor.selfRemovalBlocked')) + return + } - if (selectedAuthors.length <= 1) { - toast.custom('info', t('common.attentionTitle'), t('metadata.authorsEditor.minimumOneAuthor')) - return - } + if (selectedAuthors.length <= 1) { + toast.custom('info', t('common.attentionTitle'), t('metadata.authorsEditor.minimumOneAuthor')) + return + } - setDraft(prev => ({ - ...prev, - author: splitAuthorEntries(prev.author) - .filter(entry => normalizeComparableText(entry) !== normalizeComparableText(value)) - .join(', '), - })) - }, [selectedAuthors.length, selfAuthorSlug, t]) + setDraft(prev => ({ + ...prev, + author: splitAuthorEntries(prev.author) + .filter(entry => normalizeComparableText(entry) !== normalizeComparableText(value)) + .join(', '), + })) + }, + [selectedAuthors.length, selfAuthorSlug, t], + ) if (loading) { return @@ -812,7 +820,12 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) =
- setField('name', value)} /> + setField('name', value)} + /> = ({ addonPath, addonRelationsEnabled }) = setAuthorSearchOpen(true) }} onFocus={() => setAuthorSearchOpen(true)} - placeholder={selectedAuthors.length ? t('metadata.authorsEditor.searchPlaceholder') : t('metadata.authorsEditor.requiredPlaceholder')} + placeholder={ + selectedAuthors.length + ? t('metadata.authorsEditor.searchPlaceholder') + : t('metadata.authorsEditor.requiredPlaceholder') + } />
@@ -1120,7 +1137,11 @@ const MetadataEditor: React.FC = ({ addonPath, addonRelationsEnabled }) = {modalDependenciesDraft.map(dependencyId => (
{relationLabelMap.get(dependencyId) || dependencyId}
-
diff --git a/src/renderer/pages/extension/route/extBox/TabContent.tsx b/src/renderer/pages/extension/route/extBox/TabContent.tsx index 802f1298..db0a492f 100644 --- a/src/renderer/pages/extension/route/extBox/TabContent.tsx +++ b/src/renderer/pages/extension/route/extBox/TabContent.tsx @@ -278,7 +278,7 @@ const TabContent: React.FC = ({ return {alt} } - const activeConfig = editMode ? editConfig ?? config : config + const activeConfig = editMode ? (editConfig ?? config) : config const isConfigEmpty = !activeConfig || !Array.isArray(activeConfig.sections) || activeConfig.sections.length === 0 if (active === 'Settings') { diff --git a/src/renderer/pages/extension/route/extBox/TabNavigation.tsx b/src/renderer/pages/extension/route/extBox/TabNavigation.tsx index e25a26d6..6d58b425 100644 --- a/src/renderer/pages/extension/route/extBox/TabNavigation.tsx +++ b/src/renderer/pages/extension/route/extBox/TabNavigation.tsx @@ -14,7 +14,15 @@ interface Props { stickyTop?: number } -const TabNavigation: React.FC = ({ active, onChange, docs, hasPublicationChangelog = false, hasRelations = false, showMetadataTab = true, stickyTop }) => { +const TabNavigation: React.FC = ({ + active, + onChange, + docs, + hasPublicationChangelog = false, + hasRelations = false, + showMetadataTab = true, + stickyTop, +}) => { const { t } = useTranslation() const docTabs: TabItem[] = docs.map(d => ({ @@ -25,7 +33,9 @@ const TabNavigation: React.FC = ({ active, onChange, docs, hasPublication const tabs: TabItem[] = [ ...docTabs, - ...(hasPublicationChangelog ? [{ title: t('extensions.tabs.changelog'), value: PUBLICATION_CHANGELOG_TAB, icon: }] : []), + ...(hasPublicationChangelog + ? [{ title: t('extensions.tabs.changelog'), value: PUBLICATION_CHANGELOG_TAB, icon: }] + : []), ...(hasRelations ? [{ title: t('extensions.tabs.relations'), value: RELATIONS_TAB, icon: }] : []), { title: t('extensions.tabs.settings'), value: 'Settings', icon: }, ...(showMetadataTab ? [{ title: t('extensions.tabs.metadata'), value: 'Metadata', icon: }] : []), diff --git a/src/renderer/pages/extension/route/extBox/ThemeInfo/index.tsx b/src/renderer/pages/extension/route/extBox/ThemeInfo/index.tsx index fb03e86b..e5d566a5 100644 --- a/src/renderer/pages/extension/route/extBox/ThemeInfo/index.tsx +++ b/src/renderer/pages/extension/route/extBox/ThemeInfo/index.tsx @@ -300,7 +300,6 @@ const ThemeInfo: React.FC = ({ {addon.installSource === 'store' ? t('extensions.source.store') : t('extensions.source.local')}
-
@@ -340,7 +339,11 @@ const ThemeInfo: React.FC = ({ title={!isEnabled && enableBlockedReason ? enableBlockedReason : undefined} onClick={() => onToggleEnabled(!isEnabled)} > - {isEnabled ? t('common.disable') : enableBlockedReason ? t('extensions.relations.enableBlockedButtonLabel') : t('common.enable')} + {isEnabled + ? t('common.disable') + : enableBlockedReason + ? t('extensions.relations.enableBlockedButtonLabel') + : t('common.enable')} )} diff --git a/src/renderer/pages/extension/route/extensionview.module.scss b/src/renderer/pages/extension/route/extensionview.module.scss index 3d909061..44530b75 100644 --- a/src/renderer/pages/extension/route/extensionview.module.scss +++ b/src/renderer/pages/extension/route/extensionview.module.scss @@ -301,9 +301,7 @@ padding: 14px 16px; border-radius: 16px; border: 1px solid rgba(129, 141, 179, 0.14); - background: - linear-gradient(180deg, rgba(73, 81, 107, 0.24) 0%, rgba(53, 59, 79, 0.34) 100%), - rgba(129, 141, 179, 0.08); + background: linear-gradient(180deg, rgba(73, 81, 107, 0.24) 0%, rgba(53, 59, 79, 0.34) 100%), rgba(129, 141, 179, 0.08); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02); } @@ -317,9 +315,7 @@ &:hover { border-color: rgba(129, 141, 179, 0.28); - background: - linear-gradient(180deg, rgba(87, 96, 126, 0.28) 0%, rgba(60, 67, 90, 0.38) 100%), - rgba(129, 141, 179, 0.1); + background: linear-gradient(180deg, rgba(87, 96, 126, 0.28) 0%, rgba(60, 67, 90, 0.38) 100%), rgba(129, 141, 179, 0.1); transform: translateY(-1px); } } diff --git a/src/renderer/pages/extension/route/extensionview.tsx b/src/renderer/pages/extension/route/extensionview.tsx index aedc9f3d..344946c4 100644 --- a/src/renderer/pages/extension/route/extensionview.tsx +++ b/src/renderer/pages/extension/route/extensionview.tsx @@ -60,21 +60,26 @@ const ExtensionView: React.FC = ({ const canEditMetadata = useMemo(() => { const currentUserCandidates = [user.username, user.nickname, user.id] - .map(value => String(value || '').trim().toLowerCase()) + .map(value => + String(value || '') + .trim() + .toLowerCase(), + ) .filter(Boolean) if (!currentUserCandidates.length) { return false } - const addonAuthors = - Array.isArray(addon.author) ? - addon.author - : typeof addon.author === 'string' ? - addon.author.split(',') - : [] + const addonAuthors = Array.isArray(addon.author) ? addon.author : typeof addon.author === 'string' ? addon.author.split(',') : [] - const normalizedAuthors = addonAuthors.map(author => String(author || '').trim().toLowerCase()).filter(Boolean) + const normalizedAuthors = addonAuthors + .map(author => + String(author || '') + .trim() + .toLowerCase(), + ) + .filter(Boolean) if (!normalizedAuthors.length) { return false } diff --git a/src/renderer/pages/home/model/homeDashboard.ts b/src/renderer/pages/home/model/homeDashboard.ts index 9b1c10bd..6977760b 100644 --- a/src/renderer/pages/home/model/homeDashboard.ts +++ b/src/renderer/pages/home/model/homeDashboard.ts @@ -1,5 +1,5 @@ export type HomePrimaryComponent = { - id: 'mod' | 'client' | 'music', + id: 'mod' | 'client' | 'music' titleKey: string iconAsset: string } diff --git a/src/renderer/pages/home/ui/HomeNewsSection.tsx b/src/renderer/pages/home/ui/HomeNewsSection.tsx index ffce90b1..45f89680 100644 --- a/src/renderer/pages/home/ui/HomeNewsSection.tsx +++ b/src/renderer/pages/home/ui/HomeNewsSection.tsx @@ -115,10 +115,7 @@ export default function HomeNewsSection() { return `${day}.${month}.${year}` }, []) - const formatCompactReadTime = useCallback( - (value: number) => `${value} ${i18n.language === 'ru' ? 'мин' : 'min'}`, - [i18n.language], - ) + const formatCompactReadTime = useCallback((value: number) => `${value} ${i18n.language === 'ru' ? 'мин' : 'min'}`, [i18n.language]) const openArticle = useCallback((slug: string) => { if (!slug) { diff --git a/src/renderer/pages/home/ui/HomePrimaryComponentsSection.tsx b/src/renderer/pages/home/ui/HomePrimaryComponentsSection.tsx index e2895de4..0b576d28 100644 --- a/src/renderer/pages/home/ui/HomePrimaryComponentsSection.tsx +++ b/src/renderer/pages/home/ui/HomePrimaryComponentsSection.tsx @@ -31,7 +31,6 @@ export default function HomePrimaryComponentsSection({ items, versions, isModIns
{items.map(item => (
-
diff --git a/src/renderer/pages/home/ui/home.module.scss b/src/renderer/pages/home/ui/home.module.scss index 22c07af9..b54304a1 100644 --- a/src/renderer/pages/home/ui/home.module.scss +++ b/src/renderer/pages/home/ui/home.module.scss @@ -12,9 +12,7 @@ min-height: 0; padding: 18px; border-radius: 14px; - background: - linear-gradient(180deg, rgba(79, 87, 116, 0.08) 0%, rgba(79, 87, 116, 0) 100%), - #2f3446; + background: linear-gradient(180deg, rgba(79, 87, 116, 0.08) 0%, rgba(79, 87, 116, 0) 100%), #2f3446; border: 1px solid rgba(91, 100, 128, 0.55); box-sizing: border-box; } @@ -36,7 +34,7 @@ .panelHollow { min-height: 0; - gap: 10px + gap: 10px; } .newsPanel { @@ -73,7 +71,7 @@ font-weight: 900; font-size: 24px; line-height: 33px; - color: #F2F3F5; + color: #f2f3f5; } .newsSubtitle { @@ -144,25 +142,46 @@ } .ymItem { - background: conic-gradient(from 0deg at 100% 100%, #FED42B -1.26deg, rgba(0, 0, 0, 0.31) 8.08deg, #FED42B 358.74deg, rgba(0, 0, 0, 0.31) 368.08deg), #232632; + background: conic-gradient( + from 0deg at 100% 100%, + #fed42b -1.26deg, + rgba(0, 0, 0, 0.31) 8.08deg, + #fed42b 358.74deg, + rgba(0, 0, 0, 0.31) 368.08deg + ), + #232632; } .modItem { - background: conic-gradient(from 0deg at 100% 100%, #5865F2 -1.26deg, rgba(0, 0, 0, 0.31) 8.08deg, #5865F2 358.74deg, rgba(0, 0, 0, 0.31) 368.08deg), #232632; + background: conic-gradient( + from 0deg at 100% 100%, + #5865f2 -1.26deg, + rgba(0, 0, 0, 0.31) 8.08deg, + #5865f2 358.74deg, + rgba(0, 0, 0, 0.31) 368.08deg + ), + #232632; .actionButton { - color: #505BD8; + color: #505bd8; &:not(:disabled):active { - color: #505BD8; + color: #505bd8; } } } .clientItem { - background: conic-gradient(from 0deg at 100% 100%, #5865F2 -1.26deg, rgba(0, 0, 0, 0.31) 8.08deg, #5865F2 358.74deg, rgba(0, 0, 0, 0.31) 368.08deg), #232632; + background: conic-gradient( + from 0deg at 100% 100%, + #5865f2 -1.26deg, + rgba(0, 0, 0, 0.31) 8.08deg, + #5865f2 358.74deg, + rgba(0, 0, 0, 0.31) 368.08deg + ), + #232632; .actionButton { - color: #505BD8; + color: #505bd8; &:not(:disabled):active { - color: #505BD8; + color: #505bd8; } } } @@ -179,12 +198,11 @@ align-items: center; flex-grow: 0; - background: #3B4052; + background: #3b4052; .componentLogo { box-shadow: none; } - } .secondaryItemMain { @@ -225,7 +243,6 @@ flex: none; order: 0; flex-grow: 1; - } .componentTitle { @@ -235,9 +252,8 @@ font-weight: 700; font-size: 18px; line-height: 110%; - color: #FFFFFF; + color: #ffffff; user-select: text; - } .componentVersion, @@ -266,7 +282,7 @@ padding: 10px; gap: 4px; - background: #FFFFFF; + background: #ffffff; border-radius: 8px; flex: none; @@ -278,21 +294,21 @@ font-size: 14px; line-height: 110%; - color: #C8A826; + color: #c8a826; transition: transform 0.16s ease, background-color 0.16s ease; &:not(:disabled):hover { - background: #FFFFFF; + background: #ffffff; transform: translateY(-1px); } &:not(:disabled):active { background: #ececec; transform: translateY(0); - color: #C8A826; + color: #c8a826; } &:disabled { @@ -321,7 +337,7 @@ padding: 10px; gap: 4px; - background: #FFFFFF; + background: #ffffff; border-radius: 8px; flex: none; @@ -337,11 +353,11 @@ margin-left: auto; transition: - transform 0.16s ease, - background-color 0.16s ease; + transform 0.16s ease, + background-color 0.16s ease; &:not(:disabled):hover { - background: #FFFFFF; + background: #ffffff; transform: translateY(-1px); } @@ -420,8 +436,8 @@ .newsFeaturedCard { overflow: hidden; border-radius: 16px; - background-color: #4D536B; - border: 1px solid #2A2A2A; + background-color: #4d536b; + border: 1px solid #2a2a2a; transition: transform 0.22s ease, border-color 0.22s ease; @@ -448,8 +464,7 @@ .newsListItemMediaFallback { position: absolute; inset: 0; - background: - radial-gradient(circle at top right, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 30%), + background: radial-gradient(circle at top right, rgba(255, 255, 255, 0.24) 0%, rgba(255, 255, 255, 0) 30%), linear-gradient(135deg, #6073a7 0%, #32394f 55%, #232836 100%); } @@ -558,10 +573,9 @@ .newsListItem { overflow: hidden; border-radius: 16px; - background: #4D536B; - border: 1px solid #2A2A2A; - transition: - transform 0.18s ease; + background: #4d536b; + border: 1px solid #2a2a2a; + transition: transform 0.18s ease; &:hover, &:focus-visible { @@ -587,9 +601,7 @@ align-items: center; justify-content: center; text-align: center; - background: - radial-gradient(circle at top, rgba(144, 161, 214, 0.2) 0%, rgba(144, 161, 214, 0) 45%), - rgba(34, 39, 55, 0.76); + background: radial-gradient(circle at top, rgba(144, 161, 214, 0.2) 0%, rgba(144, 161, 214, 0) 45%), rgba(34, 39, 55, 0.76); border: 1px solid rgba(118, 131, 172, 0.24); } @@ -645,7 +657,7 @@ .newsSkeletonItemTitle { background: linear-gradient(90deg, rgba(115, 127, 164, 0.18) 0%, rgba(159, 173, 214, 0.28) 50%, rgba(115, 127, 164, 0.18) 100%); background-size: 220% 100%; - animation: newsShimmer 1.35s ease-in-out .75s infinite; + animation: newsShimmer 1.35s ease-in-out 0.75s infinite; } .newsSkeletonImage { diff --git a/src/renderer/pages/profile/[username].tsx b/src/renderer/pages/profile/[username].tsx index 0d390c55..e54aed64 100644 --- a/src/renderer/pages/profile/[username].tsx +++ b/src/renderer/pages/profile/[username].tsx @@ -81,7 +81,8 @@ const ProfilePage: React.FC = () => { return payload } - const hasLiveAchievementData = (Array.isArray(user.userAchievements) && user.userAchievements.length > 0) || Number(user.levelInfoV2?.totalPoints || 0) > 0 + const hasLiveAchievementData = + (Array.isArray(user.userAchievements) && user.userAchievements.length > 0) || Number(user.levelInfoV2?.totalPoints || 0) > 0 const liveStatus = socketConnected ? 'online' : user.status || payload.status const liveLastOnline = user.lastOnline || payload.lastOnline diff --git a/src/renderer/pages/store/index.tsx b/src/renderer/pages/store/index.tsx index 7cfd3eff..8d034967 100644 --- a/src/renderer/pages/store/index.tsx +++ b/src/renderer/pages/store/index.tsx @@ -196,16 +196,19 @@ export default function StorePage() { const hasSearchOrFilter = Boolean(debouncedSearchQuery) || typeFilter !== 'all' const shouldShowPendingSection = isDeveloperUser && (pendingAddons.length > 0 || hasSearchOrFilter) - const handleSortOptionClick = useCallback((option: StoreSortKey) => { - setSortKey(option) - setSortOrder(currentOrder => { - if (sortKey === option) { - return currentOrder === 'asc' ? 'desc' : 'asc' - } + const handleSortOptionClick = useCallback( + (option: StoreSortKey) => { + setSortKey(option) + setSortOrder(currentOrder => { + if (sortKey === option) { + return currentOrder === 'asc' ? 'desc' : 'asc' + } - return getDefaultSortOrder(option) - }) - }, [sortKey]) + return getDefaultSortOrder(option) + }) + }, + [sortKey], + ) const handleStoreAddonAction = useCallback( async (addon: StoreAddon, release: StoreAddon['currentRelease'], installedStoreAddon?: Addon) => { @@ -422,7 +425,8 @@ export default function StorePage() { const shimmerCount = useMemo(() => { const columns = Math.max(1, gridColumns) const fallbackViewportHeight = - scrollViewportHeight || (typeof window === 'undefined' ? STORE_CARD_MIN_HEIGHT * 2 : Math.max(window.innerHeight - 220, STORE_CARD_MIN_HEIGHT * 2)) + scrollViewportHeight || + (typeof window === 'undefined' ? STORE_CARD_MIN_HEIGHT * 2 : Math.max(window.innerHeight - 220, STORE_CARD_MIN_HEIGHT * 2)) const rowHeight = STORE_CARD_MIN_HEIGHT + STORE_GRID_ROW_GAP const rows = Math.max(2, Math.ceil(fallbackViewportHeight / rowHeight) + 1) @@ -595,9 +599,11 @@ export default function StorePage() { : t('store.filters.downloads')} {sortKey === option && - (sortOrder === 'asc' ? + (sortOrder === 'asc' ? ( - : )} + ) : ( + + ))} ))} diff --git a/src/renderer/pages/store/store.module.scss b/src/renderer/pages/store/store.module.scss index d0074172..b65d6258 100644 --- a/src/renderer/pages/store/store.module.scss +++ b/src/renderer/pages/store/store.module.scss @@ -351,7 +351,6 @@ .store_sectionGrid { grid-template-columns: minmax(0, 1fr); } - } @media (max-width: 640px) { diff --git a/src/renderer/shared/lib/utils.ts b/src/renderer/shared/lib/utils.ts index 139de10a..bcff1831 100644 --- a/src/renderer/shared/lib/utils.ts +++ b/src/renderer/shared/lib/utils.ts @@ -9,7 +9,7 @@ export const checkInternetAccess = async (): Promise => { try { const response = await rendererHttpClient.get(`${config.SERVER_v2_URL}/api/v2/health`, { responseType: 'text', - timeoutMs: 10000 + timeoutMs: 10000, }) return response.status === 200 } catch (error) { diff --git a/src/renderer/shared/ui/PSUI/SelectInput/index.tsx b/src/renderer/shared/ui/PSUI/SelectInput/index.tsx index b4181fb6..c5ad9715 100644 --- a/src/renderer/shared/ui/PSUI/SelectInput/index.tsx +++ b/src/renderer/shared/ui/PSUI/SelectInput/index.tsx @@ -56,7 +56,12 @@ const SelectInput: React.FC = ({ return options } - return options.filter(option => String(option.searchText ?? option.label).trim().toLowerCase().includes(normalizedQuery)) + return options.filter(option => + String(option.searchText ?? option.label) + .trim() + .toLowerCase() + .includes(normalizedQuery), + ) }, [options, searchable, searchQuery]) const idxByValue = useMemo(() => filteredOptions.findIndex(o => String(o.value) === String(value)), [filteredOptions, value]) @@ -127,8 +132,7 @@ const SelectInput: React.FC = ({ if (next) { updatePanelLayout() setHover(idxByValue >= 0 ? idxByValue : 0) - } - else setSearchQuery('') + } else setSearchQuery('') return next }) } diff --git a/src/renderer/shared/ui/PSUI/Shimmer/styles/_skeleton.scss b/src/renderer/shared/ui/PSUI/Shimmer/styles/_skeleton.scss index 74e26b2a..92f760fa 100644 --- a/src/renderer/shared/ui/PSUI/Shimmer/styles/_skeleton.scss +++ b/src/renderer/shared/ui/PSUI/Shimmer/styles/_skeleton.scss @@ -17,7 +17,7 @@ inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.12) 42%, rgba(255, 255, 255, 0.22) 50%, transparent 100%); - animation: shimmer-slide 1.35s ease-in-out .75s infinite; + animation: shimmer-slide 1.35s ease-in-out 0.75s infinite; } } diff --git a/src/renderer/shared/ui/PSUI/Shimmer/variants/ModChangelogShimmer.tsx b/src/renderer/shared/ui/PSUI/Shimmer/variants/ModChangelogShimmer.tsx index fc800fe8..a40af7da 100644 --- a/src/renderer/shared/ui/PSUI/Shimmer/variants/ModChangelogShimmer.tsx +++ b/src/renderer/shared/ui/PSUI/Shimmer/variants/ModChangelogShimmer.tsx @@ -18,11 +18,7 @@ export default function ModChangelogShimmer() { {Array.from({ length: bulletCount }, (_, bulletIndex) => (
-
+
))}
diff --git a/src/renderer/widgets/layout/header.tsx b/src/renderer/widgets/layout/header.tsx index cff1ce0b..2bfae127 100644 --- a/src/renderer/widgets/layout/header.tsx +++ b/src/renderer/widgets/layout/header.tsx @@ -237,7 +237,7 @@ const Header: React.FC

= () => { const [loadingModChanges, setLoadingModChanges] = useState(false) const [modError, setModError] = useState(null) const [isMaximized, setIsMaximized] = useState(false) - const [isMac, setIsMac] = useState(window.electron.isMac()); + const [isMac, setIsMac] = useState(window.electron.isMac()) const appUpdatesLoadedRef = useRef(false) const appUpdatesLoadingRef = useRef(false) const modChangesLoadedKeyRef = useRef(null) @@ -263,18 +263,18 @@ const Header: React.FC

= () => { setAppError(null) try { - const nextAppUpdates = isAutonomousMode ? - (((await window.desktopEvents?.invoke(MainEvents.GET_CLIENT_CHANGELOG)) as AppInfoInterface[] | undefined) ?? []) - : await (async () => { - const response = await rendererHttpClient.get<{ appInfo?: AppInfoInterface[]; ok?: boolean }>('/api/v1/app/info') - const data = response.data + const nextAppUpdates = isAutonomousMode + ? (((await window.desktopEvents?.invoke(MainEvents.GET_CLIENT_CHANGELOG)) as AppInfoInterface[] | undefined) ?? []) + : await (async () => { + const response = await rendererHttpClient.get<{ appInfo?: AppInfoInterface[]; ok?: boolean }>('/api/v1/app/info') + const data = response.data - if (!response.ok || !data?.ok || !Array.isArray(data.appInfo)) { - throw new Error('Failed to fetch app info') - } + if (!response.ok || !data?.ok || !Array.isArray(data.appInfo)) { + throw new Error('Failed to fetch app info') + } - return data.appInfo - })() + return data.appInfo + })() if (!active) { return @@ -321,19 +321,19 @@ const Header: React.FC

= () => { setModError(null) try { - const nextModChanges = isAutonomousMode ? - ((((await window.desktopEvents?.invoke(MainEvents.GET_MOD_CHANGELOG)) as ModChangelogEntry[] | undefined) ?? []).filter( - entry => compareVersions(entry.version, app.mod.version || '') <= 0, - )) - : await (async () => { - const result = await client.query({ - query: GetModUpdates, - variables: { modVersion: app.mod.version || '' }, - fetchPolicy: 'no-cache', - }) - - return Array.isArray(result.data?.getChangelogEntries) ? result.data.getChangelogEntries : [] - })() + const nextModChanges = isAutonomousMode + ? (((await window.desktopEvents?.invoke(MainEvents.GET_MOD_CHANGELOG)) as ModChangelogEntry[] | undefined) ?? []).filter( + entry => compareVersions(entry.version, app.mod.version || '') <= 0, + ) + : await (async () => { + const result = await client.query({ + query: GetModUpdates, + variables: { modVersion: app.mod.version || '' }, + fetchPolicy: 'no-cache', + }) + + return Array.isArray(result.data?.getChangelogEntries) ? result.data.getChangelogEntries : [] + })() if (!active) { return @@ -484,21 +484,23 @@ const Header: React.FC

= () => { )}

- {!isMac &&
- - - -
} + {!isMac && ( +
+ + + +
+ )}
diff --git a/src/renderer/widgets/layout/index.tsx b/src/renderer/widgets/layout/index.tsx index 10a9653c..a88915d5 100644 --- a/src/renderer/widgets/layout/index.tsx +++ b/src/renderer/widgets/layout/index.tsx @@ -26,8 +26,19 @@ interface LayoutProps { } const Layout: React.FC = ({ title, children, goBack }) => { - const { user, app, setApp, updateAvailable, setUpdate, modInfo, modInfoFetched, musicInstalled, setMusicInstalled, setMusicVersion, isAutonomousMode } = - useContext(userContext) + const { + user, + app, + setApp, + updateAvailable, + setUpdate, + modInfo, + modInfoFetched, + musicInstalled, + setMusicInstalled, + setMusicVersion, + isAutonomousMode, + } = useContext(userContext) const { t } = useTranslation() const { Modals, openModal } = useModalContext() const navigate = useNavigate() diff --git a/src/renderer/widgets/layout/model/useLayoutInstallers.ts b/src/renderer/widgets/layout/model/useLayoutInstallers.ts index 980acff9..ec33376d 100644 --- a/src/renderer/widgets/layout/model/useLayoutInstallers.ts +++ b/src/renderer/widgets/layout/model/useLayoutInstallers.ts @@ -79,7 +79,9 @@ export function useLayoutInstallers({ (error: any) => { const rawMessage = typeof error?.error === 'string' ? error.error.trim() : '' const normalizedMessage = - rawMessage && rawMessage.toLowerCase().includes('aborted') ? t('layout.modInstallInterrupted') : rawMessage || t('layout.unknownError') + rawMessage && rawMessage.toLowerCase().includes('aborted') + ? t('layout.modInstallInterrupted') + : rawMessage || t('layout.unknownError') const isUpdate = currentModActionRef.current === 'update' const details = errorTypesToShow.has(error?.type) @@ -253,78 +255,57 @@ export function useLayoutInstallers({ window.desktopEvents?.removeAllListeners(RendererEvents.DOWNLOAD_FAILURE) ;(window as any).__listenersAdded = false } - }, [ - modals.LINUX_PERMISSIONS_MODAL, - modals.MOD_CHANGELOG, - openModal, - readInstalledModFromStore, - setApp, - setMusicInstalled, - setMusicVersion, - t, - ]) - - const startUpdate = useCallback( - () => { - if (window.electron.isLinux()) { - const savedPath = window.electron.store.get('settings.modSavePath') - if (!savedPath) { - openModal(modals.LINUX_ASAR_PATH) - return - } - } - if (isUpdating) { - toast.custom( - 'error', - t('common.errorTitle'), - app.mod.installed ? t('layout.modUpdateAlreadyRunning') : t('layout.modInstallAlreadyRunning'), - ) - return - } - if (modInfo.length === 0) { - toast.custom( - 'error', - app.mod.installed ? t('layout.noModUpdatesAvailable') : t('layout.noModInstallsAvailable'), - app.mod.installed ? t('layout.modUpdateLoadError') : t('layout.modInstallErrorTitle'), - ) + }, [modals.LINUX_PERMISSIONS_MODAL, modals.MOD_CHANGELOG, openModal, readInstalledModFromStore, setApp, setMusicInstalled, setMusicVersion, t]) + + const startUpdate = useCallback(() => { + if (window.electron.isLinux()) { + const savedPath = window.electron.store.get('settings.modSavePath') + if (!savedPath) { + openModal(modals.LINUX_ASAR_PATH) return } + } + if (isUpdating) { + toast.custom( + 'error', + t('common.errorTitle'), + app.mod.installed ? t('layout.modUpdateAlreadyRunning') : t('layout.modInstallAlreadyRunning'), + ) + return + } + if (modInfo.length === 0) { + toast.custom( + 'error', + app.mod.installed ? t('layout.noModUpdatesAvailable') : t('layout.noModInstallsAvailable'), + app.mod.installed ? t('layout.modUpdateLoadError') : t('layout.modInstallErrorTitle'), + ) + return + } - setIsUpdating(true) - setModInstallError(null) - currentModActionRef.current = app.mod.installed ? 'update' : 'install' - const id = toast.custom('loading', app.mod.installed ? t('layout.modUpdateStart') : t('layout.modInstallStart'), t('common.pleaseWait'), { - id: MOD_DOWNLOAD_TOAST_ID, - duration: Infinity, - }) - downloadToastIdRef.current = id - - const { - modVersion, - realMusicVersion, - downloadUrl, - checksum_v2, - name, - shouldReinstall, - downloadUnpackedUrl, - unpackedChecksum, - source, - } = modInfo[0] - - window.desktopEvents?.send(MainEvents.INSTALL_MOD, { - version: modVersion, - musicVersion: realMusicVersion, - name, - link: downloadUrl, - unpackLink: downloadUnpackedUrl, - unpackedChecksum, - checksum: checksum_v2, - shouldReinstall, - source: source || 'backend', - }) - }, - [app.mod.installed, isUpdating, modInfo, modals.LINUX_ASAR_PATH, openModal, t], - ) + setIsUpdating(true) + setModInstallError(null) + currentModActionRef.current = app.mod.installed ? 'update' : 'install' + const id = toast.custom('loading', app.mod.installed ? t('layout.modUpdateStart') : t('layout.modInstallStart'), t('common.pleaseWait'), { + id: MOD_DOWNLOAD_TOAST_ID, + duration: Infinity, + }) + downloadToastIdRef.current = id + + const { modVersion, realMusicVersion, downloadUrl, checksum_v2, name, shouldReinstall, downloadUnpackedUrl, unpackedChecksum, source } = + modInfo[0] + + window.desktopEvents?.send(MainEvents.INSTALL_MOD, { + version: modVersion, + musicVersion: realMusicVersion, + name, + link: downloadUrl, + unpackLink: downloadUnpackedUrl, + unpackedChecksum, + checksum: checksum_v2, + shouldReinstall, + source: source || 'backend', + }) + }, [app.mod.installed, isUpdating, modInfo, modals.LINUX_ASAR_PATH, openModal, t]) useEffect(() => { if (!modInfoFetched || modInfo.length === 0 || isUpdating || !app.mod.installed || !app.mod.version) return diff --git a/src/renderer/widgets/layout/ui/HeaderModals.tsx b/src/renderer/widgets/layout/ui/HeaderModals.tsx index b6ee2c70..901e48df 100644 --- a/src/renderer/widgets/layout/ui/HeaderModals.tsx +++ b/src/renderer/widgets/layout/ui/HeaderModals.tsx @@ -74,18 +74,18 @@ export default function HeaderModals({ {!loadingAppUpdates && !appError && visibleAppUpdates.map(info => ( -
-
-

{info.version}

- {formatDate(info.createdAt)} -
-
- - {info.changelog} - -
+
+
+

{info.version}

+ {formatDate(info.createdAt)}
- ))} +
+ + {info.changelog} + +
+
+ ))} {!loadingAppUpdates && !appError && visibleAppUpdates.length === 0 &&

{t('header.noChangelogFound')}

}
diff --git a/src/renderer/widgets/layout/ui/ModUpdateBanner.tsx b/src/renderer/widgets/layout/ui/ModUpdateBanner.tsx index 679a4d37..9cfde373 100644 --- a/src/renderer/widgets/layout/ui/ModUpdateBanner.tsx +++ b/src/renderer/widgets/layout/ui/ModUpdateBanner.tsx @@ -20,14 +20,7 @@ type Props = { t: (key: string, options?: Record) => string } -export default function ModUpdateBanner({ - app, - isModUpdateAvailable, - modInstallError, - modInfo, - onStartUpdate, - t, -}: Props) { +export default function ModUpdateBanner({ app, isModUpdateAvailable, modInstallError, modInfo, onStartUpdate, t }: Props) { if (!isModUpdateAvailable) return null return ( @@ -65,7 +58,7 @@ export default function ModUpdateBanner({ {modInstallError?.showProxyHint && (
{t('layout.modInstallErrorProxyHint')}
- {modInstallProxyDomains.map((domain) => ( + {modInstallProxyDomains.map(domain => (
{domain}
))}
diff --git a/src/renderer/widgets/modalContainer/modals/ExperimentOverridesDevModal.tsx b/src/renderer/widgets/modalContainer/modals/ExperimentOverridesDevModal.tsx index 6eb8393e..ea8531dc 100644 --- a/src/renderer/widgets/modalContainer/modals/ExperimentOverridesDevModal.tsx +++ b/src/renderer/widgets/modalContainer/modals/ExperimentOverridesDevModal.tsx @@ -248,7 +248,9 @@ const ExperimentOverridesDevModal: React.FC = () => { {sortedExperiments.map(experiment => { const isSelected = experiment.key === selectedKey const hasOverride = Boolean(localOverrides[experiment.key]) - const activeExperiment = hasOverride ? localOverrides[experiment.key] : experiments.find(active => active.key === experiment.key) + const activeExperiment = hasOverride + ? localOverrides[experiment.key] + : experiments.find(active => active.key === experiment.key) return (
{activeExperiment?.group || t('header.devOverrides.noGroup')} - {t('header.devOverrides.groupsCount', { count: experiment.groups.length })} + + {t('header.devOverrides.groupsCount', { count: experiment.groups.length })} +
) @@ -291,7 +295,9 @@ const ExperimentOverridesDevModal: React.FC = () => { {selectedExperiment.description &&

{selectedExperiment.description}

}
- {t('header.devOverrides.groupsCount', { count: selectedExperiment.groups.length })} + + {t('header.devOverrides.groupsCount', { count: selectedExperiment.groups.length })} + {selectedOverride && {t('header.devOverrides.overrideActive')}}
@@ -308,7 +314,9 @@ const ExperimentOverridesDevModal: React.FC = () => { >
{group.group} - {t('header.devOverrides.groupRollout', { percentage: group.rollout })} + + {t('header.devOverrides.groupRollout', { percentage: group.rollout })} +
{group.description || t('header.devOverrides.groupDescriptionEmpty')} diff --git a/src/renderer/widgets/modalContainer/modals/ExtensionPublicationModal.tsx b/src/renderer/widgets/modalContainer/modals/ExtensionPublicationModal.tsx index 271d6e8c..b4689ab7 100644 --- a/src/renderer/widgets/modalContainer/modals/ExtensionPublicationModal.tsx +++ b/src/renderer/widgets/modalContainer/modals/ExtensionPublicationModal.tsx @@ -39,9 +39,18 @@ function PublicationCheckbox({ checked, onChange, children }: PublicationCheckbo const ExtensionPublicationModal: React.FC = () => { const { t, i18n } = useTranslation() const { Modals, closeModal, isModalOpen, getModalState, setModalState } = useModalContext() - const { addon, authorsDisplay, publication, publicationBusy, changelogText, githubUrlText, onChangeChangelog, onChangeGithubUrl, onPublish, onUpdate } = getModalState( - Modals.EXTENSION_PUBLICATION_MODAL, - ) + const { + addon, + authorsDisplay, + publication, + publicationBusy, + changelogText, + githubUrlText, + onChangeChangelog, + onChangeGithubUrl, + onPublish, + onUpdate, + } = getModalState(Modals.EXTENSION_PUBLICATION_MODAL) const isPublicationModalOpen = isModalOpen(Modals.EXTENSION_PUBLICATION_MODAL) const publicationRelease = publication?.currentRelease const [rulesAccepted, setRulesAccepted] = useState(false) diff --git a/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.module.scss b/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.module.scss index fbe9df37..c73e55e3 100644 --- a/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.module.scss +++ b/src/renderer/widgets/modalContainer/modals/SubscriptionGiveawaysModal.module.scss @@ -95,7 +95,7 @@ } span:not(:last-child)::after { - content: ""; + content: ''; width: 4px; height: 4px; display: inline-flex; @@ -173,7 +173,7 @@ background: #2c313d; &::after { - content: ""; + content: ''; position: absolute; inset: 0; transform: translateX(-100%); @@ -340,7 +340,7 @@ } span:not(:last-child)::after { - content: ""; + content: ''; width: 3px; height: 3px; display: inline-flex; diff --git a/yarn.lock b/yarn.lock index e31bca8b..b8f03046 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2271,64 +2271,64 @@ "@sentry/replay" "10.60.0" "@sentry/replay-canvas" "10.60.0" -"@sentry/cli-darwin@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-3.5.1.tgz#a5493c3f9636a5c14fc47f95e59224cb59ed6481" - integrity sha512-GxHAtZaXRA650egcepQXU0pR0dZ8ZNvQS8eq3wBxhCK8os5VQpLyBABIaN6AdrE/b8M7UvqGjsuVm0giI1GZ7w== - -"@sentry/cli-linux-arm64@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-3.5.1.tgz#48637c57bb8c278ab9605948a01c509d8a8593ac" - integrity sha512-Qa0NLXG/FSYWGhKjdm2mxp/GgpluFFkj/J+CpmVzwvezNC/Uy1omquv7J+VficqskNYuptjsoB4dNIPPcXpbxg== - -"@sentry/cli-linux-arm@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-3.5.1.tgz#4cec807c212772636869d3d5d43a1aa69756ee30" - integrity sha512-JOfgXCDZKbxQpw8Z4Kbkt8Hl+ASW5pYVUYw8Hc2msPN3HtfTmsc1z0y6jq3TCUWDWbWpWbI1zfXa0v02vQf3gw== - -"@sentry/cli-linux-i686@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-3.5.1.tgz#c2cfc1ddddf471371a59955ad5b8eeb096f1d778" - integrity sha512-/Bqcl8EyS6T8RIjBeeMYUPgjMJ8kb4plF0w3QOG6TY+bUEqHEnZyfzKJWZY/OqhmrSgj+ZYynaSHIIR6dWluMA== - -"@sentry/cli-linux-x64@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-3.5.1.tgz#a1a64b5409db64ffd0c92388cb46bd83b50dba94" - integrity sha512-iUJT2GI/soc0myi1sSnXdu71oBkUER3fBeXn10bcEZX+UK9GomsqL40OLlygDipZZ6FqhODORqLeTxHdHbRuOw== - -"@sentry/cli-win32-arm64@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-3.5.1.tgz#2db5c7658b79b8ce6f5597cfa9fa9dda0da015e4" - integrity sha512-Hs4jHOsTKrrI2W8c4wEIvQzSuHqvrjsDHT7iwujHjp4oeAOnYjBUm+/BgDH2Eg1/jL5ilEnJbpnAn2AE2KQUXQ== - -"@sentry/cli-win32-i686@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-3.5.1.tgz#2512439eb1bcb35fcad5ee6de714dd636f4fb789" - integrity sha512-Op+9MYAg0RbVWOQ9/NtFunWcknS8MlqhEa03ENFUrRDQE0m+iHioknGOagKrNXYXj1UbGEf0G9UzIMcRwB/UCQ== - -"@sentry/cli-win32-x64@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-3.5.1.tgz#7ebcf29759a994ada669f84f1ba7d0c20da5dcdd" - integrity sha512-Vf+CtFPkuGzjddD6dJoAVKszw5QB7P1ffc++yAbU54ditqJdgswo45oyTFOiQFO2isCmhgICzimqe8SkU+p2Mw== - -"@sentry/cli@3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-3.5.1.tgz#f786c3cc2c4383023b0e8ca9bcdb1b09f8a70f3e" - integrity sha512-h710aEXT8At4lg7GbXDZkatDDq0uA5QKZmSiF/8Hy6jJZ/9dh6EBDZZpTYnDpNrwRvE8tqhnwEjTZDlHjOhPNQ== +"@sentry/cli-darwin@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-3.6.0.tgz#8186789f4cd8c14251f290a0ad27221dcccfa4c7" + integrity sha512-C2SWHKaEP8NoYkLDr5jwrzklTwlJkzPIx7lu2LZrwBuHG/sNhWdili0ED2mD86b6Q90hlcMtuBm5IHUwcZ6FTQ== + +"@sentry/cli-linux-arm64@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-3.6.0.tgz#c1b4433b7b9d8e88f59375038ae410d4a7b6db0b" + integrity sha512-Rc+DB8vuTDpwqY2BxIPnooYk2ZDYQytF+n4nfi6pZZsJtr3SFFe+3wIWVmCVqxiHkaISb33+iJIDxOOqhkSNbQ== + +"@sentry/cli-linux-arm@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-3.6.0.tgz#09826f9ca6beeb7cf7cd7fe9010bb23bb0c71f3d" + integrity sha512-S9xsDZTvybOGbrqqZ7DvF7JCNKp4cakDWJ4LdvQX+z82cHQSoLkYOXkA3EafDfWV9BGIRMIXitMMiSsV2PMU4g== + +"@sentry/cli-linux-i686@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-3.6.0.tgz#3271a02b552b0a3cec66fb0ee7516659f9774b3e" + integrity sha512-PQ7+ctNmWtHgmbKa+rJheHU7D9GHJXafgWYfVW6gt7R0Ag9LxiDVYLGjrL4G/i7AGFNgudXFaLkGKNX7HUjc+g== + +"@sentry/cli-linux-x64@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-3.6.0.tgz#b976325e3efb7a640461e2701c24513bbd215a1a" + integrity sha512-c+7xNg5BAaPE8N2Q6pg3Q/kt97JSaskuQIjRxHaFuDbCkJvww4VozY6mW5NUMJaW48rEs3mahWKamTLqJsO3wQ== + +"@sentry/cli-win32-arm64@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-arm64/-/cli-win32-arm64-3.6.0.tgz#33e863b1067d0cf29b022ae7c308ea8b4e8f6413" + integrity sha512-zhZ7YyGreHSKZ92Mwb9h4cEyL0I/eND7W6XIUXUW0BCCmxFOMc71vlQpUw8gijHIsFDbv8c8a6VOSkeRuwbSwQ== + +"@sentry/cli-win32-i686@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-3.6.0.tgz#b2a2b302cb892801a1b385def2f8c78a0e388363" + integrity sha512-JaxzVDdyetrPBp8NHh2yNAYdDk79ROXqfAfjQwG5z6V764MMMrf2WrhQ7EwoKXOPtBLm/drbOcYgaxHuDZKGRw== + +"@sentry/cli-win32-x64@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-3.6.0.tgz#4437101b8a07228bcdf0c30a6e5d3f9b27e79522" + integrity sha512-LW0078VlxaUeVMMLA15A1zhkvZ5vby/lwthtBXPoBSDdTcgbTE4D4gQXM9vEapV28SvCO3fVIek3pbtrkaeDMw== + +"@sentry/cli@3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-3.6.0.tgz#75609e353e5a4f6baaa13b25ac1b2dba21c1ac7f" + integrity sha512-79XC8o59G/i3VqmnoQD74a/QD/422eQQlUqiPd3sEvcYCxnaZialRVAsxuNEFd6sx4aVGpqt775MMDT9cV/lMg== dependencies: progress "^2.0.3" proxy-from-env "^1.1.0" undici "^6.22.0" which "^2.0.2" optionalDependencies: - "@sentry/cli-darwin" "3.5.1" - "@sentry/cli-linux-arm" "3.5.1" - "@sentry/cli-linux-arm64" "3.5.1" - "@sentry/cli-linux-i686" "3.5.1" - "@sentry/cli-linux-x64" "3.5.1" - "@sentry/cli-win32-arm64" "3.5.1" - "@sentry/cli-win32-i686" "3.5.1" - "@sentry/cli-win32-x64" "3.5.1" + "@sentry/cli-darwin" "3.6.0" + "@sentry/cli-linux-arm" "3.6.0" + "@sentry/cli-linux-arm64" "3.6.0" + "@sentry/cli-linux-i686" "3.6.0" + "@sentry/cli-linux-x64" "3.6.0" + "@sentry/cli-win32-arm64" "3.6.0" + "@sentry/cli-win32-i686" "3.6.0" + "@sentry/cli-win32-x64" "3.6.0" "@sentry/conventions@^0.12.0": version "0.12.0" @@ -5327,10 +5327,10 @@ eslint-visitor-keys@^5.0.1: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz#9e3c9489697824d2d4ce3a8ad12628f91e9f59be" integrity sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA== -eslint@^10.5.0: - version "10.5.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.5.0.tgz#5fca69d6b41fe7e00ba22d4100b2e44efe439ad5" - integrity sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ== +eslint@^10.6.0: + version "10.6.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-10.6.0.tgz#e1b4059c582be950c7088c9b55f984738b243c27" + integrity sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg== dependencies: "@eslint-community/eslint-utils" "^4.8.0" "@eslint-community/regexpp" "^4.12.2" @@ -6248,10 +6248,10 @@ https-proxy-agent@^7.0.0: agent-base "^7.1.2" debug "4" -i18next@^26.3.2: - version "26.3.2" - resolved "https://registry.yarnpkg.com/i18next/-/i18next-26.3.2.tgz#1800dd8cbeddd47ee87208c47aeb7f7816b51a6d" - integrity sha512-QQkXAM1sPDHqhxMQuBeHVMUn6mJchF+wdpOoQerciLAFqO3ZYdxO0EUbeEhruyutnNwpUQIITDVzLjwnNL0T1w== +i18next@^26.3.3: + version "26.3.3" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-26.3.3.tgz#acab67bb8608deaf01886a1d573dbd6f2c201b9b" + integrity sha512-aYVegyBdXSO93CMMihvr47jI7GHSOcIahMpJX+qzUXDzW4xDJf2uenIA+45vDU+YhiVdcfsql70AC9RVdMNrHg== iconv-lite@^0.4.24: version "0.4.24" @@ -8432,10 +8432,10 @@ prettier@^3.4.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== -prettier@^3.8.4: - version "3.8.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.4.tgz#f334f013ac04a96676f24dabc23c1c4ae1bae411" - integrity sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q== +prettier@^3.8.5: + version "3.8.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.5.tgz#81cf9de0cf46db973fa85103ff06dfcdb0d9bc39" + integrity sha512-zxcTTCedNGJM4R8sj/Cq/F0W/c4iE0afWBcBwMTRtw4WHYP9TWkYjdiH3npPRUYsXQCPR0hTU9yjovOu+E6EQA== proc-log@^7.0.0: version "7.0.0" From 1b27875419ab153e0a5fc79792d8efc343c5ca88 Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 15:18:05 +0300 Subject: [PATCH 37/43] fix: isolate GlitchTip sourcemaps by dist Took 10 seconds --- scripts/build.ts | 9 +++++++++ scripts/glitchtip-sourcemaps.ts | 11 +++++++++++ src/common/errorTracking.ts | 2 ++ src/main/modules/errorTracking.ts | 2 ++ .../events/registerSocketClientEvents.ts | 17 +++++++++++------ src/renderer/app/errorTracking.ts | 2 ++ vite-env.d.ts | 1 + vite.main.config.ts | 2 ++ vite.renderer.config.ts | 2 ++ 9 files changed, 42 insertions(+), 6 deletions(-) diff --git a/scripts/build.ts b/scripts/build.ts index 4a22dc9a..8f474088 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -226,6 +226,13 @@ async function runCommandStep(name: string, command: string): Promise { } } +function setBuildDist(platform: NodeJS.Platform, arch: string): string { + const dist = `${platform}-${arch}` + process.env.PULSESYNC_BUILD_DIST = dist + log(LogLevel.INFO, `GlitchTip dist: ${dist}`) + return dist +} + function ensureNodeHeapForMac(): void { if (os.platform() !== 'darwin') return const currentOptions = process.env.NODE_OPTIONS ?? '' @@ -409,8 +416,10 @@ async function main(): Promise { if (os.platform() === 'darwin') { const targetArch = macX64Build ? 'x64' : 'arm64' + setBuildDist('darwin', targetArch) await runCommandStep(`Package (electron-forge:${targetArch})`, `electron-forge package --arch ${targetArch}`) } else { + setBuildDist(os.platform(), os.arch()) await runCommandStep('Package (electron-forge)', 'electron-forge package') const nativeDir = path.resolve(__dirname, '../nativeModules') diff --git a/scripts/glitchtip-sourcemaps.ts b/scripts/glitchtip-sourcemaps.ts index 19fd2aaa..8a5186ca 100644 --- a/scripts/glitchtip-sourcemaps.ts +++ b/scripts/glitchtip-sourcemaps.ts @@ -95,6 +95,15 @@ function getViteUrlPrefix(stagedDirectory: string): string { return `app:///.vite/${relativeParts.slice(1).join('/')}` } +function getViteDist(stagedDirectory: string): string { + const [dist] = path.relative(stagingRoot, stagedDirectory).split(path.sep).filter(Boolean) + if (!dist) { + throw new Error(`Unexpected GlitchTip source-map staging directory: ${stagedDirectory}`) + } + + return dist +} + async function ensureGlitchTipRelease(release: string): Promise { const baseUrl = process.env.SENTRY_URL!.trim().replace(/\/$/u, '') const organization = process.env.SENTRY_ORG!.trim() @@ -196,6 +205,8 @@ export async function uploadGlitchTipSourceMaps(version: string): Promise process.env.SENTRY_ORG!.trim(), '--project', process.env.SENTRY_PROJECT!.trim(), + '--dist', + getViteDist(sourceMapDirectory), '--url-prefix', getViteUrlPrefix(sourceMapDirectory), '--validate', diff --git a/src/common/errorTracking.ts b/src/common/errorTracking.ts index f5f1896d..2967c44a 100644 --- a/src/common/errorTracking.ts +++ b/src/common/errorTracking.ts @@ -2,9 +2,11 @@ export const ERROR_TRACKING_DSN = 'https://f8abbc9ce46c42989b72758349a3a245@ru-n export const ERROR_TRACKING_ENABLED = import.meta.env.PROD export const ERROR_TRACKING_ENVIRONMENT = import.meta.env.PROD ? 'production' : 'development' export const ERROR_TRACKING_RELEASE = `pulsesync-client@${PULSESYNC_VERSION}` +export const ERROR_TRACKING_DIST = PULSESYNC_DIST export const ERROR_TRACKING_BUILD_TAGS = { branch: PULSESYNC_BRANCH || 'unknown', + dist: ERROR_TRACKING_DIST || 'unknown', } type ErrorTrackingEvent = { diff --git a/src/main/modules/errorTracking.ts b/src/main/modules/errorTracking.ts index 87671fff..b1dd034d 100644 --- a/src/main/modules/errorTracking.ts +++ b/src/main/modules/errorTracking.ts @@ -4,6 +4,7 @@ import { addErrorTrackingDebugIds, addErrorTrackingRuntimeTags, ERROR_TRACKING_BUILD_TAGS, + ERROR_TRACKING_DIST, ERROR_TRACKING_DSN, ERROR_TRACKING_ENABLED, ERROR_TRACKING_ENVIRONMENT, @@ -21,6 +22,7 @@ export const initMainErrorTracking = (): void => { Sentry.init({ dsn: ERROR_TRACKING_DSN, release: ERROR_TRACKING_RELEASE, + dist: ERROR_TRACKING_DIST, environment: ERROR_TRACKING_ENVIRONMENT, sendDefaultPii: false, maxBreadcrumbs: 0, diff --git a/src/main/modules/httpServer/events/registerSocketClientEvents.ts b/src/main/modules/httpServer/events/registerSocketClientEvents.ts index c302494d..4aecfa1f 100644 --- a/src/main/modules/httpServer/events/registerSocketClientEvents.ts +++ b/src/main/modules/httpServer/events/registerSocketClientEvents.ts @@ -40,6 +40,11 @@ export const registerSocketClientEvents = ({ updateData, handleBrowserAuth, }: RegisterSocketClientEventsOptions) => { + const sendToRenderer = (channel: string, ...args: any[]) => { + if (mainWindow.isDestroyed() || mainWindow.webContents.isDestroyed()) return + mainWindow.webContents.send(channel, ...args) + } + const version = (socket.handshake.query.v as string) || state.get('mod.version') const clientType = (socket.handshake.query.type as string) || 'yaMusic' ;(socket as any).clientType = clientType @@ -52,7 +57,7 @@ export const registerSocketClientEvents = ({ logger.http.log('READY received from client') if ((socket as any).clientType !== 'yaMusic') return - mainWindow.webContents.send(RendererEvents.CLIENT_READY) + sendToRenderer(RendererEvents.CLIENT_READY) ;(socket as any).hasPong = true if (getAuthorized()) { sendDataToMusic({ @@ -68,7 +73,7 @@ export const registerSocketClientEvents = ({ if (!getAuthorized()) { logger.http.warn('Unauthorized IS_PREMIUM_USER request, ignoring.') } else { - mainWindow.webContents.send(RendererEvents.IS_PREMIUM_USER) + sendToRenderer(RendererEvents.IS_PREMIUM_USER) } }) @@ -79,7 +84,7 @@ export const registerSocketClientEvents = ({ socket.on('BROWSER_BAN', (args: any) => { logger.http.log('BROWSER_BAN received:', args) - mainWindow.webContents.send(RendererEvents.AUTH_BANNED, { reason: args.reason }) + sendToRenderer(RendererEvents.AUTH_BANNED, { reason: args.reason }) }) socket.on('UPDATE_DATA', (payload: any) => { @@ -91,7 +96,7 @@ export const registerSocketClientEvents = ({ socket.on('UPDATE_DOWNLOAD_INFO', (payload: any) => { if (!getAuthorized()) return logger.http.log('UPDATE_DOWNLOAD_INFO received:', payload) - mainWindow.webContents.send(RendererEvents.TRACK_INFO, getTrackData()) + sendToRenderer(RendererEvents.TRACK_INFO, getTrackData()) }) socket.on('INSTALL_MOD_UPDATE_FROM', async (payload: any) => { @@ -112,12 +117,12 @@ export const registerSocketClientEvents = ({ socket.on(RendererEvents.SEND_TRACK, (payload: any) => { if (!getAuthorized()) return logger.http.log('SEND_TRACK received:', payload) - mainWindow.webContents.send(RendererEvents.SEND_TRACK, payload.data) + sendToRenderer(RendererEvents.SEND_TRACK, payload.data) }) socket.on('disconnect', () => { logger.http.log('Client disconnected') - mainWindow.webContents.send(RendererEvents.TRACK_INFO, { + sendToRenderer(RendererEvents.TRACK_INFO, { type: 'refresh', }) }) diff --git a/src/renderer/app/errorTracking.ts b/src/renderer/app/errorTracking.ts index 7b942f21..b216be59 100644 --- a/src/renderer/app/errorTracking.ts +++ b/src/renderer/app/errorTracking.ts @@ -4,6 +4,7 @@ import { addErrorTrackingDebugIds, addErrorTrackingRuntimeTags, ERROR_TRACKING_BUILD_TAGS, + ERROR_TRACKING_DIST, ERROR_TRACKING_ENABLED, ERROR_TRACKING_ENVIRONMENT, ERROR_TRACKING_RELEASE, @@ -18,6 +19,7 @@ export const initRendererErrorTracking = (): void => { try { Sentry.init({ release: ERROR_TRACKING_RELEASE, + dist: ERROR_TRACKING_DIST, environment: ERROR_TRACKING_ENVIRONMENT, sendDefaultPii: false, maxBreadcrumbs: 0, diff --git a/vite-env.d.ts b/vite-env.d.ts index 4b4acf10..81e6dc38 100644 --- a/vite-env.d.ts +++ b/vite-env.d.ts @@ -12,6 +12,7 @@ declare const PRELOADER_VITE_NAME: string declare const PULSESYNC_VERSION: string declare const PULSESYNC_BRANCH: string +declare const PULSESYNC_DIST: string interface ImportMetaEnv { readonly VITE_APP_TITLE: string diff --git a/vite.main.config.ts b/vite.main.config.ts index 5ad65716..2933d75a 100644 --- a/vite.main.config.ts +++ b/vite.main.config.ts @@ -8,6 +8,7 @@ const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'package. version: string buildInfo?: { BRANCH?: string } } +const buildDist = process.env.PULSESYNC_BUILD_DIST || `${process.platform}-${process.arch}` export default defineConfig(({ mode, forgeConfigSelf }: any): UserConfig => { const isDevMode = mode === 'development' @@ -36,6 +37,7 @@ export default defineConfig(({ mode, forgeConfigSelf }: any): UserConfig => { define: { PULSESYNC_VERSION: JSON.stringify(packageJson.version), PULSESYNC_BRANCH: JSON.stringify(packageJson.buildInfo?.BRANCH ?? 'unknown'), + PULSESYNC_DIST: JSON.stringify(buildDist), 'process.env.BRANCH': JSON.stringify((packageJson as any).buildInfo?.BRANCH), 'process.env.VERSION': JSON.stringify(packageJson.version), 'import.meta.env.DEV': JSON.stringify(isDevMode), diff --git a/vite.renderer.config.ts b/vite.renderer.config.ts index b73539c2..4b7f4542 100644 --- a/vite.renderer.config.ts +++ b/vite.renderer.config.ts @@ -11,6 +11,7 @@ const packageJson = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'package. version: string buildInfo?: { BRANCH?: string } } +const buildDist = process.env.PULSESYNC_BUILD_DIST || `${process.platform}-${process.arch}` const rendererHtmlEntries: Record = { main_window: 'src/renderer/index.html', @@ -39,6 +40,7 @@ export default defineConfig(({ mode, forgeConfigSelf }: any) => { define: { PULSESYNC_VERSION: JSON.stringify(packageJson.version), PULSESYNC_BRANCH: JSON.stringify(packageJson.buildInfo?.BRANCH ?? 'unknown'), + PULSESYNC_DIST: JSON.stringify(buildDist), 'import.meta.env.DEV': JSON.stringify(isDevMode), 'import.meta.env.PROD': JSON.stringify(!isDevMode), }, From f74ecc2174a0904b5e68fd904a39d392b7e304a2 Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 15:18:18 +0300 Subject: [PATCH 38/43] chore: bump deps Took 2 minutes --- package.json | 8 ++++---- yarn.lock | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index b146f810..ccac0f19 100644 --- a/package.json +++ b/package.json @@ -64,12 +64,12 @@ "@typescript-eslint/parser": "^8.62.0", "@vitejs/plugin-react": "^6.0.3", "babel-plugin-react-compiler": "^1.0.0", - "electron": "42.5.0", + "electron": "42.5.1", "electron-builder": "^26.15.3", "eslint": "^10.6.0", "eslint-plugin-import": "^2.32.0", "iconv-lite": "^0.7.2", - "prettier": "^3.8.5", + "prettier": "^3.9.3", "sass": "^1.101.0", "ts-node": "^10.9.2", "tsx": "4.22.4", @@ -89,7 +89,7 @@ "@sentry/electron": "7.14.0", "acorn": "^8.17.0", "acorn-walk": "^8.3.5", - "adm-zip": "^0.5.17", + "adm-zip": "^0.5.18", "axios": "^1.18.1", "browserify-fs": "^1.0.0", "browserify-sign": "^4.2.6", @@ -132,7 +132,7 @@ "stream-browserify": "^3.0.0", "string-similarity": "^4.0.4", "systeminformation": "^5.31.11", - "tar": "^7.5.17", + "tar": "^7.5.19", "url": "^0.11.4", "uuid": "^14.0.1", "yaml": "^2.9.0", diff --git a/yarn.lock b/yarn.lock index b8f03046..68a05627 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3419,10 +3419,10 @@ adm-zip@^0.5.16: resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.16.tgz#0b5e4c779f07dedea5805cdccb1147071d94a909" integrity sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ== -adm-zip@^0.5.17: - version "0.5.17" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.17.tgz#5c0b65f37aeec5c2a94995c024f931f62e4bbc5a" - integrity sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ== +adm-zip@^0.5.18: + version "0.5.18" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.5.18.tgz#283a05f2bf1e3fd315f0f31cde29b7a6e3c1619d" + integrity sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng== agent-base@6: version "6.0.2" @@ -4951,10 +4951,10 @@ electron@*: "@types/node" "^24.9.0" extract-zip "^2.0.1" -electron@42.5.0: - version "42.5.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-42.5.0.tgz#09de6f3f10146ed03c401405da695b5743ecdec7" - integrity sha512-cYEKS9XFz+c9fAB4jI0x49yz1FFzB55r3q96wu9YkwwJMv7t9202IE/ltlgy6yitl/J4M7C8JQcmUqdzDvPl/w== +electron@42.5.1: + version "42.5.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-42.5.1.tgz#ca6c6119c119b818c145c7e838c3484e36c35b05" + integrity sha512-2VFNJcHHbrhIpGsJHdkLoi/nWPZPxN3GHVPe+9At3Oz3/TJRwpr+7JL97ddBDbKyLmHGx3GfI2jvzcEQL28uFw== dependencies: "@electron-internal/extract-zip" "^1.0.1" "@electron/get" "^5.0.0" @@ -8432,10 +8432,10 @@ prettier@^3.4.2: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173" integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg== -prettier@^3.8.5: - version "3.8.5" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.5.tgz#81cf9de0cf46db973fa85103ff06dfcdb0d9bc39" - integrity sha512-zxcTTCedNGJM4R8sj/Cq/F0W/c4iE0afWBcBwMTRtw4WHYP9TWkYjdiH3npPRUYsXQCPR0hTU9yjovOu+E6EQA== +prettier@^3.9.3: + version "3.9.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.9.3.tgz#4caabe6fb955e8b8f13319f25872522ece519534" + integrity sha512-HWmu+K+zvHNpaMfSnYeqdqrDbR16cuIXaPx8WoHaviQkDJh1/0BNtOZmHVQI5jc3wXv0H1yXc9wjvFdXh+n3hQ== proc-log@^7.0.0: version "7.0.0" @@ -9701,10 +9701,10 @@ tar@*: minizlib "^3.1.0" yallist "^5.0.0" -tar@^7.5.17: - version "7.5.17" - resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.17.tgz#5eace4af68b088bb1d737ba9fffdacbbb70ba6e0" - integrity sha512-wPEBwzapC+2PaTYPH6e2L+cNOEE227S47wUYFqlegcs8zlLLmeb9Fcff1HVZY4Fwku/1Eyv38n7GYwB2aaS71g== +tar@^7.5.19: + version "7.5.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-7.5.19.tgz#d8915e6b717f8036a79d839ca9448198b002b0a7" + integrity sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" From 92c12477cae50ba0b15611889710437fcdfe5e03 Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 15:25:48 +0300 Subject: [PATCH 39/43] chore: replace buildInfo filter with node hook --- .githooks/pre-commit | 44 +++++++++++++++++++++++++++++ package.json | 7 +---- scripts/strip-package-buildinfo.cjs | 16 +++++++++++ 3 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 .githooks/pre-commit create mode 100644 scripts/strip-package-buildinfo.cjs diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 00000000..8af91ca6 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,44 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process' +import fs from 'node:fs' + +const stagedPackageJson = spawnSync('git', ['diff', '--cached', '--name-only', '--', 'package.json'], { + encoding: 'utf8', +}) + +if (stagedPackageJson.status !== 0) { + process.stderr.write(stagedPackageJson.stderr || 'Failed to inspect staged package.json\n') + process.exit(stagedPackageJson.status ?? 1) +} + +const hasStagedPackageJson = stagedPackageJson.stdout + .split(/\r?\n/u) + .map(line => line.trim()) + .includes('package.json') + +if (!hasStagedPackageJson) { + process.exit(0) +} + +const packageJsonPath = 'package.json' +const packageJson = fs.readFileSync(packageJsonPath, 'utf8') +const strippedPackageJson = spawnSync(process.execPath, ['scripts/strip-package-buildinfo.cjs'], { + encoding: 'utf8', + input: packageJson, +}) + +if (strippedPackageJson.status !== 0) { + process.stderr.write(strippedPackageJson.stderr || 'Failed to strip package.json buildInfo\n') + process.exit(strippedPackageJson.status ?? 1) +} + +if (strippedPackageJson.stdout !== packageJson) { + fs.writeFileSync(packageJsonPath, strippedPackageJson.stdout, 'utf8') +} + +const addPackageJson = spawnSync('git', ['add', '--', packageJsonPath], { + stdio: 'inherit', +}) + +process.exit(addPackageJson.status ?? 1) diff --git a/package.json b/package.json index ccac0f19..62264aa4 100644 --- a/package.json +++ b/package.json @@ -147,13 +147,8 @@ "config": { "forge": "./forge.config.ts" }, - "buildInfo": { - "VERSION": "2.17.0-beta", - "BRANCH": "fb384f80", - "BUILD_TIME": "2026-06-21T11:33:36.092Z" - }, "optionalDependencies": { "bufferutil": "^4.1.0", "utf-8-validate": "^6.0.6" } -} \ No newline at end of file +} diff --git a/scripts/strip-package-buildinfo.cjs b/scripts/strip-package-buildinfo.cjs new file mode 100644 index 00000000..1ca7eb74 --- /dev/null +++ b/scripts/strip-package-buildinfo.cjs @@ -0,0 +1,16 @@ +#!/usr/bin/env node + +const chunks = [] + +process.stdin.on('data', chunk => chunks.push(chunk)) +process.stdin.on('end', () => { + const input = Buffer.concat(chunks).toString('utf8') + + try { + const packageJson = JSON.parse(input) + delete packageJson.buildInfo + process.stdout.write(`${JSON.stringify(packageJson, null, 4)}\n`) + } catch { + process.stdout.write(input) + } +}) From 7eb607521f18dc3bb5b7e00096195b9f70aaceec Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 15:35:40 +0300 Subject: [PATCH 40/43] feat: tag GlitchTip events with user identity --- src/main/events/index.ts | 2 ++ src/main/modules/errorTracking.ts | 19 ++++++++++++++++++- src/renderer/app/errorTracking.ts | 19 ++++++++++++++++++- src/renderer/app/model/useAppAuthorization.ts | 3 +++ 4 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 3bae507f..89decea4 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -53,6 +53,7 @@ import { } from '../modules/updater/updateChannel' import { getUpdateSource, setUpdateSource } from '../modules/updater/updateSource' import { getModReleasesForSource } from '../modules/mod/network/releaseCatalog' +import { setMainErrorTrackingUser } from '../modules/errorTracking' import { CLIENT_REPO, listStableGitHubReleases, @@ -817,6 +818,7 @@ const registerLoggingEvents = (window: BrowserWindow): void => { ipcMain.on(MainEvents.AUTH_STATUS, (_event, data: any) => { authorized = data.status + setMainErrorTrackingUser(data.status ? data.user : null) tryOpenPendingAddon() }) ipcMain.handle(MainEvents.START_BROWSER_AUTH, async () => { diff --git a/src/main/modules/errorTracking.ts b/src/main/modules/errorTracking.ts index b1dd034d..8a511cc3 100644 --- a/src/main/modules/errorTracking.ts +++ b/src/main/modules/errorTracking.ts @@ -24,7 +24,9 @@ export const initMainErrorTracking = (): void => { release: ERROR_TRACKING_RELEASE, dist: ERROR_TRACKING_DIST, environment: ERROR_TRACKING_ENVIRONMENT, - sendDefaultPii: false, + dataCollection: { + userInfo: false, + }, maxBreadcrumbs: 0, tracesSampleRate: 0, attachScreenshot: false, @@ -51,6 +53,21 @@ export const initMainErrorTracking = (): void => { } } +export const setMainErrorTrackingUser = (user?: { id?: string | null; email?: string | null } | null): void => { + if (!initialized) return + const id = user?.id?.trim() + if (!id || id === '-1') { + Sentry.setUser(null) + return + } + + const email = user?.email?.trim() + Sentry.setUser({ + id, + ...(email ? { email } : {}), + }) +} + export const captureMainException = (error: unknown, source: string): void => { if (!initialized) return try { diff --git a/src/renderer/app/errorTracking.ts b/src/renderer/app/errorTracking.ts index b216be59..72041fa9 100644 --- a/src/renderer/app/errorTracking.ts +++ b/src/renderer/app/errorTracking.ts @@ -21,7 +21,9 @@ export const initRendererErrorTracking = (): void => { release: ERROR_TRACKING_RELEASE, dist: ERROR_TRACKING_DIST, environment: ERROR_TRACKING_ENVIRONMENT, - sendDefaultPii: false, + dataCollection: { + userInfo: false, + }, maxBreadcrumbs: 0, tracesSampleRate: 0, beforeSend: event => addErrorTrackingDebugIds(addErrorTrackingRuntimeTags(sanitizeErrorTrackingEvent(event))), @@ -37,6 +39,21 @@ export const initRendererErrorTracking = (): void => { } } +export const setRendererErrorTrackingUser = (user?: { id?: string | null; email?: string | null } | null): void => { + if (!initialized) return + const id = user?.id?.trim() + if (!id || id === '-1') { + Sentry.setUser(null) + return + } + + const email = user?.email?.trim() + Sentry.setUser({ + id, + ...(email ? { email } : {}), + }) +} + export const captureRendererException = (error: unknown, source: string): void => { if (!initialized) return try { diff --git a/src/renderer/app/model/useAppAuthorization.ts b/src/renderer/app/model/useAppAuthorization.ts index 78bd583d..0a9ca5f2 100644 --- a/src/renderer/app/model/useAppAuthorization.ts +++ b/src/renderer/app/model/useAppAuthorization.ts @@ -12,6 +12,7 @@ import getUserToken from '@shared/lib/auth/getUserToken' import config from '@common/appConfig' import { checkInternetAccess, notifyUserRetries } from '@shared/lib/utils' import type { GetMeData, GetMeVars } from '@app/AppShell.types' +import { setRendererErrorTrackingUser } from '@app/errorTracking' type Params = { router: { @@ -72,6 +73,7 @@ export function useAppAuthorization({ router, setIsAppDeprecated, setLoading, se const sendAuthStatus = useCallback((user?: Partial | null) => { if (user?.id) { + setRendererErrorTrackingUser({ id: user.id, email: user.email }) window.desktopEvents?.send(MainEvents.AUTH_STATUS, { status: true, user: { @@ -83,6 +85,7 @@ export function useAppAuthorization({ router, setIsAppDeprecated, setLoading, se return } + setRendererErrorTrackingUser(null) window.desktopEvents?.send(MainEvents.AUTH_STATUS, { status: false }) }, []) From 019dabb4cfc5465a761f535e2f6fc2a9c7d1194c Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 15:42:54 +0300 Subject: [PATCH 41/43] chore: update patchnotes for 2.17.0 --- PATCHNOTES.md | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/PATCHNOTES.md b/PATCHNOTES.md index 0beebf48..c7b22498 100644 --- a/PATCHNOTES.md +++ b/PATCHNOTES.md @@ -1,21 +1,27 @@ ### Новое -- Добавили поддержку системного прокси. PulseSync теперь корректнее работает в сетях, где интернет идёт через прокси. -- Добавили окно розыгрышей подписок и уведомления по ним. +- Добавили нативные кнопки управления окном на macOS. +- Добавили поддержку системного прокси при загрузке и установке мода. +- Добавили уведомления о покупке подписки, окончании подписки и активных розыгрышах. +- Обновили внешний вид профиля пользователя. ### Улучшено -- Улучшили установку Yandex Music и порядок проверки прав на macOS. -- Улучшили публикацию аддонов: окно публикации стало стабильнее. +- Установка и обновление мода стали стабильнее и лучше восстанавливаются после ошибок. +- Улучшили импорт `.pext` и `.zip` аддонов: PulseSync теперь проверяет архив перед установкой и сохраняет настройки существующего аддона. +- Улучшили работу аддонов после перезапуска PulseSync: активные темы, скрипты и настройки синхронизируются корректнее. +- Улучшили сообщения об ошибках при установке мода и обновлении приложения. - Обновили лицензионные файлы. ### Исправлено -- Настройки аддонов больше не сбрасываются при установке или обновлении аддона. -- Если у аддона нет README, но есть патчноуты, теперь по умолчанию открываются патчноуты, а не настройки. -- Toast-уведомления больше не оставляют невидимую область, которая мешает нажимать кнопки, и снова закрываются по клику. -- Исправили фокус окна после успешной авторизации. -- Исправили ошибку с форматом сообщения для `privacySettings`. -- Исправили отображение shimmer-блока новостей и таймаут проверки онлайна. -- Исправили несколько проблем в процессе установки мода и обновления приложения. +- Аддоны больше не пропадают и не сбрасываются после перезапуска PulseSync. +- OBS-виджет теперь может получать данные о треке без входа в аккаунт. +- В автономном режиме разделы, требующие авторизации, теперь показывают понятное окно входа вместо молчаливого отключения. +- Исправили определение версии и данных установленной Яндекс Музыки. +- Исправили верхнее действие контекстного меню. +- Исправили отступы новостей, стиль поиска пользователей и отображение подсказок на главной странице. +- В русской локализации заменили "Каталог расширений" на "Каталог аддонов". +- В нужных местах снова можно выделять текст. ### Техническое -- Убрали устаревший HTTP-эндпоинт `/get_handle`. -- Поправили CORS-обработку локального HTTP-сервера. +- Обновили внутренний модуль для работы с файлами, установкой мода и проверкой приложения. +- Улучшили автоматическую диагностику сбоев, чтобы ошибки было проще находить и исправлять. +- Обновили сборку и служебные инструменты релиза. From 4e376c95af0f51ca85f0eadebee91bbeb555a894 Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 16:13:02 +0300 Subject: [PATCH 42/43] fix: scope dev build change detection --- .github/workflows/build-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 4dbf0e60..8737665c 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -26,6 +26,7 @@ jobs: - uses: dorny/paths-filter@v4.0.1 id: filter with: + base: ${{ github.ref_name }} list-files: shell filters: | build: @@ -49,7 +50,6 @@ jobs: - 'Info.plist' - 'graphql.config.yml' - 'schema.graphql' - - '.github/workflows/build-dev-windows.yml' - name: Report changed build-related files shell: bash From 061bc90c78c0905752212363be11424db337759b Mon Sep 17 00:00:00 2001 From: foreA-adoxid Date: Mon, 29 Jun 2026 16:20:57 +0300 Subject: [PATCH 43/43] fix: remove broad negation from dev build filter --- .github/workflows/build-dev.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 8737665c..c7cde25d 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -36,7 +36,6 @@ jobs: - 'nativeModules/**' - 'scripts/**' - 'scriptsInstaller/**' - - '!packaging/**' - 'package.json' - 'yarn.lock' - '.yarnrc.yml'