From 04a50b8b46bef378c8151a2aabb5360f254a3e21 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 00:17:46 +0700 Subject: [PATCH 001/340] test: debug farcaster --- .../src/integrations/social/farcaster.provider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts index 32e68bfa..9c0d8492 100644 --- a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts @@ -64,6 +64,7 @@ export class FarcasterProvider codeVerifier: string; refresh?: string; }) { + console.log(Buffer.from(params.code, 'base64').toString()); const data = JSON.parse(Buffer.from(params.code, 'base64').toString()); return { id: String(data.fid), From b2abb20da730325c168994e73ab53b20f9427225 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 00:21:00 +0700 Subject: [PATCH 002/340] test: debug farcaster --- apps/backend/src/api/routes/integrations.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index c4b1ff50..b2aaf372 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -48,7 +48,7 @@ export class IntegrationsController { private _postService: PostsService ) {} @Get('/') - getIntegration() { + getIntegrations() { return this._integrationManager.getAllIntegrations(); } From 98edbc45889fc02dff6783759f21712218803d1b Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 00:42:27 +0700 Subject: [PATCH 003/340] feat: fix farcaster --- apps/frontend/src/components/auth/nayner.auth.button.tsx | 1 + .../src/integrations/social/farcaster.provider.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/auth/nayner.auth.button.tsx b/apps/frontend/src/components/auth/nayner.auth.button.tsx index 345828eb..68cd5667 100644 --- a/apps/frontend/src/components/auth/nayner.auth.button.tsx +++ b/apps/frontend/src/components/auth/nayner.auth.button.tsx @@ -31,6 +31,7 @@ export const NeynarAuthButton: FC<{ ) { authWindowRef.current?.close(); window.removeEventListener('message', handleMessage); // Remove listener here + delete event.data.user.profile; const _user = { signer_uuid: event.data.signer_uuid, ...event.data.user, diff --git a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts index 9c0d8492..32e68bfa 100644 --- a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts @@ -64,7 +64,6 @@ export class FarcasterProvider codeVerifier: string; refresh?: string; }) { - console.log(Buffer.from(params.code, 'base64').toString()); const data = JSON.parse(Buffer.from(params.code, 'base64').toString()); return { id: String(data.fid), From 4eac6b9ff858a28900a9d915a67ea95b9db169eb Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 20:37:09 +0700 Subject: [PATCH 004/340] feat: gmb --- .../src/api/routes/integrations.controller.ts | 9 + apps/frontend/public/icons/platforms/gmb.png | Bin 0 -> 16779 bytes .../launches/add.provider.component.tsx | 3 +- .../components/layout/continue.provider.tsx | 132 ++--- .../continue-provider/gmb/gmb.continue.tsx | 157 ++++++ .../providers/continue-provider/list.tsx | 3 + .../new-launch/providers/gmb/gmb.provider.tsx | 193 +++++++ .../providers/show.all.providers.tsx | 5 + .../integrations/integration.service.ts | 35 ++ .../all.providers.settings.ts | 3 + .../providers-settings/gmb.settings.dto.ts | 68 +++ .../src/integrations/integration.manager.ts | 2 + .../src/integrations/social/gmb.provider.ts | 518 ++++++++++++++++++ 13 files changed, 1061 insertions(+), 67 deletions(-) create mode 100644 apps/frontend/public/icons/platforms/gmb.png create mode 100644 apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index b2aaf372..c1c2990b 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -560,6 +560,15 @@ export class IntegrationsController { return this._integrationService.saveLinkedin(org.id, id, body.page); } + @Post('/gmb/:id') + async saveGmb( + @Param('id') id: string, + @Body() body: { id: string; accountId: string; locationName: string }, + @GetOrgFromRequest() org: Organization + ) { + return this._integrationService.saveGmb(org.id, id, body); + } + @Post('/enable') enableChannel( @GetOrgFromRequest() org: Organization, diff --git a/apps/frontend/public/icons/platforms/gmb.png b/apps/frontend/public/icons/platforms/gmb.png new file mode 100644 index 0000000000000000000000000000000000000000..2ff782a9e9be0b0a799ecb110e619866608edde4 GIT binary patch literal 16779 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 N#>Y void) => { const modal = useModals(); @@ -457,7 +458,7 @@ export const AddProviderComponent: FC<{ ) : ( )} diff --git a/apps/frontend/src/components/layout/continue.provider.tsx b/apps/frontend/src/components/layout/continue.provider.tsx index 46d7f9c8..0e5a5660 100644 --- a/apps/frontend/src/components/layout/continue.provider.tsx +++ b/apps/frontend/src/components/layout/continue.provider.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useMemo } from 'react'; +import React, { FC, useCallback, useEffect, useMemo } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration'; @@ -7,6 +7,7 @@ import useSWR, { useSWRConfig } from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { continueProviderList } from '@gitroom/frontend/components/new-launch/providers/continue-provider/list'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; +import { useModals } from '@gitroom/frontend/components/layout/new-modal'; export const Null: FC<{ closeModal: () => void; existingId: string[]; @@ -46,74 +47,73 @@ export const ContinueProvider: FC = () => { continueProviderList[added as keyof typeof continueProviderList] || Null ); }, [added]); + if (!added || !continueId || !integrations) { return null; } + return ( -
-
e.stopPropagation()} - > -
- - -
- - p.internalId)} - /> - -
-
-
-
+ p.internalId)} + provider={Provider} + /> ); }; + +const ModalContent: FC<{ + continueId: string; + added: any; + provider: any; + closeModal: () => void; + integrations: string[]; +}> = ({ continueId, added, provider: Provider, closeModal, integrations }) => { + return ( + + + + ); +}; + +const ContinueModal: FC<{ + continueId: string; + added: any; + provider: any; + integrations: string[]; +}> = (props) => { + const modals = useModals(); + + useEffect(() => { + modals.openModal({ + title: 'Configure Channel', + children: (close) => , + }); + }, []); + + return null; +}; diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx new file mode 100644 index 00000000..54ceb1ec --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx @@ -0,0 +1,157 @@ +'use client'; + +import { FC, useCallback, useMemo, useState } from 'react'; +import useSWR from 'swr'; +import clsx from 'clsx'; +import { Button } from '@gitroom/react/form/button'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; + +export const GmbContinue: FC<{ + closeModal: () => void; + existingId: string[]; +}> = (props) => { + const { closeModal, existingId } = props; + const call = useCustomProviderFunction(); + const { integration } = useIntegration(); + const [location, setSelectedLocation] = useState(null); + const fetch = useFetch(); + const t = useT(); + + const loadPages = useCallback(async () => { + try { + const pages = await call.get('pages'); + return pages; + } catch (e) { + closeModal(); + } + }, []); + + const setLocation = useCallback( + (param: { id: string; accountId: string; locationName: string }) => () => { + setSelectedLocation(param); + }, + [] + ); + + const { data, isLoading } = useSWR('load-gmb-locations', loadPages, { + refreshWhenHidden: false, + refreshWhenOffline: false, + revalidateOnFocus: false, + revalidateIfStale: false, + revalidateOnMount: true, + revalidateOnReconnect: false, + refreshInterval: 0, + }); + + const saveGmb = useCallback(async () => { + await fetch(`/integrations/gmb/${integration?.id}`, { + method: 'POST', + body: JSON.stringify(location), + }); + closeModal(); + }, [integration, location]); + + const filteredData = useMemo(() => { + return ( + data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] + ); + }, [data, existingId]); + + if (!isLoading && !data?.length) { + return ( +
+ {t( + 'gmb_no_locations_found', + "We couldn't find any business locations connected to your account." + )} +
+
+ {t( + 'gmb_ensure_business_verified', + 'Please ensure your business is verified on Google My Business.' + )} +
+
+ {t( + 'gmb_try_again', + 'Please close this dialog, delete the integration and try again.' + )} +
+ ); + } + + return ( +
+
{t('select_location', 'Select Business Location:')}
+
+ {filteredData?.map( + (p: { + id: string; + name: string; + accountId: string; + locationName: string; + picture: { + data: { + url: string; + }; + }; + }) => ( +
+
+ {p.picture?.data?.url ? ( + {p.name} + ) : ( +
+ + + + +
+ )} +
+
{p.name}
+
+ ) + )} +
+
+ +
+
+ ); +}; + diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx index 03b182e2..f8052fae 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx @@ -3,8 +3,11 @@ import { InstagramContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/instagram/instagram.continue'; import { FacebookContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/facebook/facebook.continue'; import { LinkedinContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/linkedin/linkedin.continue'; +import { GmbContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/gmb/gmb.continue'; + export const continueProviderList = { instagram: InstagramContinue, facebook: FacebookContinue, 'linkedin-page': LinkedinContinue, + gmb: GmbContinue, }; diff --git a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx new file mode 100644 index 00000000..2a1b428c --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx @@ -0,0 +1,193 @@ +'use client'; + +import { FC, useCallback, useEffect } from 'react'; +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { GmbSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/gmb.settings.dto'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { Input } from '@gitroom/react/form/input'; +import { Select } from '@gitroom/react/form/select'; +import { useWatch } from 'react-hook-form'; + +const topicTypes = [ + { + label: 'Standard Update', + value: 'STANDARD', + }, + { + label: 'Event', + value: 'EVENT', + }, + { + label: 'Offer', + value: 'OFFER', + }, +]; + +const callToActionTypes = [ + { + label: 'None', + value: '', + }, + { + label: 'Book', + value: 'BOOK', + }, + { + label: 'Order Online', + value: 'ORDER', + }, + { + label: 'Shop', + value: 'SHOP', + }, + { + label: 'Learn More', + value: 'LEARN_MORE', + }, + { + label: 'Sign Up', + value: 'SIGN_UP', + }, + { + label: 'Get Offer', + value: 'GET_OFFER', + }, + { + label: 'Call', + value: 'CALL', + }, +]; + +const GmbSettings: FC = () => { + const { register, control } = useSettings(); + const topicType = useWatch({ control, name: 'topicType' }); + const callToActionType = useWatch({ control, name: 'callToActionType' }); + + return ( +
+ + + + + {callToActionType && callToActionType !== 'CALL' && ( + + )} + + {topicType === 'EVENT' && ( +
+
Event Details
+ +
+ + +
+
+ + +
+
+ )} + + {topicType === 'OFFER' && ( +
+
Offer Details
+ + + +
+ )} +
+ ); +}; + +export default withProvider({ + postComment: PostComment.POST, + minimumCharacters: [], + SettingsComponent: GmbSettings, + CustomPreviewComponent: undefined, + dto: GmbSettingsDto, + checkValidity: async (items, settings) => { + // GMB posts can have text only, or text with one image + if (items.length > 0 && items[0].length > 1) { + return 'Google My Business posts can only have one image'; + } + + // Check for video - GMB doesn't support video in local posts + if (items.length > 0 && items[0].length > 0) { + const media = items[0][0]; + if (media.path.indexOf('mp4') > -1) { + return 'Google My Business posts do not support video attachments'; + } + } + + // Event posts require a title + if (settings.topicType === 'EVENT' && !settings.eventTitle) { + return 'Event posts require an event title'; + } + + return true; + }, + maximumCharacters: 1500, +}); + diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 9b1c9e51..187a9657 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -32,6 +32,7 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { PostComment } from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; import WordpressProvider from '@gitroom/frontend/components/new-launch/providers/wordpress/wordpress.provider'; import ListmonkProvider from '@gitroom/frontend/components/new-launch/providers/listmonk/listmonk.provider'; +import GmbProvider from '@gitroom/frontend/components/new-launch/providers/gmb/gmb.provider'; export const Providers = [ { @@ -138,6 +139,10 @@ export const Providers = [ identifier: 'listmonk', component: ListmonkProvider, }, + { + identifier: 'gmb', + component: GmbProvider, + }, ]; export const ShowAllProviders = forwardRef((props, ref) => { const { date, current, global, selectedIntegrations, allIntegrations } = diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index a54758e6..ef843407 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -11,6 +11,7 @@ import { import { Integration, Organization } from '@prisma/client'; import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; import { LinkedinPageProvider } from '@gitroom/nestjs-libraries/integrations/social/linkedin.page.provider'; +import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; import dayjs from 'dayjs'; import { timer } from '@gitroom/helpers/utils/timer'; import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; @@ -367,6 +368,40 @@ export class IntegrationService { return { success: true }; } + async saveGmb( + org: string, + id: string, + data: { id: string; accountId: string; locationName: string } + ) { + const getIntegration = await this._integrationRepository.getIntegrationById( + org, + id + ); + if (getIntegration && !getIntegration.inBetweenSteps) { + throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); + } + + const gmb = this._integrationManager.getSocialIntegration( + 'gmb' + ) as GmbProvider; + const getIntegrationInformation = await gmb.fetchPageInformation( + getIntegration?.token!, + data + ); + + await this.checkForDeletedOnceAndUpdate(org, getIntegrationInformation.id); + await this._integrationRepository.updateIntegration(id, { + picture: getIntegrationInformation.picture, + internalId: getIntegrationInformation.id, + name: getIntegrationInformation.name, + inBetweenSteps: false, + token: getIntegration?.token!, + profile: getIntegrationInformation.username, + }); + + return { success: true }; + } + async checkAnalytics( org: Organization, integration: string, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 0e892e60..7579f460 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -15,6 +15,7 @@ import { DevToSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/hashnode.settings.dto'; import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto'; import { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/listmonk.dto'; +import { GmbSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/gmb.settings.dto'; export type ProviderExtension = { __type: T } & M; export type AllProvidersSettings = @@ -36,6 +37,7 @@ export type AllProvidersSettings = | ProviderExtension<'hashnode', HashnodeSettingsDto> | ProviderExtension<'wordpress', WordpressDto> | ProviderExtension<'listmonk', ListmonkDto> + | ProviderExtension<'gmb', GmbSettingsDto> | ProviderExtension<'facebook', None> | ProviderExtension<'threads', None> | ProviderExtension<'mastodon', None> @@ -67,6 +69,7 @@ export const allProviders = (setEmpty?: any) => { { value: WordpressDto, name: 'wordpress' }, { value: HashnodeSettingsDto, name: 'hashnode' }, { value: ListmonkDto, name: 'listmonk' }, + { value: GmbSettingsDto, name: 'gmb' }, { value: setEmpty, name: 'facebook' }, { value: setEmpty, name: 'threads' }, { value: setEmpty, name: 'mastodon' }, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts new file mode 100644 index 00000000..221880d8 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts @@ -0,0 +1,68 @@ +import { IsOptional, IsString, IsIn, IsUrl, ValidateIf } from 'class-validator'; + +export class GmbSettingsDto { + @IsOptional() + @IsIn(['STANDARD', 'EVENT', 'OFFER']) + topicType?: 'STANDARD' | 'EVENT' | 'OFFER'; + + @IsOptional() + @IsIn([ + 'BOOK', + 'ORDER', + 'SHOP', + 'LEARN_MORE', + 'SIGN_UP', + 'GET_OFFER', + 'CALL', + ]) + callToActionType?: + | 'BOOK' + | 'ORDER' + | 'SHOP' + | 'LEARN_MORE' + | 'SIGN_UP' + | 'GET_OFFER' + | 'CALL'; + + @IsOptional() + @ValidateIf((o) => o.callToActionType) + @IsUrl() + callToActionUrl?: string; + + // Event-specific fields + @IsOptional() + @ValidateIf((o) => o.topicType === 'EVENT') + @IsString() + eventTitle?: string; + + @IsOptional() + @IsString() + eventStartDate?: string; + + @IsOptional() + @IsString() + eventEndDate?: string; + + @IsOptional() + @IsString() + eventStartTime?: string; + + @IsOptional() + @IsString() + eventEndTime?: string; + + // Offer-specific fields + @IsOptional() + @IsString() + offerCouponCode?: string; + + @IsOptional() + @ValidateIf((o) => o.offerRedeemUrl) + @IsUrl() + offerRedeemUrl?: string; + + @IsOptional() + @IsString() + offerTerms?: string; +} + diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index dea5122d..a8ef22a4 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -28,6 +28,7 @@ import { NostrProvider } from '@gitroom/nestjs-libraries/integrations/social/nos import { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.provider'; import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social/wordpress.provider'; import { ListmonkProvider } from '@gitroom/nestjs-libraries/integrations/social/listmonk.provider'; +import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; export const socialIntegrationList: SocialProvider[] = [ new XProvider(), @@ -39,6 +40,7 @@ export const socialIntegrationList: SocialProvider[] = [ new FacebookProvider(), new ThreadsProvider(), new YoutubeProvider(), + new GmbProvider(), new TiktokProvider(), new PinterestProvider(), new DribbbleProvider(), diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts new file mode 100644 index 00000000..002de587 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -0,0 +1,518 @@ +import { + AnalyticsData, + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { google } from 'googleapis'; +import { OAuth2Client } from 'google-auth-library/build/src/auth/oauth2client'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import * as process from 'node:process'; +import dayjs from 'dayjs'; +import { Rules } from '@gitroom/nestjs-libraries/chat/rules.description.decorator'; +import { GmbSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/gmb.settings.dto'; + +const clientAndGmb = () => { + const client = new google.auth.OAuth2({ + clientId: process.env.GOOGLE_GMB_CLIENT_ID || process.env.YOUTUBE_CLIENT_ID, + clientSecret: + process.env.GOOGLE_GMB_CLIENT_SECRET || process.env.YOUTUBE_CLIENT_SECRET, + redirectUri: `${process.env.FRONTEND_URL}/integrations/social/gmb`, + }); + + const oauth2 = (newClient: OAuth2Client) => + google.oauth2({ + version: 'v2', + auth: newClient, + }); + + return { client, oauth2 }; +}; + +@Rules( + 'Google My Business posts can have text content and optionally one image. Posts can be updates, events, or offers.' +) +export class GmbProvider extends SocialAbstract implements SocialProvider { + override maxConcurrentJob = 3; + identifier = 'gmb'; + name = 'Google My Business'; + isBetweenSteps = true; + scopes = [ + 'https://www.googleapis.com/auth/userinfo.profile', + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/business.manage', + ]; + editor = 'normal' as const; + dto = GmbSettingsDto; + + maxLength() { + return 1500; + } + + override handleErrors(body: string): + | { + type: 'refresh-token' | 'bad-body'; + value: string; + } + | undefined { + if (body.includes('UNAUTHENTICATED') || body.includes('invalid_grant')) { + return { + type: 'refresh-token', + value: 'Please re-authenticate your Google My Business account', + }; + } + + if (body.includes('PERMISSION_DENIED')) { + return { + type: 'refresh-token', + value: + 'Permission denied. Please ensure you have access to this business location.', + }; + } + + if (body.includes('NOT_FOUND')) { + return { + type: 'bad-body', + value: 'Business location not found. It may have been deleted.', + }; + } + + if (body.includes('INVALID_ARGUMENT')) { + return { + type: 'bad-body', + value: 'Invalid post content. Please check your post details.', + }; + } + + if (body.includes('RESOURCE_EXHAUSTED')) { + return { + type: 'bad-body', + value: 'Rate limit exceeded. Please try again later.', + }; + } + + return undefined; + } + + async refreshToken(refresh_token: string): Promise { + const { client, oauth2 } = clientAndGmb(); + client.setCredentials({ refresh_token }); + const { credentials } = await client.refreshAccessToken(); + const user = oauth2(client); + const expiryDate = new Date(credentials.expiry_date!); + const unixTimestamp = + Math.floor(expiryDate.getTime() / 1000) - + Math.floor(new Date().getTime() / 1000); + + const { data } = await user.userinfo.get(); + + return { + accessToken: credentials.access_token!, + expiresIn: unixTimestamp!, + refreshToken: credentials.refresh_token || refresh_token, + id: data.id!, + name: data.name!, + picture: data?.picture || '', + username: '', + }; + } + + async generateAuthUrl() { + const state = makeId(7); + const { client } = clientAndGmb(); + return { + url: client.generateAuthUrl({ + access_type: 'offline', + prompt: 'consent', + state, + redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/gmb`, + scope: this.scopes.slice(0), + }), + codeVerifier: makeId(11), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const { client, oauth2 } = clientAndGmb(); + const { tokens } = await client.getToken(params.code); + client.setCredentials(tokens); + const { scopes } = await client.getTokenInfo(tokens.access_token!); + this.checkScopes(this.scopes, scopes); + + const user = oauth2(client); + const { data } = await user.userinfo.get(); + + const expiryDate = new Date(tokens.expiry_date!); + const unixTimestamp = + Math.floor(expiryDate.getTime() / 1000) - + Math.floor(new Date().getTime() / 1000); + + return { + accessToken: tokens.access_token!, + expiresIn: unixTimestamp, + refreshToken: tokens.refresh_token!, + id: data.id!, + name: data.name!, + picture: data?.picture || '', + username: '', + }; + } + + async pages(accessToken: string) { + // Get all accounts first + const accountsResponse = await fetch( + 'https://mybusinessaccountmanagement.googleapis.com/v1/accounts', + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + const accountsData = await accountsResponse.json(); + + if (!accountsData.accounts || accountsData.accounts.length === 0) { + return []; + } + + // Get locations for each account + const allLocations: Array<{ + id: string; + name: string; + picture: { data: { url: string } }; + accountId: string; + locationName: string; + }> = []; + + for (const account of accountsData.accounts) { + const accountName = account.name; // format: accounts/{accountId} + + try { + const locationsResponse = await fetch( + `https://mybusinessbusinessinformation.googleapis.com/v1/${accountName}/locations?readMask=name,title,storefrontAddress,metadata`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + const locationsData = await locationsResponse.json(); + + if (locationsData.locations) { + for (const location of locationsData.locations) { + // Get profile photo if available + let photoUrl = ''; + try { + const mediaResponse = await fetch( + `https://mybusinessbusinessinformation.googleapis.com/v1/${location.name}/media`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + const mediaData = await mediaResponse.json(); + if (mediaData.mediaItems && mediaData.mediaItems.length > 0) { + const profilePhoto = mediaData.mediaItems.find( + (m: any) => + m.mediaFormat === 'PHOTO' && + m.locationAssociation?.category === 'PROFILE' + ); + if (profilePhoto?.googleUrl) { + photoUrl = profilePhoto.googleUrl; + } else if (mediaData.mediaItems[0]?.googleUrl) { + photoUrl = mediaData.mediaItems[0].googleUrl; + } + } + } catch { + // Ignore media fetch errors + } + + allLocations.push({ + id: location.name, // format: locations/{locationId} + name: location.title || 'Unnamed Location', + picture: { data: { url: photoUrl } }, + accountId: accountName, + locationName: location.name, + }); + } + } + } catch (error) { + // Continue with other accounts if one fails + console.error(`Failed to fetch locations for account ${accountName}:`, error); + } + } + + return allLocations; + } + + async fetchPageInformation( + accessToken: string, + data: { id: string; accountId: string; locationName: string } + ) { + // Fetch location details + const locationResponse = await fetch( + `https://mybusinessbusinessinformation.googleapis.com/v1/${data.id}?readMask=name,title,storefrontAddress,metadata`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + const locationData = await locationResponse.json(); + + // Try to get profile photo + let photoUrl = ''; + try { + const mediaResponse = await fetch( + `https://mybusinessbusinessinformation.googleapis.com/v1/${data.id}/media`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + const mediaData = await mediaResponse.json(); + if (mediaData.mediaItems && mediaData.mediaItems.length > 0) { + const profilePhoto = mediaData.mediaItems.find( + (m: any) => + m.mediaFormat === 'PHOTO' && + m.locationAssociation?.category === 'PROFILE' + ); + if (profilePhoto?.googleUrl) { + photoUrl = profilePhoto.googleUrl; + } else if (mediaData.mediaItems[0]?.googleUrl) { + photoUrl = mediaData.mediaItems[0].googleUrl; + } + } + } catch { + // Ignore media fetch errors + } + + return { + id: data.id, + name: locationData.title || 'Unnamed Location', + access_token: accessToken, + picture: photoUrl, + username: '', + }; + } + + async reConnect( + id: string, + requiredId: string, + accessToken: string + ): Promise { + const pages = await this.pages(accessToken); + const findPage = pages.find((p) => p.id === requiredId); + + if (!findPage) { + throw new Error('Location not found'); + } + + const information = await this.fetchPageInformation(accessToken, { + id: requiredId, + accountId: findPage.accountId, + locationName: findPage.locationName, + }); + + return { + id: information.id, + name: information.name, + accessToken: information.access_token, + refreshToken: information.access_token, + expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), + picture: information.picture, + username: information.username, + }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[] + ): Promise { + const [firstPost] = postDetails; + const { settings } = firstPost; + + // Build the local post request body + const postBody: any = { + languageCode: 'en', + summary: firstPost.message, + topicType: settings?.topicType || 'STANDARD', + }; + + // Add call to action if provided + if (settings?.callToActionType && settings?.callToActionUrl) { + postBody.callToAction = { + actionType: settings.callToActionType, + url: settings.callToActionUrl, + }; + } + + // Add media if provided + if (firstPost.media && firstPost.media.length > 0) { + const mediaItem = firstPost.media[0]; + postBody.media = [ + { + mediaFormat: mediaItem.type === 'video' ? 'VIDEO' : 'PHOTO', + sourceUrl: mediaItem.path, + }, + ]; + } + + // Add event details if it's an event post + if (settings?.topicType === 'EVENT' && settings?.eventTitle) { + postBody.event = { + title: settings.eventTitle, + schedule: { + startDate: this.formatDate(settings.eventStartDate), + endDate: this.formatDate(settings.eventEndDate), + ...(settings.eventStartTime && { + startTime: this.formatTime(settings.eventStartTime), + }), + ...(settings.eventEndTime && { + endTime: this.formatTime(settings.eventEndTime), + }), + }, + }; + } + + // Add offer details if it's an offer post + if (settings?.topicType === 'OFFER') { + postBody.offer = { + couponCode: settings?.offerCouponCode || undefined, + redeemOnlineUrl: settings?.offerRedeemUrl || undefined, + termsConditions: settings?.offerTerms || undefined, + }; + } + + // Create the local post + const response = await this.fetch( + `https://mybusiness.googleapis.com/v4/${id}/localPosts`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(postBody), + }, + 'create local post' + ); + + const postData = await response.json(); + + // Extract the post ID and construct the URL + const postId = postData.name || ''; + const locationId = id.split('/').pop(); + + // GMB posts don't have direct URLs, but we can link to the business profile + const releaseURL = `https://business.google.com/locations/${locationId}`; + + return [ + { + id: firstPost.id, + postId: postId, + releaseURL: releaseURL, + status: 'success', + }, + ]; + } + + private formatDate(dateString?: string): any { + if (!dateString) { + return { + year: dayjs().year(), + month: dayjs().month() + 1, + day: dayjs().date(), + }; + } + const date = dayjs(dateString); + return { + year: date.year(), + month: date.month() + 1, + day: date.date(), + }; + } + + private formatTime(timeString?: string): any { + if (!timeString) { + return undefined; + } + const [hours, minutes] = timeString.split(':').map(Number); + return { + hours: hours || 0, + minutes: minutes || 0, + seconds: 0, + nanos: 0, + }; + } + + async analytics( + id: string, + accessToken: string, + date: number + ): Promise { + try { + const endDate = dayjs().format('YYYY-MM-DD'); + const startDate = dayjs().subtract(date, 'day').format('YYYY-MM-DD'); + + // Use the Business Profile Performance API + const response = await fetch( + `https://businessprofileperformance.googleapis.com/v1/${id}:getDailyMetricsTimeSeries?dailyMetric=WEBSITE_CLICKS&dailyMetric=CALL_CLICKS&dailyMetric=BUSINESS_DIRECTION_REQUESTS&dailyMetric=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetric=BUSINESS_IMPRESSIONS_MOBILE_MAPS&dailyRange.startDate.year=${dayjs(startDate).year()}&dailyRange.startDate.month=${dayjs(startDate).month() + 1}&dailyRange.startDate.day=${dayjs(startDate).date()}&dailyRange.endDate.year=${dayjs(endDate).year()}&dailyRange.endDate.month=${dayjs(endDate).month() + 1}&dailyRange.endDate.day=${dayjs(endDate).date()}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + + const data = await response.json(); + + if (!data.timeSeries || data.timeSeries.length === 0) { + return []; + } + + const metricLabels: { [key: string]: string } = { + WEBSITE_CLICKS: 'Website Clicks', + CALL_CLICKS: 'Phone Calls', + BUSINESS_DIRECTION_REQUESTS: 'Direction Requests', + BUSINESS_IMPRESSIONS_DESKTOP_MAPS: 'Desktop Map Views', + BUSINESS_IMPRESSIONS_MOBILE_MAPS: 'Mobile Map Views', + }; + + const analytics: AnalyticsData[] = []; + + for (const series of data.timeSeries) { + const metricName = series.dailyMetric; + const label = metricLabels[metricName] || metricName; + + const dataPoints = + series.timeSeries?.datedValues?.map((dv: any) => ({ + total: dv.value || 0, + date: `${dv.date.year}-${String(dv.date.month).padStart(2, '0')}-${String(dv.date.day).padStart(2, '0')}`, + })) || []; + + if (dataPoints.length > 0) { + analytics.push({ + label, + percentageChange: 0, + data: dataPoints, + }); + } + } + + return analytics; + } catch (error) { + console.error('Error fetching GMB analytics:', error); + return []; + } + } +} From 69e8944437298191d6dae834aa0f584e3dece714 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 20:56:57 +0700 Subject: [PATCH 005/340] feat: google my business --- .../src/api/routes/integrations.controller.ts | 2 +- .../continue-provider/gmb/gmb.continue.tsx | 8 +-- .../new-launch/providers/gmb/gmb.provider.tsx | 29 +++++----- .../integrations/integration.service.ts | 2 +- .../providers-settings/gmb.settings.dto.ts | 3 +- .../src/integrations/social/gmb.provider.ts | 55 ++++++++++++++----- 6 files changed, 63 insertions(+), 36 deletions(-) diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index c1c2990b..3cd717c1 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -563,7 +563,7 @@ export class IntegrationsController { @Post('/gmb/:id') async saveGmb( @Param('id') id: string, - @Body() body: { id: string; accountId: string; locationName: string }, + @Body() body: { id: string; accountName: string; locationName: string }, @GetOrgFromRequest() org: Organization ) { return this._integrationService.saveGmb(org.id, id, body); diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx index 54ceb1ec..a1862c0a 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx @@ -18,7 +18,7 @@ export const GmbContinue: FC<{ const { integration } = useIntegration(); const [location, setSelectedLocation] = useState(null); const fetch = useFetch(); @@ -34,7 +34,7 @@ export const GmbContinue: FC<{ }, []); const setLocation = useCallback( - (param: { id: string; accountId: string; locationName: string }) => () => { + (param: { id: string; accountName: string; locationName: string }) => () => { setSelectedLocation(param); }, [] @@ -95,7 +95,7 @@ export const GmbContinue: FC<{ (p: { id: string; name: string; - accountId: string; + accountName: string; locationName: string; picture: { data: { @@ -111,7 +111,7 @@ export const GmbContinue: FC<{ )} onClick={setLocation({ id: p.id, - accountId: p.accountId, + accountName: p.accountName, locationName: p.locationName, })} > diff --git a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx index 2a1b428c..7b32a4dd 100644 --- a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx @@ -29,7 +29,7 @@ const topicTypes = [ const callToActionTypes = [ { label: 'None', - value: '', + value: 'NONE', }, { label: 'Book', @@ -84,7 +84,7 @@ const GmbSettings: FC = () => { - {callToActionType && callToActionType !== 'CALL' && ( - - )} + {callToActionType && + callToActionType !== 'NONE' && + callToActionType !== 'CALL' && ( + + )} {topicType === 'EVENT' && (
@@ -116,11 +118,7 @@ const GmbSettings: FC = () => { type="date" {...register('eventStartDate')} /> - +
0 && items[0].length > 1) { return 'Google My Business posts can only have one image'; } - + // Check for video - GMB doesn't support video in local posts if (items.length > 0 && items[0].length > 0) { const media = items[0][0]; @@ -190,4 +188,3 @@ export default withProvider({ }, maximumCharacters: 1500, }); - diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index ef843407..554c6c35 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -371,7 +371,7 @@ export class IntegrationService { async saveGmb( org: string, id: string, - data: { id: string; accountId: string; locationName: string } + data: { id: string; accountName: string; locationName: string } ) { const getIntegration = await this._integrationRepository.getIntegrationById( org, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts index 221880d8..a4fb5e9d 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/gmb.settings.dto.ts @@ -7,6 +7,7 @@ export class GmbSettingsDto { @IsOptional() @IsIn([ + 'NONE', 'BOOK', 'ORDER', 'SHOP', @@ -16,6 +17,7 @@ export class GmbSettingsDto { 'CALL', ]) callToActionType?: + | 'NONE' | 'BOOK' | 'ORDER' | 'SHOP' @@ -65,4 +67,3 @@ export class GmbSettingsDto { @IsString() offerTerms?: string; } - diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts index 002de587..cd20998a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -186,7 +186,7 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { id: string; name: string; picture: { data: { url: string } }; - accountId: string; + accountName: string; locationName: string; }> = []; @@ -206,6 +206,11 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { if (locationsData.locations) { for (const location of locationsData.locations) { + // location.name is in format: locations/{locationId} + // We need the full path: accounts/{accountId}/locations/{locationId} + const locationId = location.name.replace('locations/', ''); + const fullResourceName = `${accountName}/locations/${locationId}`; + // Get profile photo if available let photoUrl = ''; try { @@ -235,17 +240,21 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { } allLocations.push({ - id: location.name, // format: locations/{locationId} + // id is the full resource path for the v4 API: accounts/{accountId}/locations/{locationId} + id: fullResourceName, name: location.title || 'Unnamed Location', picture: { data: { url: photoUrl } }, - accountId: accountName, + accountName: accountName, locationName: location.name, }); } } } catch (error) { // Continue with other accounts if one fails - console.error(`Failed to fetch locations for account ${accountName}:`, error); + console.error( + `Failed to fetch locations for account ${accountName}:`, + error + ); } } @@ -254,11 +263,13 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { async fetchPageInformation( accessToken: string, - data: { id: string; accountId: string; locationName: string } + data: { id: string; accountName: string; locationName: string } ) { - // Fetch location details + // data.id is the full resource path: accounts/{accountId}/locations/{locationId} + // data.locationName is the v1 API format: locations/{locationId} + // Fetch location details using the v1 API format const locationResponse = await fetch( - `https://mybusinessbusinessinformation.googleapis.com/v1/${data.id}?readMask=name,title,storefrontAddress,metadata`, + `https://mybusinessbusinessinformation.googleapis.com/v1/${data.locationName}?readMask=name,title,storefrontAddress,metadata`, { headers: { Authorization: `Bearer ${accessToken}`, @@ -271,7 +282,7 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { let photoUrl = ''; try { const mediaResponse = await fetch( - `https://mybusinessbusinessinformation.googleapis.com/v1/${data.id}/media`, + `https://mybusinessbusinessinformation.googleapis.com/v1/${data.locationName}/media`, { headers: { Authorization: `Bearer ${accessToken}`, @@ -296,6 +307,7 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { } return { + // Return the full resource path as id (for v4 Local Posts API) id: data.id, name: locationData.title || 'Unnamed Location', access_token: accessToken, @@ -318,7 +330,7 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { const information = await this.fetchPageInformation(accessToken, { id: requiredId, - accountId: findPage.accountId, + accountName: findPage.accountName, locationName: findPage.locationName, }); @@ -348,8 +360,12 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { topicType: settings?.topicType || 'STANDARD', }; - // Add call to action if provided - if (settings?.callToActionType && settings?.callToActionUrl) { + // Add call to action if provided (and not NONE) + if ( + settings?.callToActionType && + settings.callToActionType !== 'NONE' && + settings?.callToActionUrl + ) { postBody.callToAction = { actionType: settings.callToActionType, url: settings.callToActionUrl, @@ -466,7 +482,17 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { // Use the Business Profile Performance API const response = await fetch( - `https://businessprofileperformance.googleapis.com/v1/${id}:getDailyMetricsTimeSeries?dailyMetric=WEBSITE_CLICKS&dailyMetric=CALL_CLICKS&dailyMetric=BUSINESS_DIRECTION_REQUESTS&dailyMetric=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetric=BUSINESS_IMPRESSIONS_MOBILE_MAPS&dailyRange.startDate.year=${dayjs(startDate).year()}&dailyRange.startDate.month=${dayjs(startDate).month() + 1}&dailyRange.startDate.day=${dayjs(startDate).date()}&dailyRange.endDate.year=${dayjs(endDate).year()}&dailyRange.endDate.month=${dayjs(endDate).month() + 1}&dailyRange.endDate.day=${dayjs(endDate).date()}`, + `https://businessprofileperformance.googleapis.com/v1/${id}:getDailyMetricsTimeSeries?dailyMetric=WEBSITE_CLICKS&dailyMetric=CALL_CLICKS&dailyMetric=BUSINESS_DIRECTION_REQUESTS&dailyMetric=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetric=BUSINESS_IMPRESSIONS_MOBILE_MAPS&dailyRange.startDate.year=${dayjs( + startDate + ).year()}&dailyRange.startDate.month=${ + dayjs(startDate).month() + 1 + }&dailyRange.startDate.day=${dayjs( + startDate + ).date()}&dailyRange.endDate.year=${dayjs( + endDate + ).year()}&dailyRange.endDate.month=${ + dayjs(endDate).month() + 1 + }&dailyRange.endDate.day=${dayjs(endDate).date()}`, { headers: { Authorization: `Bearer ${accessToken}`, @@ -497,7 +523,10 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { const dataPoints = series.timeSeries?.datedValues?.map((dv: any) => ({ total: dv.value || 0, - date: `${dv.date.year}-${String(dv.date.month).padStart(2, '0')}-${String(dv.date.day).padStart(2, '0')}`, + date: `${dv.date.year}-${String(dv.date.month).padStart( + 2, + '0' + )}-${String(dv.date.day).padStart(2, '0')}`, })) || []; if (dataPoints.length > 0) { From 7921ef3d81457dfa73dcb3b3c4f3bd3dda37c5c5 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 21:18:31 +0700 Subject: [PATCH 006/340] feat: fix errors --- .../src/components/new-launch/providers/gmb/gmb.provider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx index 7b32a4dd..edb05772 100644 --- a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx @@ -165,7 +165,7 @@ export default withProvider({ SettingsComponent: GmbSettings, CustomPreviewComponent: undefined, dto: GmbSettingsDto, - checkValidity: async (items, settings) => { + checkValidity: async (items, settings: any) => { // GMB posts can have text only, or text with one image if (items.length > 0 && items[0].length > 1) { return 'Google My Business posts can only have one image'; From 8dc422ee315b0e34ca53ab38827be537cead7dbf Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 27 Nov 2025 22:44:33 +0700 Subject: [PATCH 007/340] feat: gmb analytics --- .../platform-analytics/platform.analytics.tsx | 5 ++- .../src/integrations/social/gmb.provider.ts | 32 ++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/apps/frontend/src/components/platform-analytics/platform.analytics.tsx b/apps/frontend/src/components/platform-analytics/platform.analytics.tsx index 12e2f838..0d2c865e 100644 --- a/apps/frontend/src/components/platform-analytics/platform.analytics.tsx +++ b/apps/frontend/src/components/platform-analytics/platform.analytics.tsx @@ -24,6 +24,7 @@ const allowedIntegrations = [ 'linkedin-page', // 'tiktok', 'youtube', + 'gmb', 'pinterest', 'threads', 'x', @@ -83,6 +84,7 @@ export const PlatformAnalytics = () => { 'pinterest', 'youtube', 'threads', + 'gmb', 'x', ].indexOf(currentIntegration.identifier) !== -1 ) { @@ -100,6 +102,7 @@ export const PlatformAnalytics = () => { 'pinterest', 'youtube', 'threads', + 'gmb', 'x', ].indexOf(currentIntegration.identifier) !== -1 ) { @@ -109,7 +112,7 @@ export const PlatformAnalytics = () => { }); } if ( - ['facebook', 'linkedin-page', 'pinterest', 'youtube', 'x'].indexOf( + ['facebook', 'linkedin-page', 'pinterest', 'youtube', 'x', 'gmb'].indexOf( currentIntegration.identifier ) !== -1 ) { diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts index cd20998a..2fde8307 100644 --- a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -480,9 +480,14 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { const endDate = dayjs().format('YYYY-MM-DD'); const startDate = dayjs().subtract(date, 'day').format('YYYY-MM-DD'); + // id is in format: accounts/{accountId}/locations/{locationId} + // Business Profile Performance API expects: locations/{locationId} + const locationId = id.split('/locations/')[1]; + const locationPath = `locations/${locationId}`; + // Use the Business Profile Performance API const response = await fetch( - `https://businessprofileperformance.googleapis.com/v1/${id}:getDailyMetricsTimeSeries?dailyMetric=WEBSITE_CLICKS&dailyMetric=CALL_CLICKS&dailyMetric=BUSINESS_DIRECTION_REQUESTS&dailyMetric=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetric=BUSINESS_IMPRESSIONS_MOBILE_MAPS&dailyRange.startDate.year=${dayjs( + `https://businessprofileperformance.googleapis.com/v1/${locationPath}:fetchMultiDailyMetricsTimeSeries?dailyMetrics=WEBSITE_CLICKS&dailyMetrics=CALL_CLICKS&dailyMetrics=BUSINESS_DIRECTION_REQUESTS&dailyMetrics=BUSINESS_IMPRESSIONS_DESKTOP_MAPS&dailyMetrics=BUSINESS_IMPRESSIONS_MOBILE_MAPS&dailyRange.startDate.year=${dayjs( startDate ).year()}&dailyRange.startDate.month=${ dayjs(startDate).month() + 1 @@ -502,7 +507,11 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { const data = await response.json(); - if (!data.timeSeries || data.timeSeries.length === 0) { + // Response structure: { multiDailyMetricTimeSeries: [{ dailyMetricTimeSeries: [...] }] } + const dailyMetricTimeSeries = + data.multiDailyMetricTimeSeries?.[0]?.dailyMetricTimeSeries; + + if (!dailyMetricTimeSeries || dailyMetricTimeSeries.length === 0) { return []; } @@ -516,18 +525,19 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { const analytics: AnalyticsData[] = []; - for (const series of data.timeSeries) { + for (const series of dailyMetricTimeSeries) { const metricName = series.dailyMetric; const label = metricLabels[metricName] || metricName; - const dataPoints = - series.timeSeries?.datedValues?.map((dv: any) => ({ - total: dv.value || 0, - date: `${dv.date.year}-${String(dv.date.month).padStart( - 2, - '0' - )}-${String(dv.date.day).padStart(2, '0')}`, - })) || []; + const datedValues = series.timeSeries?.datedValues || []; + + const dataPoints = datedValues.map((dv: any) => ({ + total: parseInt(dv.value || '0', 10), + date: `${dv.date.year}-${String(dv.date.month).padStart( + 2, + '0' + )}-${String(dv.date.day).padStart(2, '0')}`, + })); if (dataPoints.length > 0) { analytics.push({ From 12ab71b6bdc212425312ea6864d856a7c1128f1c Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:22:53 +0700 Subject: [PATCH 008/340] Clean up README by removing unnecessary elements Removed outdated links and images from README. --- README.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.md b/README.md index 91350317..866927af 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ -

- - automate - -

-

@@ -90,6 +84,7 @@ - Collaborate with other team members to exchange or buy posts. - Invite your team members to collaborate, comment, and schedule posts. - At the moment there is no difference between the hosted version to the self-hosted version +- Perfect for automation (API) with platforms like N8N, Make.com, Zapier, etc. ## Tech Stack From 1e4b1b8baf79091500d7890cffbbb35d6b7e7176 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 28 Nov 2025 18:08:41 +0700 Subject: [PATCH 009/340] feat: add www to threads --- .../src/integrations/social/threads.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 3a39b13e..54dd02ad 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -58,7 +58,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { const state = makeId(6); return { url: - 'https://threads.net/oauth/authorize' + + 'https://www.threads.net/oauth/authorize' + `?client_id=${process.env.THREADS_APP_ID}` + `&redirect_uri=${encodeURIComponent( `${ From 6ab8a2471b7c2c54fdb71c1e0c83787b835e04f1 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 29 Nov 2025 23:05:43 +0700 Subject: [PATCH 010/340] feat: billing fix --- .../billing/main.billing.component.tsx | 215 ++++++++++-------- 1 file changed, 119 insertions(+), 96 deletions(-) diff --git a/apps/frontend/src/components/billing/main.billing.component.tsx b/apps/frontend/src/components/billing/main.billing.component.tsx index 03feb5fd..3485012a 100644 --- a/apps/frontend/src/components/billing/main.billing.component.tsx +++ b/apps/frontend/src/components/billing/main.billing.component.tsx @@ -239,46 +239,15 @@ export const MainBillingComponent: FC<{ return subscription?.subscriptionTier; }, [subscription, initialChannels, monthlyOrYearly, period]); const moveToCheckout = useCallback( - (billing: 'STANDARD' | 'PRO' | 'FREE') => async () => { - const messages = []; - if ( - !pricing[billing].team_members && - pricing[subscription?.subscriptionTier!]?.team_members - ) { - messages.push( - `Your team members will be removed from your organization` - ); - } - if (billing === 'FREE') { - if ( - subscription?.cancelAt || - (await deleteDialog( - `Are you sure you want to cancel your subscription? ${messages.join( - ', ' - )}`, - 'Yes, cancel', - 'Cancel Subscription' - )) - ) { - const info = await new Promise((res) => { - modal.openModal({ - title: t( - 'we_are_sorry_to_see_you_go', - 'We are sorry to see you go :(' - ), - withCloseButton: true, - classNames: { - modal: 'bg-transparent text-textColor', - }, - children: res(e)} />, - }); - }); + (billing: 'STANDARD' | 'PRO' | 'FREE', reactivate = false) => + async () => { + if (reactivate) { setLoading(true); const { cancel_at } = await ( await fetch('/billing/cancel', { method: 'POST', body: JSON.stringify({ - feedback: info, + feedback: '', }), headers: { 'Content-Type': 'application/json', @@ -289,72 +258,126 @@ export const MainBillingComponent: FC<{ ...subs!, cancelAt: cancel_at, })); - if (cancel_at) - toast.show('Subscription set to canceled successfully'); - if (!cancel_at) toast.show('Subscription reactivated successfully'); + + toast.show('Subscription reactivated successfully'); setLoading(false); + return; } - return; - } - if ( - messages.length && - !(await deleteDialog(messages.join(', '), 'Yes, continue')) - ) { - return; - } - setLoading(true); - const { url, portal } = await ( - await fetch('/billing/subscribe', { - method: 'POST', - body: JSON.stringify({ - period: monthlyOrYearly === 'on' ? 'YEARLY' : 'MONTHLY', - utm, - billing, - tolt: tolt(), - }), - }) - ).json(); - if (url) { - await track(TrackEnum.InitiateCheckout, { - value: - pricing[billing][ - monthlyOrYearly === 'on' ? 'year_price' : 'month_price' - ], - }); - window.location.href = url; - return; - } - if (portal) { + + const messages = []; if ( - await deleteDialog( - 'We could not charge your credit card, please update your payment method', - 'Update', - 'Payment Method Required' - ) + !pricing[billing].team_members && + pricing[subscription?.subscriptionTier!]?.team_members ) { - window.open(portal); + messages.push( + `Your team members will be removed from your organization` + ); } - } else { - setPeriod(monthlyOrYearly === 'on' ? 'YEARLY' : 'MONTHLY'); - setSubscription((subs) => ({ - ...subs!, - subscriptionTier: billing, - cancelAt: null, - })); - mutate( - '/user/self', - { - ...user, - tier: billing, - }, - { - revalidate: false, + if (billing === 'FREE') { + if ( + subscription?.cancelAt || + (await deleteDialog( + `Are you sure you want to cancel your subscription? ${messages.join( + ', ' + )}`, + 'Yes, cancel', + 'Cancel Subscription' + )) + ) { + const info = await new Promise((res) => { + modal.openModal({ + title: t( + 'we_are_sorry_to_see_you_go', + 'We are sorry to see you go :(' + ), + withCloseButton: true, + classNames: { + modal: 'bg-transparent text-textColor', + }, + children: res(e)} />, + }); + }); + setLoading(true); + const { cancel_at } = await ( + await fetch('/billing/cancel', { + method: 'POST', + body: JSON.stringify({ + feedback: info, + }), + headers: { + 'Content-Type': 'application/json', + }, + }) + ).json(); + setSubscription((subs) => ({ + ...subs!, + cancelAt: cancel_at, + })); + if (cancel_at) + toast.show('Subscription set to canceled successfully'); + setLoading(false); } - ); - toast.show('Subscription updated successfully'); - } - setLoading(false); - }, + return; + } + if ( + messages.length && + !(await deleteDialog(messages.join(', '), 'Yes, continue')) + ) { + return; + } + setLoading(true); + const { url, portal } = await ( + await fetch('/billing/subscribe', { + method: 'POST', + body: JSON.stringify({ + period: monthlyOrYearly === 'on' ? 'YEARLY' : 'MONTHLY', + utm, + billing, + tolt: tolt(), + }), + }) + ).json(); + if (url) { + await track(TrackEnum.InitiateCheckout, { + value: + pricing[billing][ + monthlyOrYearly === 'on' ? 'year_price' : 'month_price' + ], + }); + window.location.href = url; + return; + } + if (portal) { + if ( + await deleteDialog( + 'We could not charge your credit card, please update your payment method', + 'Update', + 'Payment Method Required' + ) + ) { + window.open(portal); + } + } else { + setPeriod(monthlyOrYearly === 'on' ? 'YEARLY' : 'MONTHLY'); + setSubscription((subs) => ({ + ...subs!, + subscriptionTier: billing, + cancelAt: null, + })); + mutate( + '/user/self', + { + ...user, + tier: billing, + }, + { + revalidate: false, + } + ); + toast.show('Subscription updated successfully'); + } + setLoading(false); + }, [monthlyOrYearly, subscription, user, utm] ); if (user?.isLifetime) { @@ -401,7 +424,7 @@ export const MainBillingComponent: FC<{

); }; + +const Accept: FC<{ resolve: (res: boolean) => void }> = ({ resolve }) => { + const [loading, setLoading] = useState(false); + const fetch = useFetch(); + const toaster = useToaster(); + + const apply = useCallback(async () => { + setLoading(true); + await fetch('/billing/apply-discount', { + method: 'POST', + }); + + resolve(true); + toaster.show('50% discount applied successfully'); + }, []); + + return ( +
+
+ Would you accept 50% discount for 3 months instead? 🙏🏻 +
+
+ + +
+
+ ); +}; const Info: FC<{ proceed: (feedback: string) => void; }> = (props) => { @@ -277,13 +309,34 @@ export const MainBillingComponent: FC<{ if ( subscription?.cancelAt || (await deleteDialog( - `Are you sure you want to cancel your subscription? ${messages.join( - ', ' - )}`, + `Are you sure you want to cancel your subscription? + ${messages.join(', ')}`, 'Yes, cancel', 'Cancel Subscription' )) ) { + const checkDiscount = await ( + await fetch('/billing/check-discount') + ).json(); + if (checkDiscount.offerCoupon) { + const info = await new Promise((res) => { + modal.openModal({ + title: 'Before you cancel', + withCloseButton: true, + classNames: { + modal: 'bg-transparent text-textColor', + }, + children: , + }); + }); + + modal.closeAll(); + + if (info) { + return; + } + } + const info = await new Promise((res) => { modal.openModal({ title: t( @@ -297,6 +350,7 @@ export const MainBillingComponent: FC<{ children: res(e)} />, }); }); + setLoading(true); const { cancel_at } = await ( await fetch('/billing/cancel', { diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index 5a7f5949..e27e0a8b 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -475,6 +475,72 @@ export class StripeService { }); } + async checkDiscount(customer: string) { + if (!process.env.STRIPE_DISCOUNT_ID) { + return false; + } + + const list = await stripe.charges.list({ + customer, + limit: 1, + }); + + if (!list.data.filter(f => f.amount > 1000).length) { + return false; + } + + const currentUserSubscription = { + data: ( + await stripe.subscriptions.list({ + customer, + status: 'all', + expand: ['data.discounts'], + }) + ).data.find((f) => f.status === 'active' || f.status === 'trialing'), + }; + + if (!currentUserSubscription) { + return false; + } + + if ( + currentUserSubscription.data?.items.data[0]?.price.recurring?.interval === + 'year' || + currentUserSubscription.data?.discounts.length + ) { + return false; + } + + return true; + } + + async applyDiscount(customer: string) { + const check = this.checkDiscount(customer); + if (!check) { + return false; + } + + const currentUserSubscription = { + data: ( + await stripe.subscriptions.list({ + customer, + status: 'all', + expand: ['data.discounts'], + }) + ).data.find((f) => f.status === 'active' || f.status === 'trialing'), + }; + + await stripe.subscriptions.update(currentUserSubscription.data.id, { + discounts: [ + { + coupon: process.env.STRIPE_DISCOUNT_ID!, + }, + ], + }); + + return true; + } + async checkSubscription(organizationId: string, subscriptionId: string) { const orgValue = await this._subscriptionService.checkSubscription( organizationId, From fd7b755bef86d5b0488b10050af0900d4e99c568 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 2 Dec 2025 12:15:48 +0700 Subject: [PATCH 012/340] feat: refresh list --- .../src/components/layout/continue.provider.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/layout/continue.provider.tsx b/apps/frontend/src/components/layout/continue.provider.tsx index 0e5a5660..2602325c 100644 --- a/apps/frontend/src/components/layout/continue.provider.tsx +++ b/apps/frontend/src/components/layout/continue.provider.tsx @@ -32,7 +32,7 @@ export const ContinueProvider: FC = () => { refreshWhenOffline: false, fallbackData: [], }); - const closeModal = useCallback(() => { + const refreshList = useCallback(() => { mutate('/integrations/list'); const url = new URL(window.location.href); url.searchParams.delete('added'); @@ -54,6 +54,7 @@ export const ContinueProvider: FC = () => { return ( p.internalId)} @@ -105,13 +106,22 @@ const ContinueModal: FC<{ added: any; provider: any; integrations: string[]; + refreshList: () => void; }> = (props) => { const modals = useModals(); useEffect(() => { modals.openModal({ title: 'Configure Channel', - children: (close) => , + children: (close) => ( + { + props.refreshList(); + close(); + }} + /> + ), }); }, []); From e1225681a97fe7eabf516ee67a715bd85a318e67 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 2 Dec 2025 15:39:20 +0700 Subject: [PATCH 013/340] feat: youtube select page --- .../src/api/routes/integrations.controller.ts | 9 + .../providers/continue-provider/list.tsx | 2 + .../youtube/youtube.continue.tsx | 157 ++++++++++++++++++ .../integrations/integration.service.ts | 31 ++++ .../integrations/social/youtube.provider.ts | 93 ++++++++++- 5 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 3cd717c1..743843de 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -569,6 +569,15 @@ export class IntegrationsController { return this._integrationService.saveGmb(org.id, id, body); } + @Post('/youtube/:id') + async saveYoutube( + @Param('id') id: string, + @Body() body: { id: string }, + @GetOrgFromRequest() org: Organization + ) { + return this._integrationService.saveYoutube(org.id, id, body); + } + @Post('/enable') enableChannel( @GetOrgFromRequest() org: Organization, diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx index f8052fae..3331557b 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/list.tsx @@ -4,10 +4,12 @@ import { InstagramContinue } from '@gitroom/frontend/components/new-launch/provi import { FacebookContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/facebook/facebook.continue'; import { LinkedinContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/linkedin/linkedin.continue'; import { GmbContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/gmb/gmb.continue'; +import { YoutubeContinue } from '@gitroom/frontend/components/new-launch/providers/continue-provider/youtube/youtube.continue'; export const continueProviderList = { instagram: InstagramContinue, facebook: FacebookContinue, 'linkedin-page': LinkedinContinue, gmb: GmbContinue, + youtube: YoutubeContinue, }; diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx new file mode 100644 index 00000000..5798c0b4 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx @@ -0,0 +1,157 @@ +'use client'; + +import { FC, useCallback, useMemo, useState } from 'react'; +import useSWR from 'swr'; +import clsx from 'clsx'; +import { Button } from '@gitroom/react/form/button'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; + +export const YoutubeContinue: FC<{ + closeModal: () => void; + existingId: string[]; +}> = (props) => { + const { closeModal, existingId } = props; + const call = useCustomProviderFunction(); + const { integration } = useIntegration(); + const [channel, setSelectedChannel] = useState(null); + const fetch = useFetch(); + const t = useT(); + + const loadChannels = useCallback(async () => { + try { + const channels = await call.get('pages'); + return channels; + } catch (e) { + closeModal(); + } + }, []); + + const setChannel = useCallback( + (param: { id: string }) => () => { + setSelectedChannel(param); + }, + [] + ); + + const { data, isLoading } = useSWR('load-youtube-channels', loadChannels, { + refreshWhenHidden: false, + refreshWhenOffline: false, + revalidateOnFocus: false, + revalidateIfStale: false, + revalidateOnMount: true, + revalidateOnReconnect: false, + refreshInterval: 0, + }); + + const saveYoutube = useCallback(async () => { + await fetch(`/integrations/youtube/${integration?.id}`, { + method: 'POST', + body: JSON.stringify(channel), + }); + closeModal(); + }, [integration, channel]); + + const filteredData = useMemo(() => { + return ( + data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] + ); + }, [data, existingId]); + + if (!isLoading && !data?.length) { + return ( +
+ {t( + 'youtube_no_channels_found', + "We couldn't find any YouTube channels connected to your account." + )} +
+
+ {t( + 'youtube_ensure_channel_exists', + 'Please ensure you have a YouTube channel created.' + )} +
+
+ {t( + 'youtube_try_again', + 'Please close this dialog, delete the integration and try again.' + )} +
+ ); + } + + return ( +
+
{t('select_channel', 'Select YouTube Channel:')}
+
+ {filteredData?.map( + (p: { + id: string; + name: string; + username: string; + subscriberCount: string; + picture: { + data: { + url: string; + }; + }; + }) => ( +
+
+ {p.picture?.data?.url ? ( + {p.name} + ) : ( +
+ + + + +
+ )} +
+
{p.name}
+ {p.username && ( +
{p.username}
+ )} + {p.subscriberCount && ( +
+ {parseInt(p.subscriberCount).toLocaleString()} subscribers +
+ )} +
+ ) + )} +
+
+ +
+
+ ); +}; + diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 554c6c35..26cd9b15 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -12,6 +12,7 @@ import { Integration, Organization } from '@prisma/client'; import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; import { LinkedinPageProvider } from '@gitroom/nestjs-libraries/integrations/social/linkedin.page.provider'; import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; +import { YoutubeProvider } from '@gitroom/nestjs-libraries/integrations/social/youtube.provider'; import dayjs from 'dayjs'; import { timer } from '@gitroom/helpers/utils/timer'; import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; @@ -402,6 +403,36 @@ export class IntegrationService { return { success: true }; } + async saveYoutube(org: string, id: string, data: { id: string }) { + const getIntegration = await this._integrationRepository.getIntegrationById( + org, + id + ); + if (getIntegration && !getIntegration.inBetweenSteps) { + throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); + } + + const youtube = this._integrationManager.getSocialIntegration( + 'youtube' + ) as YoutubeProvider; + const getIntegrationInformation = await youtube.fetchPageInformation( + getIntegration?.token!, + data + ); + + await this.checkForDeletedOnceAndUpdate(org, getIntegrationInformation.id); + await this._integrationRepository.updateIntegration(id, { + picture: getIntegrationInformation.picture, + internalId: getIntegrationInformation.id, + name: getIntegrationInformation.name, + inBetweenSteps: false, + token: getIntegration?.token!, + profile: getIntegrationInformation.username, + }); + + return { success: true }; + } + async checkAnalytics( org: Organization, integration: string, diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts index 36bffc53..890ba1a5 100644 --- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts @@ -53,7 +53,7 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { override maxConcurrentJob = 1; // YouTube has strict upload quotas identifier = 'youtube'; name = 'YouTube'; - isBetweenSteps = false; + isBetweenSteps = true; dto = YoutubeSettingsDto; scopes = [ 'https://www.googleapis.com/auth/userinfo.profile', @@ -189,6 +189,97 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { }; } + async pages(accessToken: string) { + const { client, youtube } = clientAndYoutube(); + client.setCredentials({ access_token: accessToken }); + const youtubeClient = youtube(client); + + try { + // Get all channels the user has access to + const response = await youtubeClient.channels.list({ + part: ['snippet', 'contentDetails', 'statistics'], + mine: true, + }); + + const channels = response.data.items || []; + + return channels.map((channel) => ({ + id: channel.id!, + name: channel.snippet?.title || 'Unnamed Channel', + picture: { + data: { + url: channel.snippet?.thumbnails?.default?.url || '', + }, + }, + username: channel.snippet?.customUrl || '', + subscriberCount: channel.statistics?.subscriberCount || '0', + })); + } catch (error) { + console.error('Failed to fetch YouTube channels:', error); + return []; + } + } + + async fetchPageInformation( + accessToken: string, + data: { id: string } + ) { + const { client, youtube } = clientAndYoutube(); + client.setCredentials({ access_token: accessToken }); + const youtubeClient = youtube(client); + + try { + const response = await youtubeClient.channels.list({ + part: ['snippet', 'contentDetails', 'statistics'], + id: [data.id], + }); + + const channel = response.data.items?.[0]; + + if (!channel) { + throw new Error('Channel not found'); + } + + return { + id: channel.id!, + name: channel.snippet?.title || 'Unnamed Channel', + access_token: accessToken, + picture: channel.snippet?.thumbnails?.default?.url || '', + username: channel.snippet?.customUrl || '', + }; + } catch (error) { + console.error('Failed to fetch YouTube channel information:', error); + throw error; + } + } + + async reConnect( + id: string, + requiredId: string, + accessToken: string + ): Promise { + const pages = await this.pages(accessToken); + const findPage = pages.find((p) => p.id === requiredId); + + if (!findPage) { + throw new Error('Channel not found'); + } + + const information = await this.fetchPageInformation(accessToken, { + id: requiredId, + }); + + return { + id: information.id, + name: information.name, + accessToken: information.access_token, + refreshToken: information.access_token, + expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), + picture: information.picture, + username: information.username, + }; + } + async post( id: string, accessToken: string, From 292c3e480ba6eb957345b1fbbee15db2fbcec69d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 2 Dec 2025 16:04:54 +0700 Subject: [PATCH 014/340] feat: unified continue providers --- .../src/api/routes/integrations.controller.ts | 44 +---- .../components/layout/continue.provider.tsx | 17 +- .../facebook/facebook.continue.tsx | 25 +-- .../continue-provider/gmb/gmb.continue.tsx | 18 +- .../instagram/instagram.continue.tsx | 21 +-- .../linkedin/linkedin.continue.tsx | 19 +- .../youtube/youtube.continue.tsx | 18 +- .../integrations/integration.service.ts | 162 ++---------------- .../integrations/social/facebook.provider.ts | 10 +- .../social/linkedin.page.provider.ts | 10 +- .../social/social.integrations.interface.ts | 12 ++ .../integrations/social/threads.provider.ts | 15 +- 12 files changed, 98 insertions(+), 273 deletions(-) diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 743843de..3a8a72e8 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -533,49 +533,13 @@ export class IntegrationsController { return this._integrationService.disableChannel(org.id, id); } - @Post('/instagram/:id') - async saveInstagram( + @Post('/provider/:id/connect') + async saveProviderPage( @Param('id') id: string, - @Body() body: { pageId: string; id: string }, + @Body() body: any, @GetOrgFromRequest() org: Organization ) { - return this._integrationService.saveInstagram(org.id, id, body); - } - - @Post('/facebook/:id') - async saveFacebook( - @Param('id') id: string, - @Body() body: { page: string }, - @GetOrgFromRequest() org: Organization - ) { - return this._integrationService.saveFacebook(org.id, id, body.page); - } - - @Post('/linkedin-page/:id') - async saveLinkedin( - @Param('id') id: string, - @Body() body: { page: string }, - @GetOrgFromRequest() org: Organization - ) { - return this._integrationService.saveLinkedin(org.id, id, body.page); - } - - @Post('/gmb/:id') - async saveGmb( - @Param('id') id: string, - @Body() body: { id: string; accountName: string; locationName: string }, - @GetOrgFromRequest() org: Organization - ) { - return this._integrationService.saveGmb(org.id, id, body); - } - - @Post('/youtube/:id') - async saveYoutube( - @Param('id') id: string, - @Body() body: { id: string }, - @GetOrgFromRequest() org: Organization - ) { - return this._integrationService.saveYoutube(org.id, id, body); + return this._integrationService.saveProviderPage(org.id, id, body); } @Post('/enable') diff --git a/apps/frontend/src/components/layout/continue.provider.tsx b/apps/frontend/src/components/layout/continue.provider.tsx index 2602325c..feb16746 100644 --- a/apps/frontend/src/components/layout/continue.provider.tsx +++ b/apps/frontend/src/components/layout/continue.provider.tsx @@ -9,7 +9,7 @@ import { continueProviderList } from '@gitroom/frontend/components/new-launch/pr import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; export const Null: FC<{ - closeModal: () => void; + onSave: (data: any) => Promise; existingId: string[]; }> = () => null; export const ContinueProvider: FC = () => { @@ -70,6 +70,19 @@ const ModalContent: FC<{ closeModal: () => void; integrations: string[]; }> = ({ continueId, added, provider: Provider, closeModal, integrations }) => { + const fetch = useFetch(); + + const onSave = useCallback( + async (data: any) => { + await fetch(`/integrations/provider/${continueId}/connect`, { + method: 'POST', + body: JSON.stringify(data), + }); + closeModal(); + }, + [continueId, closeModal] + ); + return ( - + ); }; diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx index a1c9d72c..59391cb4 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx @@ -4,25 +4,22 @@ import { FC, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import clsx from 'clsx'; import { Button } from '@gitroom/react/form/button'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; -import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; + export const FacebookContinue: FC<{ - closeModal: () => void; + onSave: (data: any) => Promise; existingId: string[]; }> = (props) => { - const { closeModal, existingId } = props; + const { onSave, existingId } = props; const call = useCustomProviderFunction(); - const { integration } = useIntegration(); const [page, setSelectedPage] = useState(null); - const fetch = useFetch(); const loadPages = useCallback(async () => { try { const pages = await call.get('pages'); return pages; } catch (e) { - closeModal(); + // Handle error silently } }, []); const setPage = useCallback( @@ -42,15 +39,9 @@ export const FacebookContinue: FC<{ }); const t = useT(); - const saveInstagram = useCallback(async () => { - await fetch(`/integrations/facebook/${integration?.id}`, { - method: 'POST', - body: JSON.stringify({ - page, - }), - }); - closeModal(); - }, [integration, page]); + const saveFacebook = useCallback(async () => { + await onSave({ page }); + }, [onSave, page]); const filteredData = useMemo(() => { return ( data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] @@ -112,7 +103,7 @@ export const FacebookContinue: FC<{ )}
-
diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx index a1862c0a..87e2cafb 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx @@ -4,24 +4,20 @@ import { FC, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import clsx from 'clsx'; import { Button } from '@gitroom/react/form/button'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; -import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; export const GmbContinue: FC<{ - closeModal: () => void; + onSave: (data: any) => Promise; existingId: string[]; }> = (props) => { - const { closeModal, existingId } = props; + const { onSave, existingId } = props; const call = useCustomProviderFunction(); - const { integration } = useIntegration(); const [location, setSelectedLocation] = useState(null); - const fetch = useFetch(); const t = useT(); const loadPages = useCallback(async () => { @@ -29,7 +25,7 @@ export const GmbContinue: FC<{ const pages = await call.get('pages'); return pages; } catch (e) { - closeModal(); + // Handle error silently } }, []); @@ -51,12 +47,8 @@ export const GmbContinue: FC<{ }); const saveGmb = useCallback(async () => { - await fetch(`/integrations/gmb/${integration?.id}`, { - method: 'POST', - body: JSON.stringify(location), - }); - closeModal(); - }, [integration, location]); + await onSave(location); + }, [onSave, location]); const filteredData = useMemo(() => { return ( diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx index 4798d85c..9b7abd7f 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx @@ -4,28 +4,25 @@ import { FC, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import clsx from 'clsx'; import { Button } from '@gitroom/react/form/button'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; -import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; + export const InstagramContinue: FC<{ - closeModal: () => void; + onSave: (data: any) => Promise; existingId: string[]; }> = (props) => { - const { closeModal, existingId } = props; + const { onSave, existingId } = props; const call = useCustomProviderFunction(); - const { integration } = useIntegration(); const [page, setSelectedPage] = useState(null); - const fetch = useFetch(); const loadPages = useCallback(async () => { try { const pages = await call.get('pages'); return pages; } catch (e) { - closeModal(); + // Handle error silently } }, []); const t = useT(); @@ -46,12 +43,8 @@ export const InstagramContinue: FC<{ refreshInterval: 0, }); const saveInstagram = useCallback(async () => { - await fetch(`/integrations/instagram/${integration?.id}`, { - method: 'POST', - body: JSON.stringify(page), - }); - closeModal(); - }, [integration, page]); + await onSave(page); + }, [onSave, page]); const filteredData = useMemo(() => { return ( data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] @@ -103,7 +96,7 @@ export const InstagramContinue: FC<{ >
profile diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx index e66b0ff6..c9da01a2 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx @@ -4,30 +4,27 @@ import { FC, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import clsx from 'clsx'; import { Button } from '@gitroom/react/form/button'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; -import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; + export const LinkedinContinue: FC<{ - closeModal: () => void; + onSave: (data: any) => Promise; existingId: string[]; }> = (props) => { - const { closeModal, existingId } = props; + const { onSave, existingId } = props; const t = useT(); const call = useCustomProviderFunction(); - const { integration } = useIntegration(); const [page, setSelectedPage] = useState(null); - const fetch = useFetch(); const loadPages = useCallback(async () => { try { const pages = await call.get('companies'); return pages; } catch (e) { - closeModal(); + // Handle error silently } }, []); const setPage = useCallback( @@ -46,12 +43,8 @@ export const LinkedinContinue: FC<{ refreshInterval: 0, }); const saveLinkedin = useCallback(async () => { - await fetch(`/integrations/linkedin-page/${integration?.id}`, { - method: 'POST', - body: JSON.stringify(page), - }); - closeModal(); - }, [integration, page]); + await onSave({ page: page?.id }); + }, [onSave, page]); const filteredData = useMemo(() => { return ( data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx index 5798c0b4..ace60189 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx @@ -4,20 +4,16 @@ import { FC, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import clsx from 'clsx'; import { Button } from '@gitroom/react/form/button'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; -import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; export const YoutubeContinue: FC<{ - closeModal: () => void; + onSave: (data: any) => Promise; existingId: string[]; }> = (props) => { - const { closeModal, existingId } = props; + const { onSave, existingId } = props; const call = useCustomProviderFunction(); - const { integration } = useIntegration(); const [channel, setSelectedChannel] = useState(null); - const fetch = useFetch(); const t = useT(); const loadChannels = useCallback(async () => { @@ -25,7 +21,7 @@ export const YoutubeContinue: FC<{ const channels = await call.get('pages'); return channels; } catch (e) { - closeModal(); + // Handle error silently } }, []); @@ -47,12 +43,8 @@ export const YoutubeContinue: FC<{ }); const saveYoutube = useCallback(async () => { - await fetch(`/integrations/youtube/${integration?.id}`, { - method: 'POST', - body: JSON.stringify(channel), - }); - closeModal(); - }, [integration, channel]); + await onSave(channel); + }, [onSave, channel]); const filteredData = useMemo(() => { return ( diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 26cd9b15..e55f11d8 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -1,8 +1,6 @@ import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import { IntegrationRepository } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.repository'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; -import { InstagramProvider } from '@gitroom/nestjs-libraries/integrations/social/instagram.provider'; -import { FacebookProvider } from '@gitroom/nestjs-libraries/integrations/social/facebook.provider'; import { AnalyticsData, AuthTokenDetails, @@ -10,9 +8,6 @@ import { } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; import { Integration, Organization } from '@prisma/client'; import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; -import { LinkedinPageProvider } from '@gitroom/nestjs-libraries/integrations/social/linkedin.page.provider'; -import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; -import { YoutubeProvider } from '@gitroom/nestjs-libraries/integrations/social/youtube.provider'; import dayjs from 'dayjs'; import { timer } from '@gitroom/helpers/utils/timer'; import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; @@ -270,64 +265,39 @@ export class IntegrationService { return this._integrationRepository.checkForDeletedOnceAndUpdate(org, page); } - async saveInstagram( - org: string, - id: string, - data: { pageId: string; id: string } - ) { + async saveProviderPage(org: string, id: string, data: any) { const getIntegration = await this._integrationRepository.getIntegrationById( org, id ); - if (getIntegration && !getIntegration.inBetweenSteps) { + if (!getIntegration) { + throw new HttpException('Integration not found', HttpStatus.NOT_FOUND); + } + if (!getIntegration.inBetweenSteps) { throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); } - const instagram = this._integrationManager.getSocialIntegration( - 'instagram' - ) as InstagramProvider; - const getIntegrationInformation = await instagram.fetchPageInformation( - getIntegration?.token!, + const provider = this._integrationManager.getSocialIntegration( + getIntegration.providerIdentifier + ); + + if (!provider.fetchPageInformation) { + throw new HttpException( + 'Provider does not support page selection', + HttpStatus.BAD_REQUEST + ); + } + + const getIntegrationInformation = await provider.fetchPageInformation( + getIntegration.token, data ); - await this.checkForDeletedOnceAndUpdate(org, getIntegrationInformation.id); - await this._integrationRepository.updateIntegration(id, { - picture: getIntegrationInformation.picture, - internalId: getIntegrationInformation.id, - name: getIntegrationInformation.name, - inBetweenSteps: false, - token: getIntegrationInformation.access_token, - profile: getIntegrationInformation.username, - }); - - return { success: true }; - } - - async saveLinkedin(org: string, id: string, page: string) { - const getIntegration = await this._integrationRepository.getIntegrationById( - org, - id - ); - if (getIntegration && !getIntegration.inBetweenSteps) { - throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); - } - - const linkedin = this._integrationManager.getSocialIntegration( - 'linkedin-page' - ) as LinkedinPageProvider; - - const getIntegrationInformation = await linkedin.fetchPageInformation( - getIntegration?.token!, - page - ); - await this.checkForDeletedOnceAndUpdate( org, String(getIntegrationInformation.id) ); - - await this._integrationRepository.updateIntegration(String(id), { + await this._integrationRepository.updateIntegration(id, { picture: getIntegrationInformation.picture, internalId: String(getIntegrationInformation.id), name: getIntegrationInformation.name, @@ -339,100 +309,6 @@ export class IntegrationService { return { success: true }; } - async saveFacebook(org: string, id: string, page: string) { - const getIntegration = await this._integrationRepository.getIntegrationById( - org, - id - ); - if (getIntegration && !getIntegration.inBetweenSteps) { - throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); - } - - const facebook = this._integrationManager.getSocialIntegration( - 'facebook' - ) as FacebookProvider; - const getIntegrationInformation = await facebook.fetchPageInformation( - getIntegration?.token!, - page - ); - - await this.checkForDeletedOnceAndUpdate(org, getIntegrationInformation.id); - await this._integrationRepository.updateIntegration(id, { - picture: getIntegrationInformation.picture, - internalId: getIntegrationInformation.id, - name: getIntegrationInformation.name, - inBetweenSteps: false, - token: getIntegrationInformation.access_token, - profile: getIntegrationInformation.username, - }); - - return { success: true }; - } - - async saveGmb( - org: string, - id: string, - data: { id: string; accountName: string; locationName: string } - ) { - const getIntegration = await this._integrationRepository.getIntegrationById( - org, - id - ); - if (getIntegration && !getIntegration.inBetweenSteps) { - throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); - } - - const gmb = this._integrationManager.getSocialIntegration( - 'gmb' - ) as GmbProvider; - const getIntegrationInformation = await gmb.fetchPageInformation( - getIntegration?.token!, - data - ); - - await this.checkForDeletedOnceAndUpdate(org, getIntegrationInformation.id); - await this._integrationRepository.updateIntegration(id, { - picture: getIntegrationInformation.picture, - internalId: getIntegrationInformation.id, - name: getIntegrationInformation.name, - inBetweenSteps: false, - token: getIntegration?.token!, - profile: getIntegrationInformation.username, - }); - - return { success: true }; - } - - async saveYoutube(org: string, id: string, data: { id: string }) { - const getIntegration = await this._integrationRepository.getIntegrationById( - org, - id - ); - if (getIntegration && !getIntegration.inBetweenSteps) { - throw new HttpException('Invalid request', HttpStatus.BAD_REQUEST); - } - - const youtube = this._integrationManager.getSocialIntegration( - 'youtube' - ) as YoutubeProvider; - const getIntegrationInformation = await youtube.fetchPageInformation( - getIntegration?.token!, - data - ); - - await this.checkForDeletedOnceAndUpdate(org, getIntegrationInformation.id); - await this._integrationRepository.updateIntegration(id, { - picture: getIntegrationInformation.picture, - internalId: getIntegrationInformation.id, - name: getIntegrationInformation.name, - inBetweenSteps: false, - token: getIntegration?.token!, - profile: getIntegrationInformation.username, - }); - - return { success: true }; - } - async checkAnalytics( org: Organization, integration: string, diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index bebd91a2..a3dae5d8 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -183,10 +183,9 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { requiredId: string, accessToken: string ): Promise { - const information = await this.fetchPageInformation( - accessToken, - requiredId - ); + const information = await this.fetchPageInformation(accessToken, { + page: requiredId, + }); return { id: information.id, @@ -266,7 +265,8 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { return data; } - async fetchPageInformation(accessToken: string, pageId: string) { + async fetchPageInformation(accessToken: string, data: { page: string }) { + const pageId = data.page; const { id, name, diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index fdb66eb6..feb349a5 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -150,10 +150,9 @@ export class LinkedinPageProvider requiredId: string, accessToken: string ): Promise { - const information = await this.fetchPageInformation( - accessToken, - requiredId - ); + const information = await this.fetchPageInformation(accessToken, { + page: requiredId, + }); return { id: information.id, @@ -166,7 +165,8 @@ export class LinkedinPageProvider }; } - async fetchPageInformation(accessToken: string, pageId: string) { + async fetchPageInformation(accessToken: string, params: { page: string }) { + const pageId = params.page; const data = await ( await fetch( `https://api.linkedin.com/v2/organizations/${pageId}?projection=(id,localizedName,vanityName,logoV2(original~:playableStreams))`, diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 9b623965..8da48809 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -107,6 +107,14 @@ export type MediaContent = { thumbnailTimestamp?: number; }; +export type FetchPageInformationResult = { + id: string; + name: string; + access_token: string; + picture: string; + username: string; +}; + export interface SocialProvider extends IAuthenticator, ISocialMediaIntegration { @@ -138,4 +146,8 @@ export interface SocialProvider token: string, data: { query: string }, id: string, integration: Integration ) => Promise<{ id: string; label: string; image: string, doNotCache?: boolean }[] | {none: true}>; mentionFormat?(idOrHandle: string, name: string): string; + fetchPageInformation?( + accessToken: string, + data: any + ): Promise; } diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 54dd02ad..bac4c775 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -39,7 +39,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { ) ).json(); - const { id, name, username, picture } = await this.fetchPageInformation( + const { id, name, username, picture } = await this.fetchUserInfo( access_token ); @@ -49,7 +49,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { accessToken: access_token, refreshToken: access_token, expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), - picture: picture?.data?.url || '', + picture: picture || '', username: '', }; } @@ -105,7 +105,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { ) ).json(); - const { id, name, username, picture } = await this.fetchPageInformation( + const { id, name, username, picture } = await this.fetchUserInfo( access_token ); @@ -115,7 +115,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { accessToken: access_token, refreshToken: access_token, expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), - picture: picture?.data?.url || '', + picture: picture || '', username: username, }; } @@ -143,8 +143,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { return this.checkLoaded(mediaContainerId, accessToken); } - async fetchPageInformation(accessToken: string) { - const { id, username, threads_profile_picture_url, access_token } = await ( + private async fetchUserInfo(accessToken: string) { + const { id, username, threads_profile_picture_url } = await ( await this.fetch( `https://graph.threads.net/v1.0/me?fields=id,username,threads_profile_picture_url&access_token=${accessToken}` ) @@ -153,8 +153,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { return { id, name: username, - access_token, - picture: { data: { url: threads_profile_picture_url } }, + picture: threads_profile_picture_url || '', username, }; } From b1ccd6d6ce7b493cfa84e105aff33a586891abf6 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 2 Dec 2025 19:45:53 +0700 Subject: [PATCH 015/340] feat: missing dto --- .../dtos/posts/providers-settings/all.providers.settings.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 7579f460..c8273339 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -16,6 +16,8 @@ import { HashnodeSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/provid import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto'; import { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/listmonk.dto'; import { GmbSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/gmb.settings.dto'; +import { FarcasterDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/farcaster.dto'; +import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/facebook.dto'; export type ProviderExtension = { __type: T } & M; export type AllProvidersSettings = @@ -70,11 +72,11 @@ export const allProviders = (setEmpty?: any) => { { value: HashnodeSettingsDto, name: 'hashnode' }, { value: ListmonkDto, name: 'listmonk' }, { value: GmbSettingsDto, name: 'gmb' }, - { value: setEmpty, name: 'facebook' }, + { value: FarcasterDto, name: 'wrapcast' }, + { value: FacebookDto, name: 'facebook' }, { value: setEmpty, name: 'threads' }, { value: setEmpty, name: 'mastodon' }, { value: setEmpty, name: 'bluesky' }, - { value: setEmpty, name: 'wrapcast' }, { value: setEmpty, name: 'telegram' }, { value: setEmpty, name: 'nostr' }, { value: setEmpty, name: 'vk' }, From 71174c1b8febbd5095a1a5c04857e45c904a6b25 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 2 Dec 2025 19:46:26 +0700 Subject: [PATCH 016/340] feat: typing --- .../dtos/posts/providers-settings/all.providers.settings.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index c8273339..8ad6b8b8 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -40,11 +40,11 @@ export type AllProvidersSettings = | ProviderExtension<'wordpress', WordpressDto> | ProviderExtension<'listmonk', ListmonkDto> | ProviderExtension<'gmb', GmbSettingsDto> - | ProviderExtension<'facebook', None> + | ProviderExtension<'facebook', FacebookDto> + | ProviderExtension<'wrapcast', FarcasterDto> | ProviderExtension<'threads', None> | ProviderExtension<'mastodon', None> | ProviderExtension<'bluesky', None> - | ProviderExtension<'wrapcast', None> | ProviderExtension<'telegram', None> | ProviderExtension<'nostr', None> | ProviderExtension<'vk', None>; From 943acec8e42b96673bcbf24511a8e1f23ce6120d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 4 Dec 2025 14:12:33 +0700 Subject: [PATCH 017/340] fix: refresh token unified, and found some bugs --- .../src/api/routes/integrations.controller.ts | 41 +++----- .../chat/tools/integration.trigger.tool.ts | 51 ++++------ .../src/database/prisma/database.module.ts | 2 + .../integrations/integration.service.ts | 96 ++++--------------- .../database/prisma/posts/posts.service.ts | 56 ++--------- .../refresh.integration.service.ts | 84 ++++++++++++++++ .../integrations/social/facebook.provider.ts | 4 +- .../src/integrations/social/gmb.provider.ts | 4 +- .../integrations/social/instagram.provider.ts | 5 +- .../social/linkedin.page.provider.ts | 4 +- .../social/social.integrations.interface.ts | 2 +- .../integrations/social/youtube.provider.ts | 4 +- 12 files changed, 154 insertions(+), 199 deletions(-) create mode 100644 libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 3a8a72e8..78179693 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -38,6 +38,7 @@ import { Sections, } from '@gitroom/backend/services/auth/permissions/permission.exception.class'; import { uniqBy } from 'lodash'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; @ApiTags('Integrations') @Controller('/integrations') @@ -45,7 +46,8 @@ export class IntegrationsController { constructor( private _integrationManager: IntegrationManager, private _integrationService: IntegrationService, - private _postService: PostsService + private _postService: PostsService, + private _refreshIntegrationService: RefreshIntegrationService ) {} @Get('/') getIntegrations() { @@ -338,37 +340,24 @@ export class IntegrationsController { return load; } catch (err) { if (err instanceof RefreshToken) { - const { accessToken, refreshToken, expiresIn, additionalSettings } = - await integrationProvider.refreshToken(getIntegration.refreshToken); + const data = await this._refreshIntegrationService.refresh( + getIntegration + ); + + if (!data) { + return; + } + + const { accessToken } = data; if (accessToken) { - await this._integrationService.createOrUpdateIntegration( - additionalSettings, - !!integrationProvider.oneTimeToken, - getIntegration.organizationId, - getIntegration.name, - getIntegration.picture!, - 'social', - getIntegration.internalId, - getIntegration.providerIdentifier, - accessToken, - refreshToken, - expiresIn - ); - - getIntegration.token = accessToken; - if (integrationProvider.refreshWait) { await timer(10000); } return this.functionIntegration(org, body); - } else { - await this._integrationService.disconnectChannel( - org.id, - getIntegration - ); - return false; } + + return false; } return false; @@ -459,7 +448,7 @@ export class IntegrationsController { refresh, auth.accessToken ); - return res(newAuth); + return res({ ...newAuth, refreshToken: body.refresh }); } return res(auth); diff --git a/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts b/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts index b548d22c..9de31dc2 100644 --- a/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts +++ b/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts @@ -11,12 +11,14 @@ import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/in import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { timer } from '@gitroom/helpers/utils/timer'; import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; @Injectable() export class IntegrationTriggerTool implements AgentToolInterface { constructor( private _integrationManager: IntegrationManager, - private _integrationService: IntegrationService + private _integrationService: IntegrationService, + private _refreshIntegrationService: RefreshIntegrationService ) {} name = 'triggerTool'; @@ -103,40 +105,12 @@ export class IntegrationTriggerTool implements AgentToolInterface { return { output: load }; } catch (err) { - console.log(err); if (err instanceof RefreshToken) { - const { - accessToken, - refreshToken, - expiresIn, - additionalSettings, - } = await integrationProvider.refreshToken( - getIntegration.refreshToken + const data = await this._refreshIntegrationService.refresh( + getIntegration ); - if (accessToken) { - await this._integrationService.createOrUpdateIntegration( - additionalSettings, - !!integrationProvider.oneTimeToken, - getIntegration.organizationId, - getIntegration.name, - getIntegration.picture!, - 'social', - getIntegration.internalId, - getIntegration.providerIdentifier, - accessToken, - refreshToken, - expiresIn - ); - - getIntegration.token = accessToken; - - if (integrationProvider.refreshWait) { - await timer(10000); - } - - continue; - } else { + if (!data) { await this._integrationService.disconnectChannel( organizationId, getIntegration @@ -146,6 +120,19 @@ export class IntegrationTriggerTool implements AgentToolInterface { 'We had to disconnect the channel as the token expired', }; } + + const { accessToken } = data; + + if (accessToken) { + getIntegration.token = accessToken; + + if (integrationProvider.refreshWait) { + await timer(10000); + } + + continue; + } else { + } } return { output: 'Unexpected error' }; } diff --git a/libraries/nestjs-libraries/src/database/prisma/database.module.ts b/libraries/nestjs-libraries/src/database/prisma/database.module.ts index 17198f05..73763d0e 100644 --- a/libraries/nestjs-libraries/src/database/prisma/database.module.ts +++ b/libraries/nestjs-libraries/src/database/prisma/database.module.ts @@ -41,6 +41,7 @@ import { ThirdPartyRepository } from '@gitroom/nestjs-libraries/database/prisma/ import { ThirdPartyService } from '@gitroom/nestjs-libraries/database/prisma/third-party/third-party.service'; import { VideoManager } from '@gitroom/nestjs-libraries/videos/video.manager'; import { FalService } from '@gitroom/nestjs-libraries/openai/fal.service'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; @Global() @Module({ @@ -80,6 +81,7 @@ import { FalService } from '@gitroom/nestjs-libraries/openai/fal.service'; ItemUserService, MessagesService, IntegrationManager, + RefreshIntegrationService, ExtractContentService, OpenaiService, FalService, diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index e55f11d8..0ce96727 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -1,4 +1,4 @@ -import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; +import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; import { IntegrationRepository } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.repository'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { @@ -19,6 +19,7 @@ import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/cl import { difference, uniq } from 'lodash'; import utc from 'dayjs/plugin/utc'; import { AutopostRepository } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.repository'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; dayjs.extend(utc); @@ -30,7 +31,9 @@ export class IntegrationService { private _autopostsRepository: AutopostRepository, private _integrationManager: IntegrationManager, private _notificationService: NotificationService, - private _workerServiceProducer: BullMqClient + private _workerServiceProducer: BullMqClient, + @Inject(forwardRef(() => RefreshIntegrationService)) + private _refreshIntegrationService: RefreshIntegrationService ) {} async changeActiveCron(orgId: string) { @@ -333,39 +336,16 @@ export class IntegrationService { dayjs(getIntegration?.tokenExpiration).isBefore(dayjs()) || forceRefresh ) { - const { accessToken, expiresIn, refreshToken, additionalSettings } = - await new Promise((res) => { - return integrationProvider - .refreshToken(getIntegration.refreshToken!) - .then((r) => res(r)) - .catch(() => { - res({ - error: '', - accessToken: '', - id: '', - name: '', - picture: '', - username: '', - additionalSettings: undefined, - }); - }); - }); + const data = await this._refreshIntegrationService.refresh( + getIntegration + ); + if (!data) { + return []; + } + + const { accessToken } = data; if (accessToken) { - await this.createOrUpdateIntegration( - additionalSettings, - !!integrationProvider.oneTimeToken, - getIntegration.organizationId, - getIntegration.name, - getIntegration.picture!, - 'social', - getIntegration.internalId, - getIntegration.providerIdentifier, - accessToken, - refreshToken, - expiresIn - ); - getIntegration.token = accessToken; if (integrationProvider.refreshWait) { @@ -464,51 +444,13 @@ export class IntegrationService { dayjs(getIntegration?.tokenExpiration).isBefore(dayjs()) || forceRefresh ) { - const { accessToken, expiresIn, refreshToken, additionalSettings } = - await new Promise((res) => { - getSocialIntegration - .refreshToken(getIntegration.refreshToken!) - .then((r) => res(r)) - .catch(() => - res({ - accessToken: '', - expiresIn: 0, - refreshToken: '', - id: '', - name: '', - username: '', - picture: '', - additionalSettings: undefined, - }) - ); - }); - - if (!accessToken) { - await this.refreshNeeded( - getIntegration.organizationId, - getIntegration.id - ); - - await this.informAboutRefreshError( - getIntegration.organizationId, - getIntegration - ); - return {}; - } - - await this.createOrUpdateIntegration( - additionalSettings, - !!getSocialIntegration.oneTimeToken, - getIntegration.organizationId, - getIntegration.name, - getIntegration.picture!, - 'social', - getIntegration.internalId, - getIntegration.providerIdentifier, - accessToken, - refreshToken, - expiresIn + const data = await this._refreshIntegrationService.refresh( + getIntegration ); + if (!data) { + return; + } + const { accessToken } = data; getIntegration.token = accessToken; diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 12163659..d20b7faa 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -39,6 +39,7 @@ import { validate } from 'class-validator'; import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; dayjs.extend(utc); import * as Sentry from '@sentry/nestjs'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; type PostWithConditionals = Post & { integration?: Integration; @@ -59,7 +60,8 @@ export class PostsService { private _mediaService: MediaService, private _shortLinkService: ShortLinkService, private _webhookService: WebhooksService, - private openaiService: OpenaiService + private openaiService: OpenaiService, + private _refreshIntegrationService: RefreshIntegrationService ) {} checkPending15minutesBack() { @@ -405,7 +407,7 @@ export class PostsService { integration: Integration, posts: Post[], forceRefresh = false - ): Promise> { + ): Promise | undefined> { const getIntegration = this._integrationManager.getSocialIntegration( integration.providerIdentifier ); @@ -415,53 +417,13 @@ export class PostsService { } if (dayjs(integration?.tokenExpiration).isBefore(dayjs()) || forceRefresh) { - const { accessToken, expiresIn, refreshToken, additionalSettings } = - await new Promise((res) => { - getIntegration - .refreshToken(integration.refreshToken!) - .then((r) => res(r)) - .catch(() => - res({ - accessToken: '', - expiresIn: 0, - refreshToken: '', - id: '', - name: '', - username: '', - picture: '', - additionalSettings: undefined, - }) - ); - }); + const data = await this._refreshIntegrationService.refresh(integration); - if (!accessToken) { - await this._integrationService.refreshNeeded( - integration.organizationId, - integration.id - ); - - await this._integrationService.informAboutRefreshError( - integration.organizationId, - integration - ); - return {}; + if (!data) { + return undefined; } - await this._integrationService.createOrUpdateIntegration( - additionalSettings, - !!getIntegration.oneTimeToken, - integration.organizationId, - integration.name, - integration.picture!, - 'social', - integration.internalId, - integration.providerIdentifier, - accessToken, - refreshToken, - expiresIn - ); - - integration.token = accessToken; + integration.token = data.accessToken; if (getIntegration.refreshWait) { await timer(10000); @@ -718,7 +680,7 @@ export class PostsService { }); } - Sentry.metrics.count("post_created", 1); + Sentry.metrics.count('post_created', 1); postList.push({ postId: posts[0].id, integration: post.integration.id, diff --git a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts new file mode 100644 index 00000000..80f7e3d7 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts @@ -0,0 +1,84 @@ +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { Integration } from '@prisma/client'; +import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; +import { + AuthTokenDetails, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; + +@Injectable() +export class RefreshIntegrationService { + constructor( + private _integrationManager: IntegrationManager, + @Inject(forwardRef(() => IntegrationService)) + private _integrationService: IntegrationService + ) {} + async refresh(integration: Integration): Promise { + const socialProvider = this._integrationManager.getSocialIntegration( + integration.providerIdentifier + ); + + const refresh = await this.refreshProcess(integration, socialProvider); + + if (!refresh) { + return false as const; + } + + await this._integrationService.createOrUpdateIntegration( + undefined, + !!socialProvider.oneTimeToken, + integration.organizationId, + integration.name, + integration.picture!, + 'social', + integration.internalId, + integration.providerIdentifier, + refresh.accessToken, + refresh.refreshToken, + refresh.expiresIn + ); + + return refresh; + } + + private async refreshProcess( + integration: Integration, + socialProvider: SocialProvider + ): Promise { + const refresh: false | AuthTokenDetails = await socialProvider + .refreshToken(integration.refreshToken) + .catch((err) => false); + + if (!refresh) { + await this._integrationService.refreshNeeded( + integration.organizationId, + integration.id + ); + + await this._integrationService.informAboutRefreshError( + integration.organizationId, + integration + ); + + await this._integrationService.disconnectChannel(integration.organizationId, integration); + + return false; + } + + if (!socialProvider.reConnect) { + return refresh; + } + + const reConnect = await socialProvider.reConnect( + integration.rootInternalId, + integration.internalId, + refresh.accessToken + ); + + return { + ...refresh, + ...reConnect, + }; + } +} diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index a3dae5d8..a37e0e42 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -182,7 +182,7 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { id: string, requiredId: string, accessToken: string - ): Promise { + ): Promise> { const information = await this.fetchPageInformation(accessToken, { page: requiredId, }); @@ -191,8 +191,6 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { id: information.id, name: information.name, accessToken: information.access_token, - refreshToken: information.access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), picture: information.picture, username: information.username, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts index 2fde8307..c3e15314 100644 --- a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -320,7 +320,7 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { id: string, requiredId: string, accessToken: string - ): Promise { + ): Promise> { const pages = await this.pages(accessToken); const findPage = pages.find((p) => p.id === requiredId); @@ -338,8 +338,6 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { id: information.id, name: information.name, accessToken: information.access_token, - refreshToken: information.access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), picture: information.picture, username: information.username, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 2cc499e2..13e3701e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -292,7 +292,6 @@ export class InstagramProvider }; } - console.log('err', body); return undefined; } @@ -300,7 +299,7 @@ export class InstagramProvider id: string, requiredId: string, accessToken: string - ): Promise { + ): Promise> { const findPage = (await this.pages(accessToken)).find( (p) => p.id === requiredId ); @@ -314,8 +313,6 @@ export class InstagramProvider id: information.id, name: information.name, accessToken: information.access_token, - refreshToken: information.access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), picture: information.picture, username: information.username, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index feb349a5..b30ae6f5 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -149,7 +149,7 @@ export class LinkedinPageProvider id: string, requiredId: string, accessToken: string - ): Promise { + ): Promise> { const information = await this.fetchPageInformation(accessToken, { page: requiredId, }); @@ -158,8 +158,6 @@ export class LinkedinPageProvider id: information.id, name: information.name, accessToken: information.access_token, - refreshToken: information.access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), picture: information.picture, username: information.username, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 8da48809..3653b97e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -19,7 +19,7 @@ export interface IAuthenticator { id: string, requiredId: string, accessToken: string - ): Promise; + ): Promise>; generateAuthUrl( clientInformation?: ClientInformation ): Promise; diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts index 890ba1a5..d35762f5 100644 --- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts @@ -257,7 +257,7 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { id: string, requiredId: string, accessToken: string - ): Promise { + ): Promise> { const pages = await this.pages(accessToken); const findPage = pages.find((p) => p.id === requiredId); @@ -273,8 +273,6 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { id: information.id, name: information.name, accessToken: information.access_token, - refreshToken: information.access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), picture: information.picture, username: information.username, }; From 45294e07b56a58855f5504602ad0378389058f14 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 4 Dec 2025 18:16:50 +0700 Subject: [PATCH 018/340] feat: better layout --- .../src/components/launches/calendar.tsx | 76 ++++----- .../launches/launches.component.tsx | 150 +++++++++--------- 2 files changed, 118 insertions(+), 108 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 92cd9b57..c1650ce5 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -142,37 +142,39 @@ export const DayView = () => { }, [integrations, posts]); return ( -
- {options.map((option) => ( - -
- {newDayjs() - .utc() - .startOf('day') - .add(option[0].time, 'minute') - .local() - .format(isUSCitizen() ? 'hh:mm A' : 'LT')} -
-
- p.integration), - }} +
+
+ {options.map((option) => ( + +
+ {newDayjs() + .utc() + .startOf('day') + .add(option[0].time, 'minute') + .local() + .format(isUSCitizen() ? 'hh:mm A' : 'LT')} +
+
- - -
-
- ))} + p.integration), + }} + > + + +
+ + ))} +
); }; @@ -200,13 +202,13 @@ export const WeekView = () => { return (
-
-
-
+
+
+
{localizedDays.map((day, index) => (
{day.name} @@ -299,12 +301,12 @@ export const MonthView = () => { return (
-
-
+
+
{localizedDays.map((day) => (
{day}
diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index 39cbfcd6..2a1ea2d7 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -493,84 +493,92 @@ export const LaunchesComponent = () => {
-
-

- {t('channels')} -

-
setCollapseMenu(collapseMenu === '1' ? '0' : '1')} - className="group-[.sidebar]:rotate-[180deg] group-[.sidebar]:mx-auto text-btnText bg-btnSimple rounded-[6px] w-[24px] h-[24px] flex items-center justify-center cursor-pointer select-none" - > - +
+

+ {t('channels')} +

+
+ setCollapseMenu(collapseMenu === '1' ? '0' : '1') + } + className="group-[.sidebar]:rotate-[180deg] group-[.sidebar]:mx-auto text-btnText bg-btnSimple rounded-[6px] w-[24px] h-[24px] flex items-center justify-center cursor-pointer select-none" > - - -
-
-
- update(true)} /> -
- {sortedIntegrations?.length > 0 && } - {sortedIntegrations?.length > 0 && - user?.tier?.ai && - billingEnabled && } -
-
-
- {sortedIntegrations.length === 0 && collapseMenu === '0' && ( -
-
- No channels + -
- {t('no_channels', 'No channels yet')} -
-
- {t('connect_your_accounts')} + +
+
+
+ update(true)} /> +
+ {sortedIntegrations?.length > 0 && } + {sortedIntegrations?.length > 0 && + user?.tier?.ai && + billingEnabled && } +
+
+
+ {sortedIntegrations.length === 0 && collapseMenu === '0' && ( +
+
+ No channels +
+ {t('no_channels', 'No channels yet')} +
+
+ {t('connect_your_accounts')} +
-
- )} - {menuIntegrations.map((menu) => ( - - ))} -
-
- {process.env.NEXT_PUBLIC_VERSION - ? process.env.NEXT_PUBLIC_VERSION - : ''} + )} + {menuIntegrations.map((menu) => ( + + ))} +
+
+ {process.env.NEXT_PUBLIC_VERSION + ? process.env.NEXT_PUBLIC_VERSION + : ''} +
From 2b32d561e96f1e9a13af099e0eca3cafba9f7c7b Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 4 Dec 2025 19:13:43 +0700 Subject: [PATCH 019/340] feat: fix z-index --- apps/frontend/src/components/launches/calendar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index c1650ce5..880708e2 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -208,7 +208,7 @@ export const WeekView = () => { {localizedDays.map((day, index) => (
{day.name} @@ -306,7 +306,7 @@ export const MonthView = () => { {localizedDays.map((day) => (
{day}
From d89eb44b8fa136582443be8061c48f9f2ddc0a5f Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 5 Dec 2025 11:39:00 +0700 Subject: [PATCH 020/340] feat: ui fixes --- apps/frontend/src/components/launches/calendar.tsx | 2 +- .../src/components/notifications/notification.component.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 880708e2..01516c90 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -314,7 +314,7 @@ export const MonthView = () => { {calendarDays.map((date, index) => (
{ return (
Date: Fri, 5 Dec 2025 14:32:20 +0700 Subject: [PATCH 021/340] feat: notifcations settings --- .../src/api/routes/users.controller.ts | 14 + .../email-notifications.component.tsx | 146 ++++++ .../components/settings/global.settings.tsx | 3 + .../integrations/integration.service.ts | 4 +- .../notifications/notification.service.ts | 69 ++- .../organizations/organization.repository.ts | 2 + .../database/prisma/posts/posts.service.ts | 25 +- .../src/database/prisma/schema.prisma | 424 ++++++++++++------ .../database/prisma/users/users.repository.ts | 25 ++ .../database/prisma/users/users.service.ts | 9 + .../src/dtos/users/email-notifications.dto.ts | 10 + package.json | 1 + 12 files changed, 586 insertions(+), 146 deletions(-) create mode 100644 apps/frontend/src/components/settings/email-notifications.component.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts diff --git a/apps/backend/src/api/routes/users.controller.ts b/apps/backend/src/api/routes/users.controller.ts index 3d9dd968..88e80263 100644 --- a/apps/backend/src/api/routes/users.controller.ts +++ b/apps/backend/src/api/routes/users.controller.ts @@ -22,6 +22,7 @@ import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions import { ApiTags } from '@nestjs/swagger'; import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto'; +import { EmailNotificationsDto } from '@gitroom/nestjs-libraries/dtos/users/email-notifications.dto'; import { HttpForbiddenException } from '@gitroom/nestjs-libraries/services/exception.filter'; import { RealIP } from 'nestjs-real-ip'; import { UserAgent } from '@gitroom/nestjs-libraries/user/user.agent'; @@ -125,6 +126,19 @@ export class UsersController { return this._userService.changePersonal(user.id, body); } + @Get('/email-notifications') + async getEmailNotifications(@GetUserFromRequest() user: User) { + return this._userService.getEmailNotifications(user.id); + } + + @Post('/email-notifications') + async updateEmailNotifications( + @GetUserFromRequest() user: User, + @Body() body: EmailNotificationsDto + ) { + return this._userService.updateEmailNotifications(user.id, body); + } + @Get('/subscription') @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) async getSubscription(@GetOrgFromRequest() organization: Organization) { diff --git a/apps/frontend/src/components/settings/email-notifications.component.tsx b/apps/frontend/src/components/settings/email-notifications.component.tsx new file mode 100644 index 00000000..e780ae4a --- /dev/null +++ b/apps/frontend/src/components/settings/email-notifications.component.tsx @@ -0,0 +1,146 @@ +'use client'; + +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import useSWR from 'swr'; +import { Slider } from '@gitroom/react/form/slider'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +interface EmailNotifications { + sendSuccessEmails: boolean; + sendFailureEmails: boolean; +} + +export const useEmailNotifications = () => { + const fetch = useFetch(); + + const load = useCallback(async () => { + return (await fetch('/user/email-notifications')).json(); + }, []); + + return useSWR('email-notifications', load, { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + revalidateOnMount: true, + refreshWhenHidden: false, + refreshWhenOffline: false, + }); +}; + +const EmailNotificationsComponent = () => { + const t = useT(); + const fetch = useFetch(); + const toaster = useToaster(); + const { data, isLoading } = useEmailNotifications(); + + const [localSettings, setLocalSettings] = useState({ + sendSuccessEmails: true, + sendFailureEmails: true, + }); + + // Keep a ref to always have the latest state + const settingsRef = useRef(localSettings); + settingsRef.current = localSettings; + + // Sync local state with fetched data + useEffect(() => { + if (data) { + setLocalSettings(data); + } + }, [data]); + + const updateSetting = useCallback( + async (key: keyof EmailNotifications, value: boolean) => { + // Use ref to get the latest state + const currentSettings = settingsRef.current; + const newData = { + ...currentSettings, + [key]: value, + }; + + // Update local state immediately + setLocalSettings(newData); + + await fetch('/user/email-notifications', { + method: 'POST', + body: JSON.stringify(newData), + }); + + toaster.show(t('settings_updated', 'Settings updated'), 'success'); + }, + [] + ); + + const handleSuccessEmailsChange = useCallback( + (value: 'on' | 'off') => { + updateSetting('sendSuccessEmails', value === 'on'); + }, + [updateSetting] + ); + + const handleFailureEmailsChange = useCallback( + (value: 'on' | 'off') => { + updateSetting('sendFailureEmails', value === 'on'); + }, + [updateSetting] + ); + + if (isLoading) { + return ( +
+
+ {t('loading', 'Loading...')} +
+
+ ); + } + + return ( +
+
+ {t('email_notifications', 'Email Notifications')} +
+
+
+
+ {t('success_emails', 'Success Emails')} +
+
+ {t( + 'success_emails_description', + 'Receive email notifications when posts are published successfully' + )} +
+
+ +
+
+
+
+ {t('failure_emails', 'Failure Emails')} +
+
+ {t( + 'failure_emails_description', + 'Receive email notifications when posts fail to publish' + )} +
+
+ +
+
+ ); +}; + +export default EmailNotificationsComponent; + diff --git a/apps/frontend/src/components/settings/global.settings.tsx b/apps/frontend/src/components/settings/global.settings.tsx index 449e38f8..970a960a 100644 --- a/apps/frontend/src/components/settings/global.settings.tsx +++ b/apps/frontend/src/components/settings/global.settings.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import dynamic from 'next/dynamic'; +import EmailNotificationsComponent from '@gitroom/frontend/components/settings/email-notifications.component'; const MetricComponent = dynamic( () => import('@gitroom/frontend/components/settings/metric.component'), @@ -10,12 +11,14 @@ const MetricComponent = dynamic( ssr: false, } ); + export const GlobalSettings = () => { const t = useT(); return (

{t('global_settings', 'Global Settings')}

+
); }; diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 0ce96727..5ab3c3c4 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -187,7 +187,9 @@ export class IntegrationService { orgId, `Could not refresh your ${integration.providerIdentifier} channel ${err}`, `Could not refresh your ${integration.providerIdentifier} channel ${err}. Please go back to the system and connect it again ${process.env.FRONTEND_URL}/launches`, - true + true, + false, + 'info' ); } diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index c100e832..70dce81b 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -6,6 +6,8 @@ import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/cl import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; import dayjs from 'dayjs'; +export type NotificationType = 'success' | 'fail' | 'info'; + @Injectable() export class NotificationService { constructor( @@ -41,7 +43,8 @@ export class NotificationService { subject: string, message: string, sendEmail = false, - digest = false + digest = false, + type: NotificationType = 'success' ) { const date = new Date().toISOString(); await this._notificationRepository.createNotification(orgId, message); @@ -52,6 +55,12 @@ export class NotificationService { if (digest) { await ioRedis.watch('digest_' + orgId); const value = await ioRedis.get('digest_' + orgId); + + // Track notification types in the digest + const typesKey = 'digest_types_' + orgId; + await ioRedis.sadd(typesKey, type); + await ioRedis.expire(typesKey, 120); // Slightly longer than digest window + if (value) { return; } @@ -77,12 +86,66 @@ export class NotificationService { return; } - await this.sendEmailsToOrg(orgId, subject, message); + await this.sendEmailsToOrg(orgId, subject, message, type); } - async sendEmailsToOrg(orgId: string, subject: string, message: string) { + async sendEmailsToOrg( + orgId: string, + subject: string, + message: string, + type?: NotificationType + ) { const userOrg = await this._organizationRepository.getAllUsersOrgs(orgId); for (const user of userOrg?.users || []) { + // 'info' type is always sent regardless of preferences + if (type !== 'info') { + // Filter users based on their email preferences + if (type === 'success' && !user.user.sendSuccessEmails) { + continue; + } + if (type === 'fail' && !user.user.sendFailureEmails) { + continue; + } + } + await this.sendEmail(user.user.email, subject, message); + } + } + + async getDigestTypes(orgId: string): Promise { + const typesKey = 'digest_types_' + orgId; + const types = await ioRedis.smembers(typesKey); + // Clean up the types key after reading + await ioRedis.del(typesKey); + return types as NotificationType[]; + } + + async sendDigestEmailsToOrg( + orgId: string, + subject: string, + message: string, + types: NotificationType[] + ) { + const userOrg = await this._organizationRepository.getAllUsersOrgs(orgId); + const hasInfo = types.includes('info'); + const hasSuccess = types.includes('success'); + const hasFail = types.includes('fail'); + + for (const user of userOrg?.users || []) { + // 'info' type is always sent regardless of preferences + if (hasInfo) { + await this.sendEmail(user.user.email, subject, message); + continue; + } + + // For digest, check if user wants any of the notification types in the digest + const wantsSuccess = hasSuccess && user.user.sendSuccessEmails; + const wantsFail = hasFail && user.user.sendFailureEmails; + + // Only send if user wants at least one type of notification in the digest + if (!wantsSuccess && !wantsFail) { + continue; + } + await this.sendEmail(user.user.email, subject, message); } } diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index 919c7b7a..6a574c78 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -293,6 +293,8 @@ export class OrganizationRepository { select: { email: true, id: true, + sendSuccessEmails: true, + sendFailureEmails: true, }, }, }, diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index d20b7faa..ee82d18a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -304,7 +304,9 @@ export class PostsService { firstPost.organizationId, `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name} because you need to reconnect it. Please enable it and try again.`, - true + true, + false, + 'info' ); return; } @@ -314,7 +316,9 @@ export class PostsService { firstPost.organizationId, `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name} because it's disabled. Please enable it and try again.`, - true + true, + false, + 'info' ); return; } @@ -343,7 +347,9 @@ export class PostsService { firstPost.organizationId, `Error posting on ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, `An error occurred while posting on ${firstPost.integration?.providerIdentifier}`, - true + true, + false, + 'fail' ); return; @@ -362,7 +368,9 @@ export class PostsService { `An error occurred while posting on ${ firstPost.integration?.providerIdentifier }${err?.message ? `: ${err?.message}` : ``}`, - true + true, + false, + 'fail' ); console.error( @@ -959,15 +967,20 @@ export class PostsService { return; } + // Get the types of notifications in this digest + const types = await this._notificationService.getDigestTypes(orgId); + const message = getNotificationsForOrgSince .map((p) => p.content) .join('
'); - await this._notificationService.sendEmailsToOrg( + + await this._notificationService.sendDigestEmailsToOrg( orgId, getNotificationsForOrgSince.length === 1 ? subject : '[Postiz] Your latest notifications', - message + message, + types.length > 0 ? types : ['success'] // Default to success if no types tracked ); } } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 333e6e7d..e0a2c836 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -1,6 +1,3 @@ -// This is your Prisma schema file, -// learn more about it in the docs: https://pris.ly/d/prisma-schema - generator client { provider = "prisma-client-js" runtime = "nodejs" @@ -16,32 +13,32 @@ model Organization { name String description String? apiKey String? - users UserOrganization[] - media Media[] paymentId String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - github GitHub[] - subscription Subscription? - Integration Integration[] - post Post[] @relation("organization") - submittedPost Post[] @relation("submittedForOrg") allowTrial Boolean @default(false) isTrailing Boolean @default(false) - Comments Comments[] - notifications Notifications[] - buyerOrganization MessagesGroup[] - usedCodes UsedCodes[] - credits Credits[] - plugs Plugs[] - customers Customer[] - webhooks Webhooks[] - tags Tags[] - signatures Signatures[] autoPost AutoPost[] - sets Sets[] - thirdParty ThirdParty[] + Comments Comments[] + credits Credits[] + customers Customer[] errors Errors[] + github GitHub[] + Integration Integration[] + media Media[] + buyerOrganization MessagesGroup[] + notifications Notifications[] + plugs Plugs[] + post Post[] @relation("organization") + submittedPost Post[] @relation("submittedForOrg") + sets Sets[] + signatures Signatures[] + subscription Subscription? + tags Tags[] + thirdParty ThirdParty[] + usedCodes UsedCodes[] + users UserOrganization[] + webhooks Webhooks[] } model Tags { @@ -49,11 +46,11 @@ model Tags { name String color String orgId String - organization Organization @relation(fields: [orgId], references: [id]) - posts TagsPosts[] deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + organization Organization @relation(fields: [orgId], references: [id]) + posts TagsPosts[] @@index([orgId]) @@index([deletedAt]) @@ -61,50 +58,52 @@ model Tags { model TagsPosts { postId String - post Post @relation(fields: [postId], references: [id]) tagId String - tag Tags @relation(fields: [tagId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + post Post @relation(fields: [postId], references: [id]) + tag Tags @relation(fields: [tagId], references: [id]) @@id([postId, tagId]) @@unique([postId, tagId]) } model User { - id String @id @default(uuid()) + id String @id @default(uuid()) email String password String? providerName Provider name String? lastName String? - isSuperAdmin Boolean @default(false) + isSuperAdmin Boolean @default(false) bio String? - audience Int @default(0) + audience Int @default(0) pictureId String? - picture Media? @relation(fields: [pictureId], references: [id]) providerId String? - organizations UserOrganization[] timezone Int - comments Comments[] - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - lastReadNotifications DateTime @default(now()) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + lastReadNotifications DateTime @default(now()) inviteId String? - activated Boolean @default(true) - items ItemUser[] - marketplace Boolean @default(true) + activated Boolean @default(true) + marketplace Boolean @default(true) account String? - connectedAccount Boolean @default(false) - groupBuyer MessagesGroup[] @relation("groupBuyer") - groupSeller MessagesGroup[] @relation("groupSeller") - orderBuyer Orders[] @relation("orderBuyer") - orderSeller Orders[] @relation("orderSeller") - payoutProblems PayoutProblems[] - lastOnline DateTime @default(now()) - agencies SocialMediaAgency[] + connectedAccount Boolean @default(false) + lastOnline DateTime @default(now()) ip String? agent String? + comments Comments[] + items ItemUser[] + groupBuyer MessagesGroup[] @relation("groupBuyer") + groupSeller MessagesGroup[] @relation("groupSeller") + orderBuyer Orders[] @relation("orderBuyer") + orderSeller Orders[] @relation("orderSeller") + payoutProblems PayoutProblems[] + agencies SocialMediaAgency? + picture Media? @relation(fields: [pictureId], references: [id]) + organizations UserOrganization[] + sendSuccessEmails Boolean @default(true) + sendFailureEmails Boolean @default(true) @@unique([email, providerName]) @@index([lastReadNotifications]) @@ -118,23 +117,23 @@ model UsedCodes { id String @id @default(uuid()) code String orgId String - organization Organization @relation(fields: [orgId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + organization Organization @relation(fields: [orgId], references: [id]) @@index([code]) } model UserOrganization { id String @id @default(uuid()) - user User @relation(fields: [userId], references: [id]) userId String - organization Organization @relation(fields: [organizationId], references: [id]) organizationId String disabled Boolean @default(false) role Role @default(USER) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + organization Organization @relation(fields: [organizationId], references: [id]) + user User @relation(fields: [userId], references: [id]) @@unique([userId, organizationId]) @@index([disabled]) @@ -146,10 +145,10 @@ model GitHub { name String? token String jobId String? - organization Organization @relation(fields: [organizationId], references: [id]) organizationId String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + organization Organization @relation(fields: [organizationId], references: [id]) @@index([login]) @@index([organizationId]) @@ -158,13 +157,12 @@ model GitHub { model Trending { id String @id @default(uuid()) trendingList String - language String? + language String? @unique hash String date DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - @@unique([language]) @@index([hash]) } @@ -176,9 +174,9 @@ model TrendingLog { model ItemUser { id String @id @default(uuid()) - user User @relation(fields: [userId], references: [id]) userId String key String + user User @relation(fields: [userId], references: [id]) @@unique([userId, key]) @@index([userId]) @@ -203,18 +201,18 @@ model Media { id String @id @default(uuid()) name String path String - organization Organization @relation(fields: [organizationId], references: [id]) organizationId String - thumbnail String? - thumbnailTimestamp Int? - alt String? - fileSize Int @default(0) - type String @default("image") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - userPicture User[] - agencies SocialMediaAgency[] deletedAt DateTime? + fileSize Int @default(0) + type String @default("image") + thumbnail String? + alt String? + thumbnailTimestamp Int? + organization Organization @relation(fields: [organizationId], references: [id]) + agencies SocialMediaAgency[] + userPicture User[] @@index([name]) @@index([organizationId]) @@ -222,15 +220,12 @@ model Media { } model SocialMediaAgency { - id String @id @default(uuid()) - user User @relation(fields: [userId], references: [id]) - userId String @unique() - name String - logoId String? - logo Media? @relation(fields: [logoId], references: [id]) - website String? - slug String? - + id String @id @default(uuid()) + userId String @unique + name String + logoId String? + website String? + slug String? facebook String? instagram String? twitter String? @@ -238,15 +233,15 @@ model SocialMediaAgency { youtube String? tiktok String? otherSocialMedia String? - shortDescription String description String - niches SocialMediaAgencyNiche[] approved Boolean @default(false) - - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - deletedAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime? + logo Media? @relation(fields: [logoId], references: [id]) + user User @relation(fields: [userId], references: [id]) + niches SocialMediaAgencyNiche[] @@index([userId]) @@index([deletedAt]) @@ -255,20 +250,20 @@ model SocialMediaAgency { model SocialMediaAgencyNiche { agencyId String - agency SocialMediaAgency @relation(fields: [agencyId], references: [id]) niche String + agency SocialMediaAgency @relation(fields: [agencyId], references: [id]) @@id([agencyId, niche]) } model Credits { id String @id @default(uuid()) - organization Organization @relation(fields: [organizationId], references: [id]) organizationId String credits Int - type String @default("ai_images") createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + type String @default("ai_images") + organization Organization @relation(fields: [organizationId], references: [id]) @@index([organizationId]) @@index([createdAt]) @@ -277,7 +272,6 @@ model Credits { model Subscription { id String @id @default(cuid()) organizationId String @unique - organization Organization @relation(fields: [organizationId], references: [id]) subscriptionTier SubscriptionTier identifier String? cancelAt DateTime? @@ -287,6 +281,7 @@ model Subscription { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + organization Organization @relation(fields: [organizationId], references: [id]) @@index([organizationId]) @@index([deletedAt]) @@ -296,11 +291,11 @@ model Customer { id String @id @default(uuid()) name String orgId String - organization Organization @relation(fields: [orgId], references: [id]) - integrations Integration[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + organization Organization @relation(fields: [orgId], references: [id]) + integrations Integration[] @@unique([orgId, name, deletedAt]) } @@ -310,7 +305,6 @@ model Integration { internalId String organizationId String name String - organization Organization @relation(fields: [organizationId], references: [id]) picture String? providerIdentifier String type String @@ -318,23 +312,24 @@ model Integration { disabled Boolean @default(false) tokenExpiration DateTime? refreshToken String? - posts Post[] profile String? deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime? @updatedAt - orderItems OrderItems[] inBetweenSteps Boolean @default(false) refreshNeeded Boolean @default(false) postingTimes String @default("[{\"time\":120}, {\"time\":400}, {\"time\":700}]") customInstanceDetails String? customerId String? - customer Customer? @relation(fields: [customerId], references: [id]) - plugs Plugs[] - exisingPlugData ExisingPlugData[] rootInternalId String? additionalSettings String? @default("[]") + exisingPlugData ExisingPlugData[] + customer Customer? @relation(fields: [customerId], references: [id]) + organization Organization @relation(fields: [organizationId], references: [id]) webhooks IntegrationsWebhooks[] + orderItems OrderItems[] + plugs Plugs[] + posts Post[] @@unique([organizationId, internalId]) @@index([rootInternalId]) @@ -352,12 +347,12 @@ model Integration { model Signatures { id String @id @default(uuid()) organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) content String autoAdd Boolean createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + organization Organization @relation(fields: [organizationId], references: [id]) @@index([createdAt]) @@index([organizationId]) @@ -368,14 +363,14 @@ model Comments { id String @id @default(uuid()) content String organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) postId String - post Post @relation(fields: [postId], references: [id]) userId String - user User @relation(fields: [userId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + organization Organization @relation(fields: [organizationId], references: [id]) + post Post @relation(fields: [postId], references: [id]) + user User @relation(fields: [userId], references: [id]) @@index([createdAt]) @@index([organizationId]) @@ -392,33 +387,33 @@ model Post { integrationId String content String group String - organization Organization @relation("organization", fields: [organizationId], references: [id]) - integration Integration @relation(fields: [integrationId], references: [id]) title String? description String? parentPostId String? releaseId String? releaseURL String? settings String? - parentPost Post? @relation("parentPostId", fields: [parentPostId], references: [id]) - childrenPost Post[] @relation("parentPostId") image String? submittedForOrderId String? - submittedForOrder Orders? @relation(fields: [submittedForOrderId], references: [id]) submittedForOrganizationId String? - submittedForOrganization Organization? @relation("submittedForOrg", fields: [submittedForOrganizationId], references: [id]) approvedSubmitForOrder APPROVED_SUBMIT_FOR_ORDER @default(NO) lastMessageId String? - lastMessage Messages? @relation(fields: [lastMessageId], references: [id]) intervalInDays Int? - payoutProblems PayoutProblems[] - comments Comments[] - tags TagsPosts[] - errors Errors[] error String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + comments Comments[] + errors Errors[] + payoutProblems PayoutProblems[] + integration Integration @relation(fields: [integrationId], references: [id]) + lastMessage Messages? @relation(fields: [lastMessageId], references: [id]) + organization Organization @relation("organization", fields: [organizationId], references: [id]) + parentPost Post? @relation("parentPostId", fields: [parentPostId], references: [id]) + childrenPost Post[] @relation("parentPostId") + submittedForOrder Orders? @relation(fields: [submittedForOrderId], references: [id]) + submittedForOrganization Organization? @relation("submittedForOrg", fields: [submittedForOrganizationId], references: [id]) + tags TagsPosts[] @@index([group]) @@index([deletedAt]) @@ -439,12 +434,12 @@ model Post { model Notifications { id String @id @default(uuid()) organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) content String link String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + organization Organization @relation(fields: [organizationId], references: [id]) @@index([createdAt]) @@index([organizationId]) @@ -454,15 +449,15 @@ model Notifications { model MessagesGroup { id String @id @default(uuid()) buyerOrganizationId String - buyerOrganization Organization @relation(fields: [buyerOrganizationId], references: [id]) buyerId String - buyer User @relation("groupBuyer", fields: [buyerId], references: [id]) sellerId String - seller User @relation("groupSeller", fields: [sellerId], references: [id]) - messages Messages[] - orders Orders[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + messages Messages[] + buyer User @relation("groupBuyer", fields: [buyerId], references: [id]) + buyerOrganization Organization @relation(fields: [buyerOrganizationId], references: [id]) + seller User @relation("groupSeller", fields: [sellerId], references: [id]) + orders Orders[] @@unique([buyerId, sellerId]) @@index([createdAt]) @@ -474,31 +469,31 @@ model PayoutProblems { id String @id @default(uuid()) status String orderId String - order Orders @relation(fields: [orderId], references: [id]) userId String - user User @relation(fields: [userId], references: [id]) postId String? - post Post? @relation(fields: [postId], references: [id]) amount Int createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + order Orders @relation(fields: [orderId], references: [id]) + post Post? @relation(fields: [postId], references: [id]) + user User @relation(fields: [userId], references: [id]) } model Orders { id String @id @default(uuid()) buyerId String sellerId String - posts Post[] - buyer User @relation("orderBuyer", fields: [buyerId], references: [id]) - seller User @relation("orderSeller", fields: [sellerId], references: [id]) status OrderStatus - ordersItems OrderItems[] messageGroupId String - messageGroup MessagesGroup @relation(fields: [messageGroupId], references: [id]) captureId String? - payoutProblems PayoutProblems[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + ordersItems OrderItems[] + buyer User @relation("orderBuyer", fields: [buyerId], references: [id]) + messageGroup MessagesGroup @relation(fields: [messageGroupId], references: [id]) + seller User @relation("orderSeller", fields: [sellerId], references: [id]) + payoutProblems PayoutProblems[] + posts Post[] @@index([buyerId]) @@index([sellerId]) @@ -510,11 +505,11 @@ model Orders { model OrderItems { id String @id @default(uuid()) orderId String - order Orders @relation(fields: [orderId], references: [id]) integrationId String - integration Integration @relation(fields: [integrationId], references: [id]) quantity Int price Int + integration Integration @relation(fields: [integrationId], references: [id]) + order Orders @relation(fields: [orderId], references: [id]) @@index([orderId]) @@index([integrationId]) @@ -525,12 +520,12 @@ model Messages { from From content String? groupId String - group MessagesGroup @relation(fields: [groupId], references: [id]) special String? - posts Post[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + group MessagesGroup @relation(fields: [groupId], references: [id]) + posts Post[] @@index([groupId]) @@index([createdAt]) @@ -540,12 +535,12 @@ model Messages { model Plugs { id String @id @default(uuid()) organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) plugFunction String data String integrationId String - integration Integration @relation(fields: [integrationId], references: [id]) activated Boolean @default(true) + integration Integration @relation(fields: [integrationId], references: [id]) + organization Organization @relation(fields: [organizationId], references: [id]) @@unique([plugFunction, integrationId]) @@index([organizationId]) @@ -554,9 +549,9 @@ model Plugs { model ExisingPlugData { id String @id @default(uuid()) integrationId String - integration Integration @relation(fields: [integrationId], references: [id]) methodName String value String + integration Integration @relation(fields: [integrationId], references: [id]) @@unique([integrationId, methodName, value]) } @@ -573,8 +568,8 @@ model PopularPosts { model IntegrationsWebhooks { integrationId String - integration Integration @relation(fields: [integrationId], references: [id]) webhookId String + integration Integration @relation(fields: [integrationId], references: [id]) webhook Webhooks @relation(fields: [webhookId], references: [id]) @@id([integrationId, webhookId]) @@ -587,12 +582,12 @@ model Webhooks { id String @id @default(uuid()) name String organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) - integrations IntegrationsWebhooks[] url String deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + integrations IntegrationsWebhooks[] + organization Organization @relation(fields: [organizationId], references: [id]) @@index([organizationId]) @@index([deletedAt]) @@ -601,7 +596,6 @@ model Webhooks { model AutoPost { id String @id @default(uuid()) organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) title String content String? onSlot Boolean @@ -615,6 +609,7 @@ model AutoPost { deletedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + organization Organization @relation(fields: [organizationId], references: [id]) @@index([deletedAt]) } @@ -622,11 +617,11 @@ model AutoPost { model Sets { id String @id @default(uuid()) organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) name String content String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + organization Organization @relation(fields: [organizationId], references: [id]) @@index([organizationId]) } @@ -634,7 +629,6 @@ model Sets { model ThirdParty { id String @id @default(uuid()) organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) identifier String name String internalId String @@ -642,6 +636,7 @@ model ThirdParty { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? + organization Organization @relation(fields: [organizationId], references: [id]) @@unique([organizationId, internalId]) @@index([organizationId]) @@ -651,14 +646,14 @@ model ThirdParty { model Errors { id String @id @default(uuid()) message String - body String @default("{}") platform String organizationId String - organization Organization @relation(fields: [organizationId], references: [id]) - postId String - post Post @relation(fields: [postId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + postId String + body String @default("{}") + organization Organization @relation(fields: [organizationId], references: [id]) + post Post @relation(fields: [postId], references: [id]) @@index([organizationId]) @@index([createdAt]) @@ -668,14 +663,171 @@ model Mentions { name String username String platform String - image String createdAt DateTime @default(now()) updatedAt DateTime @updatedAt + image String @@id([name, username, platform, image]) @@index([createdAt]) } +/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client. +model mastra_ai_spans { + traceId String + spanId String + parentSpanId String? + name String + scope Json? + spanType String + attributes Json? + metadata Json? + links Json? + input Json? + output Json? + error Json? + startedAt DateTime @db.Timestamp(6) + endedAt DateTime? @db.Timestamp(6) + createdAt DateTime @db.Timestamp(6) + updatedAt DateTime? @db.Timestamp(6) + isEvent Boolean + startedAtZ DateTime? @default(now()) @db.Timestamptz(6) + endedAtZ DateTime? @default(now()) @db.Timestamptz(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + updatedAtZ DateTime? @default(now()) @db.Timestamptz(6) + + @@index([name], map: "public_mastra_ai_spans_name_idx") + @@index([parentSpanId, startedAt(sort: Desc)], map: "public_mastra_ai_spans_parentspanid_startedat_idx") + @@index([spanType, startedAt(sort: Desc)], map: "public_mastra_ai_spans_spantype_startedat_idx") + @@index([traceId, startedAt(sort: Desc)], map: "public_mastra_ai_spans_traceid_startedat_idx") + @@ignore +} + +/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client. +model mastra_evals { + input String + output String + result Json + agent_name String + metric_name String + instructions String + test_info Json? + global_run_id String + run_id String + created_at DateTime @db.Timestamp(6) + createdAt DateTime? @db.Timestamp(6) + created_atZ DateTime? @default(now()) @db.Timestamptz(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + + @@index([agent_name, created_at(sort: Desc)], map: "public_mastra_evals_agent_name_created_at_idx") + @@ignore +} + +model mastra_messages { + id String @id + thread_id String + content String + role String + type String + createdAt DateTime @db.Timestamp(6) + resourceId String? + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + + @@index([thread_id, createdAt(sort: Desc)], map: "public_mastra_messages_thread_id_createdat_idx") +} + +model mastra_resources { + id String @id + workingMemory String? + metadata Json? + createdAt DateTime @db.Timestamp(6) + updatedAt DateTime @db.Timestamp(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + updatedAtZ DateTime? @default(now()) @db.Timestamptz(6) +} + +model mastra_scorers { + id String @id + scorerId String + traceId String? + runId String + scorer Json + preprocessStepResult Json? + extractStepResult Json? + analyzeStepResult Json? + score Float + reason String? + metadata Json? + preprocessPrompt String? + extractPrompt String? + generateScorePrompt String? + generateReasonPrompt String? + analyzePrompt String? + reasonPrompt String? + input Json + output Json + additionalContext Json? + runtimeContext Json? + entityType String? + entity Json? + entityId String? + source String + resourceId String? + threadId String? + createdAt DateTime @db.Timestamp(6) + updatedAt DateTime @db.Timestamp(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + updatedAtZ DateTime? @default(now()) @db.Timestamptz(6) + spanId String? + + @@index([traceId, spanId, createdAt(sort: Desc)], map: "public_mastra_scores_trace_id_span_id_created_at_idx") +} + +model mastra_threads { + id String @id + resourceId String + title String + metadata String? + createdAt DateTime @db.Timestamp(6) + updatedAt DateTime @db.Timestamp(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + updatedAtZ DateTime? @default(now()) @db.Timestamptz(6) + + @@index([resourceId, createdAt(sort: Desc)], map: "public_mastra_threads_resourceid_createdat_idx") +} + +model mastra_traces { + id String @id + parentSpanId String? + name String + traceId String + scope String + kind Int + attributes Json? + status Json? + events Json? + links Json? + other String? + startTime BigInt + endTime BigInt + createdAt DateTime @db.Timestamp(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + + @@index([name, startTime(sort: Desc)], map: "public_mastra_traces_name_starttime_idx") +} + +model mastra_workflow_snapshot { + workflow_name String + run_id String + resourceId String? + snapshot String + createdAt DateTime @db.Timestamp(6) + updatedAt DateTime @db.Timestamp(6) + createdAtZ DateTime? @default(now()) @db.Timestamptz(6) + updatedAtZ DateTime? @default(now()) @db.Timestamptz(6) + + @@unique([workflow_name, run_id], map: "public_mastra_workflow_snapshot_workflow_name_run_id_key") +} + enum OrderStatus { PENDING ACCEPTED diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts index 2df55338..1c4e6c50 100644 --- a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts @@ -5,6 +5,7 @@ import { AuthService } from '@gitroom/helpers/auth/auth.service'; import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto'; import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list'; import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto'; +import { EmailNotificationsDto } from '@gitroom/nestjs-libraries/dtos/users/email-notifications.dto'; @Injectable() export class UsersRepository { @@ -161,6 +162,30 @@ export class UsersRepository { }); } + async getEmailNotifications(userId: string) { + return this._user.model.user.findUnique({ + where: { + id: userId, + }, + select: { + sendSuccessEmails: true, + sendFailureEmails: true, + }, + }); + } + + async updateEmailNotifications(userId: string, body: EmailNotificationsDto) { + await this._user.model.user.update({ + where: { + id: userId, + }, + data: { + sendSuccessEmails: body.sendSuccessEmails, + sendFailureEmails: body.sendFailureEmails, + }, + }); + } + async getMarketplacePeople(orgId: string, userId: string, items: ItemsDto) { const info = { id: { diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts index e7580632..9bc6d9b1 100644 --- a/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts @@ -3,6 +3,7 @@ import { UsersRepository } from '@gitroom/nestjs-libraries/database/prisma/users import { Provider } from '@prisma/client'; import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto'; import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto'; +import { EmailNotificationsDto } from '@gitroom/nestjs-libraries/dtos/users/email-notifications.dto'; import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository'; @Injectable() @@ -55,4 +56,12 @@ export class UsersService { changePersonal(userId: string, body: UserDetailDto) { return this._usersRepository.changePersonal(userId, body); } + + getEmailNotifications(userId: string) { + return this._usersRepository.getEmailNotifications(userId); + } + + updateEmailNotifications(userId: string, body: EmailNotificationsDto) { + return this._usersRepository.updateEmailNotifications(userId, body); + } } diff --git a/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts b/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts new file mode 100644 index 00000000..d9d62d08 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts @@ -0,0 +1,10 @@ +import { IsBoolean } from 'class-validator'; + +export class EmailNotificationsDto { + @IsBoolean() + sendSuccessEmails: boolean; + + @IsBoolean() + sendFailureEmails: boolean; +} + diff --git a/package.json b/package.json index d6446ba6..b71b6a96 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "cron": "rm -rf dist/cron && pnpm --filter ./apps/cron run dev", "prisma-generate": "pnpm dlx prisma@6.5.0 generate --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-db-push": "pnpm dlx prisma@6.5.0 db push --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", + "prisma-db-pull": "pnpm dlx prisma@6.5.0 db pull --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-reset": "cd ./libraries/nestjs-libraries/src/database/prisma && pnpm dlx prisma@6.5.0 db push --force-reset && pnpx prisma@6.5.0 db push", "docker-build": "./var/docker/docker-build.sh", "docker-create": "./var/docker/docker-create.sh", From 2706d511cebbc5e8f3ca8c787aa76be2f404f535 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 5 Dec 2025 15:55:14 +0700 Subject: [PATCH 022/340] fix: hot fix for menu items --- .../src/components/launches/menu/menu.tsx | 42 +++++++++++++++---- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index 55b3b768..bca334d1 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -1,11 +1,7 @@ 'use client'; import React, { - FC, - MouseEventHandler, - useCallback, - useMemo, - useState, + FC, MouseEventHandler, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { useClickOutside } from '@mantine/hooks'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; @@ -60,17 +56,39 @@ export const Menu: FC<{ const { integrations, reloadCalendarView } = useCalendar(); const toast = useToaster(); const modal = useModals(); - const [show, setShow] = useState(false); + const [show, setShow] = useState(false); + const menuRef = useRef(null); const ref = useClickOutside(() => { setShow(false); }); + const showRef = useRef(); + + // Adjust menu position if it would overflow viewport + useLayoutEffect(() => { + if (show && menuRef.current) { + const menuRect = menuRef.current.getBoundingClientRect(); + const viewportHeight = window.innerHeight; + const padding = 10; + + // Check if menu overflows bottom of viewport + if (menuRect.bottom > viewportHeight - padding) { + const newY = Math.max(padding, viewportHeight - menuRect.height - padding); + // Only update if position actually changed significantly to avoid infinite loop + if (Math.abs(show.y - newY) > 1) { + setShow(prev => prev ? { ...prev, y: newY } : false); + } + } + } + }, [show]); const findIntegration: any = useMemo(() => { return integrations.find((integration) => integration.id === id); }, [integrations, id]); const changeShow: MouseEventHandler = useCallback( (e) => { e.stopPropagation(); - setShow(!show); + // @ts-ignore + const boundBox = showRef?.current?.getBoundingClientRect(); + setShow(show ? false : { x: boundBox?.left, y: boundBox?.top + boundBox?.height }); }, [show] ); @@ -284,9 +302,10 @@ export const Menu: FC<{ ), }); }, []); + return (
@@ -303,10 +322,15 @@ export const Menu: FC<{ fill="currentColor" /> +
+
+
{show && (
e.stopPropagation()} - className={`absolute top-[100%] start-0 p-[12px] bg-newBgColorInner shadow-menu flex flex-col gap-[16px] z-[100] rounded-[8px] border border-tableBorder text-nowrap`} + style={{left: show.x, top: show.y}} + className={`fixed p-[12px] bg-newBgColorInner shadow-menu flex flex-col gap-[16px] z-[100] rounded-[8px] border border-tableBorder text-nowrap`} > {canDisable && !findIntegration?.refreshNeeded && (
Date: Fri, 5 Dec 2025 21:29:57 +0700 Subject: [PATCH 023/340] fix: nostr --- .../src/integrations/social/nostr.provider.ts | 54 +++++++++---------- package.json | 4 +- pnpm-lock.yaml | 28 +++++----- 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts index 3797117c..291dd89d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts @@ -7,7 +7,8 @@ import { import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import dayjs from 'dayjs'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; -import { getPublicKey, Relay, finalizeEvent } from 'nostr-tools'; +import { getPublicKey, Relay, finalizeEvent, SimplePool } from 'nostr-tools'; + import WebSocket from 'ws'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; @@ -15,14 +16,15 @@ import { AuthService } from '@gitroom/helpers/auth/auth.service'; global.WebSocket = WebSocket; const list = [ - 'wss://relay.primal.net', + 'wss://nos.lol', 'wss://relay.damus.io', 'wss://relay.snort.social', - 'wss://nostr.wine', - 'wss://nos.lol', - 'wss://relay.primal.net', + 'wss://temp.iris.to', + 'wss://vault.iris.to', ]; +const pool = new SimplePool(); + export class NostrProvider extends SocialAbstract implements SocialProvider { override maxConcurrentJob = 5; // Nostr relays typically have generous limits identifier = 'nostr'; @@ -68,29 +70,25 @@ export class NostrProvider extends SocialAbstract implements SocialProvider { } private async findRelayInformation(pubkey: string) { - for (const relay of list) { - const relayInstance = await Relay.connect(relay); - const value = await new Promise((resolve) => { - console.log('connecting'); - relayInstance.subscribe([{ kinds: [0], authors: [pubkey] }], { - eoseTimeout: 6000, - onevent: (event) => { - resolve(event); - }, - oneose: () => { - resolve({}); - }, - onclose: () => { - resolve({}); - }, - }); - }); + // This queries ALL relays in parallel and resolves with + // the first matching event from ANY relay. + const evt = await pool.get(list, { + kinds: [0], + authors: [pubkey], + limit: 1, + }); - relayInstance.close(); - const content = JSON.parse(value?.content || '{}'); - if (content.name || content.displayName || content.display_name) { - return content; - } + if (!evt) return {}; + + let content: any = {}; + try { + content = JSON.parse(evt.content || '{}'); + } catch { + return {}; + } + + if (content.name || content.displayName || content.display_name) { + return content; } return {}; @@ -146,7 +144,7 @@ export class NostrProvider extends SocialAbstract implements SocialProvider { const user = await this.findRelayInformation(pubkey); return { - id: String(user.pubkey), + id: pubkey, name: user.display_name || user.displayName || 'No Name', accessToken: AuthService.signJWT({ password: body.password }), refreshToken: '', diff --git a/package.json b/package.json index b71b6a96..e94b0c8a 100644 --- a/package.json +++ b/package.json @@ -80,9 +80,9 @@ "@postiz/wallets": "^0.0.1", "@prisma/client": "6.5.0", "@sentry/nestjs": "^10.25.0", - "@sentry/react": "^10.25.0", "@sentry/nextjs": "^10.25.0", "@sentry/profiling-node": "^10.25.0", + "@sentry/react": "^10.25.0", "@solana/wallet-adapter-react": "^0.15.35", "@solana/wallet-adapter-react-ui": "^0.9.35", "@swc/helpers": "0.5.13", @@ -182,7 +182,7 @@ "node-fetch": "^3.3.2", "node-telegram-bot-api": "^0.66.0", "nodemailer": "^6.9.15", - "nostr-tools": "^2.10.4", + "nostr-tools": "^2.18.2", "openai": "^6.2.0", "p-limit": "^3.1.0", "parse5": "^6.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3d402cc..33b68f5a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -424,8 +424,8 @@ importers: specifier: ^6.9.15 version: 6.10.1 nostr-tools: - specifier: ^2.10.4 - version: 2.17.3(typescript@5.5.4) + specifier: ^2.18.2 + version: 2.18.2(typescript@5.5.4) openai: specifier: ^6.2.0 version: 6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) @@ -13250,8 +13250,8 @@ packages: normalize.css@8.0.1: resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} - nostr-tools@2.17.3: - resolution: {integrity: sha512-86v635D/0cvUEd0sHkh4h4Mp706UUMa5UHSXZ6YaIsURvyGgah4CwWDieJRzbVHdKBfGsQm4B20e9JFhEpAhxg==} + nostr-tools@2.18.2: + resolution: {integrity: sha512-lUCJQd9YZG3kEvxV5Zgm7qUkBpaeuvFrtqBz4TJLAxHzUn2pE7nmZZRDQmNzp5neEw20tQS3jR16o7XzzF8ncg==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -23435,8 +23435,8 @@ snapshots: '@scure/bip32@1.3.1': dependencies: '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.1 - '@scure/base': 1.1.1 + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.9 '@scure/bip32@1.4.0': dependencies: @@ -23452,14 +23452,14 @@ snapshots: '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@scure/bip39@1.2.1': dependencies: - '@noble/hashes': 1.3.1 - '@scure/base': 1.1.1 + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.9 '@scure/bip39@1.3.0': dependencies: @@ -33500,7 +33500,7 @@ snapshots: normalize.css@8.0.1: {} - nostr-tools@2.17.3(typescript@5.5.4): + nostr-tools@2.18.2(typescript@5.5.4): dependencies: '@noble/ciphers': 0.5.3 '@noble/curves': 1.2.0 @@ -33723,10 +33723,10 @@ snapshots: ox@0.6.7(typescript@5.5.4)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.8.1 - '@noble/hashes': 1.7.1 - '@scure/bip32': 1.6.2 - '@scure/bip39': 1.5.4 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 abitype: 1.0.8(typescript@5.5.4)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: From 16d4b4508fd308cfa13cc2f0d7b101b2e3828699 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 5 Dec 2025 23:08:27 +0700 Subject: [PATCH 024/340] feat: fix nostr --- .../nestjs-libraries/src/integrations/social/nostr.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts index 291dd89d..8a7921e2 100644 --- a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts @@ -145,7 +145,7 @@ export class NostrProvider extends SocialAbstract implements SocialProvider { return { id: pubkey, - name: user.display_name || user.displayName || 'No Name', + name: user.display_name || user.displayName || user.name || 'No Name', accessToken: AuthService.signJWT({ password: body.password }), refreshToken: '', expiresIn: dayjs().add(200, 'year').unix() - dayjs().unix(), From 4ac671f646e31ca4355b5371e8a8c59d136bf66f Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 6 Dec 2025 22:46:41 +0100 Subject: [PATCH 025/340] feat/Github Issue templates --- .../{02_bug_report.yml => 01_bug_report.yml} | 0 .../ISSUE_TEMPLATE/01_installation.prolem.yml | 19 ------------------- ...ure_request.yml => 02_feature_request.yml} | 0 .github/ISSUE_TEMPLATE/config.yml | 14 ++++++++++++++ 4 files changed, 14 insertions(+), 19 deletions(-) rename .github/ISSUE_TEMPLATE/{02_bug_report.yml => 01_bug_report.yml} (100%) delete mode 100644 .github/ISSUE_TEMPLATE/01_installation.prolem.yml rename .github/ISSUE_TEMPLATE/{03_feature_request.yml => 02_feature_request.yml} (100%) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/02_bug_report.yml b/.github/ISSUE_TEMPLATE/01_bug_report.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/02_bug_report.yml rename to .github/ISSUE_TEMPLATE/01_bug_report.yml diff --git a/.github/ISSUE_TEMPLATE/01_installation.prolem.yml b/.github/ISSUE_TEMPLATE/01_installation.prolem.yml deleted file mode 100644 index 6348c629..00000000 --- a/.github/ISSUE_TEMPLATE/01_installation.prolem.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: "🙏🏻 Installation Problem" -description: "Report an issue with installation" -title: "Installation Problem" -labels: ["type: installation"] -body: - - type: markdown - attributes: - value: For installation issues, please visit our [Discord Support](https://discord.postiz.com) for assistance. - - type: textarea - id: feature-description - validations: - required: true - attributes: - label: For installation issues, please visit our https://discord.postiz.com for assistance. - description: For installation issues, please visit our [Discord Support](https://discord.postiz.com) for assistance. - placeholder: | - For installation issues, please visit our https://discord.postiz.com for assistance. - Please do not save this issue - do not submit installation issues on GitHub. - diff --git a/.github/ISSUE_TEMPLATE/03_feature_request.yml b/.github/ISSUE_TEMPLATE/02_feature_request.yml similarity index 100% rename from .github/ISSUE_TEMPLATE/03_feature_request.yml rename to .github/ISSUE_TEMPLATE/02_feature_request.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..b72f48ca --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,14 @@ +# Disable the default option to open a blank issue +blank_issues_enabled: true + +# Define your custom links +contact_links: + # The first link definition + - name: 🙏 Installation Issue + url: https://discord.postiz.com + about: If you have an installation / configuration issue. + + # You can add more links if needed + - name: Security Issue + url: https://github.com/gitroomhq/postiz-app/security/advisories/new + about: Please submit security Issues our GitHub Security Advisories. From 10d807430b43530afeabda3a4dccb4bcef24e802 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 6 Dec 2025 23:06:13 +0100 Subject: [PATCH 026/340] feat/dependabot --- .github/dependabot.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..5f0889ce --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" From 63043549532500c15f1f1f0954498ed8fcb547f2 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 6 Dec 2025 23:08:29 +0100 Subject: [PATCH 027/340] feat/github CI cleanup --- .github/workflows/build-pr | 71 ------------------------ .github/workflows/{build => build.yml} | 0 .github/workflows/codeql.yml | 2 +- .github/workflows/{eslint => eslint.yml} | 0 4 files changed, 1 insertion(+), 72 deletions(-) delete mode 100644 .github/workflows/build-pr rename .github/workflows/{build => build.yml} (100%) rename .github/workflows/{eslint => eslint.yml} (100%) diff --git a/.github/workflows/build-pr b/.github/workflows/build-pr deleted file mode 100644 index 513fe803..00000000 --- a/.github/workflows/build-pr +++ /dev/null @@ -1,71 +0,0 @@ ---- -name: Build - -on: - pull_request: - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: ['20.17.0'] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - environment: - name: build-pr - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Install pnpm - uses: pnpm/action-setup@v2 - with: - version: 8 - run_install: false - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - name: Setup pnpm cache - uses: actions/cache@v4 - with: - path: | - ${{ env.STORE_PATH }} - ${{ github.workspace }}/.next/cache - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} - restore-keys: | - ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}- - - - name: Install dependencies - run: pnpm install - - - name: Build - run: pnpm run build - - - name: Get Commit SHA (short) - id: get_version - run: | - # Get the short 8-character commit SHA - VERSION=$(git rev-parse --short=8 HEAD) - echo "Commit SHA is $VERSION" - echo "tag=$VERSION" >> $GITHUB_OUTPUT - - - name: SonarQube Analysis (Pull Request) - uses: SonarSource/sonarqube-scan-action@v6 - with: - args: > - -Dsonar.projectVersion=${{ steps.get_version.outputs.tag }} - -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} - -Dsonar.pullrequest.branch=${{ github.event.pull_request.head.ref }} - -Dsonar.pullrequest.base=${{ github.event.pull_request.base.ref }} diff --git a/.github/workflows/build b/.github/workflows/build.yml similarity index 100% rename from .github/workflows/build rename to .github/workflows/build.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index fee77313..79d64857 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,7 +4,7 @@ name: "Code Quality Analysis" on: push: branches: - - dev1 + - main paths: - apps/** - '!apps/docs/**' diff --git a/.github/workflows/eslint b/.github/workflows/eslint.yml similarity index 100% rename from .github/workflows/eslint rename to .github/workflows/eslint.yml From f7aa06475ca210dcc027b81def597af8ce4d3e65 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 6 Dec 2025 23:14:38 +0100 Subject: [PATCH 028/340] Refactor build workflow by removing unnecessary steps Removed the SonarQube analysis step and commit SHA retrieval from the build workflow. --- .github/workflows/build.yml | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d40e7f14..8e866231 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,20 +50,3 @@ jobs: - name: Build run: pnpm run build - - - name: Get Commit SHA (short) - id: get_version - run: | - # Get the short 8-character commit SHA - VERSION=$(git rev-parse --short=8 HEAD) - echo "Commit SHA is $VERSION" - echo "tag=$VERSION" >> $GITHUB_OUTPUT - - - name: SonarQube Analysis (Branch) - uses: SonarSource/sonarqube-scan-action@v6 - env: - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} - with: - args: > - -Dsonar.projectVersion=${{ steps.get_version.outputs.tag }} From ce3cd7ba0e9bfb828bc14c4aee6cfcf2b4d3ef0f Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 6 Dec 2025 23:15:17 +0100 Subject: [PATCH 029/340] Change cache dependency path to pnpm-lock.json Updated cache dependency path to use pnpm-lock.json instead of package-lock.json. --- .github/workflows/eslint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 8579a8e1..ad5daa83 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -39,7 +39,7 @@ jobs: node-version: '20' cache: 'npm' cache-dependency-path: | - **/package-lock.json + **/pnpm-lock.json - name: Install ESLint run: | From aa35492fb72352b3a416beedd408e317628eac28 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 6 Dec 2025 23:17:02 +0100 Subject: [PATCH 030/340] Update cache dependency path to pnpm-lock.yaml --- .github/workflows/eslint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index ad5daa83..356bb215 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -39,7 +39,7 @@ jobs: node-version: '20' cache: 'npm' cache-dependency-path: | - **/pnpm-lock.json + **/pnpm-lock.yaml - name: Install ESLint run: | From 7ae9ddbd02ff950780d00795c5901c5762a10fd0 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 9 Dec 2025 21:36:16 +0700 Subject: [PATCH 031/340] feat: no follow in terms and privacy --- apps/frontend/src/components/auth/register.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 13d925d2..0558cb1f 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -208,12 +208,14 @@ export function RegisterAfter({ {t('terms_of_service', 'Terms of Service')}   {t('and', 'and')}  {t('privacy_policy', 'Privacy Policy')} From 8e30271fbcb288e310cf2ebad30fe3bf43461c26 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Tue, 9 Dec 2025 16:00:17 +0100 Subject: [PATCH 032/340] Temporarily disable ESLint --- .github/workflows/{eslint.yml => eslint} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{eslint.yml => eslint} (100%) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint similarity index 100% rename from .github/workflows/eslint.yml rename to .github/workflows/eslint From 6c4cf4164c824956aa7cc63fc580ad143da4c2b2 Mon Sep 17 00:00:00 2001 From: Adam Kovacs <13062491+adambkovacs@users.noreply.github.com> Date: Sun, 14 Dec 2025 21:21:04 -0800 Subject: [PATCH 033/340] fix(ci): Force fresh base image in Docker builds Add --pull and --no-cache flags to docker buildx build to ensure: - Base image (node:22.20-alpine) is always pulled fresh - No cached layers from previous builds are used This fixes issue #1079 where published images contained outdated Node.js v20.18.x instead of the expected v22.20.x from Dockerfile.dev, causing Prisma 7 compatibility failures. Fixes #1079 --- .github/workflows/build-containers.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml index e0fc50f6..a6932814 100644 --- a/.github/workflows/build-containers.yml +++ b/.github/workflows/build-containers.yml @@ -53,6 +53,8 @@ jobs: -f Dockerfile.dev \ -t ghcr.io/gitroomhq/postiz-app:${{ env.CONTAINERVER }}-${{ matrix.arch }} \ --build-arg NEXT_PUBLIC_VERSION=${{ env.NEXT_PUBLIC_VERSION }} \ + --pull \ + --no-cache \ --provenance=false --sbom=false \ --output "type=registry,name=ghcr.io/gitroomhq/postiz-app:${{ env.CONTAINERVER }}-${{ matrix.arch }}" . From f4ce7f74f987aae040b2061c05781b159932f870 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 18 Dec 2025 12:15:04 +0700 Subject: [PATCH 034/340] feat: register change --- apps/frontend/public/logo-text.svg | 9 + apps/frontend/src/app/(app)/auth/layout.tsx | 108 +++++----- apps/frontend/src/app/global.scss | 29 ++- .../components/auth/nayner.auth.button.tsx | 2 +- .../auth/providers/farcaster.provider.tsx | 39 ++-- .../auth/providers/google.provider.tsx | 4 +- .../auth/providers/oauth.provider.tsx | 2 +- .../placeholder/wallet.ui.provider.tsx | 19 +- .../auth/providers/wallet.provider.tsx | 2 +- .../frontend/src/components/auth/register.tsx | 196 ++++++++++-------- .../src/components/auth/testimonial.tsx | 16 ++ apps/frontend/tailwind.config.js | 2 + .../src/helpers/testomonials.tsx | 18 ++ 13 files changed, 264 insertions(+), 182 deletions(-) create mode 100644 apps/frontend/public/logo-text.svg create mode 100644 apps/frontend/src/components/auth/testimonial.tsx create mode 100644 libraries/react-shared-libraries/src/helpers/testomonials.tsx diff --git a/apps/frontend/public/logo-text.svg b/apps/frontend/public/logo-text.svg new file mode 100644 index 00000000..ba10640a --- /dev/null +++ b/apps/frontend/public/logo-text.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/frontend/src/app/(app)/auth/layout.tsx b/apps/frontend/src/app/(app)/auth/layout.tsx index a107cfd4..b20788a0 100644 --- a/apps/frontend/src/app/(app)/auth/layout.tsx +++ b/apps/frontend/src/app/(app)/auth/layout.tsx @@ -3,9 +3,8 @@ import { getT } from '@gitroom/react/translation/get.translation.service.backend export const dynamic = 'force-dynamic'; import { ReactNode } from 'react'; import Image from 'next/image'; -import clsx from 'clsx'; import loadDynamic from 'next/dynamic'; -import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side'; +import { Testimonial } from '@gitroom/frontend/components/auth/testimonial'; const ReturnUrlComponent = loadDynamic(() => import('./return.url.component')); export default async function AuthLayout({ children, @@ -15,68 +14,55 @@ export default async function AuthLayout({ const t = await getT(); return ( -
+
+ -
-
-
-
-
- Logo -
- {isGeneralServerSide() ? ( - - - - - - - ) : ( -
{t('gitroom', 'Gitroom')}
- )} -
+
+
+ Postiz +
{children}
+
+
+
+
+ Over 18,000+{' '} + Entrepreneurs use
Postiz + To Grow Their Social Presence +
+
+
+
+
+
+ {[1, 2].map((p) => ( +
+ + + + + + + + +
+ ))} +
+
+ {[1, 2].map((p) => ( +
+ + + + + + + + + +
+ ))}
-
- {children} -
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index 75993ff0..ce2787f6 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -703,4 +703,31 @@ html[dir='rtl'] [dir='ltr'] { .copilotKitMessage a { color: var(--new-btn-text) !important; -} \ No newline at end of file +} + +@keyframes marquee-up { + //0% { + // transform: translateY(0); + //} + //100% { + // transform: translateY(-50%); + //} +} + +@keyframes marquee-down { + //0% { + // transform: translateY(-50%); + //} + //100% { + // transform: translateY(0%); + //} +} + +.blackGradBottomBg { + background: linear-gradient(180deg, #0e0e0e 0%, rgba(14, 14, 14, 0) 100%); + rotate: 180deg; +} + +.blackGradTopBg { + background: linear-gradient(180deg, #0e0e0e 0%, rgba(14, 14, 14, 0) 100%); +} diff --git a/apps/frontend/src/components/auth/nayner.auth.button.tsx b/apps/frontend/src/components/auth/nayner.auth.button.tsx index 68cd5667..da450b6d 100644 --- a/apps/frontend/src/components/auth/nayner.auth.button.tsx +++ b/apps/frontend/src/components/auth/nayner.auth.button.tsx @@ -81,5 +81,5 @@ export const NeynarAuthButton: FC<{ document.removeEventListener('mousedown', handleOutsideClick); }; }, [showModal, handleOutsideClick]); - return
{children}
; + return
{children}
; }; diff --git a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx index 07b9408a..ee58c963 100644 --- a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx +++ b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx @@ -26,29 +26,32 @@ export const ButtonCaster: FC<{ >
- - - + + + + + + + + + -
{t('continue_with_farcaster', 'Continue with Farcaster')}
+
Farcaster
diff --git a/apps/frontend/src/components/auth/providers/google.provider.tsx b/apps/frontend/src/components/auth/providers/google.provider.tsx index 0aeb3069..e511c9a9 100644 --- a/apps/frontend/src/components/auth/providers/google.provider.tsx +++ b/apps/frontend/src/components/auth/providers/google.provider.tsx @@ -11,7 +11,7 @@ export const GoogleProvider = () => { return (
{ />
-
{t('continue_with_google', 'Continue with Google')}
+
Google
); }; diff --git a/apps/frontend/src/components/auth/providers/oauth.provider.tsx b/apps/frontend/src/components/auth/providers/oauth.provider.tsx index 7d4ed7bb..6e293fe0 100644 --- a/apps/frontend/src/components/auth/providers/oauth.provider.tsx +++ b/apps/frontend/src/components/auth/providers/oauth.provider.tsx @@ -26,7 +26,7 @@ export const OauthProvider = () => { return (
{ const t = useT(); return (
+ - {t('continue_with_your_wallet', 'Continue with your Wallet')} + Wallet
); }; diff --git a/apps/frontend/src/components/auth/providers/wallet.provider.tsx b/apps/frontend/src/components/auth/providers/wallet.provider.tsx index 16f5a2ee..83585dde 100644 --- a/apps/frontend/src/components/auth/providers/wallet.provider.tsx +++ b/apps/frontend/src/components/auth/providers/wallet.provider.tsx @@ -174,7 +174,7 @@ const InnerWallet = () => { } }, [buttonState]); return ( -
walletModal.setVisible(true)}> +
walletModal.setVisible(true)} className="flex-1">
); diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 0558cb1f..467413fa 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -144,99 +144,115 @@ export function RegisterAfter({ }; return ( -
-
-

- {t('sign_up', 'Sign Up')} -

-
- {!isAfterProvider && - (!isGeneral ? ( - - ) : ( -
- {genericOauth && isGeneral ? ( - + +
+
+

+ {t('sign_up', 'Sign Up')} +

+
+
Continue With
+
+ {!isAfterProvider && + (!isGeneral ? ( + ) : ( - - )} - {!!neynarClientId && } - {billingEnabled && } -
- ))} - {!isAfterProvider && ( -
-
-
-
{t('or', 'OR')}
+
+ {genericOauth && isGeneral ? ( + + ) : ( + + )} + {!!neynarClientId && } + {billingEnabled && } +
+ ))} + {!isAfterProvider && ( +
+
+
+
+ or +
+
+
+ )} +
+
+ {!isAfterProvider && ( + <> + + + + )} + +
+
+ {t( + 'by_registering_you_agree_to_our', + 'By registering you agree to our' + )} +   + + {t('terms_of_service', 'Terms of Service')} + +   + {t('and', 'and')}  + + {t('privacy_policy', 'Privacy Policy')} + +   +
+
+
+ +
+

+ {t('already_have_an_account', 'Already Have An Account?')} +   + + {t('sign_in', 'Sign In')} + +

+
- )} -
- {!isAfterProvider && ( - <> - - - - )} - -
-
-
-
- -
-

- {t('already_have_an_account', 'Already Have An Account?')}  - - {t('sign_in', 'Sign In')} - -

diff --git a/apps/frontend/src/components/auth/testimonial.tsx b/apps/frontend/src/components/auth/testimonial.tsx new file mode 100644 index 00000000..2604079a --- /dev/null +++ b/apps/frontend/src/components/auth/testimonial.tsx @@ -0,0 +1,16 @@ +export const Testimonial = () => { + return ( +
+
+
+
+
name
+
description
+
+
+
+ content +
+
+ ); +}; diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index 19527704..810f8292 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -116,6 +116,8 @@ module.exports = { fadeDown: 'fadeDown 4s ease-in-out forwards', normalFadeDown: 'normalFadeDown 0.5s ease-in-out forwards', newMessages: 'newMessages 1s ease-in-out 4s forwards', + marqueeUp: 'marquee-up 10s linear infinite', + marqueeDown: 'marquee-down 10s linear infinite', }, boxShadow: { yellow: '0 0 60px 20px #6b6237', diff --git a/libraries/react-shared-libraries/src/helpers/testomonials.tsx b/libraries/react-shared-libraries/src/helpers/testomonials.tsx new file mode 100644 index 00000000..7b625b90 --- /dev/null +++ b/libraries/react-shared-libraries/src/helpers/testomonials.tsx @@ -0,0 +1,18 @@ +export const testomonials1 = [ + { + picture: '', + name: '', + handle: '', + content: <>Content, + }, +]; + + +export const testomonials2 = [ + { + picture: '', + name: '', + handle: '', + content: <>Content, + }, +]; From 7294cad60b2551ee26d267a3e8bc06282d61400b Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 18 Dec 2025 14:27:57 +0700 Subject: [PATCH 035/340] feat: branded signin-signup --- apps/frontend/public/auth/avatars/ali.jpg | Bin 0 -> 10318 bytes apps/frontend/public/auth/avatars/andy.jpeg | Bin 0 -> 3538 bytes apps/frontend/public/auth/avatars/anica.jpg | Bin 0 -> 17944 bytes apps/frontend/public/auth/avatars/bart.jpg | Bin 0 -> 32493 bytes apps/frontend/public/auth/avatars/david.jpg | Bin 0 -> 3630 bytes apps/frontend/public/auth/avatars/dilini.jpeg | Bin 0 -> 4269 bytes apps/frontend/public/auth/avatars/george.jpg | Bin 0 -> 4125 bytes apps/frontend/public/auth/avatars/henry.jpg | Bin 0 -> 3252 bytes apps/frontend/public/auth/avatars/iorn.jpg | Bin 0 -> 13392 bytes apps/frontend/public/auth/avatars/johna.jpg | Bin 0 -> 12624 bytes apps/frontend/public/auth/avatars/josh.jpg | Bin 0 -> 3245 bytes apps/frontend/public/auth/avatars/kiley.jpeg | Bin 0 -> 2646 bytes apps/frontend/public/auth/avatars/maria.jpg | Bin 0 -> 20304 bytes .../frontend/public/auth/avatars/michael.jpeg | Bin 0 -> 4301 bytes apps/frontend/public/auth/avatars/serge.jpeg | Bin 0 -> 1742 bytes apps/frontend/public/auth/avatars/vince.jpeg | Bin 0 -> 4078 bytes apps/frontend/public/auth/avatars/vincent.jpg | Bin 0 -> 5826 bytes apps/frontend/src/app/(app)/auth/layout.tsx | 49 +--- apps/frontend/src/app/global.scss | 24 +- .../frontend/src/components/auth/activate.tsx | 4 +- apps/frontend/src/components/auth/forgot.tsx | 87 +++---- apps/frontend/src/components/auth/login.tsx | 133 +++++----- .../auth/providers/farcaster.provider.tsx | 2 +- .../auth/providers/google.provider.tsx | 2 +- .../placeholder/wallet.ui.provider.tsx | 2 +- .../components/auth/testimonial.component.tsx | 44 ++++ .../src/components/auth/testimonial.tsx | 32 ++- apps/frontend/tailwind.config.js | 4 +- .../src/helpers/testomonials.tsx | 231 +++++++++++++++++- 29 files changed, 433 insertions(+), 181 deletions(-) create mode 100644 apps/frontend/public/auth/avatars/ali.jpg create mode 100644 apps/frontend/public/auth/avatars/andy.jpeg create mode 100644 apps/frontend/public/auth/avatars/anica.jpg create mode 100644 apps/frontend/public/auth/avatars/bart.jpg create mode 100644 apps/frontend/public/auth/avatars/david.jpg create mode 100644 apps/frontend/public/auth/avatars/dilini.jpeg create mode 100644 apps/frontend/public/auth/avatars/george.jpg create mode 100644 apps/frontend/public/auth/avatars/henry.jpg create mode 100644 apps/frontend/public/auth/avatars/iorn.jpg create mode 100644 apps/frontend/public/auth/avatars/johna.jpg create mode 100644 apps/frontend/public/auth/avatars/josh.jpg create mode 100644 apps/frontend/public/auth/avatars/kiley.jpeg create mode 100644 apps/frontend/public/auth/avatars/maria.jpg create mode 100644 apps/frontend/public/auth/avatars/michael.jpeg create mode 100644 apps/frontend/public/auth/avatars/serge.jpeg create mode 100644 apps/frontend/public/auth/avatars/vince.jpeg create mode 100644 apps/frontend/public/auth/avatars/vincent.jpg create mode 100644 apps/frontend/src/components/auth/testimonial.component.tsx diff --git a/apps/frontend/public/auth/avatars/ali.jpg b/apps/frontend/public/auth/avatars/ali.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a3102f60b75be0ac8b9b4d10697d7d98ab072a85 GIT binary patch literal 10318 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%0DHrh^e$)mPDR25!Rl2%ZjS5 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 V+4}Hn*F#cO=P)+Q)zAM{{y%6^LNWjV literal 0 HcmV?d00001 diff --git a/apps/frontend/public/auth/avatars/david.jpg b/apps/frontend/public/auth/avatars/david.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3fcf60292b6761344d9b7e4317ddf5e25acd8c04 GIT binary patch literal 3630 zcmYjR1yq#V7XBy60fr7~kWMM3Q#wUOX&7N>29ORZ;S$OaL${1bhk%5Dw1iSpR}c^c zX#`1~hwt6D-uup4d!HTWTi-tGtV5V3d;zF*wRE%q5C{N3R|_C40MP&h3??NbqaY?G zBmNIaN&ngWzxyZmZ%0CMP#AxHp9%?YECfD>`4nh?`+OWN^xW1iHhncAlm)zjT@@KMK> zG&FXPU!7;Ux&YY!v;Ug_3WF0}6(z28fWS})91e#3Bj6AF6}g0}$rBitop;JUjbu?> z_cHf5t(qyEFb|MlHG+{>3Y3913%UI+W;u*UI87LyM$eIpeK~>ZFZB!S0=iC80^r{z+Ehy@CG>9Pq z7Qw%Ndf*o4Fj&AsdE0YW4*QUkXmGvPKI-@Ew;J)G4Q}=^Y2}OF@(n>1mUc??AZAi0 zeYp3Ac}zbZCrZ-jy=;16zP9P~v)O5~>oxIA z;#$AJ1HHcQ(*=2FBJWSHx%Tn#!g27QVbjEps2^8FGE8MQ?jD1JQY4eJ?zM_eXoqe z;W@`nBF8t>-Qs3|GbR!*hKQt?(wu#V;khjn&`D7spEataCVM|sHrpn8!q_M<#Xhu> z>Hz(su+;aM?~J;5(Ly@RaAqGCdpdl7agm`m@$ImJ>Qf1<*5?N~CSk?x$241E$dA5L zF2f8ALN-&KY-8ma-<9Ixyx*?*r0C&Om5OHc^?$Q=Rgn%DoVlOu#Fxmu4lZuo|6%nh zXA{#K3`P}*^eTRktRL88y7?`hM=fF$8ej3U9;_$C(Rvq6@!Tqb8@=6s3-ffO(wBB1 znlZ!-V3Y~~bXL1S@-YJmQ0Ii`(|XEdmgC+UM~;2_%{a!d_r@*)o{H)pMO6m6s~?aj z28CDI%N+5l)XWw{3!B`kHJuQY;KPhGY0En9x}8}pd_;Z1h01u^;{19Q4kC5VwAmjo zW$=Hv>zmeA$IO?Qviwk5WBQsRzP7gM3t30}$jx2W>_V(&AE$vMC8^&28vSE@^dNq7 z^F9u`h!%qgH5KM^@{YV=%53iM9*C#yg7b<;@NC5ZdGE~V;anmdr-$J!;i~cM@1ujC zp(L#8Ol&S@m*464dt7hLpfy6c95@!USkG(*+cRSuA6h1~I15ZAvc)%cSj9c_x(31= z81S?WK@U}JS)*0`Hm#dRLN-oaXBa2T{rv|wYiIq*UUY2VbZT?Tk*w0V%#iqqwiPq% zyPvLM+g@j=npMfx#Q%B})iHe*HmplWTjhRpY2W3fU_N9bL^#4spx9y%M4M2z>?*7B zkyrCMV>MRo)!Wk70!Dm3;YK|~9UWf|jc|6EK)Vt7c4T# zzSykwkgGyrg^b!YzH(D3<47$%4_e_(mmFF1%UGp! z3D$VVfl9vLTG1NI$sA)7nTZ%Z)gl1zU9VTi-6?IfXIft`7KBZ6V{j*A4X_P`c#iY& z%_)1{O6IGi!IvduL=PuP;K=Bguk=WeedCN&R{4^MLxs3^3c<8}hhV^SDr z_CguSb3?D=bE0k;j#k#oVZuYT@vvm&ptM2Sj60LHjUaSQ@T4uvv7*6Fp5d06)!l~L z&+`;ybj{KZ6Jk1LO02CJ_VC}4Z?_GyUfZAapsa zxLTC#5Wc-r0^z(%urR#hvt9x~mKf0HtGerx{x$EgWaN~t z>0_I_>ZWh?UQ~$h)^rd@V^MPP2<(T^@`9qF^@@zFXho=7kwY2_ASlgm0kIme<$jQd zoeSPN;qG4L)hct{{dIIfZFqQ5P#}1@b0=F@6a`GYzkp%<(0oIdNJq3&rB`anEsBfk z@FnghylK7wF9RC0I(}fiP2AG?=!WE}jkv$G>T>~8{>X0q$R|AJEWZOBAEmZx92S{W z=(Itrx5D~x-ni&TrTzp!#1Yw6DOhPA-Pv(`5d<`U8lOuW1Cv{ zr@oa~Q}6#EHKMbBV!JO2NVxISgi&U1#?LVmeJ)?!WP7YG1XZO)JUZdkE`(tpZ@@*~ z<>H~mEh+DHMYq}gW0r;^9Ny=fPP^8PIyC3yLA>k11fZY4{9{REqF;Uu4xGCsTdIp& zU=#BU%BnWc7~*MwldDs?%vPt`CYLaKx|7DuCA=n84{$WOBfa@uwa!_)P4>-^l$X^p z8Q0DQ=gG{TS9t6rZS>*z^X!cuhdDqgdsbl`4XI45q`yO=K8n0Xa&GV2g|yeSC!SW> zKmPB2W4RNb-ATn}5e7?D{{v^JlwI5eI8UBg_2G|WapR1-$!*_8dBakAixb?;%u=Sn z_Lu=Su-Ii^G8X*3r%^LwW7qCk5GzBOkR8`#nhnw3woU(#u6XuJb%)%MEzA5nMk1=0 zcgs%hvq-5hGy9VdJB-=*i#Md!!6>#ZSKoB4`(}6q1^>9rr4U@ATie=O0&2Fi7NZ_< zg|W)ha_GQb}Aiswh(VtNvGKas2avgDh z9{RmT)&?f$^bLi2YNWyDe!?SL{^v5Z#PwRM%u+8ebzJL(3nZ!U_lX50Hvw`r@^ zOVJq@niRg(CMxSWsm>|H6L+KH^R9p0?dH4oL0mq)SlB~h+wD$zmD-aA`wmxnCM3~H z`OCbqC8MhIq^2$N&U_Z{5ko2!QiDim)=HX|S(?X&i)|k60_EQk)VsSE=<9kCY#!VP zwtgt%Db^qbkI%$d$>HEnFO*|mbKw~ndO3xwH#*yi604mIy%}3Z`s;--t%Zp$Py@wz8;(=V@Xp6Uq5yPk1Mj?{tMhV5pzWmf@+3 z(P)sfdAO_6Wa*`;W{jGs0S(81*Tm4(r~8=guSv|kX$Lb$IB`#K;r*hl+o!DOp zN+*x&wkuhWp|GqM!#WP@7Asz_lrG^J8g|LtQ}5zlUccWC_`k_9(ZPh*XMmI!$-YS z+e+&k_{^%kkDNC~X|&Cz$i`Sf?WcUG$L03tfCy3?s44?D2Wh7s@1zW#rd!9HOcyeuz4(9tLxKUnfzC-L)mffb5&R zmnG+`h@LYaQNa%-lZUSTFE+l0@cK==o(dAZ?sN>aSk5uoZ%MQ4FKX^UH4bn~YZt}f*c+H$4bGVJ3+ZcE* z+YzeG6J&!gZ^?0s9pYeJjJ4CDnMe}r$bd#lc%-k@N?ImuU+=>70I*OZe5#+c z%3R&I&Jm`n0g>2|g}n2;pB?Q#t8>nNQR)k+X(r`PX!99-`UIa622UAK-D0WI&HHDz>@qIjAXAcga0T|58tQQs~pY z1OYy_;RjOk$@b!Pb);JB-EGBc-UwPrR&BvDmJ&@VxO5Q!ORo=F MV=<)2Xb55MUz_fH>i_@% literal 0 HcmV?d00001 diff --git a/apps/frontend/public/auth/avatars/dilini.jpeg b/apps/frontend/public/auth/avatars/dilini.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..0cbe820a780acb2231b3a705be045a700e390681 GIT binary patch literal 4269 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/public/auth/avatars/george.jpg b/apps/frontend/public/auth/avatars/george.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ca11fcba2c3716b3684e182236b6e6ad075c572c GIT binary patch literal 4125 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 H>-qlyEuVM? literal 0 HcmV?d00001 diff --git a/apps/frontend/public/auth/avatars/henry.jpg b/apps/frontend/public/auth/avatars/henry.jpg new file mode 100644 index 0000000000000000000000000000000000000000..72df9f6b59eec97a9eca0222a0935189082565ae GIT binary patch literal 3252 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 mGIszCylN7mo9!!J!u|q=`rE#3F_f|Z0000vP) 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/public/auth/avatars/michael.jpeg b/apps/frontend/public/auth/avatars/michael.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..9bd2e64189eedc370c0fdc4e8a54e8794207d195 GIT binary patch literal 4301 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 IM4g@f4{d^EcK`qY literal 0 HcmV?d00001 diff --git a/apps/frontend/public/auth/avatars/serge.jpeg b/apps/frontend/public/auth/avatars/serge.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c7879babddcd3312b1c63e3318b15acd64be1851 GIT binary patch literal 1742 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 JXHD1B{{xt1n8W}8 literal 0 HcmV?d00001 diff --git a/apps/frontend/src/app/(app)/auth/layout.tsx b/apps/frontend/src/app/(app)/auth/layout.tsx index b20788a0..1ee91cff 100644 --- a/apps/frontend/src/app/(app)/auth/layout.tsx +++ b/apps/frontend/src/app/(app)/auth/layout.tsx @@ -4,7 +4,7 @@ export const dynamic = 'force-dynamic'; import { ReactNode } from 'react'; import Image from 'next/image'; import loadDynamic from 'next/dynamic'; -import { Testimonial } from '@gitroom/frontend/components/auth/testimonial'; +import { TestimonialComponent } from '@gitroom/frontend/components/auth/testimonial.component'; const ReturnUrlComponent = loadDynamic(() => import('./return.url.component')); export default async function AuthLayout({ children, @@ -15,55 +15,22 @@ export default async function AuthLayout({ return (

- + {/**/} -
+
Postiz
{children}
-
+
Over 18,000+{' '} - Entrepreneurs use
Postiz - To Grow Their Social Presence -
-
-
-
-
-
- {[1, 2].map((p) => ( -
- - - - - - - - -
- ))} -
-
- {[1, 2].map((p) => ( -
- - - - - - - - - -
- ))} -
-
+ Entrepreneurs use +
+ Postiz To Grow Their Social Presence
+
); diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index ce2787f6..9f9c95cc 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -706,21 +706,21 @@ html[dir='rtl'] [dir='ltr'] { } @keyframes marquee-up { - //0% { - // transform: translateY(0); - //} - //100% { - // transform: translateY(-50%); - //} + 0% { + transform: translateY(0); + } + 100% { + transform: translateY(-50%); + } } @keyframes marquee-down { - //0% { - // transform: translateY(-50%); - //} - //100% { - // transform: translateY(0%); - //} + 0% { + transform: translateY(-50%); + } + 100% { + transform: translateY(0%); + } } .blackGradBottomBg { diff --git a/apps/frontend/src/components/auth/activate.tsx b/apps/frontend/src/components/auth/activate.tsx index a8191c12..3ffbd423 100644 --- a/apps/frontend/src/components/auth/activate.tsx +++ b/apps/frontend/src/components/auth/activate.tsx @@ -5,7 +5,7 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; export function Activate() { const t = useT(); return ( - <> +

{t('activate_your_account', 'Activate your account')} @@ -19,6 +19,6 @@ export function Activate() { 'Please check your email to activate your account.' )}

- +
); } diff --git a/apps/frontend/src/components/auth/forgot.tsx b/apps/frontend/src/components/auth/forgot.tsx index 57300f63..d6f5110c 100644 --- a/apps/frontend/src/components/auth/forgot.tsx +++ b/apps/frontend/src/components/auth/forgot.tsx @@ -36,52 +36,57 @@ export function Forgot() { setLoading(false); }; return ( - -
-
-

- {t('forgot_password_1', 'Forgot Password')} -

-
- {!state ? ( - <> -
- -
-
-
- +
+ + +
+

+ {t('forgot_password_1', 'Forgot Password')} +

+
+ {!state ? ( + <> +
+ +
+
+
+ +
+

+ + {t('go_back_to_login', 'Go back to login')} + +

+
+ + ) : ( + <> +
+ {t( + 'we_have_send_you_an_email_with_a_link_to_reset_your_password', + 'We have send you an email with a link to reset your password.' + )}

{t('go_back_to_login', 'Go back to login')}

-
- - ) : ( - <> -
- {t( - 'we_have_send_you_an_email_with_a_link_to_reset_your_password', - 'We have send you an email with a link to reset your password.' - )} -
-

- - {t('go_back_to_login', 'Go back to login')} - -

- - )} - - + + )} + + +
); } diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index 6344b918..370f5f8a 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -55,70 +55,81 @@ export function Login() { }; return ( -
-
-

- {t('sign_in', 'Sign In')} -

-
- {isGeneral && genericOauth ? ( - - ) : !isGeneral ? ( - - ) : ( -
- - {!!neynarClientId && } - {billingEnabled && } + +
+
+

+ {t('sign_in', 'Sign In')} +

- )} -
-
-
-
{t('or', 'OR')}
+
+ {t('continue_with', 'Continue With')}
-
- -
- - -
-
-
- +
+ {isGeneral && genericOauth ? ( + + ) : !isGeneral ? ( + + ) : ( +
+ + {!!neynarClientId && } + {billingEnabled && } +
+ )} +
+
+
+
or
+
+
+
+
+ + +
+
+
+ +
+

+ {t('don_t_have_an_account', "Don't Have An Account?")}  + + {t('sign_up', 'Sign Up')} + +

+

+ + {t('forgot_password', 'Forgot password')} + +

+
+
-

- {t('don_t_have_an_account', "Don't Have An Account?")}  - - {t('sign_up', 'Sign Up')} - -

-

- - {t('forgot_password', 'Forgot password')} - -

diff --git a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx index ee58c963..ce4e492a 100644 --- a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx +++ b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx @@ -26,7 +26,7 @@ export const ButtonCaster: FC<{ >
{ return (
{ const t = useT(); return (
{ + return ( +
+
+
+
+
+
+ {[1, 2].flatMap((p) => + testimonials1.flatMap((a) => ( +
+ +
+ )) + )} +
+
+ {[1, 2].flatMap((p) => + testimonials2.flatMap((a) => ( +
+ +
+ )) + )} +
+
+
+
+ ); +}; diff --git a/apps/frontend/src/components/auth/testimonial.tsx b/apps/frontend/src/components/auth/testimonial.tsx index 2604079a..a9eceddf 100644 --- a/apps/frontend/src/components/auth/testimonial.tsx +++ b/apps/frontend/src/components/auth/testimonial.tsx @@ -1,15 +1,31 @@ -export const Testimonial = () => { +import { FC } from 'react'; +import Image from 'next/image'; + +export const Testimonial: FC<{ + picture: string; + name: string; + description: string; + content: any; +}> = ({ content, description, name, picture }) => { return (
-
-
-
-
name
-
description
+ {/* Header */} +
+
+ {name} +
+ +
+
{name}
+
+ {description} +
-
- content + + {/* Content */} +
+ {typeof content === 'string' ? content.replace(/\\n/g, '\n') : content}
); diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index 810f8292..e5c3f7e9 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -116,8 +116,8 @@ module.exports = { fadeDown: 'fadeDown 4s ease-in-out forwards', normalFadeDown: 'normalFadeDown 0.5s ease-in-out forwards', newMessages: 'newMessages 1s ease-in-out 4s forwards', - marqueeUp: 'marquee-up 10s linear infinite', - marqueeDown: 'marquee-down 10s linear infinite', + marqueeUp: 'marquee-up 100s linear infinite', + marqueeDown: 'marquee-down 100s linear infinite', }, boxShadow: { yellow: '0 0 60px 20px #6b6237', diff --git a/libraries/react-shared-libraries/src/helpers/testomonials.tsx b/libraries/react-shared-libraries/src/helpers/testomonials.tsx index 7b625b90..69d9c3f8 100644 --- a/libraries/react-shared-libraries/src/helpers/testomonials.tsx +++ b/libraries/react-shared-libraries/src/helpers/testomonials.tsx @@ -1,18 +1,227 @@ -export const testomonials1 = [ +export const testimonials1 = [ { - picture: '', - name: '', - handle: '', - content: <>Content, + picture: '/auth/avatars/vincent.jpg', + name: 'Vincent L.', + description: 'Marketing Coordinator', + content: ( + <> + The UI is friendly and the AI content assistant is surprisingly + effective for professional tones. I especially like how it adjusts to + different industries. + + ), + }, + { + picture: '/auth/avatars/dilini.jpeg', + name: 'Dilini R.', + description: 'AI & Tech Consultant', + content: ( + <> + I just found out about Postiz, a tool for scheduling social media.{' '} + {'\n'} + Exactly what I wish there was a few years back, I even thought of + building one myself at one point, but didn't have the time to. {'\n'} + What I like about it so far: {'\n\n'} + It connects to LinkedIn, X, Instagram, Facebook (and others) from one + dashboard. {'\n'} + {'\n'} + Because it's open-source, you can see how it works and even tweak it if + you need to. {'\n'} + {'\n'} + I've used a few scheduling tools before and most of them are either + expensive or try to be "all-in-one marketing platforms." {'\n'} + {'\n'} + Postiz seems to focus on just doing one thing well. {'\n'} + {'\n'} + + ), + }, + { + picture: '/auth/avatars/johna.jpg', + name: 'Johannes D.', + description: 'CEO', + content: ( + <> + As a privacy-first company we appreciate being able to self-host Postiz! + It brings all the core functionality of a social media scheduler plus a + lot of AI to make things faster. It's also very easy to deploy and use, + great work! + + ), + }, + { + picture: '/auth/avatars/george.jpg', + name: 'George B.', + description: 'Marketing Assistant', + content: ( + <> + It's so easy to jump in and start scheduling. I like that I can see all + planned posts at a glance and edit them quickly if needed. + + ), + }, + { + picture: '/auth/avatars/maria.jpg', + name: 'Maria Camila A.', + description: 'Data Analyst', + content: ( + <> + Postiz changed how we manage our social media presence by aggregating + our platforms into one effective tool. Post scheduling, and AI post + ideation are two of the many features that come with Postiz, and have + made our management of social media simple and effective! Highly + recommend + + ), + }, + { + picture: '/auth/avatars/bart.jpg', + name: 'Bartolomeo H.', + description: 'CEO', + content: ( + <> + It only takes 10 minutes to set up your X scheduling automation. {'\n'} + n8n + Postiz =🔥Never miss a day of posting again: {'\n\n'}→ Easy to get + started {'\n'}→ Tutorial video included {'\n'}→ Automated content + creation {'\n'}→ Multi-platform publishing {'\n'}→ Self-hosted (no + monthly fees) {'\n'}→ Open-source (customize everything) {'\n'} + + ), + }, + { + picture: '/auth/avatars/henry.jpg', + name: 'Henry H.', + description: 'Social Media Coordinator', + content: ( + <> + The interface is clean and simple. I love how the AI assistant helps + speed up caption writing without sounding generic. It's really helpful + when I'm on a tight schedule. + + ), + }, + { + picture: '/auth/avatars/andy.jpeg', + name: 'Andy C.', + description: 'AI Specialist', + content: ( + <> + Manage all your social media accounts from a single place: Postiz, a + really cool tool I recently discovered :D! {'\n'} + It comes with a bunch of cool tools for posting at specific times, + posting across multiple platforms simultaneously, etc. And all of this + can potentially be self-hosted for free; you just need a small server to + configure everything ;D! It works really well :D! + + ), }, ]; - -export const testomonials2 = [ +export const testimonials2 = [ { - picture: '', - name: '', - handle: '', - content: <>Content, + picture: '/auth/avatars/michael.jpeg', + name: 'Michael H.', + description: 'Senior frontend developer', + content: ( + <> + 🌟 Exciting news! 🚀 I've just started using Postiz, a fantastic new + tool for scheduling my social media content! {'\n\n'} + Why did I choose Postiz? The ability to self-host it means significant + savings for me! 💰 {'\n\n'} + Postiz is an open-source scheduling tool that allows you to plan and + automate posts across 19+ platforms, including X, LinkedIn, BlueSky, and + Mastodon. {'\n\n'} + With its powerful editor, you can easily connect your accounts, create + rich scheduled posts, and manage multiple channels all in one place. + Plus, it supports image uploads, recurring posts, and timezone-aware + scheduling! 📅✨ {'\n\n'} + Built with privacy and flexibility in mind, Postiz can run on your own + infrastructure or be used as a hosted service. It's perfect for + individuals, teams, and communities looking for control and automation + without the unnecessary bloat. {'\n\n'} + + ), + }, + { + picture: '/auth/avatars/kiley.jpeg', + name: 'Kiley H.', + description: 'Content Creator', + content: ( + <> + The unified dashboard helps me manage Instagram, Facebook, and LinkedIn + from one place. I love that it saves time and keeps our campaigns + aligned across all platforms. + + ), + }, + { + picture: '/auth/avatars/iorn.jpg', + name: 'Iornienge S.', + description: 'Social Media Manager', + content: ( + <> + There are several things I love about this suite. Some of these things + include {'\n'}- Ease of use {'\n'}- Helps me organize my social media + accounts {'\n'}- I get work done faster {'\n'}- It does not consume my + time {'\n'}- it has a professional interface {'\n'} + + ), + }, + { + picture: '/auth/avatars/david.jpg', + name: 'David C.', + description: 'Digital Marketing Manager', + content: ( + <> + Postiz makes it so easy to plan ahead. The AI suggestions are relevant, + and the platform feels lightweight but powerful + + ), + }, + { + picture: '/auth/avatars/serge.jpeg', + name: 'Serge A.', + description: 'CEO', + content: ( + <> + Good tool for social media campaigns. The great thing is that the + platform constantly evolves - new features appear all the time, so I can + follow the latest trends (latest AI developments) without leaving + Postiz. + + ), + }, + { + picture: '/auth/avatars/anica.jpg', + name: 'Anica R.', + description: 'University Applications Specialist', + content: ( + <> + It is easy to use, manages your posts simple.It is a helpful tool that + let you organize your content. + + ), + }, + { + picture: '/auth/avatars/josh.jpg', + name: 'Josh W.', + description: 'Content Manager', + content: ( + <> + It's super easy to use even if you're not very techy. The AI writing + tool gives good drafts so I don't have to start from scratch every time + + ), + }, + { + picture: '/auth/avatars/vince.jpeg', + name: 'Vince C.', + description: 'Developer Relations Engineer', + content: ( + <> + I work in Developer Relations, so having a tool that helps me manage and + crosspost to different platforms saves me so, so, so much time! + + ), }, ]; From b7ce23cc5d54c8d8a13d31191c646a0f4c33a2a1 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Thu, 18 Dec 2025 14:38:40 +0700 Subject: [PATCH 036/340] fix: mobile cons --- .../src/components/auth/providers/farcaster.provider.tsx | 2 +- apps/frontend/src/components/auth/providers/google.provider.tsx | 2 +- .../auth/providers/placeholder/wallet.ui.provider.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx index ce4e492a..2bfbfbc7 100644 --- a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx +++ b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx @@ -51,7 +51,7 @@ export const ButtonCaster: FC<{ -
Farcaster
+
Farcaster
diff --git a/apps/frontend/src/components/auth/providers/google.provider.tsx b/apps/frontend/src/components/auth/providers/google.provider.tsx index 19c6ce65..8f94299f 100644 --- a/apps/frontend/src/components/auth/providers/google.provider.tsx +++ b/apps/frontend/src/components/auth/providers/google.provider.tsx @@ -38,7 +38,7 @@ export const GoogleProvider = () => { />
-
Google
+
Google
); }; diff --git a/apps/frontend/src/components/auth/providers/placeholder/wallet.ui.provider.tsx b/apps/frontend/src/components/auth/providers/placeholder/wallet.ui.provider.tsx index d17dc92c..94bfe050 100644 --- a/apps/frontend/src/components/auth/providers/placeholder/wallet.ui.provider.tsx +++ b/apps/frontend/src/components/auth/providers/placeholder/wallet.ui.provider.tsx @@ -23,7 +23,7 @@ export const WalletUiProvider: FC = () => { fill="#0E0E0E" /> - Wallet +
Wallet
); }; From 022f9bf8c6915769e700f57bc9d8b521fd86588a Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 19 Dec 2025 10:56:50 +0700 Subject: [PATCH 037/340] feat: width change --- apps/frontend/src/components/auth/testimonial.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/auth/testimonial.component.tsx b/apps/frontend/src/components/auth/testimonial.component.tsx index 79ff322a..ca22fb03 100644 --- a/apps/frontend/src/components/auth/testimonial.component.tsx +++ b/apps/frontend/src/components/auth/testimonial.component.tsx @@ -8,7 +8,7 @@ import { Testimonial } from '@gitroom/frontend/components/auth/testimonial'; export const TestimonialComponent = () => { return ( -
+
From 991d354c3439678955496f651adbd50ae9f091c7 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 22 Dec 2025 16:55:51 +0700 Subject: [PATCH 038/340] feat: billing change --- .../src/api/routes/billing.controller.ts | 17 ++ apps/frontend/public/logo-text.svg | 8 +- apps/frontend/public/stripe.svg | 1 + apps/frontend/src/app/(app)/layout.tsx | 1 + apps/frontend/src/app/global.scss | 9 + .../components/billing/embedded.billing.tsx | 182 ++++++++++++ .../src/components/billing/faq.component.tsx | 6 +- .../billing/first.billing.component.tsx | 271 ++++++++++++++++++ .../src/components/layout/check.payment.tsx | 33 ++- .../src/components/layout/mode.component.tsx | 5 + .../src/components/layout/new-modal.tsx | 23 +- .../new-layout/layout.component.tsx | 107 +++---- .../components/ui/check.icon.component.tsx | 28 ++ .../src/components/ui/logo-text.component.tsx | 45 +++ .../src/services/stripe.service.ts | 122 +++++++- .../src/helpers/variable.context.tsx | 2 + package.json | 2 + pnpm-lock.yaml | 28 ++ 18 files changed, 815 insertions(+), 75 deletions(-) create mode 100644 apps/frontend/public/stripe.svg create mode 100644 apps/frontend/src/components/billing/embedded.billing.tsx create mode 100644 apps/frontend/src/components/billing/first.billing.component.tsx create mode 100644 apps/frontend/src/components/ui/check.icon.component.tsx create mode 100644 apps/frontend/src/components/ui/logo-text.component.tsx diff --git a/apps/backend/src/api/routes/billing.controller.ts b/apps/backend/src/api/routes/billing.controller.ts index 2ba8eb1f..5856898c 100644 --- a/apps/backend/src/api/routes/billing.controller.ts +++ b/apps/backend/src/api/routes/billing.controller.ts @@ -62,6 +62,23 @@ export class BillingController { }; } + @Post('/embedded') + embedded( + @GetOrgFromRequest() org: Organization, + @GetUserFromRequest() user: User, + @Body() body: BillingSubscribeDto, + @Req() req: Request + ) { + const uniqueId = req?.cookies?.track; + return this._stripeService.embedded( + uniqueId, + org.id, + user.id, + body, + org.allowTrial + ); + } + @Post('/subscribe') subscribe( @GetOrgFromRequest() org: Organization, diff --git a/apps/frontend/public/logo-text.svg b/apps/frontend/public/logo-text.svg index ba10640a..83596798 100644 --- a/apps/frontend/public/logo-text.svg +++ b/apps/frontend/public/logo-text.svg @@ -1,8 +1,8 @@ - - - - + + + + diff --git a/apps/frontend/public/stripe.svg b/apps/frontend/public/stripe.svg new file mode 100644 index 00000000..25d948f1 --- /dev/null +++ b/apps/frontend/public/stripe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/frontend/src/app/(app)/layout.tsx b/apps/frontend/src/app/(app)/layout.tsx index bea3e05e..cb980aeb 100644 --- a/apps/frontend/src/app/(app)/layout.tsx +++ b/apps/frontend/src/app/(app)/layout.tsx @@ -52,6 +52,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { environment={process.env.NODE_ENV!} backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!} plontoKey={process.env.NEXT_PUBLIC_POLOTNO!} + stripeClient={process.env.STRIPE_PUBLISHABLE_KEY!} billingEnabled={!!process.env.STRIPE_PUBLISHABLE_KEY} discordUrl={process.env.NEXT_PUBLIC_DISCORD_SUPPORT!} frontEndUrl={process.env.FRONTEND_URL!} diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index 9f9c95cc..a5f342b9 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -731,3 +731,12 @@ html[dir='rtl'] [dir='ltr'] { .blackGradTopBg { background: linear-gradient(180deg, #0e0e0e 0%, rgba(14, 14, 14, 0) 100%); } + +.billing-form input { + background: transparent !important; +} + +//html body iframe[title="Stripe developer tools frame"] { +// display: none !important; +// height: 0 !important; +//} \ No newline at end of file diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx new file mode 100644 index 00000000..1d240468 --- /dev/null +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -0,0 +1,182 @@ +'use client'; + +import { Stripe } from '@stripe/stripe-js'; + +import { FC, useEffect, useState } from 'react'; +import { + PaymentElement, + BillingAddressElement, + CheckoutProvider, + useCheckout, +} from '@stripe/react-stripe-js/checkout'; +import { modeEmitter } from '@gitroom/frontend/components/layout/mode.component'; +import useCookie from 'react-use-cookie'; +import { Button } from '@gitroom/react/form/button'; +import dayjs from 'dayjs'; +import Image from 'next/image'; +import { useToaster } from '@gitroom/react/toaster/toaster'; + +export const EmbeddedBilling: FC<{ + stripe: Promise; + secret: string; +}> = ({ stripe, secret }) => { + const [saveSecret, setSaveSecret] = useState(secret); + const [loading, setLoading] = useState(false); + const [mode, setMode] = useCookie('mode', 'dark'); + + useEffect(() => { + modeEmitter.on('mode', (value) => { + setMode(value); + setLoading(true); + }); + + return () => { + modeEmitter.removeAllListeners(); + }; + }, []); + + useEffect(() => { + if (loading) { + setLoading(false); + } + }, [loading]); + + useEffect(() => { + if (secret && saveSecret !== secret) { + setSaveSecret(secret); + } + }, [secret, setSaveSecret]); + + if (saveSecret !== secret || loading) { + return null; + } + + return ( +
+ + + +
+ ); +}; + +const FormWrapper = () => { + const checkoutState = useCheckout(); + const toaster = useToaster(); + const [loading, setLoading] = useState(false); + + if (checkoutState.type === 'loading' || checkoutState.type === 'error') { + return null; + } + + const handleSubmit = async (e: any) => { + e.preventDefault(); + setLoading(true); + + const { checkout } = checkoutState; + + const confirmResult = await checkout.confirm(); + + if (confirmResult.type === 'error') { + toaster.show(confirmResult.error.message, 'warning'); + } + + setLoading(false); + }; + + return ( +
+ + + + ); +}; + +const StripeInputs = () => { + const checkout = useCheckout(); + return ( + <> +
+

+ {checkout.type === 'loading' ? '' : 'Billing Address'} +

+ +
+
+

+ {checkout.type === 'loading' ? '' : 'Payment'} +

+ + {checkout.type === 'loading' ? null : ( +
+
Powered by Stripe
+ Stripe +
+ )} +
+ + ); +}; + +const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { + const checkout = useCheckout(); + if (checkout.type === 'loading' || checkout.type === 'error') { + return null; + } + + return ( +
+
+ {checkout.checkout.recurring?.trial?.trialEnd ? ( +
+ Your 7-day trial is{' '} + 100% free ending{' '} + + {dayjs( + checkout.checkout.recurring?.trial?.trialEnd * 1000 + ).format('MMMM D, YYYY')}{' '} + —{' '} + + Cancel anytime. +
+ ) : null} +
+ +
+
+
+ ); +}; diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index 157148c3..c0150181 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -69,7 +69,7 @@ export const FAQSection: FC<{ }, [show]); return (
-
 {
             e.stopPropagation();
           }}
@@ -134,7 +134,7 @@ export const FAQComponent: FC = () => {
   const list = useFaqList();
   return (
     
-

+

{t('frequently_asked_questions', 'Frequently Asked Questions')}

diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx new file mode 100644 index 00000000..99e4bd81 --- /dev/null +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -0,0 +1,271 @@ +'use client'; + +import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; +import useSWR from 'swr'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { useVariables } from '@gitroom/react/helpers/variable.context'; +import { loadStripe, Stripe } from '@stripe/stripe-js'; +import { useSearchParams } from 'next/navigation'; +import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector'; +import { LanguageComponent } from '@gitroom/frontend/components/layout/language.component'; +import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/sentry.feedback.component'; +import NotificationComponent from '@gitroom/frontend/components/notifications/notification.component'; +import dynamic from 'next/dynamic'; +import { LogoTextComponent } from '@gitroom/frontend/components/ui/logo-text.component'; +import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; +import { capitalize } from 'lodash'; +import clsx from 'clsx'; +import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; +import { CheckIconComponent } from '@gitroom/frontend/components/ui/check.icon.component'; +import { + FAQComponent, + FAQSection, +} from '@gitroom/frontend/components/billing/faq.component'; + +const ModeComponent = dynamic( + () => import('@gitroom/frontend/components/layout/mode.component'), + { + ssr: false, + } +); + +const EmbeddedBilling = dynamic( + () => + import('@gitroom/frontend/components/billing/embedded.billing').then( + (mod) => mod.EmbeddedBilling + ), + { + ssr: false, + } +); + +export const FirstBillingComponent = () => { + const { stripeClient } = useVariables(); + const [stripe, setStripe] = useState>(null); + const [tier, setTier] = useState('STANDARD'); + const [period, setPeriod] = useState('MONTHLY'); + const fetch = useFetch(); + + useEffect(() => { + setStripe(loadStripe(stripeClient)); + }, []); + + const loadCheckout = useCallback(async () => { + return ( + await fetch('/billing/embedded', { + method: 'POST', + body: JSON.stringify({ + billing: tier, + period: period, + }), + }) + ).json(); + }, [tier, period]); + + const { data, isLoading } = useSWR( + `/billing-${tier}-${period}`, + loadCheckout, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + refreshWhenOffline: false, + refreshWhenHidden: false, + } + ); + + const price = useMemo( + () => Object.entries(pricing).filter(([key, value]) => key !== 'FREE'), + [] + ); + + return ( +
+
+
+ +
+
+
+ +
+ +
+
+ +
+ + +
+
+
+
+
+
+ Join Over{' '} + 18,000+ Entrepreneurs who + use{'\n'}Postiz To Grow Their Social Presence +
+ +
+
+
+ +
+
100% No-Risk Free Trial
+
+
+
+ +
+
Pay NOTHING for the first 7-days
+
+
+
+ +
+
Cancel anytime, hassle-free
+
+
+ + {!isLoading && data && stripe ? ( + <> + + + + ) : ( + + )} +
+
+
+
+
Choose a Plan
+
+
setPeriod('MONTHLY')} + > + Monthly +
+
setPeriod('YEARLY')} + > +
Yearly
+
+ 20% Off +
+
+
+
+
+ {price.map( + ([key, value]) => ( +
setTier(key)} + key={key} + className={clsx( + 'cursor-pointer select-none w-[266px] h-[138px] p-[24px] rounded-[20px] flex flex-col', + key === tier + ? 'bg-[linear-gradient(138deg,#4C27E1_9.56%,#2F007B_76.16%)] text-white' + : 'border border-newColColor' + )} + > +
+ {capitalize(key)} +
+
+ + $ + { + value[ + period === 'MONTHLY' ? 'month_price' : 'year_price' + ] + } + {' '} + / month +
+
+ ), + [] + )} +
+
+
Features
+ +
+
+
+
+
+ ); +}; + +export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => { + const render = useMemo(() => { + const currentPricing = pricing[tier]; + const channelsOr = currentPricing.channel; + const list = []; + list.push(`${channelsOr} ${channelsOr === 1 ? 'channel' : 'channels'}`); + list.push( + `${ + currentPricing.posts_per_month > 10000 + ? 'Unlimited' + : currentPricing.posts_per_month + } posts per month` + ); + if (currentPricing.team_members) { + list.push(`Unlimited team members`); + } + if (currentPricing?.ai) { + list.push(`AI auto-complete`); + list.push(`AI copilots`); + list.push(`AI Autocomplete`); + } + list.push(`Advanced Picture Editor`); + if (currentPricing?.image_generator) { + list.push( + `${currentPricing?.image_generation_count} AI Images per month` + ); + } + if (currentPricing?.generate_videos) { + list.push(`${currentPricing?.generate_videos} AI Videos per month`); + } + return list; + }, [tier]); + + return ( +
+ {render.map((p) => ( +
+
+ + + +
+
{p}
+
+ ))} +
+ ); +}; diff --git a/apps/frontend/src/components/layout/check.payment.tsx b/apps/frontend/src/components/layout/check.payment.tsx index 068db589..318c6345 100644 --- a/apps/frontend/src/components/layout/check.payment.tsx +++ b/apps/frontend/src/components/layout/check.payment.tsx @@ -1,15 +1,33 @@ -import { FC, useCallback, useEffect, useState } from 'react'; +import { FC, ReactNode, useCallback, useEffect, useState } from 'react'; import Loading from 'react-loading'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { timer } from '@gitroom/helpers/utils/timer'; import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useDecisionModal } from '@gitroom/frontend/components/layout/new-modal'; export const CheckPayment: FC<{ check: string; mutate: () => void; + children: ReactNode; }> = (props) => { const [showLoader, setShowLoader] = useState(true); const fetch = useFetch(); const toaster = useToaster(); + const modal = useDecisionModal(); + + useEffect(() => { + if (showLoader) { + document.querySelector('body')?.classList.add('overflow-hidden'); + Array.from(document.querySelectorAll('.blurMe') || []).map((p) => + p.classList.add('blur-xs', 'pointer-events-none') + ); + } else { + document.querySelector('body')?.classList.remove('overflow-hidden'); + Array.from(document.querySelectorAll('.blurMe') || []).map((p) => + p.classList.remove('blur-xs', 'pointer-events-none') + ); + } + }, [showLoader]); + const checkSubscription = useCallback(async () => { const { status } = await ( await fetch('/billing/check/' + props.check) @@ -19,10 +37,13 @@ export const CheckPayment: FC<{ return checkSubscription(); } if (status === 1) { - toaster.show( - 'We could not validate your payment method, please try again', - 'warning' - ); + modal.open({ + title: 'Invalid Payment', + onlyApprove: true, + approveLabel: 'OK', + description: + 'We could not validate your payment method, please try again', + }); setShowLoader(false); } if (status === 2) { @@ -42,5 +63,5 @@ export const CheckPayment: FC<{
); } - return null; + return props.children; }; diff --git a/apps/frontend/src/components/layout/mode.component.tsx b/apps/frontend/src/components/layout/mode.component.tsx index 73580ce3..e4c2640e 100644 --- a/apps/frontend/src/components/layout/mode.component.tsx +++ b/apps/frontend/src/components/layout/mode.component.tsx @@ -2,10 +2,15 @@ import { useCallback, useEffect, useState } from 'react'; import useCookie from 'react-use-cookie'; +import EventEmitter from 'events'; + +export const modeEmitter = new EventEmitter(); + const ModeComponent = () => { const [mode, setMode] = useCookie('mode', 'dark'); const changeMode = useCallback(() => { + modeEmitter.emit('mode', mode === 'dark' ? 'light' : 'dark'); setMode(mode === 'dark' ? 'light' : 'dark'); }, [mode]); diff --git a/apps/frontend/src/components/layout/new-modal.tsx b/apps/frontend/src/components/layout/new-modal.tsx index 2e868393..9e807b9e 100644 --- a/apps/frontend/src/components/layout/new-modal.tsx +++ b/apps/frontend/src/components/layout/new-modal.tsx @@ -287,8 +287,9 @@ export const DecisionModal: FC<{ description: string; approveLabel: string; cancelLabel: string; + onlyApprove: boolean; resolution: (value: boolean) => void; -}> = ({ description, cancelLabel, approveLabel, resolution }) => { +}> = ({ description, cancelLabel, approveLabel, resolution, onlyApprove }) => { const { closeCurrent } = useModals(); return (
@@ -302,14 +303,16 @@ export const DecisionModal: FC<{ > {approveLabel} - + {!onlyApprove && ( + + )}
); @@ -348,6 +351,7 @@ export const useDecisionModal = () => { ({ title = 'Are you sure?', description = 'Are you sure you want to close this modal?' as any, + onlyApprove = false, approveLabel = 'Yes', cancelLabel = 'No', newRes = undefined as any, @@ -359,6 +363,7 @@ export const useDecisionModal = () => { onClose: () => res(false), children: ( (newRes ? newRes(value) : res(value))} description={description} approveLabel={approveLabel} diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index 5f0fccd4..cdaecc70 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -1,4 +1,4 @@ - 'use client'; +'use client'; import React, { ReactNode, useCallback } from 'react'; import { Logo } from '@gitroom/frontend/components/new-layout/logo'; @@ -38,7 +38,8 @@ import NotificationComponent from '@gitroom/frontend/components/notifications/no import { BillingAfter } from '@gitroom/frontend/components/new-layout/billing.after'; import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector'; import { PreConditionComponent } from '@gitroom/frontend/components/layout/pre-condition.component'; - import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/sentry.feedback.component'; +import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/sentry.feedback.component'; +import { FirstBillingComponent } from '@gitroom/frontend/components/billing/first.billing.component'; const jakartaSans = Plus_Jakarta_Sans({ weight: ['600', '500'], @@ -74,61 +75,65 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => { showDevConsole={false} > - {user.tier === 'FREE' && searchParams.get('check') && ( - - )} - - - - - - - - -
-
{user?.admin ? :
}
- {user.tier === 'FREE' && isGeneral && billingEnabled ? ( - - ) : ( -
-
-
-
- - -
-
-
-
-
-
- - </div> - <div className="flex gap-[20px] text-textItemBlur"> - <OrganizationSelector /> - <div className="hover:text-newTextColor"> - <ModeComponent /> + <CheckPayment check={searchParams.get('check')!} mutate={mutate}> + <ShowMediaBoxModal /> + <ShowLinkedinCompany /> + <MediaSettingsLayout /> + <ShowPostSelector /> + <PreConditionComponent /> + <NewSubscription /> + <ContinueProvider /> + <div + className={clsx( + 'flex flex-col min-h-screen min-w-screen text-newTextColor p-[12px]', + jakartaSans.className + )} + > + <div>{user?.admin ? <Impersonate /> : <div />}</div> + {user.tier === 'FREE' && isGeneral && billingEnabled ? ( + <FirstBillingComponent /> + ) : ( + <div className="flex-1 flex gap-[8px]"> + <div className="flex flex-col bg-newBgColorInner w-[80px] rounded-[12px]"> + <div + className={clsx( + 'fixed h-full w-[64px] start-[17px] flex flex-1 top-0', + user?.admin && 'pt-[60px]' + )} + > + <div className="flex flex-col h-full gap-[32px] flex-1 py-[12px]"> + <Logo /> + <TopMenu /> + <Support /> </div> - <div className="w-[1px] h-[20px] bg-blockSeparator" /> - <LanguageComponent /> - <ChromeExtensionComponent /> - <div className="w-[1px] h-[20px] bg-blockSeparator" /> - <AttachToFeedbackIcon /> - <NotificationComponent /> </div> </div> - <div className="flex flex-1 gap-[1px]">{children}</div> + <div className="flex-1 bg-newBgLineColor rounded-[12px] overflow-hidden flex flex-col gap-[1px] blurMe"> + <div className="flex bg-newBgColorInner h-[80px] px-[20px] items-center"> + <div className="text-[24px] font-[600] flex flex-1"> + <Title /> + </div> + <div className="flex gap-[20px] text-textItemBlur"> + <OrganizationSelector /> + <div className="hover:text-newTextColor"> + <ModeComponent /> + </div> + <div className="w-[1px] h-[20px] bg-blockSeparator" /> + <LanguageComponent /> + <ChromeExtensionComponent /> + <div className="w-[1px] h-[20px] bg-blockSeparator" /> + <AttachToFeedbackIcon /> + <NotificationComponent /> + </div> + </div> + <div className="flex flex-1 gap-[1px]">{children}</div> + </div> </div> - </div> - )} - </div> + )} + </div> + </CheckPayment> </MantineWrapper> </CopilotKit> </ContextWrapper> diff --git a/apps/frontend/src/components/ui/check.icon.component.tsx b/apps/frontend/src/components/ui/check.icon.component.tsx new file mode 100644 index 00000000..069d0bc4 --- /dev/null +++ b/apps/frontend/src/components/ui/check.icon.component.tsx @@ -0,0 +1,28 @@ +export const CheckIconComponent = () => { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="22" + height="23" + viewBox="0 0 22 23" + fill="none" + > + <path + d="M13.2742 1.76687C5.72114 -3.89752 -3.90021 12.5553 2.93125 18.9592C4.68315 20.6017 6.79417 20.7975 9.0813 21.1959C10.475 21.4379 12.6902 21.8312 14.113 21.4755C25.5332 18.6198 20.3442 1.20854 10.339 1.20854" + stroke="#28ff12" + strokeWidth="1.2" + strokeMiterlimit="10" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M6.00586 11.2722C7.2516 12.5171 8.10319 14.104 9.22067 15.4652C9.75676 16.1175 10.0671 15.3361 10.1988 15.0462C11.4308 12.3359 14.0548 11.1902 16.0692 9.17578" + stroke="#28ff12" + strokeWidth="1.2" + strokeMiterlimit="10" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + ); +}; diff --git a/apps/frontend/src/components/ui/logo-text.component.tsx b/apps/frontend/src/components/ui/logo-text.component.tsx new file mode 100644 index 00000000..640a4942 --- /dev/null +++ b/apps/frontend/src/components/ui/logo-text.component.tsx @@ -0,0 +1,45 @@ +import React from 'react'; + +export const LogoTextComponent = () => { + return ( + <svg + width="101" + height="33" + viewBox="0 0 101 33" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <rect width="20" height="22" x={8} y={3} fill="black" /> + <path + d="M41.7953 5.76801V8.00208C42.1329 7.64463 42.5598 7.34675 43.0761 7.10845C43.5925 6.85029 44.218 6.72121 44.9528 6.72121C45.6279 6.72121 46.2634 6.85029 46.8592 7.10845C47.4748 7.36661 48.0109 7.78364 48.4677 8.35953C48.9443 8.91556 49.3216 9.66025 49.5996 10.5936C49.8776 11.5269 50.0166 12.6589 50.0166 13.9894C50.0166 14.9426 49.9273 15.8958 49.7486 16.849C49.5897 17.8022 49.3017 18.6561 48.8847 19.4107C48.4677 20.1653 47.9017 20.781 47.1868 21.2576C46.4918 21.7143 45.618 21.9427 44.5655 21.9427C43.8109 21.9427 43.2151 21.8434 42.7783 21.6448C42.3414 21.4264 42.0137 21.1781 41.7953 20.9001V28.1385L37.5059 29.2108V5.76801H41.7953ZM43.1357 19.3512C43.652 19.3512 44.0889 19.1824 44.4464 18.8448C44.8038 18.4873 45.0818 18.0306 45.2804 17.4745C45.4989 16.9185 45.6478 16.3029 45.7272 15.6277C45.8265 14.9327 45.8762 14.2475 45.8762 13.5724C45.8762 12.4801 45.7769 11.6163 45.5783 10.9808C45.3996 10.3454 45.1811 9.8787 44.923 9.58082C44.6648 9.26309 44.4067 9.0645 44.1485 8.98507C43.9102 8.90563 43.7215 8.86592 43.5825 8.86592C43.2251 8.86592 42.8776 8.995 42.54 9.25316C42.2024 9.49146 41.9541 9.85884 41.7953 10.3553V18.666C41.8946 18.8249 42.0534 18.9838 42.2719 19.1426C42.4903 19.2816 42.7783 19.3512 43.1357 19.3512Z" + fill="currentColor" + /> + <path + d="M69.9378 5.94673C70.4343 7.85314 70.8711 9.41202 71.2484 10.6234C71.6258 11.8149 71.9435 12.8177 72.2016 13.6319C72.4797 14.4461 72.6882 15.161 72.8272 15.7766C72.9861 16.3923 73.0655 17.0774 73.0655 17.832C73.4031 17.6135 73.7307 17.3852 74.0485 17.1469C74.3662 16.8887 74.6343 16.6504 74.8527 16.432H76.1038C75.5676 17.2462 75.0017 17.9213 74.4059 18.4575C73.8102 18.9738 73.2343 19.4306 72.6783 19.8278C72.3208 20.6022 71.7747 21.1483 71.0399 21.4661C70.3052 21.7838 69.5406 21.9427 68.7463 21.9427C67.8527 21.9427 67.0881 21.8235 66.4526 21.5852C65.8172 21.3271 65.2909 20.9895 64.8739 20.5724C64.4767 20.1356 64.1789 19.6391 63.9803 19.0831C63.7817 18.527 63.6824 17.9412 63.6824 17.3256C63.6824 16.6504 63.8413 16.1242 64.159 15.7469C64.4966 15.3497 64.8838 15.1511 65.3207 15.1511C66.5321 15.1511 67.1378 15.6376 67.1378 16.6107C67.1378 16.9284 67.0285 17.1965 66.8101 17.415C66.5917 17.6334 66.3136 17.7426 65.976 17.7426C65.8172 17.7426 65.6484 17.7228 65.4697 17.683C65.3108 17.6235 65.1817 17.5142 65.0824 17.3554C65.2413 18.0504 65.4994 18.5965 65.8569 18.9937C66.2342 19.3909 66.7108 19.5895 67.2867 19.5895C67.7832 19.5895 68.1505 19.4703 68.3888 19.232C68.6271 18.9738 68.7463 18.537 68.7463 17.9213C68.7463 17.266 68.6867 16.6802 68.5676 16.1639C68.4683 15.6277 68.3094 15.0419 68.091 14.4064C67.8725 13.7709 67.6044 13.0163 67.2867 12.1426C66.969 11.2489 66.6115 10.0971 66.2143 8.68719C65.2214 10.812 63.9108 12.1723 62.2824 12.7681C62.2824 12.927 62.2824 13.0858 62.2824 13.2447C62.3022 13.3837 62.3122 13.5326 62.3122 13.6915C62.3122 14.8433 62.2129 15.9256 62.0143 16.9384C61.8157 17.9313 61.4781 18.805 61.0015 19.5597C60.5448 20.2944 59.949 20.8802 59.2143 21.3171C58.4795 21.7342 57.5759 21.9427 56.5036 21.9427C55.7688 21.9427 55.0639 21.8335 54.3887 21.615C53.7333 21.3767 53.1475 20.9994 52.6312 20.4831C52.1149 19.9469 51.6979 19.2519 51.3801 18.3979C51.0823 17.5242 50.9333 16.4618 50.9333 15.2107C50.9333 14.3568 51.0227 13.4433 51.2014 12.4702C51.3801 11.4773 51.7078 10.5539 52.1844 9.69997C52.661 8.84606 53.3064 8.14109 54.1206 7.58505C54.9546 7.00916 56.0171 6.72121 57.3079 6.72121C58.6384 6.72121 59.7008 7.07866 60.4951 7.79356C61.3093 8.50847 61.8554 9.66025 62.1334 11.2489C62.8285 11.0901 63.454 10.6135 64.0101 9.81912C64.586 9.00493 65.1321 7.91271 65.6484 6.54249L69.9378 5.94673ZM57.3376 19.1426C57.7547 19.1426 58.1121 19.0036 58.41 18.7256C58.7277 18.4277 58.9859 18.0306 59.1845 17.5341C59.3831 17.0178 59.5221 16.4121 59.6015 15.7171C59.7008 15.022 59.7504 14.2674 59.7504 13.4532V13.066C58.9561 12.8872 58.5589 12.3014 58.5589 11.3085C58.5589 10.673 58.8171 10.256 59.3334 10.0574C59.115 9.42195 58.8469 9.00493 58.5291 8.80634C58.2313 8.60776 57.9731 8.50847 57.7547 8.50847C57.3178 8.50847 56.9405 8.71698 56.6227 9.13401C56.3249 9.53117 56.0766 10.0475 55.8781 10.683C55.6795 11.2986 55.5305 11.9837 55.4312 12.7383C55.3518 13.4929 55.3121 14.2178 55.3121 14.9128C55.3121 15.8064 55.3717 16.5313 55.4908 17.0873C55.6298 17.6433 55.7986 18.0703 55.9972 18.3682C56.1958 18.666 56.4142 18.8745 56.6525 18.9937C56.8908 19.093 57.1192 19.1426 57.3376 19.1426Z" + fill="currentColor" + /> + <path + d="M78.797 2.16371V6.87015H80.6141V8.06165H78.797V16.9979C78.797 17.832 78.9063 18.388 79.1247 18.666C79.363 18.9242 79.7701 19.0533 80.346 19.0533C80.9219 19.0533 81.3985 18.805 81.7758 18.3086C82.173 17.8121 82.4013 17.1866 82.4609 16.432H83.712C83.5531 17.6433 83.2751 18.6164 82.8779 19.3512C82.4808 20.0661 82.034 20.6221 81.5375 21.0193C81.041 21.3966 80.5347 21.6448 80.0183 21.7639C79.502 21.8831 79.0453 21.9427 78.6481 21.9427C77.119 21.9427 76.0467 21.5256 75.431 20.6916C74.8154 19.8377 74.5076 18.7157 74.5076 17.3256V8.06165H73.5544V6.87015H74.5076V2.75946L78.797 2.16371Z" + fill="currentColor" + /> + <path + d="M82.1214 2.9084C82.1214 2.25307 82.3498 1.69704 82.8065 1.24029C83.2632 0.763692 83.8193 0.525391 84.4746 0.525391C85.1299 0.525391 85.686 0.763692 86.1427 1.24029C86.6193 1.69704 86.8576 2.25307 86.8576 2.9084C86.8576 3.56373 86.6193 4.11976 86.1427 4.5765C85.686 5.03325 85.1299 5.26162 84.4746 5.26162C83.8193 5.26162 83.2632 5.03325 82.8065 4.5765C82.3498 4.11976 82.1214 3.56373 82.1214 2.9084ZM86.7385 6.87015V16.9979C86.7385 17.832 86.8477 18.388 87.0661 18.666C87.3044 18.9242 87.7115 19.0533 88.2874 19.0533C88.5456 19.0533 88.7342 19.0334 88.8534 18.9937C88.9924 18.954 89.1115 18.9143 89.2108 18.8745C89.2307 18.9738 89.2406 19.0731 89.2406 19.1724C89.2406 19.2717 89.2406 19.371 89.2406 19.4703C89.2406 19.9668 89.1513 20.3739 88.9725 20.6916C88.8137 21.0093 88.5952 21.2675 88.3172 21.4661C88.059 21.6448 87.7711 21.7639 87.4534 21.8235C87.1555 21.903 86.8675 21.9427 86.5895 21.9427C85.0604 21.9427 83.9881 21.5256 83.3725 20.6916C82.7569 19.8377 82.449 18.7157 82.449 17.3256V6.87015H86.7385ZM98.1471 19.0533C97.9088 19.0334 97.7301 18.954 97.6109 18.815C97.4918 18.6561 97.4322 18.4774 97.4322 18.2788C97.4322 18.0206 97.5414 17.7724 97.7599 17.5341C97.9783 17.2759 98.3358 17.1469 98.8322 17.1469C99.3883 17.1469 99.8053 17.3355 100.083 17.7128C100.361 18.0703 100.5 18.4972 100.5 18.9937C100.5 19.3114 100.441 19.6391 100.322 19.9767C100.202 20.2944 100.014 20.5923 99.7556 20.8703C99.4975 21.1285 99.1797 21.3469 98.8024 21.5256C98.4251 21.6845 97.9882 21.7639 97.4918 21.7639H89.2704L95.1088 8.47868H92.9045C92.4676 8.47868 92.1002 8.50847 91.8023 8.56804C91.5243 8.60776 91.3853 8.71698 91.3853 8.89571C91.3853 8.97514 91.4052 9.01486 91.4449 9.01486C91.5045 9.01486 91.564 9.03471 91.6236 9.07443C91.7031 9.11415 91.7626 9.19358 91.8023 9.31273C91.8619 9.43188 91.8917 9.6404 91.8917 9.93827C91.8917 10.3752 91.7527 10.6929 91.4747 10.8915C91.2165 11.0901 90.9187 11.1894 90.5811 11.1894C90.1839 11.1894 89.7966 11.0603 89.4193 10.8021C89.0619 10.5241 88.8832 10.1071 88.8832 9.55103C88.8832 9.25316 88.9427 8.95528 89.0619 8.6574C89.181 8.33967 89.3598 8.05172 89.5981 7.79356C89.8364 7.51555 90.1342 7.2971 90.4917 7.13824C90.8491 6.95951 91.2662 6.87015 91.7428 6.87015H99.9344L94.2449 19.4703C94.3641 19.4703 94.5329 19.4802 94.7513 19.5001C94.9698 19.5199 95.1981 19.5398 95.4364 19.5597C95.6946 19.5795 95.9428 19.5994 96.1811 19.6192C96.4393 19.6391 96.6677 19.649 96.8662 19.649C97.2237 19.649 97.5216 19.6093 97.7599 19.5299C98.018 19.4504 98.1471 19.2916 98.1471 19.0533Z" + fill="currentColor" + /> + <path + d="M5.02707 4.73535C5.04984 5.1031 5.08195 5.51796 5.11859 5.9915L6.44189 23.0906C6.5898 25.0018 6.66375 25.9574 7.09218 26.6586C7.46903 27.2753 8.03148 27.757 8.69889 28.0345C9.45764 28.3499 10.4132 28.2759 12.3244 28.128L25.4208 27.1145C26.6598 27.0186 27.4972 26.9538 28.124 26.8034C27.9138 27.2969 27.5895 27.7366 27.1746 28.0846C26.545 28.6126 25.6111 28.828 23.7433 29.2589L10.9438 32.2113C9.07597 32.6422 8.14206 32.8576 7.34479 32.6586C6.6435 32.4837 6.01561 32.0911 5.55111 31.5373C5.02305 30.9078 4.80762 29.9739 4.37678 28.106L0.521985 11.3946C0.0911391 9.52677 -0.124284 8.59286 0.0746573 7.79559C0.249651 7.0943 0.642167 6.46641 1.19595 6.00191C1.82552 5.47385 2.75943 5.25842 4.62725 4.82758L5.02707 4.73535Z" + fill="#612BD3" + /> + <path + d="M18.5599 14.4471C18.3196 14.7119 18.0123 14.8588 17.6378 14.8878C17.3786 14.9078 17.1659 14.8735 16.9998 14.785C16.8324 14.682 16.7083 14.5757 16.6274 14.4661L16.1615 8.43966C16.2489 8.07075 16.4083 7.79043 16.6397 7.5987C16.87 7.39257 17.1148 7.27949 17.374 7.25945C17.4748 7.25165 17.6138 7.26988 17.7911 7.31412C17.9827 7.35725 18.1811 7.48677 18.3861 7.7027C18.59 7.90423 18.7746 8.23039 18.9398 8.68117C19.1194 9.13084 19.2399 9.75168 19.3011 10.5437C19.3389 11.0333 19.3413 11.5329 19.3083 12.0424C19.2886 12.5365 19.2151 12.9913 19.0879 13.4067C18.975 13.821 18.799 14.1678 18.5599 14.4471Z" + fill="white" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M6.10722 5.22211C6.00627 3.91762 5.95579 3.26537 6.1711 2.74748C6.36049 2.29192 6.68924 1.90802 7.11024 1.65079C7.58884 1.35836 8.24108 1.30788 9.54557 1.20693L24.0205 0.0867131C25.325 -0.0142411 25.9772 -0.0647182 26.4951 0.150593C26.9507 0.339986 27.3346 0.668737 27.5918 1.08973C27.8843 1.56833 27.9347 2.22058 28.0357 3.52507L29.4655 22.0008C29.5665 23.3052 29.617 23.9575 29.4016 24.4754C29.2123 24.9309 28.8835 25.3148 28.4625 25.5721C27.9839 25.8645 27.3317 25.915 26.0272 26.0159L11.5522 27.1362C10.2477 27.2371 9.59548 27.2876 9.0776 27.0723C8.62205 26.8829 8.23814 26.5541 7.98091 26.1331C7.68848 25.6545 7.63801 25.0023 7.53705 23.6978L6.10722 5.22211ZM16.0296 6.73324L15.9043 5.11323L12.7939 5.35371L14.1082 22.3531L17.1585 21.335L16.7527 16.0861C16.9267 16.2755 17.1782 16.4371 17.5072 16.571C17.8352 16.6905 18.2727 16.7291 18.8199 16.6868C19.5832 16.6278 20.204 16.4132 20.6824 16.0431C21.174 15.6574 21.5499 15.1792 21.81 14.6087C22.0701 14.0381 22.231 13.4027 22.2928 12.7026C22.369 12.0014 22.3803 11.3052 22.3269 10.614C22.2523 9.64915 22.088 8.83614 21.8341 8.17492C21.5802 7.5137 21.2648 6.99485 20.8881 6.61837C20.5246 6.22637 20.1124 5.95403 19.6515 5.80134C19.205 5.64754 18.737 5.58956 18.2474 5.62742C17.7146 5.66861 17.2682 5.79728 16.9083 6.01343C16.5472 6.21518 16.2543 6.45511 16.0296 6.73324Z" + fill="white" + /> + </svg> + ); +}; diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index e27e0a8b..f452cb31 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -23,7 +23,6 @@ export class StripeService { private _subscriptionService: SubscriptionService, private _organizationService: OrganizationService, private _userService: UsersService, - private _messagesService: MessagesService, private _trackService: TrackService ) {} validateRequest(rawBody: Buffer, signature: string, endpointSecret: string) { @@ -190,7 +189,9 @@ export class StripeService { return organization.paymentId; } + const users = await this._organizationService.getTeam(organization.id); const customer = await stripe.customers.create({ + email: users.users[0].user.email, name: organization.name, }); await this._subscriptionService.updateCustomerId( @@ -361,6 +362,57 @@ export class StripeService { }); } + private async createEmbeddedCheckout( + ud: string, + uniqueId: string, + customer: string, + body: BillingSubscribeDto, + price: string, + userId: string, + allowTrial: boolean + ) { + const stripeCustom = new Stripe(process.env.STRIPE_SECRET_KEY!, { + // @ts-ignore + apiVersion: '2025-03-31.basil', + }); + const isUtm = body.utm ? `&utm_source=${body.utm}` : ''; + // @ts-ignore + const { client_secret } = await stripeCustom.checkout.sessions.create({ + ui_mode: 'custom', + customer, + return_url: + process.env['FRONTEND_URL'] + + `/launches?onboarding=true&check=${uniqueId}${isUtm}`, + mode: 'subscription', + subscription_data: { + ...(allowTrial ? { trial_period_days: 7 } : {}), + metadata: { + service: 'gitroom', + ...body, + userId, + uniqueId, + ud, + }, + }, + ...(body.tolt + ? { + metadata: { + tolt_referral: body.tolt, + }, + } + : {}), + allow_promotion_codes: body.period === 'MONTHLY', + line_items: [ + { + price, + quantity: 1, + }, + ], + }); + + return { client_secret }; + } + private async createCheckoutSession( ud: string, uniqueId: string, @@ -485,7 +537,7 @@ export class StripeService { limit: 1, }); - if (!list.data.filter(f => f.amount > 1000).length) { + if (!list.data.filter((f) => f.amount > 1000).length) { return false; } @@ -626,6 +678,72 @@ export class StripeService { return { url }; } + async embedded( + uniqueId: string, + organizationId: string, + userId: string, + body: BillingSubscribeDto, + allowTrial: boolean + ) { + const id = makeId(10); + const priceData = pricing[body.billing]; + const org = await this._organizationService.getOrgById(organizationId); + const customer = await this.createOrGetCustomer(org!); + const allProducts = await stripe.products.list({ + active: true, + expand: ['data.prices'], + }); + + const findProduct = + allProducts.data.find( + (product) => product.name.toUpperCase() === body.billing.toUpperCase() + ) || + (await stripe.products.create({ + active: true, + name: body.billing, + })); + + const pricesList = await stripe.prices.list({ + active: true, + product: findProduct!.id, + }); + + const findPrice = + pricesList.data.find( + (p) => + p?.recurring?.interval?.toLowerCase() === + (body.period === 'MONTHLY' ? 'month' : 'year') && + p?.unit_amount === + (body.period === 'MONTHLY' + ? priceData.month_price + : priceData.year_price) * + 100 + ) || + (await stripe.prices.create({ + active: true, + product: findProduct!.id, + currency: 'usd', + nickname: body.billing + ' ' + body.period, + unit_amount: + (body.period === 'MONTHLY' + ? priceData.month_price + : priceData.year_price) * 100, + recurring: { + interval: body.period === 'MONTHLY' ? 'month' : 'year', + }, + })); + + return this.createEmbeddedCheckout( + uniqueId, + id, + customer, + body, + findPrice!.id, + userId, + allowTrial + ); + } + async subscribe( uniqueId: string, organizationId: string, diff --git a/libraries/react-shared-libraries/src/helpers/variable.context.tsx b/libraries/react-shared-libraries/src/helpers/variable.context.tsx index 79ca812a..817ca8fa 100644 --- a/libraries/react-shared-libraries/src/helpers/variable.context.tsx +++ b/libraries/react-shared-libraries/src/helpers/variable.context.tsx @@ -2,6 +2,7 @@ import { createContext, FC, ReactNode, useContext, useEffect } from 'react'; interface VariableContextInterface { + stripeClient: string; billingEnabled: boolean; isGeneral: boolean; genericOauth: boolean; @@ -26,6 +27,7 @@ interface VariableContextInterface { sentryDsn: string; } const VariableContext = createContext({ + stripeClient: '', billingEnabled: false, isGeneral: true, genericOauth: false, diff --git a/package.json b/package.json index e94b0c8a..3e06e95a 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,8 @@ "@sentry/react": "^10.25.0", "@solana/wallet-adapter-react": "^0.15.35", "@solana/wallet-adapter-react-ui": "^0.9.35", + "@stripe/react-stripe-js": "^5.4.1", + "@stripe/stripe-js": "^8.6.0", "@swc/helpers": "0.5.13", "@sweetalert2/theme-dark": "^5.0.16", "@tailwindcss/postcss": "^4.1.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33b68f5a..299304c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,6 +132,12 @@ importers: '@solana/wallet-adapter-react-ui': specifier: ^0.9.35 version: 0.9.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@stripe/react-stripe-js': + specifier: ^5.4.1 + version: 5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@stripe/stripe-js': + specifier: ^8.6.0 + version: 8.6.0 '@swc/helpers': specifier: 0.5.13 version: 0.5.13 @@ -6679,6 +6685,17 @@ packages: '@storybook/types@7.6.20': resolution: {integrity: sha512-GncdY3x0LpbhmUAAJwXYtJDUQEwfF175gsjH0/fxPkxPoV7Sef9TM41jQLJW/5+6TnZoCZP/+aJZTJtq3ni23Q==} + '@stripe/react-stripe-js@5.4.1': + resolution: {integrity: sha512-ipeYcAHa4EPmjwfv0lFE+YDVkOQ0TMKkFWamW+BqmnSkEln/hO8rmxGPPWcd9WjqABx6Ro8Xg4pAS7evCcR9cw==} + peerDependencies: + '@stripe/stripe-js': '>=8.0.0 <9.0.0' + react: '>=16.8.0 <20.0.0' + react-dom: '>=16.8.0 <20.0.0' + + '@stripe/stripe-js@8.6.0': + resolution: {integrity: sha512-EB0/GGgs4hfezzkiMkinlRgWtjz8fSdwVQhwYS7Sg/RQrSvuNOz+ssPjD+lAzqaYTCB0zlbrt0fcqVziLJrufQ==} + engines: {node: '>=12.16'} + '@supercharge/request-ip@1.2.0': resolution: {integrity: sha512-wlt6JW69MHqLY2M6Sm/jVyCojNRKq2CBvwH0Hbx24SFhDQQGkgEjeKxVutDxHSyrWixFaOSLXC27euzxijhyMQ==} @@ -13109,6 +13126,7 @@ packages: next@14.2.16: resolution: {integrity: sha512-LcO7WnFu6lYSvCzZoo1dB+IO0xXz5uEv52HF1IUN0IqVTUIZGHuuR10I5efiLadGt+4oZqTcNZyVVEem/TM5nA==} engines: {node: '>=18.17.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -13127,6 +13145,7 @@ packages: next@14.2.33: resolution: {integrity: sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==} engines: {node: '>=18.17.0'} + deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -24761,6 +24780,15 @@ snapshots: '@types/express': 4.17.25 file-system-cache: 2.3.0 + '@stripe/react-stripe-js@5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@stripe/stripe-js': 8.6.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@stripe/stripe-js@8.6.0': {} + '@supercharge/request-ip@1.2.0': {} '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.5)': From 0fe10976b02c35e7460a65a5bcb1499892789396 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:08:35 +0700 Subject: [PATCH 039/340] feat: billing change --- .../components/billing/embedded.billing.tsx | 19 +- .../billing/first.billing.component.tsx | 98 +- i18n.lock | 36 +- .../translation/locales/ar/translation.json | 1045 +++++++++-------- .../translation/locales/bn/translation.json | 1041 ++++++++-------- .../translation/locales/de/translation.json | 1041 ++++++++-------- .../translation/locales/en/translation.json | 35 +- .../translation/locales/es/translation.json | 1042 ++++++++-------- .../translation/locales/fr/translation.json | 1042 ++++++++-------- .../translation/locales/he/translation.json | 1042 ++++++++-------- .../translation/locales/it/translation.json | 1042 ++++++++-------- .../translation/locales/ja/translation.json | 1040 ++++++++-------- .../translation/locales/ko/translation.json | 1040 ++++++++-------- .../translation/locales/pt/translation.json | 1042 ++++++++-------- .../translation/locales/ru/translation.json | 1042 ++++++++-------- .../translation/locales/tr/translation.json | 1041 ++++++++-------- .../translation/locales/vi/translation.json | 1040 ++++++++-------- .../translation/locales/zh/translation.json | 1040 ++++++++-------- 18 files changed, 7662 insertions(+), 7106 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index 1d240468..05d465ae 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -15,6 +15,7 @@ import { Button } from '@gitroom/react/form/button'; import dayjs from 'dayjs'; import Image from 'next/image'; import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; export const EmbeddedBilling: FC<{ stripe: Promise<Stripe>; @@ -119,22 +120,23 @@ const FormWrapper = () => { const StripeInputs = () => { const checkout = useCheckout(); + const t = useT(); return ( <> <div> <h4 className="mb-[8px] text-[24px]"> - {checkout.type === 'loading' ? '' : 'Billing Address'} + {checkout.type === 'loading' ? '' : t('billing_billing_address', 'Billing Address')} </h4> <BillingAddressElement /> </div> <div> <h4 className="mt-[20px] mb-[8px] text-[24px]"> - {checkout.type === 'loading' ? '' : 'Payment'} + {checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')} </h4> <PaymentElement id="payment-element" options={{ layout: 'tabs' }} /> {checkout.type === 'loading' ? null : ( <div className="mt-[24px] flex gap-[10px]"> - <div>Powered by Stripe</div> + <div>{t('billing_powered_by_stripe', 'Powered by Stripe')}</div> <Image src="/stripe.svg" alt="Stripe" width={20} height={20} /> </div> )} @@ -145,6 +147,7 @@ const StripeInputs = () => { const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { const checkout = useCheckout(); + const t = useT(); if (checkout.type === 'loading' || checkout.type === 'error') { return null; } @@ -154,15 +157,15 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { <div className="w-full h-full border-t border-newColColor bg-newBgColorInner px-[80px] flex gap-[32px] justify-end items-center font-[400] text-[14px] text-[#A3A3A3]"> {checkout.checkout.recurring?.trial?.trialEnd ? ( <div> - Your 7-day trial is{' '} - <span className="text-textColor font-[600]">100% free</span> ending{' '} + {t('billing_your_7_day_trial_is', 'Your 7-day trial is')}{' '} + <span className="text-textColor font-[600]">{t('billing_100_percent_free', '100% free')}</span> {t('billing_ending', 'ending')}{' '} <span className="text-textColor font-[600]"> {dayjs( checkout.checkout.recurring?.trial?.trialEnd * 1000 ).format('MMMM D, YYYY')}{' '} —{' '} </span> - <span className="text-textColor font-[600]">Cancel anytime.</span> + <span className="text-textColor font-[600]">{t('billing_cancel_anytime_short', 'Cancel anytime.')}</span> </div> ) : null} <div> @@ -172,8 +175,8 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { loading={loading} > {checkout.checkout.recurring?.trial?.trialEnd - ? 'Pay $0 Today - Start your free trial!' - : 'Pay Now'} + ? t('billing_pay_0_start_trial', 'Pay $0 Today - Start your free trial!') + : t('billing_pay_now', 'Pay Now')} </Button> </div> </div> diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index 99e4bd81..ea42234b 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -21,6 +21,7 @@ import { FAQComponent, FAQSection, } from '@gitroom/frontend/components/billing/faq.component'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; const ModeComponent = dynamic( () => import('@gitroom/frontend/components/layout/mode.component'), @@ -45,6 +46,7 @@ export const FirstBillingComponent = () => { const [tier, setTier] = useState('STANDARD'); const [period, setPeriod] = useState('MONTHLY'); const fetch = useFetch(); + const t = useT(); useEffect(() => { setStripe(loadStripe(stripeClient)); @@ -102,9 +104,8 @@ export const FirstBillingComponent = () => { <div className="flex px-[80px] flex-1"> <div className="flex-1 py-[40px] flex flex-col pe-[40px]"> <div className="text-[36px] font-[600] leading-[110%] whitespace-pre-line"> - Join Over{' '} - <span className="text-[#FC69FF]">18,000+ Entrepreneurs</span> who - use{'\n'}Postiz To Grow Their Social Presence + {t('billing_join_over', 'Join Over')}{' '} + <span className="text-[#FC69FF]">{t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')}</span> {t('billing_who_use', 'who use')}{'\n'}{t('billing_postiz_grow_social', 'Postiz To Grow Their Social Presence')} </div> <div className="flex mt-[34px] mb-[10px]"> @@ -112,19 +113,19 @@ export const FirstBillingComponent = () => { <div> <CheckIconComponent /> </div> - <div>100% No-Risk Free Trial</div> + <div>{t('billing_no_risk_trial', '100% No-Risk Free Trial')}</div> </div> <div className="flex-1 flex gap-[8px] justify-center"> <div> <CheckIconComponent /> </div> - <div>Pay NOTHING for the first 7-days</div> + <div>{t('billing_pay_nothing_7_days', 'Pay NOTHING for the first 7-days')}</div> </div> <div className="flex gap-[8px]"> <div> <CheckIconComponent /> </div> - <div>Cancel anytime, hassle-free</div> + <div>{t('billing_cancel_anytime', 'Cancel anytime, hassle-free')}</div> </div> </div> @@ -140,7 +141,7 @@ export const FirstBillingComponent = () => { <div className="flex flex-col ps-[40px] border-l border-newColColor py-[40px]"> <div className="top-[20px] sticky"> <div className="flex mb-[24px]"> - <div className="flex-1 text-[24px] font-[700]">Choose a Plan</div> + <div className="flex-1 text-[24px] font-[700]">{t('billing_choose_plan', 'Choose a Plan')}</div> <div className="h-[44px] px-[6px] flex items-center justify-center gap-[12px] border border-newColColor rounded-[12px] select-none"> <div className={clsx( @@ -151,7 +152,7 @@ export const FirstBillingComponent = () => { )} onClick={() => setPeriod('MONTHLY')} > - Monthly + {t('billing_monthly', 'Monthly')} </div> <div className={clsx( @@ -162,9 +163,9 @@ export const FirstBillingComponent = () => { )} onClick={() => setPeriod('YEARLY')} > - <div>Yearly</div> + <div>{t('billing_yearly', 'Yearly')}</div> <div className="bg-[#AA0FA4] text-[white] px-[8px] rounded-[4px]"> - 20% Off + {t('billing_20_percent_off', '20% Off')} </div> </div> </div> @@ -194,7 +195,7 @@ export const FirstBillingComponent = () => { ] } </span>{' '} - / month + {t('billing_per_month', '/ month')} </div> </div> ), @@ -202,7 +203,7 @@ export const FirstBillingComponent = () => { )} </div> <div className="flex flex-col mt-[54px] gap-[24px]"> - <div className="text-[24px] font-[700]">Features</div> + <div className="text-[24px] font-[700]">{t('billing_features', 'Features')}</div> <BillingFeatures tier={tier} /> </div> </div> @@ -212,43 +213,72 @@ export const FirstBillingComponent = () => { ); }; +type FeatureItem = { + key: string; + defaultValue: string; + prefix?: string | number; +}; + export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => { - const render = useMemo(() => { + const t = useT(); + const features = useMemo(() => { const currentPricing = pricing[tier]; const channelsOr = currentPricing.channel; - const list = []; - list.push(`${channelsOr} ${channelsOr === 1 ? 'channel' : 'channels'}`); - list.push( - `${ - currentPricing.posts_per_month > 10000 - ? 'Unlimited' - : currentPricing.posts_per_month - } posts per month` - ); + const list: FeatureItem[] = []; + + list.push({ + key: channelsOr === 1 ? 'billing_channel' : 'billing_channels', + defaultValue: channelsOr === 1 ? 'channel' : 'channels', + prefix: channelsOr, + }); + + list.push({ + key: 'billing_posts_per_month', + defaultValue: 'posts per month', + prefix: currentPricing.posts_per_month > 10000 ? 'unlimited' : currentPricing.posts_per_month, + }); + if (currentPricing.team_members) { - list.push(`Unlimited team members`); + list.push({ key: 'billing_unlimited_team_members', defaultValue: 'Unlimited team members' }); } if (currentPricing?.ai) { - list.push(`AI auto-complete`); - list.push(`AI copilots`); - list.push(`AI Autocomplete`); + list.push({ key: 'billing_ai_auto_complete', defaultValue: 'AI auto-complete' }); + list.push({ key: 'billing_ai_copilots', defaultValue: 'AI copilots' }); + list.push({ key: 'billing_ai_autocomplete', defaultValue: 'AI Autocomplete' }); } - list.push(`Advanced Picture Editor`); + list.push({ key: 'billing_advanced_picture_editor', defaultValue: 'Advanced Picture Editor' }); if (currentPricing?.image_generator) { - list.push( - `${currentPricing?.image_generation_count} AI Images per month` - ); + list.push({ + key: 'billing_ai_images_per_month', + defaultValue: 'AI Images per month', + prefix: currentPricing?.image_generation_count, + }); } if (currentPricing?.generate_videos) { - list.push(`${currentPricing?.generate_videos} AI Videos per month`); + list.push({ + key: 'billing_ai_videos_per_month', + defaultValue: 'AI Videos per month', + prefix: currentPricing?.generate_videos, + }); } return list; }, [tier]); + const renderFeature = (feature: FeatureItem) => { + const translatedText = t(feature.key, feature.defaultValue); + if (feature.prefix === 'unlimited') { + return `${t('billing_unlimited', 'Unlimited')} ${translatedText}`; + } + if (feature.prefix !== undefined) { + return `${feature.prefix} ${translatedText}`; + } + return translatedText; + }; + return ( <div className="grid grid-cols-2 gap-y-[8px] gap-x-[32px]"> - {render.map((p) => ( - <div key={p} className="flex items-center gap-[8px]"> + {features.map((feature) => ( + <div key={feature.key} className="flex items-center gap-[8px]"> <div> <svg xmlns="http://www.w3.org/2000/svg" @@ -263,7 +293,7 @@ export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => { /> </svg> </div> - <div>{p}</div> + <div>{renderFeature(feature)}</div> </div> ))} </div> diff --git a/i18n.lock b/i18n.lock index 6cdfe6a1..05afe097 100644 --- a/i18n.lock +++ b/i18n.lock @@ -15,7 +15,7 @@ checksums: send_test: 6252eb4669859b7f7db4cdbc227580e1 select_role: 406451b1c9a26f1484164b8b71c1bd7e video_made_with_ai: c37747aaf8107d339d6238a0463f7096 - please_add_at_least: 90d3c0237b56e57c7a58d5decf6e9d3c + please_add_at_least: 776aa47593c7961b69172b49f49dc304 send_invitation_via_email: 9275e0b85147a931421b3bf6c3083cb4 global_settings: ba55734261d6bc26e792fda32de3e7ec copy_id: 831147124db35832872f8470c577e440 @@ -94,6 +94,7 @@ checksums: you_can_also_drag_drop_pictures: 40cc62ceac0cde30c1218c48796eb202 you_don_t_have_any_assets_yet: ec8d0dc0a7ebf4c2437afb95f0e4cdb5 click_the_button_below_to_upload_one: e2fb4d45f48de2277a0d71b9dd2c08ee + click_the_button_below_to_upload_other: 8999406064d8cb19c648097db01f1b1a add_selected_media: 0f1bf2187f3df8d3b90df1895c6e530b insert_media: 15267304e6c6b2c19fc7d1941b2a6a11 design_media: fee3bbf1846d9e5f4a1af783b9e3fc90 @@ -506,3 +507,36 @@ checksums: label_who_can_reply_to_this_post: 4d8913296a1fc3f197cb0aead34af73d delete_integration: ccc879ccfcf7f85bcfe09f2bc3fa0dd3 start_writing_your_post: 471efc4f2a7e2cf02a065a2de34e7213 + billing_join_over: 6e1c237241ba00ddbb07fd603344c5c3 + billing_entrepreneurs_count: 05164f2ca2e8e20de3f63977c07d39fe + billing_who_use: 63bdc59ef443e193eca87889e83ea07a + billing_postiz_grow_social: 3cf5ab166df9cb65e17b9a039ccbbbad + billing_no_risk_trial: 6e5c60d9ddf3affa8ba8870272f9f9ab + billing_pay_nothing_7_days: 2db15615cad39981069b65e6a0852b18 + billing_cancel_anytime: f69ae39eec3fb9eb6114481d67c8481e + billing_choose_plan: 6eacca8177c43945435ac9c97a1e9734 + billing_monthly: 818f1192e32bb855597f930d3e78806e + billing_yearly: 87f43e016c19cb25860f456549a2f431 + billing_20_percent_off: 2d643feeaf30cafb2dd864b90f5c014b + billing_features: 341ff316a339b106a178f0b8d362951b + billing_channel: 7f661d461a99f05a2d57195fc3d262c3 + billing_channels: f6cd2d03caa496e649e95df2d6610879 + billing_unlimited: e1a92523172cd1bdde5550689840e42d + billing_posts_per_month: ca72453dbacbffceddb12b41e3d1f559 + billing_unlimited_team_members: 254b7e4f144033e09d0127f50cd0422e + billing_ai_auto_complete: 91eab83ad474698d10e25fbde8b8ffce + billing_ai_copilots: 00054c5d5fe79ed4bb7e6decaa9f6608 + billing_ai_autocomplete: 25c332479a2cfa33c6f8a1548b58199f + billing_advanced_picture_editor: 329a6c2e8b2685f81609859a4de07b0f + billing_ai_images_per_month: 5073aa90b32654abe6200d426a97f03e + billing_ai_videos_per_month: c786199d8dc9bded54fab8f92c350b19 + billing_billing_address: 48980a775bfc7292b0c4f9b74b4d352e + billing_payment: 95142d3fd8b6a6f551aba771842e9c11 + billing_powered_by_stripe: 4b1f500613fe28f3cc17cb003ed0caf6 + billing_your_7_day_trial_is: 4b59fb559f5fd520668974e909e3479c + billing_100_percent_free: 6616fd6ee152264c06dd2537f6347e66 + billing_ending: f66133a14aa7d86ea2453df97de12cd5 + billing_cancel_anytime_short: 5b1b4a998fd56b18e4153dd83c84022c + billing_pay_0_start_trial: 28e72154e6cce7541e707b35f3a67309 + billing_pay_now: 50cb14454e1b2df4a2f83bf1ac799819 + billing_per_month: 6293d01c3d13f6938d47285122bd1a48 diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index baf5a91f..871a9ae3 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -1,510 +1,539 @@ { - "calendar": "التقويم", - "webhooks": "روابط الويب (Webhooks)", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "روابط الويب هي وسيلة لتلقي الإشعارات عند حدوث شيء ما في Postiz عبر طلب HTTP.", - "name": "الاسم", - "url": "الرابط (URL)", - "edit": "تعديل", - "delete": "حذف", - "add_a_webhook": "إضافة رابط ويب", - "save": "حفظ", - "send_test": "إرسال اختبار", - "select_role": "اختر الدور", - "video_made_with_ai": "فيديو تم إنشاؤه بالذكاء الاصطناعي", - "please_add_at_least": "يرجى إضافة 20 حرفًا على الأقل", - "send_invitation_via_email": "إرسال دعوة عبر البريد الإلكتروني؟", - "global_settings": "الإعدادات العامة", - "copy_id": "نسخ معرف القناة", - "team_members": "أعضاء الفريق", - "invite_your_assistant_or_team_member_to_manage_your_account": "ادعُ مساعدك أو أحد أعضاء فريقك لإدارة حسابك", - "remove": "إزالة", - "add_another_member": "إضافة عضو آخر", - "signatures": "التواقيع", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "يمكنك إضافة تواقيع إلى حسابك لاستخدامها في منشوراتك.", - "content": "المحتوى", - "auto_add": "إضافة تلقائية؟", - "actions": "الإجراءات", - "use_signature": "استخدم التوقيع", - "add_a_signature": "أضف توقيعًا", - "no": "لا", - "yes": "نعم", - "your_git_repository": "مستودع Git الخاص بك", - "connect_your_github_repository_to_receive_updates_and_analytics": "قم بربط مستودع GitHub الخاص بك لتلقي التحديثات والتحليلات", - "connected": "متصل:", - "disconnect": "قطع الاتصال", - "connect_your_repository": "ربط المستودع الخاص بك", - "cancel": "إلغاء", - "connect": "اتصال", - "public_api": "واجهة برمجة التطبيقات العامة", - "check_n8n": "اطلع على عقدة N8N المخصصة لدينا لـ Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "استخدم واجهة برمجة تطبيقات Postiz للدمج مع أدواتك.", - "read_how_to_use_it_over_the_documentation": "اقرأ كيفية استخدامه في الوثائق.", - "reveal": "إظهار", - "copy_key": "نسخ المفتاح", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "قم بتوصيل خادم Postiz MCP بعميلك (بث HTTP) لجدولة منشوراتك بشكل أسرع!", - "share_with_a_client": "مشاركة مع عميل", - "post": "منشور", - "comments": "تعليقات", - "user": "مستخدم", - "login_register_to_add_comments": "تسجيل الدخول / التسجيل لإضافة تعليقات", - "status": "الحالة:", - "there_are_not_plugs_matching_your_channels": "لا توجد ملحقات مطابقة لقنواتك", - "you_have_to_add_x_or_linkedin_or_threads": "يجب عليك إضافة: X أو LinkedIn أو Threads", - "go_to_the_calendar_to_add_channels": "اذهب إلى التقويم لإضافة القنوات", - "channels": "القنوات", - "activate": "تفعيل", - "this_channel_needs_to_be_refreshed": "يجب تحديث هذه القناة،", - "click_here_to_refresh": "انقر هنا للتحديث", - "can_t_show_analytics_yet": "لا يمكن عرض التحليلات بعد", - "you_have_to_add_social_media_channels": "يجب عليك إضافة قنوات التواصل الاجتماعي", - "supported": "مدعوم:", - "step": "الخطوة", - "skip_onboarding": "تخطي الإعداد", - "onboarding": "الإعداد", - "next": "التالي", - "you_are_done_from_here_you_can": "لقد انتهيت، من هنا يمكنك:", - "view_analytics": "عرض التحليلات", - "schedule_a_new_post": "جدولة منشور جديد", - "to_sell_posts_you_would_have_to": "لبيع المنشورات يجب عليك:", - "1_connect_at_least_one_channel": "1. ربط قناة واحدة على الأقل", - "2_connect_you_bank_account": "2. ربط حسابك البنكي", - "go_back_to_connect_channels": "العودة لربط القنوات", - "move_to_the_seller_page_to_connect_you_bank": "انتقل إلى صفحة البائع لربط حسابك البنكي", - "connect_channels": "ربط القنوات", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "قم بربط قنوات التواصل الاجتماعي ومواقع النشر الخاصة بك لجدولة المنشورات لاحقًا", - "social": "اجتماعي", - "publishing_platforms": "منصات النشر", - "no_channels": "لا توجد قنوات بعد", - "connect_your_accounts": "قم بربط حساباتك الاجتماعية لبدء الجدولة، النشر، والتحليل — كل ذلك في مكان واحد.", - "notifications": "الإشعارات", - "no_notifications": "لا توجد إشعارات", - "send_message": "إرسال رسالة", - "mar_28": "٢٨ مارس", - "there_are_no_messages_yet": "لا توجد رسائل بعد.", - "checkout_the_marketplace": "تصفح السوق", - "go_to_marketplace": "اذهب إلى السوق", - "all_messages": "كل الرسائل", - "previous": "السابق", - "select_or_upload_pictures_maximum_5_at_a_time": "اختر أو ارفع صورًا (بحد أقصى 5 في المرة الواحدة)", - "you_can_also_drag_drop_pictures": "يمكنك أيضًا سحب وإفلات الصور", - "you_don_t_have_any_assets_yet": "ليس لديك أي أصول بعد.", - "click_the_button_below_to_upload_zero": "", - "click_the_button_below_to_upload_one": "انقر على الزر أدناه لرفع واحدة", - "click_the_button_below_to_upload_two": "", - "click_the_button_below_to_upload_few": "", - "click_the_button_below_to_upload_many": "", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "أضف الوسائط المحددة", - "insert_media": "إدراج وسائط", - "design_media": "تصميم وسائط", - "select": "اختر", - "editor": "المحرر", - "clear": "مسح", - "order_completed": "اكتمل الطلب", - "the_order_has_been_completed": "تم اكتمال الطلب", - "post_has_been_published": "تم نشر المنشور", - "url_1": "الرابط:", - "new_offer": "عرض جديد", - "platform": "المنصة", - "posts": "المنشورات", - "pay_accept_offer": "ادفع وقبل العرض", - "accepted": "تم القبول", - "post_draft": "مسودة منشور", - "revision_needed": "مطلوب مراجعة", - "approve": "موافقة", - "preview": "معاينة", - "revision_requested": "تم طلب مراجعة", - "accepted_1": "مقبول", - "cancelled_by_the_seller": "أُلغي بواسطة البائع", - "please_select_your_country_where_your_business_is": "يرجى اختيار الدولة التي يقع فيها عملك.", - "select_country": "--اختر الدولة--", - "connect_bank_account": "ربط حساب بنكي", - "seller_mode": "وضع البائع", - "active": "نشط", - "details": "تفاصيل", - "audience_size": "حجم الجمهور", - "add_another_platform": "أضف منصة أخرى", - "send_an_offer_for": "أرسل عرضًا مقابل $", - "complete_order_and_pay_early": "أكمل الطلب وادفع مبكرًا", - "order_in_progress": "الطلب قيد التنفيذ", - "create_a_new_offer": "أنشئ عرضًا جديدًا", - "orders": "الطلبات", - "price": "السعر", - "state": "الحالة", - "showing": "عرض", - "to": "إلى", - "from": "من", - "results": "النتائج", - "content_writer": "كاتب محتوى", - "influencer": "مؤثر", - "request_service": "طلب خدمة", - "the_marketplace_is_not_opened_yet": "السوق لم يُفتح بعد", - "check_again_soon": "تحقق مرة أخرى قريبًا!", - "filter": "تصفية", - "result": "نتيجة", - "seller": "بائع", - "buyer": "مشتري", - "discord_support": "دعم ديسكورد", - "teams": "الفرق", - "webhooks_1": "ويب هوكس", - "auto_post": "نشر تلقائي", - "logout_from": "تسجيل الخروج من", - "join_10000_entrepreneurs_who_use_postiz": "انضم إلى أكثر من 10,000 رائد أعمال يستخدمون Postiz", - "to_manage_all_your_social_media_channels": "لإدارة جميع قنوات التواصل الاجتماعي الخاصة بك", - "100_no_risk_trial": "تجربة خالية من المخاطر 100%", - "pay_nothing_for_the_first_7_days": "لا تدفع شيئًا لأول 7 أيام", - "cancel_anytime_hassle_free": "يمكنك الإلغاء في أي وقت وبدون عناء", - "add_free_subscription": "-- أضف اشتراكًا مجانيًا --", - "currently_impersonating": "يتم الآن الانتحال", - "user_1": "المستخدم:", - "drag_n_drop_some_files_here": "اسحب وأفلت بعض الملفات هنا", - "add_time_slot": "أضف فترة زمنية", - "add_slot": "إضافة خانة", - "cancel_publication": "إلغاء النشر", - "statistics": "الإحصائيات", - "loading": "جارٍ التحميل", - "short_link": "رابط مختصر", - "original_link": "الرابط الأصلي", - "clicks": "النقرات", - "selected_customer": "العميل المحدد", - "customer": "العميل:", - "repeat_post_every": "تكرار النشر كل...", - "use_this_media": "استخدم هذه الوسائط", - "create_new_post": "إنشاء منشور", - "update_post": "تحديث المنشور الحالي", - "merge_comments_into_one_post": "دمج التعليقات في منشور واحد", - "accounts_that_will_engage": "الحسابات التي ستتفاعل:", - "day": "يوم", - "week": "أسبوع", - "month": "شهر", - "remove_from_customer": "إزالة من العميل", - "show_more": "+ عرض المزيد", - "show_less": "- عرض أقل", - "upload": "رفع", - "ai": "الذكاء الاصطناعي", - "add_channel": "إضافة قناة", - "add_platform": "إضافة منصة", - "articles": "مقالات", - "add_comment": "أضف تعليقًا", - "add_post": "أضف منشورًا في سلسلة النقاش", - "add_comment_or_post": "أضف تعليقًا / منشورًا", - "you_are_in_global_editing_mode": "أنت في وضع التحرير العام", - "the_post_should_be_at_least_6_characters_long": "يجب أن يكون المنشور مكونًا من 6 أحرف على الأقل", - "are_you_sure_you_want_to_delete_post": "هل أنت متأكد أنك تريد حذف هذا المنشور؟", - "post_deleted_successfully": "تم حذف المنشور بنجاح", - "delete_post": "حذف المنشور", - "save_as_draft": "حفظ كمسودة", - "post_now": "انشر الآن", - "please_add": "يرجى إضافة", - "to_your_telegram_group_channel_and_click_here": "إلى مجموعة / قناة التليجرام الخاصة بك ثم اضغط هنا:", - "connect_telegram": "ربط التليجرام", - "please_add_the_following_command_in_your_chat": "يرجى إضافة الأمر التالي في الدردشة الخاصة بك:", - "copy": "نسخ", - "settings": "الإعدادات", - "integrations": "التكاملات", - "add_integration": "إضافة تكامل", - "you_are_now_editing_only": "أنت الآن تقوم بالتحرير فقط", - "tag_a_company": "الإشارة إلى شركة", - "video_length_is_invalid_must_be_up_to": "مدة الفيديو غير صالحة، يجب أن تكون حتى", - "seconds": "ثوانٍ", - "this_feature_available_only_for_photos": "هذه الميزة متاحة فقط للصور، وسيتم إضافة موسيقى افتراضية يمكنك تغييرها لاحقًا.", - "allow_user_to": "السماح للمستخدم بـ:", - "your_video_will_be_labeled_promotional": "سيتم تصنيف الفيديو الخاص بك كـ \"محتوى ترويجي\".", - "this_cannot_be_changed_once_posted": "لا يمكن تغيير ذلك بعد نشر الفيديو الخاص بك.", - "turn_on_to_disclose_video_promotes": "فعّل هذا الخيار للإفصاح عن أن هذا الفيديو يروّج لسلع أو خدمات مقابل شيء ذي قيمة. قد يروّج الفيديو لنفسك أو لطرف ثالث أو لكليهما.", - "you_are_promoting_yourself": "أنت تروّج لنفسك أو لعلامتك التجارية الخاصة.", - "this_video_will_be_classified_brand_organic": "سيتم تصنيف هذا الفيديو كـ محتوى عضوي للعلامة التجارية.", - "you_are_promoting_another_brand": "أنت تروّج لعلامة تجارية أخرى أو لطرف ثالث.", - "this_video_will_be_classified_branded_content": "سيتم تصنيف هذا الفيديو كمحتوى يحمل علامة تجارية.", - "by_posting_you_agree_to_tiktoks": "بنشرك، أنت توافق على شروط تيك توك", - "music_usage_confirmation": "تأكيد استخدام الموسيقى", - "branded_content_policy": "سياسة المحتوى الممول", - "select_1": "--اختر--", - "select_flair": "--اختر الوسم--", - "link": "رابط", - "add_subreddit": "أضف منتدى فرعي", - "please_add_at_least_one_subreddit": "يرجى إضافة منتدى فرعي واحد على الأقل", - "add_community": "أضف مجتمع", - "select_post_type": "اختر نوع المنشور...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "لم نتمكن من العثور على أي نشاط تجاري مرتبط بصفحة لينكدإن الخاصة بك.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "يرجى إغلاق هذه النافذة، وإنشاء صفحة جديدة، ثم إضافة قناة جديدة مرة أخرى.", - "select_linkedin_page": "اختر صفحة لينكدإن:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "لم نتمكن من العثور على أي نشاط تجاري مرتبط بالصفحات المحددة.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "نوصي بربط جميع الصفحات وجميع الأنشطة التجارية.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "يرجى إغلاق هذه النافذة، حذف التكامل الخاص بك، ثم إضافة قناة جديدة مرة أخرى.", - "select_instagram_account": "اختر حساب إنستغرام:", - "select_page": "اختر الصفحة:", - "generate_image_with_ai": "توليد صورة بالذكاء الاصطناعي", - "reconnect_channel": "إعادة ربط القناة", - "update_credentials": "تحديث بيانات الاعتماد", - "additional_settings": "إعدادات إضافية", - "change_bot": "تغيير الروبوت", - "move_add_to_customer": "نقل / إضافة إلى العميل", - "edit_time_slots": "تعديل الفترات الزمنية", - "enable_channel": "تفعيل القناة", - "disable_channel": "تعطيل القناة", - "add": "إضافة", - "short_post": "منشور قصير", - "long_post": "منشور طويل", - "a_thread_with_short_posts": "سلسلة منشورات قصيرة", - "a_thread_with_long_posts": "سلسلة منشورات طويلة", - "personal_voice_i_am_happy_to_announce": "بصوت شخصي (\"يسعدني أن أعلن\")", - "company_voice_we_are_happy_to_announce": "بصوت الشركة (\"يسعدنا أن نعلن\")", - "generate": "توليد", - "generate_posts": "توليد منشورات", - "purchase_a_life_time_pro_account_with_sol_199": "اشترِ حساب PRO مدى الحياة مقابل SOL (199 دولارًا)، يرجى العلم أنه لا يوجد استرداد لهذا الشراء.", - "purchase_now": "اشترِ الآن", - "pay_today": "ادفع اليوم", - "we_are_sorry_to_see_you_go": "نأسف لرؤيتك ترحل :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "هل تمانع أن تخبرنا باختصار كيف كان بإمكاننا أن نكون أفضل؟", - "cancel_subscription": "إلغاء الاشتراك", - "plans": "الخطط", - "monthly": "شهريًا", - "yearly": "سنويًا", - "reactivate_subscription": "إعادة تفعيل الاشتراك", - "update_payment_method_invoices_history": "تحديث طريقة الدفع / سجل الفواتير", - "cancel_subscription_1": "إلغاء الاشتراك", - "your_subscription_will_be_canceled_at": "سيتم إلغاء اشتراكك في", - "you_will_never_be_charged_again": "لن يتم خصم أي رسوم منك مرة أخرى", - "current_package": "الباقة الحالية:", - "next_package": "الباقة التالية:", - "claim": "مطالبة", - "frequently_asked_questions": "الأسئلة الشائعة", - "autopost": "النشر التلقائي", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "يمكن للنشر التلقائي نشر العناصر الجديدة من موجز RSS الخاص بك تلقائيًا على وسائل التواصل الاجتماعي", - "title": "العنوان", - "add_an_autopost": "إضافة نشر تلقائي", - "post_content": "محتوى المنشور", - "sign_up": "تسجيل حساب", - "or": "أو", - "by_registering_you_agree_to_our": "بتسجيلك، أنت توافق على", - "and": "و", - "terms_of_service": "شروط الخدمة", - "privacy_policy": "سياسة الخصوصية", - "create_account": "إنشاء حساب", - "already_have_an_account": "هل لديك حساب بالفعل؟", - "sign_in": "تسجيل الدخول", - "sign_in_1": "تسجيل الدخول", - "don_t_have_an_account": "ليس لديك حساب؟", - "forgot_password": "نسيت كلمة المرور", - "forgot_password_1": "نسيت كلمة المرور", - "send_password_reset_email": "إرسال بريد إعادة تعيين كلمة المرور", - "go_back_to_login": "العودة إلى تسجيل الدخول", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "لقد أرسلنا لك بريدًا إلكترونيًا يحتوي على رابط لإعادة تعيين كلمة المرور الخاصة بك.", - "change_password": "تغيير كلمة المرور", - "we_successfully_reset_your_password_you_can_now_login_with_your": "تمت إعادة تعيين كلمة المرور بنجاح. يمكنك الآن تسجيل الدخول باستخدام", - "click_here_to_go_back_to_login": "انقر هنا للعودة إلى تسجيل الدخول", - "activate_your_account": "فعّل حسابك", - "thank_you_for_registering": "شكرًا لتسجيلك!", - "please_check_your_email_to_activate_your_account": "يرجى التحقق من بريدك الإلكتروني لتفعيل حسابك.", - "sign_in_with": "تسجيل الدخول باستخدام", - "continue_with_google": "المتابعة باستخدام جوجل", - "sign_in_with_github": "تسجيل الدخول باستخدام جيت هب", - "continue_with_farcaster": "المتابعة باستخدام فاركاستر", - "continue_with_your_wallet": "المتابعة باستخدام محفظتك", - "stars_per_day": "النجوم في اليوم", - "media": "الوسائط", - "check_launch": "تحقق من الإطلاق", - "load_your_github_repository_from_settings_to_see_analytics": "قم بتحميل مستودع GitHub الخاص بك من الإعدادات لعرض التحليلات", - "stars": "النجوم", - "processing_stars": "جاري معالجة النجوم...", - "forks": "التفريعات", - "registration_is_disabled": "التسجيل معطل", - "login_instead": "سجّل الدخول بدلًا من ذلك", - "gitroom": "جيت روم", - "select_a_conversation_and_chat_away": "اختر محادثة وابدأ الدردشة.", - "adding_channel_redirecting_you": "يتم إضافة القناة، جاري إعادة التوجيه", - "could_not_add_provider": "تعذر إضافة المزوّد.", - "you_are_being_redirected_back": "يتم إعادتك إلى الصفحة السابقة", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "نواجه بعض الصعوبات، حاول تحديث الصفحة", - "post_not_found": "لم يتم العثور على المنشور", - "publication_date": "تاريخ النشر:", - "analytics": "تحليلات", - "launches": "إطلاقات", - "plugs": "إعلانات", - "billing": "الفوترة", - "affiliate": "شريك", - "monday": "الاثنين", - "tuesday": "الثلاثاء", - "wednesday": "الأربعاء", - "thursday": "الخميس", - "friday": "الجمعة", - "saturday": "السبت", - "sunday": "الأحد", - "can_t_change_date_remove_post_from_publication": "لا يمكن تغيير التاريخ، أزل المنشور من النشر", - "predicted_github_trending_change": "التغير المتوقع في ترند GitHub", - "duplicate_post": "منشور مكرر", - "preview_post": "معاينة المنشور", - "post_statistics": "إحصائيات المنشور", - "draft": "مسودة", - "week_number": "الأسبوع {{number}}", - "top_title_edit_webhook": "تعديل Webhook", - "top_title_add_webhook": "إضافة Webhook", - "top_title_oh_no": "أوه لا", - "top_title_auto_plug": "التوصيل التلقائي: {{title}}", - "top_title_edit_autopost": "تعديل النشر التلقائي", - "top_title_add_autopost": "إضافة نشر تلقائي", - "top_title_send_a_new_offer": "إرسال عرض جديد", - "top_title_media_library": "مكتبة الوسائط", - "top_title_add_signature": "إضافة توقيع", - "top_title_send_a_message_to": "إرسال رسالة إلى {{name}}", - "top_title_configure_provider": "تهيئة المزود", - "top_title_add_member": "إضافة عضو", - "top_title_change_bot_picture": "تغيير صورة البوت", - "top_title_create_a_new_tag": "إنشاء وسم جديد", - "top_title_select_company": "اختيار الشركة", - "top_title_additional_settings": "إعدادات إضافية", - "top_title_time_table_slots": "فترات الجدول الزمني", - "top_title_design_media": "تصميم الوسائط", - "top_title_edit_post": "تعديل المنشور", - "top_title_create_post": "إنشاء منشور", - "top_title_move__add_to_customer": "نقل / إضافة إلى العميل", - "top_title_add_api_key_for": "إضافة مفتاح API لـ {{name}}", - "top_title_instance_url": "رابط النسخة", - "top_title_custom_url": "رابط مخصص", - "top_title_add_channel": "إضافة قناة", - "top_title_add_telegram": "إضافة تيليجرام", - "top_title_add_wrapcast": "إضافة Wrapcast", - "top_title_comments_for": "تعليقات ليوم {{date}}", - "top_title_edit_signature": "تعديل التوقيع", - "label_name": "الاسم", - "label_url": "الرابط", - "label_title": "العنوان", - "label_subtitle": "العنوان الفرعي", - "label_email": "البريد الإلكتروني", - "label_full_name": "الاسم الكامل", - "label_password": "كلمة المرور", - "label_confirm_password": "تأكيد كلمة المرور", - "label_api_key": "مفتاح API", - "label_instance_url": "رابط النسخة", - "label_custom_url": "رابط مخصص", - "label_feedback": "ملاحظات", - "label_bio": "نبذة تعريفية", - "label_role": "الدور", - "label_country": "الدولة", - "label_audience_size": "حجم الجمهور على جميع المنصات", - "label_pick_time": "اختر الوقت", - "label_nickname": "الاسم المستعار", - "label_write_anything": "اكتب أي شيء", - "label_output_format": "تنسيق الإخراج", - "label_add_pictures": "إضافة صور؟", - "label_hour": "ساعة", - "label_minutes": "دقائق", - "label_select_publication": "اختر النشر", - "label_canonical_link": "الرابط القانوني", - "label_cover_picture": "صورة الغلاف", - "label_tags": "الوسوم", - "label_topics": "المواضيع", - "label_tags_maximum_4": "الوسوم (بحد أقصى 4)", - "label_attachments": "المرفقات", - "label_type": "النوع", - "label_thumbnail": "الصورة المصغرة", - "label_who_can_see_this_video": "من يمكنه مشاهدة هذا الفيديو؟", - "label_content_posting_method": "طريقة نشر المحتوى", - "label_auto_add_music": "إضافة الموسيقى تلقائيًا", - "label_duet": "دويتو", - "label_stitch": "دمج", - "label_comments": "التعليقات", - "label_disclose_video_content": "الإفصاح عن محتوى الفيديو", - "label_your_brand": "علامتك التجارية", - "label_branded_content": "محتوى برعاية علامة تجارية", - "label_subreddit": "مجتمع فرعي", - "label_flair": "شارة", - "label_media": "الوسائط", - "label_search_subreddit": "البحث عن مجتمع فرعي", - "label_delay": "تأخير", - "label_post_type": "نوع المنشور", - "label_collaborators": "المتعاونون (بحد أقصى 3) - لا يمكن أن تكون الحسابات خاصة", - "label_community": "المجتمع", - "label_search_community": "ابحث في المجتمع", - "label_channel": "القناة", - "label_search_channel": "ابحث عن قناة", - "label_select_channel": "اختر قناة", - "label_new_password": "كلمة المرور الجديدة", - "label_repeat_password": "أعد إدخال كلمة المرور", - "label_platform": "المنصة", - "label_price_per_post": "السعر لكل منشور", - "label_integrations": "التكاملات", - "label_code": "الرمز", - "label_should_sync_last_post": "هل نزامن آخر منشور حالي؟", - "label_when_post": "متى يجب أن ننشره؟", - "label_autogenerate_content": "توليد المحتوى تلقائيًا", - "label_generate_picture": "توليد صورة؟", - "label_company": "الشركة", - "label_tag_color": "لون الوسم", - "label_select_board": "اختر لوحة", - "label_select_organization": "اختر منظمة", - "label_auto_add_signature": "إضافة توقيع تلقائيًا؟", - "enable_color_picker": "تفعيل منتقي الألوان", - "cancel_the_color_picker": "إلغاء منتقي الألوان", - "no_content_yet": "لا يوجد محتوى بعد", - "write_your_reply": "اكتب منشورك...", - "add_a_tag": "أضف وسمًا", - "add_to_calendar": "أضف إلى التقويم", - "select_channels_from_circles": "اختر القنوات من الدوائر أعلاه", - "not_matching_order": "الترتيب غير متطابق", - "submit_for_order": "إرسال للطلب", - "schedule": "جدولة", - "update": "تحديث", - "attachments": "المرفقات", - "tags": "الوسوم", - "public_to_everyone": "عام للجميع", - "mutual_follow_friends": "أصدقاء المتابعة المتبادلة", - "follower_of_creator": "متابع للمنشئ", - "self_only": "لنفسك فقط", - "post_content_directly_to_tiktok": "انشر المحتوى مباشرة على تيك توك", - "upload_content_to_tiktok_without_posting": "ارفع المحتوى إلى تيك توك دون نشره", - "choose_upload_without_posting_description": "اختر الرفع دون النشر إذا كنت ترغب في مراجعة وتحرير المحتوى الخاص بك داخل تطبيق تيك توك قبل النشر. هذا يمنحك إمكانية الوصول إلى أدوات التحرير المدمجة في تيك توك ويسمح لك بإجراء التعديلات النهائية قبل النشر.", - "faq_am_i_going_to_be_charged_by_postiz": "هل سيتم تحميلي رسوم من قبل Postiz؟", - "faq_to_confirm_credit_card_information_postiz_will_hold": "لتأكيد معلومات بطاقة الائتمان، سيقوم Postiz بحجز مبلغ 2 دولار وإطلاقه فوراً", - "faq_can_i_trust_postiz_gitroom": "هل يمكنني الوثوق بـ Postiz؟", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz مفتوح المصدر بكل فخر! نحن نؤمن بثقافة أخلاقية وشفافة، مما يعني أن Postiz سيبقى للأبد. يمكنك الاطلاع على الكود بالكامل أو استخدامه في مشاريعك الشخصية. لعرض مستودع المصدر المفتوح، <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">اضغط هنا</a>.", - "faq_what_are_channels": "ما هي القنوات؟", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "يتيح لك Postiz جدولة منشوراتك بين قنوات مختلفة.\nالقناة هي منصة نشر يمكنك من خلالها جدولة منشوراتك.\nعلى سبيل المثال، يمكنك جدولة منشوراتك على X، فيسبوك، إنستغرام، تيك توك، يوتيوب، ريديت، لينكدإن، Dribbble، Threads وPinterest.", - "faq_what_are_team_members": "من هم أعضاء الفريق؟", - "faq_if_you_have_a_team_with_multiple_members": "إذا كان لديك فريق يضم عدة أعضاء، يمكنك دعوتهم إلى مساحة العمل الخاصة بك للتعاون في منشوراتك وإضافة قنواتهم الشخصية", - "faq_what_is_ai_auto_complete": "ما هو الإكمال التلقائي بالذكاء الاصطناعي؟", - "faq_we_automate_chatgpt_to_help_you_write": "نحن نستخدم ChatGPT لمساعدتك في كتابة المنشورات والمقالات الاجتماعية.", - "enter_email": "أدخل البريد الإلكتروني", - "are_you_sure": "هل أنت متأكد؟", - "yes_delete_it": "نعم، احذفه!", - "no_cancel": "لا، إلغاء!", - "are_you_sure_you_want_to_delete": "هل أنت متأكد أنك تريد حذف {{name}}؟", - "are_you_sure_you_want_to_delete_the_image": "هل أنت متأكد أنك تريد حذف الصورة؟", - "are_you_sure_you_want_to_logout": "هل أنت متأكد أنك تريد تسجيل الخروج؟", - "yes_logout": "نعم، تسجيل الخروج", - "are_you_sure_you_want_to_delete_this_slot": "هل أنت متأكد أنك تريد حذف هذه الخانة؟", - "are_you_sure_you_want_to_delete_this_subreddit": "هل أنت متأكد أنك تريد حذف هذا المجتمع؟", - "are_you_sure_you_want_to_close_the_window": "هل أنت متأكد أنك تريد إغلاق النافذة؟", - "yes_close": "نعم، أغلق", - "link_copied_to_clipboard": "تم نسخ الرابط إلى الحافظة", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "هل أنت متأكد أنك تريد إغلاق هذه النافذة؟ (سيتم فقدان جميع البيانات)", - "yes_close_it": "نعم، أغلقها!", - "uploading_pictures": "جاري رفع الصور...", - "agent_starting": "يتم بدء الوكيل", - "researching_your_content": "يتم البحث في المحتوى الخاص بك...", - "understanding_the_category": "يتم فهم الفئة...", - "finding_the_topic": "يتم العثور على الموضوع...", - "finding_popular_posts_to_match_with": "يتم البحث عن منشورات شائعة للمطابقة معها...", - "generating_hook": "يتم إنشاء جملة جذب...", - "generating_content": "يتم إنشاء المحتوى...", - "generating_pictures": "يتم إنشاء الصور...", - "finding_time_to_post": "يتم تحديد وقت النشر...", - "write_anything": "اكتب أي شيء", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "يمكنك كتابة أي شيء تريده، ويمكنك أيضًا إضافة الروابط، سنقوم نحن بالبحث من أجلك...", - "output_format": "تنسيق الإخراج", - "add_pictures": "إضافة صور؟", - "7_days": "7 أيام", - "30_days": "30 يومًا", - "90_days": "90 يومًا", - "start_7_days_free_trial": "ابدأ تجربة مجانية لمدة 7 أيام", - "change_language": "تغيير اللغة", - "that_a_wrap": "انتهينا!\n\nإذا أعجبك هذا التسلسل:\n\n1. تابعني على @{{username}} للمزيد من هذه المواضيع\n2. أعد تغريد التغريدة أدناه لمشاركة هذا التسلسل مع جمهورك\n", - "post_as_images_carousel": "انشر كعرض شرائح للصور", - "save_set": "حفظ المجموعة", - "separate_post": "فصل المنشور إلى عدة منشورات", - "label_who_can_reply_to_this_post": "من يمكنه الرد على هذا المنشور؟", - "delete_integration": "حذف التكامل", - "start_writing_your_post": "ابدأ بكتابة منشورك لمعاينة" + "calendar": "التقويم", + "webhooks": "روابط الويب (Webhooks)", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "روابط الويب هي وسيلة لتلقي الإشعارات عند حدوث شيء ما في Postiz عبر طلب HTTP.", + "name": "الاسم", + "url": "الرابط (URL)", + "edit": "تعديل", + "delete": "حذف", + "add_a_webhook": "إضافة رابط ويب", + "save": "حفظ", + "send_test": "إرسال اختبار", + "select_role": "اختر الدور", + "video_made_with_ai": "فيديو تم إنشاؤه بالذكاء الاصطناعي", + "please_add_at_least": "يرجى إضافة 20 حرفًا على الأقل", + "send_invitation_via_email": "إرسال دعوة عبر البريد الإلكتروني؟", + "global_settings": "الإعدادات العامة", + "copy_id": "نسخ معرف القناة", + "team_members": "أعضاء الفريق", + "invite_your_assistant_or_team_member_to_manage_your_account": "ادعُ مساعدك أو أحد أعضاء فريقك لإدارة حسابك", + "remove": "إزالة", + "add_another_member": "إضافة عضو آخر", + "signatures": "التواقيع", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "يمكنك إضافة تواقيع إلى حسابك لاستخدامها في منشوراتك.", + "content": "المحتوى", + "auto_add": "إضافة تلقائية؟", + "actions": "الإجراءات", + "use_signature": "استخدم التوقيع", + "add_a_signature": "أضف توقيعًا", + "no": "لا", + "yes": "نعم", + "your_git_repository": "مستودع Git الخاص بك", + "connect_your_github_repository_to_receive_updates_and_analytics": "قم بربط مستودع GitHub الخاص بك لتلقي التحديثات والتحليلات", + "connected": "متصل:", + "disconnect": "قطع الاتصال", + "connect_your_repository": "ربط المستودع الخاص بك", + "cancel": "إلغاء", + "connect": "اتصال", + "public_api": "واجهة برمجة التطبيقات العامة", + "check_n8n": "اطلع على عقدة N8N المخصصة لدينا لـ Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "استخدم واجهة برمجة تطبيقات Postiz للدمج مع أدواتك.", + "read_how_to_use_it_over_the_documentation": "اقرأ كيفية استخدامه في الوثائق.", + "reveal": "إظهار", + "copy_key": "نسخ المفتاح", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "قم بتوصيل خادم Postiz MCP بعميلك (بث HTTP) لجدولة منشوراتك بشكل أسرع!", + "share_with_a_client": "مشاركة مع عميل", + "post": "منشور", + "comments": "تعليقات", + "user": "مستخدم", + "login_register_to_add_comments": "تسجيل الدخول / التسجيل لإضافة تعليقات", + "status": "الحالة:", + "there_are_not_plugs_matching_your_channels": "لا توجد ملحقات مطابقة لقنواتك", + "you_have_to_add_x_or_linkedin_or_threads": "يجب عليك إضافة: X أو LinkedIn أو Threads", + "go_to_the_calendar_to_add_channels": "اذهب إلى التقويم لإضافة القنوات", + "channels": "القنوات", + "activate": "تفعيل", + "this_channel_needs_to_be_refreshed": "يجب تحديث هذه القناة،", + "click_here_to_refresh": "انقر هنا للتحديث", + "can_t_show_analytics_yet": "لا يمكن عرض التحليلات بعد", + "you_have_to_add_social_media_channels": "يجب عليك إضافة قنوات التواصل الاجتماعي", + "supported": "مدعوم:", + "step": "الخطوة", + "skip_onboarding": "تخطي الإعداد", + "onboarding": "الإعداد", + "next": "التالي", + "you_are_done_from_here_you_can": "لقد انتهيت، من هنا يمكنك:", + "view_analytics": "عرض التحليلات", + "schedule_a_new_post": "جدولة منشور جديد", + "to_sell_posts_you_would_have_to": "لبيع المنشورات يجب عليك:", + "1_connect_at_least_one_channel": "1. ربط قناة واحدة على الأقل", + "2_connect_you_bank_account": "2. ربط حسابك البنكي", + "go_back_to_connect_channels": "العودة لربط القنوات", + "move_to_the_seller_page_to_connect_you_bank": "انتقل إلى صفحة البائع لربط حسابك البنكي", + "connect_channels": "ربط القنوات", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "قم بربط قنوات التواصل الاجتماعي ومواقع النشر الخاصة بك لجدولة المنشورات لاحقًا", + "social": "اجتماعي", + "publishing_platforms": "منصات النشر", + "no_channels": "لا توجد قنوات بعد", + "connect_your_accounts": "قم بربط حساباتك الاجتماعية لبدء الجدولة، النشر، والتحليل — كل ذلك في مكان واحد.", + "notifications": "الإشعارات", + "no_notifications": "لا توجد إشعارات", + "send_message": "إرسال رسالة", + "mar_28": "٢٨ مارس", + "there_are_no_messages_yet": "لا توجد رسائل بعد.", + "checkout_the_marketplace": "تصفح السوق", + "go_to_marketplace": "اذهب إلى السوق", + "all_messages": "كل الرسائل", + "previous": "السابق", + "select_or_upload_pictures_maximum_5_at_a_time": "اختر أو ارفع صورًا (بحد أقصى 5 في المرة الواحدة)", + "you_can_also_drag_drop_pictures": "يمكنك أيضًا سحب وإفلات الصور", + "you_don_t_have_any_assets_yet": "ليس لديك أي أصول بعد.", + "click_the_button_below_to_upload_one": "انقر على الزر أدناه لرفع واحدة", + "click_the_button_below_to_upload_other": "انقر على الزر أدناه لتحميل عدة ملفات", + "add_selected_media": "أضف الوسائط المحددة", + "insert_media": "إدراج وسائط", + "design_media": "تصميم وسائط", + "select": "اختر", + "editor": "المحرر", + "clear": "مسح", + "order_completed": "اكتمل الطلب", + "the_order_has_been_completed": "تم اكتمال الطلب", + "post_has_been_published": "تم نشر المنشور", + "url_1": "الرابط:", + "new_offer": "عرض جديد", + "platform": "المنصة", + "posts": "المنشورات", + "pay_accept_offer": "ادفع وقبل العرض", + "accepted": "تم القبول", + "post_draft": "مسودة منشور", + "revision_needed": "مطلوب مراجعة", + "approve": "موافقة", + "preview": "معاينة", + "revision_requested": "تم طلب مراجعة", + "accepted_1": "مقبول", + "cancelled_by_the_seller": "أُلغي بواسطة البائع", + "please_select_your_country_where_your_business_is": "يرجى اختيار الدولة التي يقع فيها عملك.", + "select_country": "--اختر الدولة--", + "connect_bank_account": "ربط حساب بنكي", + "seller_mode": "وضع البائع", + "active": "نشط", + "details": "تفاصيل", + "audience_size": "حجم الجمهور", + "add_another_platform": "أضف منصة أخرى", + "send_an_offer_for": "أرسل عرضًا مقابل $", + "complete_order_and_pay_early": "أكمل الطلب وادفع مبكرًا", + "order_in_progress": "الطلب قيد التنفيذ", + "create_a_new_offer": "أنشئ عرضًا جديدًا", + "orders": "الطلبات", + "price": "السعر", + "state": "الحالة", + "showing": "عرض", + "to": "إلى", + "from": "من", + "results": "النتائج", + "content_writer": "كاتب محتوى", + "influencer": "مؤثر", + "request_service": "طلب خدمة", + "the_marketplace_is_not_opened_yet": "السوق لم يُفتح بعد", + "check_again_soon": "تحقق مرة أخرى قريبًا!", + "filter": "تصفية", + "result": "نتيجة", + "seller": "بائع", + "buyer": "مشتري", + "discord_support": "دعم ديسكورد", + "teams": "الفرق", + "webhooks_1": "ويب هوكس", + "auto_post": "نشر تلقائي", + "logout_from": "تسجيل الخروج من", + "join_10000_entrepreneurs_who_use_postiz": "انضم إلى أكثر من 10,000 رائد أعمال يستخدمون Postiz", + "to_manage_all_your_social_media_channels": "لإدارة جميع قنوات التواصل الاجتماعي الخاصة بك", + "100_no_risk_trial": "تجربة خالية من المخاطر 100%", + "pay_nothing_for_the_first_7_days": "لا تدفع شيئًا لأول 7 أيام", + "cancel_anytime_hassle_free": "يمكنك الإلغاء في أي وقت وبدون عناء", + "add_free_subscription": "-- أضف اشتراكًا مجانيًا --", + "currently_impersonating": "يتم الآن الانتحال", + "user_1": "المستخدم:", + "drag_n_drop_some_files_here": "اسحب وأفلت بعض الملفات هنا", + "add_time_slot": "أضف فترة زمنية", + "add_slot": "إضافة خانة", + "cancel_publication": "إلغاء النشر", + "statistics": "الإحصائيات", + "loading": "جارٍ التحميل", + "short_link": "رابط مختصر", + "original_link": "الرابط الأصلي", + "clicks": "النقرات", + "selected_customer": "العميل المحدد", + "customer": "العميل:", + "repeat_post_every": "تكرار النشر كل...", + "use_this_media": "استخدم هذه الوسائط", + "create_new_post": "إنشاء منشور", + "update_post": "تحديث المنشور الحالي", + "merge_comments_into_one_post": "دمج التعليقات في منشور واحد", + "accounts_that_will_engage": "الحسابات التي ستتفاعل:", + "day": "يوم", + "week": "أسبوع", + "month": "شهر", + "remove_from_customer": "إزالة من العميل", + "show_more": "+ عرض المزيد", + "show_less": "- عرض أقل", + "upload": "رفع", + "ai": "الذكاء الاصطناعي", + "add_channel": "إضافة قناة", + "add_platform": "إضافة منصة", + "articles": "مقالات", + "add_comment": "أضف تعليقًا", + "add_post": "أضف منشورًا في سلسلة النقاش", + "add_comment_or_post": "أضف تعليقًا / منشورًا", + "you_are_in_global_editing_mode": "أنت في وضع التحرير العام", + "the_post_should_be_at_least_6_characters_long": "يجب أن يكون المنشور مكونًا من 6 أحرف على الأقل", + "are_you_sure_you_want_to_delete_post": "هل أنت متأكد أنك تريد حذف هذا المنشور؟", + "post_deleted_successfully": "تم حذف المنشور بنجاح", + "delete_post": "حذف المنشور", + "save_as_draft": "حفظ كمسودة", + "post_now": "انشر الآن", + "please_add": "يرجى إضافة", + "to_your_telegram_group_channel_and_click_here": "إلى مجموعة / قناة التليجرام الخاصة بك ثم اضغط هنا:", + "connect_telegram": "ربط التليجرام", + "please_add_the_following_command_in_your_chat": "يرجى إضافة الأمر التالي في الدردشة الخاصة بك:", + "copy": "نسخ", + "settings": "الإعدادات", + "integrations": "التكاملات", + "add_integration": "إضافة تكامل", + "you_are_now_editing_only": "أنت الآن تقوم بالتحرير فقط", + "tag_a_company": "الإشارة إلى شركة", + "video_length_is_invalid_must_be_up_to": "مدة الفيديو غير صالحة، يجب أن تكون حتى", + "seconds": "ثوانٍ", + "this_feature_available_only_for_photos": "هذه الميزة متاحة فقط للصور، وسيتم إضافة موسيقى افتراضية يمكنك تغييرها لاحقًا.", + "allow_user_to": "السماح للمستخدم بـ:", + "your_video_will_be_labeled_promotional": "سيتم تصنيف الفيديو الخاص بك كـ \"محتوى ترويجي\".", + "this_cannot_be_changed_once_posted": "لا يمكن تغيير ذلك بعد نشر الفيديو الخاص بك.", + "turn_on_to_disclose_video_promotes": "فعّل هذا الخيار للإفصاح عن أن هذا الفيديو يروّج لسلع أو خدمات مقابل شيء ذي قيمة. قد يروّج الفيديو لنفسك أو لطرف ثالث أو لكليهما.", + "you_are_promoting_yourself": "أنت تروّج لنفسك أو لعلامتك التجارية الخاصة.", + "this_video_will_be_classified_brand_organic": "سيتم تصنيف هذا الفيديو كـ محتوى عضوي للعلامة التجارية.", + "you_are_promoting_another_brand": "أنت تروّج لعلامة تجارية أخرى أو لطرف ثالث.", + "this_video_will_be_classified_branded_content": "سيتم تصنيف هذا الفيديو كمحتوى يحمل علامة تجارية.", + "by_posting_you_agree_to_tiktoks": "بنشرك، أنت توافق على شروط تيك توك", + "music_usage_confirmation": "تأكيد استخدام الموسيقى", + "branded_content_policy": "سياسة المحتوى الممول", + "select_1": "--اختر--", + "select_flair": "--اختر الوسم--", + "link": "رابط", + "add_subreddit": "أضف منتدى فرعي", + "please_add_at_least_one_subreddit": "يرجى إضافة منتدى فرعي واحد على الأقل", + "add_community": "أضف مجتمع", + "select_post_type": "اختر نوع المنشور...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "لم نتمكن من العثور على أي نشاط تجاري مرتبط بصفحة لينكدإن الخاصة بك.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "يرجى إغلاق هذه النافذة، وإنشاء صفحة جديدة، ثم إضافة قناة جديدة مرة أخرى.", + "select_linkedin_page": "اختر صفحة لينكدإن:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "لم نتمكن من العثور على أي نشاط تجاري مرتبط بالصفحات المحددة.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "نوصي بربط جميع الصفحات وجميع الأنشطة التجارية.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "يرجى إغلاق هذه النافذة، حذف التكامل الخاص بك، ثم إضافة قناة جديدة مرة أخرى.", + "select_instagram_account": "اختر حساب إنستغرام:", + "select_page": "اختر الصفحة:", + "generate_image_with_ai": "توليد صورة بالذكاء الاصطناعي", + "reconnect_channel": "إعادة ربط القناة", + "update_credentials": "تحديث بيانات الاعتماد", + "additional_settings": "إعدادات إضافية", + "change_bot": "تغيير الروبوت", + "move_add_to_customer": "نقل / إضافة إلى العميل", + "edit_time_slots": "تعديل الفترات الزمنية", + "enable_channel": "تفعيل القناة", + "disable_channel": "تعطيل القناة", + "add": "إضافة", + "short_post": "منشور قصير", + "long_post": "منشور طويل", + "a_thread_with_short_posts": "سلسلة منشورات قصيرة", + "a_thread_with_long_posts": "سلسلة منشورات طويلة", + "personal_voice_i_am_happy_to_announce": "بصوت شخصي (\"يسعدني أن أعلن\")", + "company_voice_we_are_happy_to_announce": "بصوت الشركة (\"يسعدنا أن نعلن\")", + "generate": "توليد", + "generate_posts": "توليد منشورات", + "purchase_a_life_time_pro_account_with_sol_199": "اشترِ حساب PRO مدى الحياة مقابل SOL (199 دولارًا)، يرجى العلم أنه لا يوجد استرداد لهذا الشراء.", + "purchase_now": "اشترِ الآن", + "pay_today": "ادفع اليوم", + "we_are_sorry_to_see_you_go": "نأسف لرؤيتك ترحل :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "هل تمانع أن تخبرنا باختصار كيف كان بإمكاننا أن نكون أفضل؟", + "cancel_subscription": "إلغاء الاشتراك", + "plans": "الخطط", + "monthly": "شهريًا", + "yearly": "سنويًا", + "reactivate_subscription": "إعادة تفعيل الاشتراك", + "update_payment_method_invoices_history": "تحديث طريقة الدفع / سجل الفواتير", + "cancel_subscription_1": "إلغاء الاشتراك", + "your_subscription_will_be_canceled_at": "سيتم إلغاء اشتراكك في", + "you_will_never_be_charged_again": "لن يتم خصم أي رسوم منك مرة أخرى", + "current_package": "الباقة الحالية:", + "next_package": "الباقة التالية:", + "claim": "مطالبة", + "frequently_asked_questions": "الأسئلة الشائعة", + "autopost": "النشر التلقائي", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "يمكن للنشر التلقائي نشر العناصر الجديدة من موجز RSS الخاص بك تلقائيًا على وسائل التواصل الاجتماعي", + "title": "العنوان", + "add_an_autopost": "إضافة نشر تلقائي", + "post_content": "محتوى المنشور", + "sign_up": "تسجيل حساب", + "or": "أو", + "by_registering_you_agree_to_our": "بتسجيلك، أنت توافق على", + "and": "و", + "terms_of_service": "شروط الخدمة", + "privacy_policy": "سياسة الخصوصية", + "create_account": "إنشاء حساب", + "already_have_an_account": "هل لديك حساب بالفعل؟", + "sign_in": "تسجيل الدخول", + "sign_in_1": "تسجيل الدخول", + "don_t_have_an_account": "ليس لديك حساب؟", + "forgot_password": "نسيت كلمة المرور", + "forgot_password_1": "نسيت كلمة المرور", + "send_password_reset_email": "إرسال بريد إعادة تعيين كلمة المرور", + "go_back_to_login": "العودة إلى تسجيل الدخول", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "لقد أرسلنا لك بريدًا إلكترونيًا يحتوي على رابط لإعادة تعيين كلمة المرور الخاصة بك.", + "change_password": "تغيير كلمة المرور", + "we_successfully_reset_your_password_you_can_now_login_with_your": "تمت إعادة تعيين كلمة المرور بنجاح. يمكنك الآن تسجيل الدخول باستخدام", + "click_here_to_go_back_to_login": "انقر هنا للعودة إلى تسجيل الدخول", + "activate_your_account": "فعّل حسابك", + "thank_you_for_registering": "شكرًا لتسجيلك!", + "please_check_your_email_to_activate_your_account": "يرجى التحقق من بريدك الإلكتروني لتفعيل حسابك.", + "sign_in_with": "تسجيل الدخول باستخدام", + "continue_with_google": "المتابعة باستخدام جوجل", + "sign_in_with_github": "تسجيل الدخول باستخدام جيت هب", + "continue_with_farcaster": "المتابعة باستخدام فاركاستر", + "continue_with_your_wallet": "المتابعة باستخدام محفظتك", + "stars_per_day": "النجوم في اليوم", + "media": "الوسائط", + "check_launch": "تحقق من الإطلاق", + "load_your_github_repository_from_settings_to_see_analytics": "قم بتحميل مستودع GitHub الخاص بك من الإعدادات لعرض التحليلات", + "stars": "النجوم", + "processing_stars": "جاري معالجة النجوم...", + "forks": "التفريعات", + "registration_is_disabled": "التسجيل معطل", + "login_instead": "سجّل الدخول بدلًا من ذلك", + "gitroom": "جيت روم", + "select_a_conversation_and_chat_away": "اختر محادثة وابدأ الدردشة.", + "adding_channel_redirecting_you": "يتم إضافة القناة، جاري إعادة التوجيه", + "could_not_add_provider": "تعذر إضافة المزوّد.", + "you_are_being_redirected_back": "يتم إعادتك إلى الصفحة السابقة", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "نواجه بعض الصعوبات، حاول تحديث الصفحة", + "post_not_found": "لم يتم العثور على المنشور", + "publication_date": "تاريخ النشر:", + "analytics": "تحليلات", + "launches": "إطلاقات", + "plugs": "إعلانات", + "billing": "الفوترة", + "affiliate": "شريك", + "monday": "الاثنين", + "tuesday": "الثلاثاء", + "wednesday": "الأربعاء", + "thursday": "الخميس", + "friday": "الجمعة", + "saturday": "السبت", + "sunday": "الأحد", + "can_t_change_date_remove_post_from_publication": "لا يمكن تغيير التاريخ، أزل المنشور من النشر", + "predicted_github_trending_change": "التغير المتوقع في ترند GitHub", + "duplicate_post": "منشور مكرر", + "preview_post": "معاينة المنشور", + "post_statistics": "إحصائيات المنشور", + "draft": "مسودة", + "week_number": "الأسبوع {{number}}", + "top_title_edit_webhook": "تعديل Webhook", + "top_title_add_webhook": "إضافة Webhook", + "top_title_oh_no": "أوه لا", + "top_title_auto_plug": "التوصيل التلقائي: {{title}}", + "top_title_edit_autopost": "تعديل النشر التلقائي", + "top_title_add_autopost": "إضافة نشر تلقائي", + "top_title_send_a_new_offer": "إرسال عرض جديد", + "top_title_media_library": "مكتبة الوسائط", + "top_title_add_signature": "إضافة توقيع", + "top_title_send_a_message_to": "إرسال رسالة إلى {{name}}", + "top_title_configure_provider": "تهيئة المزود", + "top_title_add_member": "إضافة عضو", + "top_title_change_bot_picture": "تغيير صورة البوت", + "top_title_create_a_new_tag": "إنشاء وسم جديد", + "top_title_select_company": "اختيار الشركة", + "top_title_additional_settings": "إعدادات إضافية", + "top_title_time_table_slots": "فترات الجدول الزمني", + "top_title_design_media": "تصميم الوسائط", + "top_title_edit_post": "تعديل المنشور", + "top_title_create_post": "إنشاء منشور", + "top_title_move__add_to_customer": "نقل / إضافة إلى العميل", + "top_title_add_api_key_for": "إضافة مفتاح API لـ {{name}}", + "top_title_instance_url": "رابط النسخة", + "top_title_custom_url": "رابط مخصص", + "top_title_add_channel": "إضافة قناة", + "top_title_add_telegram": "إضافة تيليجرام", + "top_title_add_wrapcast": "إضافة Wrapcast", + "top_title_comments_for": "تعليقات ليوم {{date}}", + "top_title_edit_signature": "تعديل التوقيع", + "label_name": "الاسم", + "label_url": "الرابط", + "label_title": "العنوان", + "label_subtitle": "العنوان الفرعي", + "label_email": "البريد الإلكتروني", + "label_full_name": "الاسم الكامل", + "label_password": "كلمة المرور", + "label_confirm_password": "تأكيد كلمة المرور", + "label_api_key": "مفتاح API", + "label_instance_url": "رابط النسخة", + "label_custom_url": "رابط مخصص", + "label_feedback": "ملاحظات", + "label_bio": "نبذة تعريفية", + "label_role": "الدور", + "label_country": "الدولة", + "label_audience_size": "حجم الجمهور على جميع المنصات", + "label_pick_time": "اختر الوقت", + "label_nickname": "الاسم المستعار", + "label_write_anything": "اكتب أي شيء", + "label_output_format": "تنسيق الإخراج", + "label_add_pictures": "إضافة صور؟", + "label_hour": "ساعة", + "label_minutes": "دقائق", + "label_select_publication": "اختر النشر", + "label_canonical_link": "الرابط القانوني", + "label_cover_picture": "صورة الغلاف", + "label_tags": "الوسوم", + "label_topics": "المواضيع", + "label_tags_maximum_4": "الوسوم (بحد أقصى 4)", + "label_attachments": "المرفقات", + "label_type": "النوع", + "label_thumbnail": "الصورة المصغرة", + "label_who_can_see_this_video": "من يمكنه مشاهدة هذا الفيديو؟", + "label_content_posting_method": "طريقة نشر المحتوى", + "label_auto_add_music": "إضافة الموسيقى تلقائيًا", + "label_duet": "دويتو", + "label_stitch": "دمج", + "label_comments": "التعليقات", + "label_disclose_video_content": "الإفصاح عن محتوى الفيديو", + "label_your_brand": "علامتك التجارية", + "label_branded_content": "محتوى برعاية علامة تجارية", + "label_subreddit": "مجتمع فرعي", + "label_flair": "شارة", + "label_media": "الوسائط", + "label_search_subreddit": "البحث عن مجتمع فرعي", + "label_delay": "تأخير", + "label_post_type": "نوع المنشور", + "label_collaborators": "المتعاونون (بحد أقصى 3) - لا يمكن أن تكون الحسابات خاصة", + "label_community": "المجتمع", + "label_search_community": "ابحث في المجتمع", + "label_channel": "القناة", + "label_search_channel": "ابحث عن قناة", + "label_select_channel": "اختر قناة", + "label_new_password": "كلمة المرور الجديدة", + "label_repeat_password": "أعد إدخال كلمة المرور", + "label_platform": "المنصة", + "label_price_per_post": "السعر لكل منشور", + "label_integrations": "التكاملات", + "label_code": "الرمز", + "label_should_sync_last_post": "هل نزامن آخر منشور حالي؟", + "label_when_post": "متى يجب أن ننشره؟", + "label_autogenerate_content": "توليد المحتوى تلقائيًا", + "label_generate_picture": "توليد صورة؟", + "label_company": "الشركة", + "label_tag_color": "لون الوسم", + "label_select_board": "اختر لوحة", + "label_select_organization": "اختر منظمة", + "label_auto_add_signature": "إضافة توقيع تلقائيًا؟", + "enable_color_picker": "تفعيل منتقي الألوان", + "cancel_the_color_picker": "إلغاء منتقي الألوان", + "no_content_yet": "لا يوجد محتوى بعد", + "write_your_reply": "اكتب منشورك...", + "add_a_tag": "أضف وسمًا", + "add_to_calendar": "أضف إلى التقويم", + "select_channels_from_circles": "اختر القنوات من الدوائر أعلاه", + "not_matching_order": "الترتيب غير متطابق", + "submit_for_order": "إرسال للطلب", + "schedule": "جدولة", + "update": "تحديث", + "attachments": "المرفقات", + "tags": "الوسوم", + "public_to_everyone": "عام للجميع", + "mutual_follow_friends": "أصدقاء المتابعة المتبادلة", + "follower_of_creator": "متابع للمنشئ", + "self_only": "لنفسك فقط", + "post_content_directly_to_tiktok": "انشر المحتوى مباشرة على تيك توك", + "upload_content_to_tiktok_without_posting": "ارفع المحتوى إلى تيك توك دون نشره", + "choose_upload_without_posting_description": "اختر الرفع دون النشر إذا كنت ترغب في مراجعة وتحرير المحتوى الخاص بك داخل تطبيق تيك توك قبل النشر. هذا يمنحك إمكانية الوصول إلى أدوات التحرير المدمجة في تيك توك ويسمح لك بإجراء التعديلات النهائية قبل النشر.", + "faq_am_i_going_to_be_charged_by_postiz": "هل سيتم تحميلي رسوم من قبل Postiz؟", + "faq_to_confirm_credit_card_information_postiz_will_hold": "لتأكيد معلومات بطاقة الائتمان، سيقوم Postiz بحجز مبلغ 2 دولار وإطلاقه فوراً", + "faq_can_i_trust_postiz_gitroom": "هل يمكنني الوثوق بـ Postiz؟", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz مفتوح المصدر بكل فخر! نحن نؤمن بثقافة أخلاقية وشفافة، مما يعني أن Postiz سيبقى للأبد. يمكنك الاطلاع على الكود بالكامل أو استخدامه في مشاريعك الشخصية. لعرض مستودع المصدر المفتوح، <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">اضغط هنا</a>.", + "faq_what_are_channels": "ما هي القنوات؟", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "يتيح لك Postiz جدولة منشوراتك بين قنوات مختلفة.\nالقناة هي منصة نشر يمكنك من خلالها جدولة منشوراتك.\nعلى سبيل المثال، يمكنك جدولة منشوراتك على X، فيسبوك، إنستغرام، تيك توك، يوتيوب، ريديت، لينكدإن، Dribbble، Threads وPinterest.", + "faq_what_are_team_members": "من هم أعضاء الفريق؟", + "faq_if_you_have_a_team_with_multiple_members": "إذا كان لديك فريق يضم عدة أعضاء، يمكنك دعوتهم إلى مساحة العمل الخاصة بك للتعاون في منشوراتك وإضافة قنواتهم الشخصية", + "faq_what_is_ai_auto_complete": "ما هو الإكمال التلقائي بالذكاء الاصطناعي؟", + "faq_we_automate_chatgpt_to_help_you_write": "نحن نستخدم ChatGPT لمساعدتك في كتابة المنشورات والمقالات الاجتماعية.", + "enter_email": "أدخل البريد الإلكتروني", + "are_you_sure": "هل أنت متأكد؟", + "yes_delete_it": "نعم، احذفه!", + "no_cancel": "لا، إلغاء!", + "are_you_sure_you_want_to_delete": "هل أنت متأكد أنك تريد حذف {{name}}؟", + "are_you_sure_you_want_to_delete_the_image": "هل أنت متأكد أنك تريد حذف الصورة؟", + "are_you_sure_you_want_to_logout": "هل أنت متأكد أنك تريد تسجيل الخروج؟", + "yes_logout": "نعم، تسجيل الخروج", + "are_you_sure_you_want_to_delete_this_slot": "هل أنت متأكد أنك تريد حذف هذه الخانة؟", + "are_you_sure_you_want_to_delete_this_subreddit": "هل أنت متأكد أنك تريد حذف هذا المجتمع؟", + "are_you_sure_you_want_to_close_the_window": "هل أنت متأكد أنك تريد إغلاق النافذة؟", + "yes_close": "نعم، أغلق", + "link_copied_to_clipboard": "تم نسخ الرابط إلى الحافظة", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "هل أنت متأكد أنك تريد إغلاق هذه النافذة؟ (سيتم فقدان جميع البيانات)", + "yes_close_it": "نعم، أغلقها!", + "uploading_pictures": "جاري رفع الصور...", + "agent_starting": "يتم بدء الوكيل", + "researching_your_content": "يتم البحث في المحتوى الخاص بك...", + "understanding_the_category": "يتم فهم الفئة...", + "finding_the_topic": "يتم العثور على الموضوع...", + "finding_popular_posts_to_match_with": "يتم البحث عن منشورات شائعة للمطابقة معها...", + "generating_hook": "يتم إنشاء جملة جذب...", + "generating_content": "يتم إنشاء المحتوى...", + "generating_pictures": "يتم إنشاء الصور...", + "finding_time_to_post": "يتم تحديد وقت النشر...", + "write_anything": "اكتب أي شيء", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "يمكنك كتابة أي شيء تريده، ويمكنك أيضًا إضافة الروابط، سنقوم نحن بالبحث من أجلك...", + "output_format": "تنسيق الإخراج", + "add_pictures": "إضافة صور؟", + "7_days": "7 أيام", + "30_days": "30 يومًا", + "90_days": "90 يومًا", + "start_7_days_free_trial": "ابدأ تجربة مجانية لمدة 7 أيام", + "change_language": "تغيير اللغة", + "that_a_wrap": "انتهينا!\n\nإذا أعجبك هذا التسلسل:\n\n1. تابعني على @{{username}} للمزيد من هذه المواضيع\n2. أعد تغريد التغريدة أدناه لمشاركة هذا التسلسل مع جمهورك\n", + "post_as_images_carousel": "انشر كعرض شرائح للصور", + "save_set": "حفظ المجموعة", + "separate_post": "فصل المنشور إلى عدة منشورات", + "label_who_can_reply_to_this_post": "من يمكنه الرد على هذا المنشور؟", + "delete_integration": "حذف التكامل", + "start_writing_your_post": "ابدأ بكتابة منشورك لمعاينة", + "billing_join_over": "انضم إلى أكثر من", + "billing_entrepreneurs_count": "18,000+ رائد أعمال", + "billing_who_use": "الذين يستخدمون", + "billing_postiz_grow_social": "Postiz لتنمية حضورهم على وسائل التواصل الاجتماعي", + "billing_no_risk_trial": "تجربة مجانية بدون أي مخاطرة 100%", + "billing_pay_nothing_7_days": "لا تدفع شيئًا لأول 7 أيام", + "billing_cancel_anytime": "يمكنك الإلغاء في أي وقت، بدون متاعب", + "billing_choose_plan": "اختر خطة", + "billing_monthly": "شهريًا", + "billing_yearly": "سنويًا", + "billing_20_percent_off": "خصم 20%", + "billing_features": "المميزات", + "billing_channel": "قناة", + "billing_channels": "قنوات", + "billing_unlimited": "غير محدود", + "billing_posts_per_month": "منشورات في الشهر", + "billing_unlimited_team_members": "عدد غير محدود من أعضاء الفريق", + "billing_ai_auto_complete": "إكمال تلقائي بالذكاء الاصطناعي", + "billing_ai_copilots": "مساعدون بالذكاء الاصطناعي", + "billing_ai_autocomplete": "إكمال تلقائي بالذكاء الاصطناعي", + "billing_advanced_picture_editor": "محرر صور متقدم", + "billing_ai_images_per_month": "صور بالذكاء الاصطناعي شهريًا", + "billing_ai_videos_per_month": "فيديوهات بالذكاء الاصطناعي شهريًا", + "billing_billing_address": "عنوان الفاتورة", + "billing_payment": "الدفع", + "billing_powered_by_stripe": "بدعم من سترايب", + "billing_your_7_day_trial_is": "فترة التجربة المجانية لمدة 7 أيام الخاصة بك هي", + "billing_100_percent_free": "مجانية 100%", + "billing_ending": "تنتهي", + "billing_cancel_anytime_short": "يمكنك الإلغاء في أي وقت.", + "billing_pay_0_start_trial": "ادفع 0 دولار اليوم - ابدأ تجربتك المجانية!", + "billing_pay_now": "ادفع الآن", + "billing_per_month": "/ شهريًا" } diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index 42e4f3e3..d985a5a9 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -1,506 +1,539 @@ { - "calendar": "ক্যালেন্ডার", - "webhooks": "ওয়েবহুক", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "ওয়েবহুক হল একটি HTTP অনুরোধের মাধ্যমে Postiz-এ কিছু ঘটলে বিজ্ঞপ্তি পাওয়ার একটি উপায়।", - "name": "নাম", - "url": "URL", - "edit": "সম্পাদনা", - "delete": "মুছে ফেলুন", - "add_a_webhook": "একটি ওয়েবহুক যোগ করুন", - "save": "সংরক্ষণ করুন", - "send_test": "পরীক্ষা পাঠান", - "select_role": "ভূমিকা নির্বাচন করুন", - "video_made_with_ai": "এআই দিয়ে তৈরি ভিডিও", - "please_add_at_least": "অনুগ্রহ করে অন্তত ২০টি অক্ষর বিশিষ্ট সংযোজন করুন", - "send_invitation_via_email": "ইমেইলের মাধ্যমে আমন্ত্রণ পাঠান?", - "global_settings": "গ্লোবাল সেটিংস", - "copy_id": "চ্যানেল আইডি কপি করুন", - "team_members": "দলের সদস্যরা", - "invite_your_assistant_or_team_member_to_manage_your_account": "আপনার অ্যাকাউন্ট পরিচালনা করতে আপনার সহায়ক বা দলের সদস্যকে আমন্ত্রণ জানান", - "remove": "সরান", - "add_another_member": "আরেকটি সদস্য যোগ করুন", - "signatures": "স্বাক্ষর", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "আপনি আপনার পোস্টে ব্যবহারের জন্য আপনার অ্যাকাউন্টে স্বাক্ষর যোগ করতে পারেন।", - "content": "বিষয়বস্তু", - "auto_add": "স্বয়ংক্রিয় যোগ?", - "actions": "কার্যক্রম", - "use_signature": "স্বাক্ষর ব্যবহার করুন", - "add_a_signature": "একটি স্বাক্ষর যোগ করুন", - "no": "না", - "yes": "হ্যাঁ", - "your_git_repository": "আপনার Git রিপোজিটরি", - "connect_your_github_repository_to_receive_updates_and_analytics": "আপডেট এবং বিশ্লেষণ পেতে আপনার GitHub রিপোজিটরি সংযুক্ত করুন", - "connected": "সংযুক্ত:", - "disconnect": "সংযোগ বিচ্ছিন্ন করুন", - "connect_your_repository": "আপনার রিপোজিটরি সংযুক্ত করুন", - "cancel": "বাতিল", - "connect": "সংযুক্ত করুন", - "public_api": "পাবলিক API", - "check_n8n": "Postiz-এর জন্য আমাদের N8N কাস্টম নোডটি দেখুন।", - "use_postiz_api_to_integrate_with_your_tools": "আপনার টুলগুলির সাথে একীভূত করতে Postiz API ব্যবহার করুন।", - "read_how_to_use_it_over_the_documentation": "ডকুমেন্টেশনে এটি কীভাবে ব্যবহার করবেন তা পড়ুন।", - "reveal": "প্রকাশ করুন", - "copy_key": "কী কপি করুন", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "আপনার ক্লায়েন্টকে (Http স্ট্রিমিং) Postiz MCP সার্ভারের সাথে সংযুক্ত করুন, যাতে আরও দ্রুত আপনার পোস্টগুলো নির্ধারণ করতে পারেন!", - "share_with_a_client": "একটি ক্লায়েন্টের সাথে শেয়ার করুন", - "post": "পোস্ট", - "comments": "মন্তব্য", - "user": "ব্যবহারকারী", - "login_register_to_add_comments": "মন্তব্য যোগ করতে লগইন / নিবন্ধন করুন", - "status": "অবস্থা:", - "there_are_not_plugs_matching_your_channels": "আপনার চ্যানেলের সাথে মিলে এমন কোনো প্লাগ নেই", - "you_have_to_add_x_or_linkedin_or_threads": "আপনাকে যোগ করতে হবে: X বা LinkedIn বা Threads", - "go_to_the_calendar_to_add_channels": "চ্যানেল যোগ করতে ক্যালেন্ডারে যান", - "channels": "চ্যানেল", - "activate": "সক্রিয় করুন", - "this_channel_needs_to_be_refreshed": "এই চ্যানেলটি রিফ্রেশ করা প্রয়োজন,", - "click_here_to_refresh": "রিফ্রেশ করতে এখানে ক্লিক করুন", - "can_t_show_analytics_yet": "এখনও বিশ্লেষণ দেখাতে পারছি না", - "you_have_to_add_social_media_channels": "আপনাকে সোশ্যাল মিডিয়া চ্যানেল যোগ করতে হবে", - "supported": "সমর্থিত:", - "step": "ধাপ", - "skip_onboarding": "অনবোর্ডিং এড়িয়ে যান", - "onboarding": "অনবোর্ডিং", - "next": "পরবর্তী", - "you_are_done_from_here_you_can": "আপনি সম্পন্ন করেছেন, এখান থেকে আপনি পারেন:", - "view_analytics": "বিশ্লেষণ দেখুন", - "schedule_a_new_post": "একটি নতুন পোস্ট সময়সূচী করুন", - "to_sell_posts_you_would_have_to": "পোস্ট বিক্রি করতে আপনাকে করতে হবে:", - "1_connect_at_least_one_channel": "১. কমপক্ষে একটি চ্যানেল সংযুক্ত করুন", - "2_connect_you_bank_account": "২. আপনার ব্যাংক অ্যাকাউন্ট সংযুক্ত করুন", - "go_back_to_connect_channels": "চ্যানেল সংযুক্ত করতে ফিরে যান", - "move_to_the_seller_page_to_connect_you_bank": "আপনার ব্যাংক সংযুক্ত করতে বিক্রেতা পৃষ্ঠায় যান", - "connect_channels": "চ্যানেল সংযুক্ত করুন", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "পরে পোস্ট সময়সূচী করতে আপনার সোশ্যাল মিডিয়া এবং প্রকাশনা ওয়েবসাইট চ্যানেল সংযুক্ত করুন", - "social": "সামাজিক", - "publishing_platforms": "প্রকাশনা প্ল্যাটফর্ম", - "no_channels": "এখনও কোনো চ্যানেল নেই", - "connect_your_accounts": "সময়সূচি নির্ধারণ, প্রকাশ এবং বিশ্লেষণ শুরু করতে আপনার সোশ্যাল অ্যাকাউন্টগুলো সংযুক্ত করুন — সবকিছু এক জায়গায়।", - "notifications": "বিজ্ঞপ্তি", - "no_notifications": "কোনো বিজ্ঞপ্তি নেই", - "send_message": "বার্তা পাঠান", - "mar_28": "মার্চ ২৮", - "there_are_no_messages_yet": "এখনও কোনো বার্তা নেই।", - "checkout_the_marketplace": "মার্কেটপ্লেস দেখুন", - "go_to_marketplace": "মার্কেটপ্লেসে যান", - "all_messages": "সব বার্তা", - "previous": "পূর্ববর্তী", - "select_or_upload_pictures_maximum_5_at_a_time": "ছবি নির্বাচন বা আপলোড করুন (একসাথে সর্বোচ্চ ৫টি)", - "you_can_also_drag_drop_pictures": "আপনি ছবি টেনে এনে ছাড়তেও পারেন", - "you_don_t_have_any_assets_yet": "আপনার এখনও কোনো সম্পদ নেই।", - "click_the_button_below_to_upload_one": "একটি আপলোড করতে নিচের বোতামে ক্লিক করুন", - "click_the_button_below_to_upload_other": "একাধিক আপলোড করার জন্য নীচের বাটন ক্লিক করুন", - "add_selected_media": "নির্বাচিত মিডিয়া যোগ করুন", - "insert_media": "মিডিয়া সন্নিবেশ করুন", - "design_media": "মিডিয়া ডিজাইন করুন", - "select": "নির্বাচন করুন", - "editor": "সম্পাদক", - "clear": "পরিষ্কার করুন", - "order_completed": "অর্ডার সম্পন্ন", - "the_order_has_been_completed": "অর্ডারটি সম্পন্ন হয়েছে", - "post_has_been_published": "পোস্ট প্রকাশিত হয়েছে", - "url_1": "URL:", - "new_offer": "নতুন অফার", - "platform": "প্ল্যাটফর্ম", - "posts": "পোস্ট", - "pay_accept_offer": "পেমেন্ট করুন এবং অফার গ্রহণ করুন", - "accepted": "গৃহীত", - "post_draft": "পোস্ট খসড়া", - "revision_needed": "সংশোধন প্রয়োজন", - "approve": "অনুমোদন করুন", - "preview": "পূর্বরূপ", - "revision_requested": "সংশোধনের অনুরোধ", - "accepted_1": "গৃহীত", - "cancelled_by_the_seller": "বিক্রেতা দ্বারা বাতিল", - "please_select_your_country_where_your_business_is": "অনুগ্রহ করে আপনার দেশ নির্বাচন করুন যেখানে আপনার ব্যবসা রয়েছে।", - "select_country": "--দেশ নির্বাচন করুন--", - "connect_bank_account": "ব্যাংক অ্যাকাউন্ট সংযুক্ত করুন", - "seller_mode": "বিক্রেতা মোড", - "active": "সক্রিয়", - "details": "বিস্তারিত", - "audience_size": "দর্শক সংখ্যা", - "add_another_platform": "আরেকটি প্ল্যাটফর্ম যোগ করুন", - "send_an_offer_for": "এর জন্য একটি অফার পাঠান $", - "complete_order_and_pay_early": "অর্ডার সম্পন্ন করুন এবং আগাম পেমেন্ট করুন", - "order_in_progress": "অর্ডার চলমান", - "create_a_new_offer": "একটি নতুন অফার তৈরি করুন", - "orders": "অর্ডার", - "price": "মূল্য", - "state": "অবস্থা", - "showing": "দেখানো হচ্ছে", - "to": "থেকে", - "from": "পর্যন্ত", - "results": "ফলাফল", - "content_writer": "কন্টেন্ট লেখক", - "influencer": "প্রভাবশালী", - "request_service": "সেবার অনুরোধ", - "the_marketplace_is_not_opened_yet": "মার্কেটপ্লেস এখনও খোলা হয়নি", - "check_again_soon": "শীঘ্রই আবার চেক করুন!", - "filter": "ফিল্টার", - "result": "ফলাফল", - "seller": "বিক্রেতা", - "buyer": "ক্রেতা", - "discord_support": "Discord সাপোর্ট", - "teams": "দল", - "webhooks_1": "ওয়েবহুক", - "auto_post": "স্বয়ংক্রিয় পোস্ট", - "logout_from": "থেকে লগআউট", - "join_10000_entrepreneurs_who_use_postiz": "১০,০০০+ উদ্যোক্তার সঙ্গে Postiz ব্যবহার করুন", - "to_manage_all_your_social_media_channels": "আপনার সব সোশ্যাল মিডিয়া চ্যানেল পরিচালনা করতে", - "100_no_risk_trial": "১০০% ঝুঁকিমুক্ত ট্রায়াল", - "pay_nothing_for_the_first_7_days": "প্রথম ৭ দিনের জন্য কিছুই পেমেন্ট করুন না", - "cancel_anytime_hassle_free": "যেকোনো সময় ঝামেলামুক্তভাবে বাতিল করুন", - "add_free_subscription": "-- বিনামূল্যে সাবস্ক্রিপশন যোগ করুন --", - "currently_impersonating": "বর্তমানে ছদ্মবেশ ধারণ করছেন", - "user_1": "ব্যবহারকারী:", - "drag_n_drop_some_files_here": "এখানে কিছু ফাইল টেনে এনে ছাড়ুন", - "add_time_slot": "সময় স্লট যোগ করুন", - "add_slot": "স্লট যোগ করুন", - "cancel_publication": "প্রকাশনা বাতিল করুন", - "statistics": "পরিসংখ্যান", - "loading": "লোড হচ্ছে", - "short_link": "সংক্ষিপ্ত লিংক", - "original_link": "মূল লিংক", - "clicks": "ক্লিক", - "selected_customer": "নির্বাচিত গ্রাহক", - "customer": "গ্রাহক:", - "repeat_post_every": "প্রতি পোস্ট পুনরাবৃত্তি করুন...", - "use_this_media": "এই মিডিয়া ব্যবহার করুন", - "create_new_post": "নতুন পোস্ট তৈরি করুন", - "update_post": "বিদ্যমান পোস্ট আপডেট করুন", - "merge_comments_into_one_post": "মন্তব্যগুলি একটি পোস্টে একত্রিত করুন", - "accounts_that_will_engage": "যে অ্যাকাউন্টগুলি এনগেজ করবে:", - "day": "দিন", - "week": "সপ্তাহ", - "month": "মাস", - "remove_from_customer": "গ্রাহক থেকে সরান", - "show_more": "+ আরো দেখুন", - "show_less": "- কম দেখুন", - "upload": "আপলোড", - "ai": "AI", - "add_channel": "চ্যানেল যোগ করুন", - "add_platform": "প্ল্যাটফর্ম যোগ করুন", - "articles": "নিবন্ধ", - "add_comment": "মন্তব্য যোগ করুন", - "add_post": "থ্রেডে পোস্ট যোগ করুন", - "add_comment_or_post": "মন্তব্য / পোস্ট যোগ করুন", - "you_are_in_global_editing_mode": "আপনি গ্লোবাল এডিটিং মোডে আছেন", - "the_post_should_be_at_least_6_characters_long": "পোস্টটি কমপক্ষে ৬ অক্ষরের হতে হবে", - "are_you_sure_you_want_to_delete_post": "আপনি কি নিশ্চিত যে আপনি এই পোস্টটি মুছে ফেলতে চান?", - "post_deleted_successfully": "পোস্ট সফলভাবে মুছে ফেলা হয়েছে", - "delete_post": "পোস্ট মুছে ফেলুন", - "save_as_draft": "খসড়া হিসেবে সংরক্ষণ করুন", - "post_now": "এখনই পোস্ট করুন", - "please_add": "অনুগ্রহ করে যোগ করুন", - "to_your_telegram_group_channel_and_click_here": "আপনার টেলিগ্রাম গ্রুপ / চ্যানেলে এবং এখানে ক্লিক করুন:", - "connect_telegram": "টেলিগ্রাম সংযুক্ত করুন", - "please_add_the_following_command_in_your_chat": "অনুগ্রহ করে আপনার চ্যাটে নিম্নলিখিত কমান্ড যোগ করুন:", - "copy": "কপি করুন", - "settings": "সেটিংস", - "integrations": "ইন্টিগ্রেশনসমূহ", - "add_integration": "ইন্টিগ্রেশন যোগ করুন", - "you_are_now_editing_only": "আপনি এখন শুধুমাত্র সম্পাদনা করছেন", - "tag_a_company": "একটি কোম্পানি ট্যাগ করুন", - "video_length_is_invalid_must_be_up_to": "ভিডিওর দৈর্ঘ্য অবৈধ, সর্বোচ্চ হতে হবে", - "seconds": "সেকেন্ড", - "this_feature_available_only_for_photos": "এই বৈশিষ্ট্যটি শুধুমাত্র ছবির জন্য উপলব্ধ, এটি একটি ডিফল্ট সঙ্গীত যোগ করবে যা আপনি পরে পরিবর্তন করতে পারেন।", - "allow_user_to": "ব্যবহারকারীকে অনুমতি দিন:", - "your_video_will_be_labeled_promotional": "আপনার ভিডিও \"প্রচারমূলক বিষয়বস্তু\" হিসেবে লেবেল করা হবে।", - "this_cannot_be_changed_once_posted": "একবার পোস্ট করার পর এটি পরিবর্তন করা যাবে না।", - "turn_on_to_disclose_video_promotes": "এই ভিডিও যে মূল্যবান কিছুর বিনিময়ে পণ্য বা সেবার প্রচার করে তা প্রকাশ করতে চালু করুন। আপনার ভিডিও নিজেকে, তৃতীয় পক্ষ বা উভয়ের প্রচার করতে পারে।", - "you_are_promoting_yourself": "আপনি নিজেকে বা আপনার নিজস্ব ব্র্যান্ডের প্রচার করছেন।", - "this_video_will_be_classified_brand_organic": "এই ভিডিওটি ব্র্যান্ড অর্গানিক হিসেবে শ্রেণীবদ্ধ হবে।", - "you_are_promoting_another_brand": "আপনি অন্য একটি ব্র্যান্ড বা তৃতীয় পক্ষের প্রচার করছেন।", - "this_video_will_be_classified_branded_content": "এই ভিডিওটি ব্র্যান্ডেড কন্টেন্ট হিসেবে শ্রেণীবদ্ধ হবে।", - "by_posting_you_agree_to_tiktoks": "পোস্ট করার মাধ্যমে, আপনি TikTok-এর সাথে সম্মত হচ্ছেন", - "music_usage_confirmation": "সঙ্গীত ব্যবহারের নিশ্চিতকরণ", - "branded_content_policy": "ব্র্যান্ডেড কন্টেন্ট নীতি", - "select_1": "--নির্বাচন করুন--", - "select_flair": "--ফ্লেয়ার নির্বাচন করুন--", - "link": "লিংক", - "add_subreddit": "সাবরেডিট যোগ করুন", - "please_add_at_least_one_subreddit": "অনুগ্রহ করে কমপক্ষে একটি সাবরেডিট যোগ করুন", - "add_community": "কমিউনিটি যোগ করুন", - "select_post_type": "পোস্টের ধরন নির্বাচন করুন...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "আমরা আপনার LinkedIn পৃষ্ঠার সাথে সংযুক্ত কোনো ব্যবসা খুঁজে পাইনি।", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "অনুগ্রহ করে এই ডায়ালগটি বন্ধ করুন, একটি নতুন পৃষ্ঠা তৈরি করুন এবং আবার একটি নতুন চ্যানেল যোগ করুন।", - "select_linkedin_page": "LinkedIn পৃষ্ঠা নির্বাচন করুন:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "আমরা নির্বাচিত পৃষ্ঠাগুলির সাথে সংযুক্ত কোনো ব্যবসা খুঁজে পাইনি।", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "আমরা আপনাকে সব পৃষ্ঠা এবং সব ব্যবসা সংযুক্ত করার পরামর্শ দিই।", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "অনুগ্রহ করে এই ডায়ালগটি বন্ধ করুন, আপনার ইন্টিগ্রেশন মুছে ফেলুন এবং আবার একটি নতুন চ্যানেল যোগ করুন।", - "select_instagram_account": "Instagram অ্যাকাউন্ট নির্বাচন করুন:", - "select_page": "পৃষ্ঠা নির্বাচন করুন:", - "generate_image_with_ai": "AI দিয়ে ছবি তৈরি করুন", - "reconnect_channel": "চ্যানেল পুনরায় সংযুক্ত করুন", - "update_credentials": "শংসাপত্র আপডেট করুন", - "additional_settings": "অতিরিক্ত সেটিংস", - "change_bot": "বট পরিবর্তন করুন", - "move_add_to_customer": "গ্রাহকের কাছে স্থানান্তর / যোগ করুন", - "edit_time_slots": "সময় স্লট সম্পাদনা করুন", - "enable_channel": "চ্যানেল সক্রিয় করুন", - "disable_channel": "চ্যানেল নিষ্ক্রিয় করুন", - "add": "যোগ করুন", - "short_post": "সংক্ষিপ্ত পোস্ট", - "long_post": "দীর্ঘ পোস্ট", - "a_thread_with_short_posts": "সংক্ষিপ্ত পোস্টের একটি থ্রেড", - "a_thread_with_long_posts": "দীর্ঘ পোস্টের একটি থ্রেড", - "personal_voice_i_am_happy_to_announce": "ব্যক্তিগত কণ্ঠস্বর (\"আমি ঘোষণা করতে খুশি\")", - "company_voice_we_are_happy_to_announce": "কোম্পানির কণ্ঠস্বর (\"আমরা ঘোষণা করতে খুশি\")", - "generate": "তৈরি করুন", - "generate_posts": "পোস্ট তৈরি করুন", - "purchase_a_life_time_pro_account_with_sol_199": "SOL ($199) দিয়ে একটি লাইফ-টাইম PRO অ্যাকাউন্ট কিনুন, অনুগ্রহ করে মনে রাখবেন এই ক্রয়ের জন্য কোনো রিফান্ড নেই।", - "purchase_now": "এখনই কিনুন", - "pay_today": "আজই পেমেন্ট করুন", - "we_are_sorry_to_see_you_go": "আপনাকে যেতে দেখে আমরা দুঃখিত :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "আমরা কী আরও ভাল করতে পারতাম তা সংক্ষেপে বলতে কি আপনার আপত্তি আছে?", - "cancel_subscription": "সাবস্ক্রিপশন বাতিল করুন", - "plans": "পরিকল্পনা", - "monthly": "মাসিক", - "yearly": "বার্ষিক", - "reactivate_subscription": "সাবস্ক্রিপশন পুনরায় সক্রিয় করুন", - "update_payment_method_invoices_history": "পেমেন্ট পদ্ধতি / ইনভয়েস ইতিহাস আপডেট করুন", - "cancel_subscription_1": "সাবস্ক্রিপশন বাতিল করুন", - "your_subscription_will_be_canceled_at": "আপনার সাবস্ক্রিপশন বাতিল হবে", - "you_will_never_be_charged_again": "আপনার কাছ থেকে আর কখনো চার্জ নেওয়া হবে না", - "current_package": "বর্তমান প্যাকেজ:", - "next_package": "পরবর্তী প্যাকেজ:", - "claim": "দাবি করুন", - "frequently_asked_questions": "প্রায়শই জিজ্ঞাসিত প্রশ্ন", - "autopost": "অটোপোস্ট", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "অটোপোস্ট স্বয়ংক্রিয়ভাবে আপনার RSS নতুন আইটেমগুলি সোশ্যাল মিডিয়ায় পোস্ট করতে পারে", - "title": "শিরোনাম", - "add_an_autopost": "একটি অটোপোস্ট যোগ করুন", - "post_content": "পোস্ট বিষয়বস্তু", - "sign_up": "সাইন আপ", - "or": "অথবা", - "by_registering_you_agree_to_our": "নিবন্ধন করার মাধ্যমে আপনি আমাদের সাথে সম্মত হচ্ছেন", - "and": "এবং", - "terms_of_service": "সেবার শর্তাবলী", - "privacy_policy": "গোপনীয়তা নীতি", - "create_account": "অ্যাকাউন্ট তৈরি করুন", - "already_have_an_account": "ইতিমধ্যে একটি অ্যাকাউন্ট আছে?", - "sign_in": "সাইন ইন", - "sign_in_1": "সাইন ইন", - "don_t_have_an_account": "কোনো অ্যাকাউন্ট নেই?", - "forgot_password": "পাসওয়ার্ড ভুলে গেছেন", - "forgot_password_1": "পাসওয়ার্ড ভুলে গেছেন", - "send_password_reset_email": "পাসওয়ার্ড রিসেট ইমেইল পাঠান", - "go_back_to_login": "লগইনে ফিরে যান", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "আমরা আপনার পাসওয়ার্ড রিসেট করার জন্য একটি লিংক সহ একটি ইমেইল পাঠিয়েছি।", - "change_password": "পাসওয়ার্ড পরিবর্তন করুন", - "we_successfully_reset_your_password_you_can_now_login_with_your": "আমরা সফলভাবে আপনার পাসওয়ার্ড রিসেট করেছি। আপনি এখন আপনার সাথে লগইন করতে পারেন", - "click_here_to_go_back_to_login": "লগইনে ফিরে যেতে এখানে ক্লিক করুন", - "activate_your_account": "আপনার অ্যাকাউন্ট সক্রিয় করুন", - "thank_you_for_registering": "নিবন্ধনের জন্য ধন্যবাদ!", - "please_check_your_email_to_activate_your_account": "আপনার অ্যাকাউন্ট সক্রিয় করতে অনুগ্রহ করে আপনার ইমেইল চেক করুন।", - "sign_in_with": "এর সাথে সাইন ইন করুন", - "continue_with_google": "Google এর সাথে চালিয়ে যান", - "sign_in_with_github": "GitHub এর সাথে সাইন ইন করুন", - "continue_with_farcaster": "Farcaster দিয়ে চালিয়ে যান", - "continue_with_your_wallet": "আপনার ওয়ালেট দিয়ে চালিয়ে যান", - "stars_per_day": "প্রতিদিন স্টার", - "media": "মিডিয়া", - "check_launch": "লঞ্চ চেক করুন", - "load_your_github_repository_from_settings_to_see_analytics": "বিশ্লেষণ দেখতে সেটিংস থেকে আপনার GitHub রিপোজিটরি লোড করুন", - "stars": "স্টার", - "processing_stars": "স্টার প্রক্রিয়াকরণ", - "forks": "ফর্ক", - "registration_is_disabled": "নিবন্ধন নিষ্ক্রিয়", - "login_instead": "পরিবর্তে লগইন করুন", - "gitroom": "গিটরুম", - "select_a_conversation_and_chat_away": "একটি কথোপকথন নির্বাচন করুন এবং চ্যাট করুন", - "adding_channel_redirecting_you": "চ্যানেল যোগ করা হচ্ছে, আপনাকে রিডাইরেক্ট করা হচ্ছে", - "could_not_add_provider": "প্রদানকারী যোগ করা যায়নি", - "you_are_being_redirected_back": "আপনাকে ফিরিয়ে নিয়ে যাওয়া হচ্ছে", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "আমরা কিছু সমস্যার সম্মুখীন হচ্ছি, পৃষ্ঠা রিফ্রেশ করার চেষ্টা করুন", - "post_not_found": "পোস্ট পাওয়া যায়নি", - "publication_date": "প্রকাশনার তারিখ", - "analytics": "বিশ্লেষণ", - "launches": "লঞ্চ", - "plugs": "প্লাগ", - "billing": "বিলিং", - "affiliate": "অ্যাফিলিয়েট", - "monday": "সোমবার", - "tuesday": "মঙ্গলবার", - "wednesday": "বুধবার", - "thursday": "বৃহস্পতিবার", - "friday": "শুক্রবার", - "saturday": "শনিবার", - "sunday": "রবিবার", - "can_t_change_date_remove_post_from_publication": "তারিখ পরিবর্তন করা যাবে না, প্রকাশনা থেকে পোস্ট সরান", - "predicted_github_trending_change": "পূর্বাভাসিত GitHub ট্রেন্ডিং পরিবর্তন", - "duplicate_post": "পোস্ট ডুপ্লিকেট করুন", - "preview_post": "পোস্ট প্রিভিউ", - "post_statistics": "পোস্ট পরিসংখ্যান", - "draft": "খসড়া", - "week_number": "সপ্তাহ নম্বর", - "top_title_edit_webhook": "ওয়েবহুক সম্পাদনা", - "top_title_add_webhook": "ওয়েবহুক যোগ করুন", - "top_title_oh_no": "ওহ না", - "top_title_auto_plug": "অটো প্লাগ", - "top_title_edit_autopost": "অটোপোস্ট সম্পাদনা", - "top_title_add_autopost": "অটোপোস্ট যোগ করুন", - "top_title_send_a_new_offer": "একটি নতুন অফার পাঠান", - "top_title_media_library": "মিডিয়া লাইব্রেরি", - "top_title_add_signature": "স্বাক্ষর যোগ করুন", - "top_title_send_a_message_to": "একটি বার্তা পাঠান", - "top_title_configure_provider": "প্রদানকারী কনফিগার করুন", - "top_title_add_member": "সদস্য যোগ করুন", - "top_title_change_bot_picture": "বট ছবি পরিবর্তন করুন", - "top_title_create_a_new_tag": "একটি নতুন ট্যাগ তৈরি করুন", - "top_title_select_company": "কোম্পানি নির্বাচন করুন", - "top_title_additional_settings": "অতিরিক্ত সেটিংস", - "top_title_time_table_slots": "সময়সূচী স্লট", - "top_title_design_media": "মিডিয়া ডিজাইন", - "top_title_edit_post": "পোস্ট সম্পাদনা", - "top_title_create_post": "পোস্ট তৈরি করুন", - "top_title_move__add_to_customer": "গ্রাহকের কাছে স্থানান্তর/যোগ করুন", - "top_title_add_api_key_for": "এর জন্য API কী যোগ করুন", - "top_title_instance_url": "ইনস্ট্যান্স URL", - "top_title_custom_url": "কাস্টম URL", - "top_title_add_channel": "চ্যানেল যোগ করুন", - "top_title_add_telegram": "টেলিগ্রাম যোগ করুন", - "top_title_add_wrapcast": "Wrapcast যোগ করুন", - "top_title_comments_for": "{{date}}-এর জন্য মন্তব্যসমূহ", - "top_title_edit_signature": "স্বাক্ষর সম্পাদনা করুন", - "label_name": "নাম", - "label_url": "URL", - "label_title": "শিরোনাম", - "label_subtitle": "উপশিরোনাম", - "label_email": "ইমেইল", - "label_full_name": "পূর্ণ নাম", - "label_password": "পাসওয়ার্ড", - "label_confirm_password": "পাসওয়ার্ড নিশ্চিত করুন", - "label_api_key": "API কী", - "label_instance_url": "ইনস্ট্যান্স URL", - "label_custom_url": "কাস্টম URL", - "label_feedback": "প্রতিক্রিয়া", - "label_bio": "জীবনী", - "label_role": "ভূমিকা", - "label_country": "দেশ", - "label_audience_size": "দর্শক সংখ্যা", - "label_pick_time": "সময় নির্বাচন করুন", - "label_nickname": "ডাকনাম", - "label_write_anything": "যেকোনো কিছু লিখুন", - "label_output_format": "আউটপুট ফরম্যাট", - "label_add_pictures": "ছবি যোগ করুন", - "label_hour": "ঘন্টা", - "label_minutes": "মিনিট", - "label_select_publication": "প্রকাশনা নির্বাচন করুন", - "label_canonical_link": "ক্যানোনিক্যাল লিংক", - "label_cover_picture": "কভার ছবি", - "label_tags": "ট্যাগ", - "label_topics": "বিষয়", - "label_tags_maximum_4": "ট্যাগ (সর্বোচ্চ ৪টি)", - "label_attachments": "সংযুক্তি", - "label_type": "ধরন", - "label_thumbnail": "থাম্বনেইল", - "label_who_can_see_this_video": "কে এই ভিডিও দেখতে পারবে", - "label_content_posting_method": "কন্টেন্ট পোস্টিং পদ্ধতি", - "label_auto_add_music": "স্বয়ংক্রিয় সঙ্গীত যোগ", - "label_duet": "ডুয়েট", - "label_stitch": "স্টিচ", - "label_comments": "মন্তব্য", - "label_disclose_video_content": "ভিডিও কন্টেন্ট প্রকাশ করুন", - "label_your_brand": "আপনার ব্র্যান্ড", - "label_branded_content": "ব্র্যান্ডেড কন্টেন্ট", - "label_subreddit": "সাবরেডিট", - "label_flair": "ফ্লেয়ার", - "label_media": "মিডিয়া", - "label_search_subreddit": "সাবরেডিট অনুসন্ধান", - "label_delay": "বিলম্ব", - "label_post_type": "পোস্টের ধরন", - "label_collaborators": "সহযোগী", - "label_community": "কমিউনিটি", - "label_search_community": "কমিউনিটি অনুসন্ধান", - "label_channel": "চ্যানেল", - "label_search_channel": "চ্যানেল অনুসন্ধান", - "label_select_channel": "চ্যানেল নির্বাচন করুন", - "label_new_password": "নতুন পাসওয়ার্ড", - "label_repeat_password": "পাসওয়ার্ড পুনরায় দিন", - "label_platform": "প্ল্যাটফর্ম", - "label_price_per_post": "প্রতি পোস্টের মূল্য", - "label_integrations": "ইন্টিগ্রেশন", - "label_code": "কোড", - "label_should_sync_last_post": "শেষ পোস্ট সিঙ্ক করা উচিত", - "label_when_post": "পোস্ট সময়", - "label_autogenerate_content": "কন্টেন্ট স্বয়ংক্রিয় তৈরি করা", - "label_generate_picture": "ছবি তৈরি করা", - "label_company": "কমপানি", - "label_tag_color": "ট্যাগ কলর", - "label_select_board": "বোর্ড নির্বাচন করুন", - "label_select_organization": "সংস্থান নির্বাচন করুন", - "label_auto_add_signature": "স্বাক্ষর স্বয়ংক্রিয় তৈরি করা", - "enable_color_picker": "কলর পিকার সক্রিয় করা", - "cancel_the_color_picker": "কলর পিকার বাতিল করা", - "no_content_yet": "কন্টেন্ট নেই", - "write_your_reply": "আপনার পোস্ট লিখুন...", - "add_a_tag": "ট্যাগ যোগ করুন", - "add_to_calendar": "ক্যালেন্ডারে যোগ করুন", - "select_channels_from_circles": "বলার মাধ্যমে চ্যানেল নির্বাচন করুন", - "not_matching_order": "অর্ডার মিলনায়ন না", - "submit_for_order": "অর্ডারে জমা দিন", - "schedule": "সময়সূচী করা", - "update": "আপডেট করা", - "attachments": "সংযুক্তি", - "tags": "ট্যাগ", - "public_to_everyone": "সকলের জন্য পাবলিক", - "mutual_follow_friends": "সহযোগী ফ্রেন্ডস", - "follower_of_creator": "প্রকাশকের অন্তর্গত", - "self_only": "নিজের জন্য কেবল", - "post_content_directly_to_tiktok": "টিকটকে কন্টেন্ট পোস্ট করা", - "upload_content_to_tiktok_without_posting": "পোস্ট করার প্রয়োজন ছাড়া টিকটকে কন্টেন্ট আপলোড করা", - "choose_upload_without_posting_description": "পোস্ট করার প্রয়োজন ছাড়া টিকটকে কন্টেন্ট আপলোড করার বর্ণনা", - "faq_am_i_going_to_be_charged_by_postiz": "পোস্টিজ দ্বারা চার্জ করা হবে কি?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "পোস্টিজ ক্রেডিট কার্ড তথ্য নিশ্চিত করার জন্য কি?", - "faq_can_i_trust_postiz_gitroom": "আমি কি Postiz-এ বিশ্বাস করতে পারি?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz গর্বের সাথে ওপেন-সোর্স! আমরা নৈতিক ও স্বচ্ছ সংস্কৃতিতে বিশ্বাস করি, যার মানে Postiz চিরকাল টিকে থাকবে। আপনি চাইলে সম্পূর্ণ কোড দেখতে পারেন বা ব্যক্তিগত প্রকল্পে ব্যবহার করতে পারেন। ওপেন-সোর্স রিপোজিটরি দেখতে, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">এখানে ক্লিক করুন</a>।", - "faq_what_are_channels": "চ্যানেল কী?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz আপনাকে বিভিন্ন চ্যানেলের মধ্যে আপনার পোস্টগুলো সময়সূচি নির্ধারণ করতে দেয়।\nএকটি চ্যানেল হলো এমন একটি প্রকাশনা প্ল্যাটফর্ম যেখানে আপনি আপনার পোস্টগুলো সময়সূচি অনুযায়ী প্রকাশ করতে পারেন।\nউদাহরণস্বরূপ, আপনি X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads এবং Pinterest-এ আপনার পোস্টগুলো সময়সূচি অনুযায়ী প্রকাশ করতে পারেন।", - "faq_what_are_team_members": "টিম সদস্য কী?", - "faq_if_you_have_a_team_with_multiple_members": "যদি আপনার একাধিক সদস্যের একটি টিম থাকে", - "faq_what_is_ai_auto_complete": "AI অটো কমপ্লিট কী?", - "faq_we_automate_chatgpt_to_help_you_write": "আমরা আপনাকে লিখতে সাহায্য করার জন্য ChatGPT স্বয়ংক্রিয় করি", - "enter_email": "ইমেইল প্রবেশ করান", - "are_you_sure": "আপনি কি নিশ্চিত?", - "yes_delete_it": "হ্যাঁ, মুছে ফেলুন", - "no_cancel": "না, বাতিল করুন", - "are_you_sure_you_want_to_delete": "আপনি কি নিশ্চিত যে আপনি মুছে ফেলতে চান?", - "are_you_sure_you_want_to_delete_the_image": "আপনি কি নিশ্চিত যে আপনি ছবিটি মুছে ফেলতে চান?", - "are_you_sure_you_want_to_logout": "আপনি কি নিশ্চিত যে আপনি লগআউট করতে চান?", - "yes_logout": "হ্যাঁ, লগআউট করুন", - "are_you_sure_you_want_to_delete_this_slot": "আপনি কি নিশ্চিত যে আপনি এই স্লটটি মুছে ফেলতে চান?", - "are_you_sure_you_want_to_delete_this_subreddit": "আপনি কি নিশ্চিত যে আপনি এই সাবরেডিটটি মুছে ফেলতে চান?", - "are_you_sure_you_want_to_close_the_window": "আপনি কি নিশ্চিত যে আপনি উইন্ডোটি বন্ধ করতে চান?", - "yes_close": "হ্যাঁ, বন্ধ করুন", - "link_copied_to_clipboard": "লিংক ক্লিপবোর্ডে কপি করা হয়েছে", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "আপনি কি নিশ্চিত যে আপনি এই মোডালটি বন্ধ করতে চান? সমস্ত ডেটা হারিয়ে যাবে", - "yes_close_it": "হ্যাঁ, বন্ধ করুন", - "uploading_pictures": "ছবি আপলোড করা হচ্ছে", - "agent_starting": "এজেন্ট শুরু হচ্ছে", - "researching_your_content": "আপনার কন্টেন্ট গবেষণা করা হচ্ছে", - "understanding_the_category": "বিভাগ বোঝা হচ্ছে", - "finding_the_topic": "বিষয় খোঁজা হচ্ছে", - "finding_popular_posts_to_match_with": "মিলানোর জন্য জনপ্রিয় পোস্ট খোঁজা হচ্ছে", - "generating_hook": "হুক তৈরি করা হচ্ছে", - "generating_content": "কন্টেন্ট তৈরি করা হচ্ছে", - "generating_pictures": "ছবি তৈরি করা হচ্ছে", - "finding_time_to_post": "পোস্ট করার সময় খোঁজা হচ্ছে", - "write_anything": "যেকোনো কিছু লিখুন", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "আপনি যা চান তা লিখতে পারেন এবং লিংকও যোগ করতে পারেন, আমরা আপনার জন্য গবেষণা করব", - "output_format": "আউটপুট ফরম্যাট", - "add_pictures": "ছবি যোগ করুন", - "7_days": "৭ দিন", - "30_days": "৩০ দিন", - "90_days": "৯০ দিন", - "start_7_days_free_trial": "৭ দিনের বিনামূল্যে ট্রায়াল শুরু করুন", - "change_language": "ভাষা পরিবর্তন করুন", - "that_a_wrap": "এটাই শেষ!\n\nযদি আপনি এই থ্রেডটি উপভোগ করে থাকেন:\n\n১. আরও এমন পোস্টের জন্য আমাকে @{{username}} ফলো করুন\n২. আপনার অডিয়েন্সের সাথে এই থ্রেডটি শেয়ার করতে নিচের টুইটটি রিটুইট করুন\n", - "post_as_images_carousel": "ছবির ক্যারোসেল হিসেবে পোস্ট করুন", - "save_set": "সেট সংরক্ষণ করুন", - "separate_post": "একটি পোস্টকে একাধিক পোস্টে ভাগ করুন", - "label_who_can_reply_to_this_post": "এই পোস্টে কে উত্তর দিতে পারবে?", - "delete_integration": "ইন্টিগ্রেশন মুছে ফেলুন", - "start_writing_your_post": "প্রিভিউর জন্য আপনার পোস্ট লেখা শুরু করুন" + "calendar": "ক্যালেন্ডার", + "webhooks": "ওয়েবহুক", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "ওয়েবহুক হল একটি HTTP অনুরোধের মাধ্যমে Postiz-এ কিছু ঘটলে বিজ্ঞপ্তি পাওয়ার একটি উপায়।", + "name": "নাম", + "url": "URL", + "edit": "সম্পাদনা", + "delete": "মুছে ফেলুন", + "add_a_webhook": "একটি ওয়েবহুক যোগ করুন", + "save": "সংরক্ষণ করুন", + "send_test": "পরীক্ষা পাঠান", + "select_role": "ভূমিকা নির্বাচন করুন", + "video_made_with_ai": "এআই দিয়ে তৈরি ভিডিও", + "please_add_at_least": "কমপক্ষে ২০ অক্ষর যোগ করুন", + "send_invitation_via_email": "ইমেইলের মাধ্যমে আমন্ত্রণ পাঠান?", + "global_settings": "গ্লোবাল সেটিংস", + "copy_id": "চ্যানেল আইডি কপি করুন", + "team_members": "দলের সদস্যরা", + "invite_your_assistant_or_team_member_to_manage_your_account": "আপনার অ্যাকাউন্ট পরিচালনা করতে আপনার সহায়ক বা দলের সদস্যকে আমন্ত্রণ জানান", + "remove": "সরান", + "add_another_member": "আরেকটি সদস্য যোগ করুন", + "signatures": "স্বাক্ষর", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "আপনি আপনার পোস্টে ব্যবহারের জন্য আপনার অ্যাকাউন্টে স্বাক্ষর যোগ করতে পারেন।", + "content": "বিষয়বস্তু", + "auto_add": "স্বয়ংক্রিয় যোগ?", + "actions": "কার্যক্রম", + "use_signature": "স্বাক্ষর ব্যবহার করুন", + "add_a_signature": "একটি স্বাক্ষর যোগ করুন", + "no": "না", + "yes": "হ্যাঁ", + "your_git_repository": "আপনার Git রিপোজিটরি", + "connect_your_github_repository_to_receive_updates_and_analytics": "আপডেট এবং বিশ্লেষণ পেতে আপনার GitHub রিপোজিটরি সংযুক্ত করুন", + "connected": "সংযুক্ত:", + "disconnect": "সংযোগ বিচ্ছিন্ন করুন", + "connect_your_repository": "আপনার রিপোজিটরি সংযুক্ত করুন", + "cancel": "বাতিল", + "connect": "সংযুক্ত করুন", + "public_api": "পাবলিক API", + "check_n8n": "Postiz-এর জন্য আমাদের N8N কাস্টম নোডটি দেখুন।", + "use_postiz_api_to_integrate_with_your_tools": "আপনার টুলগুলির সাথে একীভূত করতে Postiz API ব্যবহার করুন।", + "read_how_to_use_it_over_the_documentation": "ডকুমেন্টেশনে এটি কীভাবে ব্যবহার করবেন তা পড়ুন।", + "reveal": "প্রকাশ করুন", + "copy_key": "কী কপি করুন", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "আপনার ক্লায়েন্টকে (Http স্ট্রিমিং) Postiz MCP সার্ভারের সাথে সংযুক্ত করুন, যাতে আরও দ্রুত আপনার পোস্টগুলো নির্ধারণ করতে পারেন!", + "share_with_a_client": "একটি ক্লায়েন্টের সাথে শেয়ার করুন", + "post": "পোস্ট", + "comments": "মন্তব্য", + "user": "ব্যবহারকারী", + "login_register_to_add_comments": "মন্তব্য যোগ করতে লগইন / নিবন্ধন করুন", + "status": "অবস্থা:", + "there_are_not_plugs_matching_your_channels": "আপনার চ্যানেলের সাথে মিলে এমন কোনো প্লাগ নেই", + "you_have_to_add_x_or_linkedin_or_threads": "আপনাকে যোগ করতে হবে: X বা LinkedIn বা Threads", + "go_to_the_calendar_to_add_channels": "চ্যানেল যোগ করতে ক্যালেন্ডারে যান", + "channels": "চ্যানেল", + "activate": "সক্রিয় করুন", + "this_channel_needs_to_be_refreshed": "এই চ্যানেলটি রিফ্রেশ করা প্রয়োজন,", + "click_here_to_refresh": "রিফ্রেশ করতে এখানে ক্লিক করুন", + "can_t_show_analytics_yet": "এখনও বিশ্লেষণ দেখাতে পারছি না", + "you_have_to_add_social_media_channels": "আপনাকে সোশ্যাল মিডিয়া চ্যানেল যোগ করতে হবে", + "supported": "সমর্থিত:", + "step": "ধাপ", + "skip_onboarding": "অনবোর্ডিং এড়িয়ে যান", + "onboarding": "অনবোর্ডিং", + "next": "পরবর্তী", + "you_are_done_from_here_you_can": "আপনি সম্পন্ন করেছেন, এখান থেকে আপনি পারেন:", + "view_analytics": "বিশ্লেষণ দেখুন", + "schedule_a_new_post": "একটি নতুন পোস্ট সময়সূচী করুন", + "to_sell_posts_you_would_have_to": "পোস্ট বিক্রি করতে আপনাকে করতে হবে:", + "1_connect_at_least_one_channel": "১. কমপক্ষে একটি চ্যানেল সংযুক্ত করুন", + "2_connect_you_bank_account": "২. আপনার ব্যাংক অ্যাকাউন্ট সংযুক্ত করুন", + "go_back_to_connect_channels": "চ্যানেল সংযুক্ত করতে ফিরে যান", + "move_to_the_seller_page_to_connect_you_bank": "আপনার ব্যাংক সংযুক্ত করতে বিক্রেতা পৃষ্ঠায় যান", + "connect_channels": "চ্যানেল সংযুক্ত করুন", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "পরে পোস্ট সময়সূচী করতে আপনার সোশ্যাল মিডিয়া এবং প্রকাশনা ওয়েবসাইট চ্যানেল সংযুক্ত করুন", + "social": "সামাজিক", + "publishing_platforms": "প্রকাশনা প্ল্যাটফর্ম", + "no_channels": "এখনও কোনো চ্যানেল নেই", + "connect_your_accounts": "সময়সূচি নির্ধারণ, প্রকাশ এবং বিশ্লেষণ শুরু করতে আপনার সোশ্যাল অ্যাকাউন্টগুলো সংযুক্ত করুন — সবকিছু এক জায়গায়।", + "notifications": "বিজ্ঞপ্তি", + "no_notifications": "কোনো বিজ্ঞপ্তি নেই", + "send_message": "বার্তা পাঠান", + "mar_28": "মার্চ ২৮", + "there_are_no_messages_yet": "এখনও কোনো বার্তা নেই।", + "checkout_the_marketplace": "মার্কেটপ্লেস দেখুন", + "go_to_marketplace": "মার্কেটপ্লেসে যান", + "all_messages": "সব বার্তা", + "previous": "পূর্ববর্তী", + "select_or_upload_pictures_maximum_5_at_a_time": "ছবি নির্বাচন বা আপলোড করুন (একসাথে সর্বোচ্চ ৫টি)", + "you_can_also_drag_drop_pictures": "আপনি ছবি টেনে এনে ছাড়তেও পারেন", + "you_don_t_have_any_assets_yet": "আপনার এখনও কোনো সম্পদ নেই।", + "click_the_button_below_to_upload_one": "একটি আপলোড করতে নিচের বোতামে ক্লিক করুন", + "click_the_button_below_to_upload_other": "একাধিক আপলোড করার জন্য নীচের বাটন ক্লিক করুন", + "add_selected_media": "নির্বাচিত মিডিয়া যোগ করুন", + "insert_media": "মিডিয়া সন্নিবেশ করুন", + "design_media": "মিডিয়া ডিজাইন করুন", + "select": "নির্বাচন করুন", + "editor": "সম্পাদক", + "clear": "পরিষ্কার করুন", + "order_completed": "অর্ডার সম্পন্ন", + "the_order_has_been_completed": "অর্ডারটি সম্পন্ন হয়েছে", + "post_has_been_published": "পোস্ট প্রকাশিত হয়েছে", + "url_1": "URL:", + "new_offer": "নতুন অফার", + "platform": "প্ল্যাটফর্ম", + "posts": "পোস্ট", + "pay_accept_offer": "পেমেন্ট করুন এবং অফার গ্রহণ করুন", + "accepted": "গৃহীত", + "post_draft": "পোস্ট খসড়া", + "revision_needed": "সংশোধন প্রয়োজন", + "approve": "অনুমোদন করুন", + "preview": "পূর্বরূপ", + "revision_requested": "সংশোধনের অনুরোধ", + "accepted_1": "গৃহীত", + "cancelled_by_the_seller": "বিক্রেতা দ্বারা বাতিল", + "please_select_your_country_where_your_business_is": "অনুগ্রহ করে আপনার দেশ নির্বাচন করুন যেখানে আপনার ব্যবসা রয়েছে।", + "select_country": "--দেশ নির্বাচন করুন--", + "connect_bank_account": "ব্যাংক অ্যাকাউন্ট সংযুক্ত করুন", + "seller_mode": "বিক্রেতা মোড", + "active": "সক্রিয়", + "details": "বিস্তারিত", + "audience_size": "দর্শক সংখ্যা", + "add_another_platform": "আরেকটি প্ল্যাটফর্ম যোগ করুন", + "send_an_offer_for": "এর জন্য একটি অফার পাঠান $", + "complete_order_and_pay_early": "অর্ডার সম্পন্ন করুন এবং আগাম পেমেন্ট করুন", + "order_in_progress": "অর্ডার চলমান", + "create_a_new_offer": "একটি নতুন অফার তৈরি করুন", + "orders": "অর্ডার", + "price": "মূল্য", + "state": "অবস্থা", + "showing": "দেখানো হচ্ছে", + "to": "থেকে", + "from": "পর্যন্ত", + "results": "ফলাফল", + "content_writer": "কন্টেন্ট লেখক", + "influencer": "প্রভাবশালী", + "request_service": "সেবার অনুরোধ", + "the_marketplace_is_not_opened_yet": "মার্কেটপ্লেস এখনও খোলা হয়নি", + "check_again_soon": "শীঘ্রই আবার চেক করুন!", + "filter": "ফিল্টার", + "result": "ফলাফল", + "seller": "বিক্রেতা", + "buyer": "ক্রেতা", + "discord_support": "Discord সাপোর্ট", + "teams": "দল", + "webhooks_1": "ওয়েবহুক", + "auto_post": "স্বয়ংক্রিয় পোস্ট", + "logout_from": "থেকে লগআউট", + "join_10000_entrepreneurs_who_use_postiz": "১০,০০০+ উদ্যোক্তার সঙ্গে Postiz ব্যবহার করুন", + "to_manage_all_your_social_media_channels": "আপনার সব সোশ্যাল মিডিয়া চ্যানেল পরিচালনা করতে", + "100_no_risk_trial": "১০০% ঝুঁকিমুক্ত ট্রায়াল", + "pay_nothing_for_the_first_7_days": "প্রথম ৭ দিনের জন্য কিছুই পেমেন্ট করুন না", + "cancel_anytime_hassle_free": "যেকোনো সময় ঝামেলামুক্তভাবে বাতিল করুন", + "add_free_subscription": "-- বিনামূল্যে সাবস্ক্রিপশন যোগ করুন --", + "currently_impersonating": "বর্তমানে ছদ্মবেশ ধারণ করছেন", + "user_1": "ব্যবহারকারী:", + "drag_n_drop_some_files_here": "এখানে কিছু ফাইল টেনে এনে ছাড়ুন", + "add_time_slot": "সময় স্লট যোগ করুন", + "add_slot": "স্লট যোগ করুন", + "cancel_publication": "প্রকাশনা বাতিল করুন", + "statistics": "পরিসংখ্যান", + "loading": "লোড হচ্ছে", + "short_link": "সংক্ষিপ্ত লিংক", + "original_link": "মূল লিংক", + "clicks": "ক্লিক", + "selected_customer": "নির্বাচিত গ্রাহক", + "customer": "গ্রাহক:", + "repeat_post_every": "প্রতি পোস্ট পুনরাবৃত্তি করুন...", + "use_this_media": "এই মিডিয়া ব্যবহার করুন", + "create_new_post": "নতুন পোস্ট তৈরি করুন", + "update_post": "বিদ্যমান পোস্ট আপডেট করুন", + "merge_comments_into_one_post": "মন্তব্যগুলি একটি পোস্টে একত্রিত করুন", + "accounts_that_will_engage": "যে অ্যাকাউন্টগুলি এনগেজ করবে:", + "day": "দিন", + "week": "সপ্তাহ", + "month": "মাস", + "remove_from_customer": "গ্রাহক থেকে সরান", + "show_more": "+ আরো দেখুন", + "show_less": "- কম দেখুন", + "upload": "আপলোড", + "ai": "AI", + "add_channel": "চ্যানেল যোগ করুন", + "add_platform": "প্ল্যাটফর্ম যোগ করুন", + "articles": "নিবন্ধ", + "add_comment": "মন্তব্য যোগ করুন", + "add_post": "থ্রেডে পোস্ট যোগ করুন", + "add_comment_or_post": "মন্তব্য / পোস্ট যোগ করুন", + "you_are_in_global_editing_mode": "আপনি গ্লোবাল এডিটিং মোডে আছেন", + "the_post_should_be_at_least_6_characters_long": "পোস্টটি কমপক্ষে ৬ অক্ষরের হতে হবে", + "are_you_sure_you_want_to_delete_post": "আপনি কি নিশ্চিত যে আপনি এই পোস্টটি মুছে ফেলতে চান?", + "post_deleted_successfully": "পোস্ট সফলভাবে মুছে ফেলা হয়েছে", + "delete_post": "পোস্ট মুছে ফেলুন", + "save_as_draft": "খসড়া হিসেবে সংরক্ষণ করুন", + "post_now": "এখনই পোস্ট করুন", + "please_add": "অনুগ্রহ করে যোগ করুন", + "to_your_telegram_group_channel_and_click_here": "আপনার টেলিগ্রাম গ্রুপ / চ্যানেলে এবং এখানে ক্লিক করুন:", + "connect_telegram": "টেলিগ্রাম সংযুক্ত করুন", + "please_add_the_following_command_in_your_chat": "অনুগ্রহ করে আপনার চ্যাটে নিম্নলিখিত কমান্ড যোগ করুন:", + "copy": "কপি করুন", + "settings": "সেটিংস", + "integrations": "ইন্টিগ্রেশনসমূহ", + "add_integration": "ইন্টিগ্রেশন যোগ করুন", + "you_are_now_editing_only": "আপনি এখন শুধুমাত্র সম্পাদনা করছেন", + "tag_a_company": "একটি কোম্পানি ট্যাগ করুন", + "video_length_is_invalid_must_be_up_to": "ভিডিওর দৈর্ঘ্য অবৈধ, সর্বোচ্চ হতে হবে", + "seconds": "সেকেন্ড", + "this_feature_available_only_for_photos": "এই বৈশিষ্ট্যটি শুধুমাত্র ছবির জন্য উপলব্ধ, এটি একটি ডিফল্ট সঙ্গীত যোগ করবে যা আপনি পরে পরিবর্তন করতে পারেন।", + "allow_user_to": "ব্যবহারকারীকে অনুমতি দিন:", + "your_video_will_be_labeled_promotional": "আপনার ভিডিও \"প্রচারমূলক বিষয়বস্তু\" হিসেবে লেবেল করা হবে।", + "this_cannot_be_changed_once_posted": "একবার পোস্ট করার পর এটি পরিবর্তন করা যাবে না।", + "turn_on_to_disclose_video_promotes": "এই ভিডিও যে মূল্যবান কিছুর বিনিময়ে পণ্য বা সেবার প্রচার করে তা প্রকাশ করতে চালু করুন। আপনার ভিডিও নিজেকে, তৃতীয় পক্ষ বা উভয়ের প্রচার করতে পারে।", + "you_are_promoting_yourself": "আপনি নিজেকে বা আপনার নিজস্ব ব্র্যান্ডের প্রচার করছেন।", + "this_video_will_be_classified_brand_organic": "এই ভিডিওটি ব্র্যান্ড অর্গানিক হিসেবে শ্রেণীবদ্ধ হবে।", + "you_are_promoting_another_brand": "আপনি অন্য একটি ব্র্যান্ড বা তৃতীয় পক্ষের প্রচার করছেন।", + "this_video_will_be_classified_branded_content": "এই ভিডিওটি ব্র্যান্ডেড কন্টেন্ট হিসেবে শ্রেণীবদ্ধ হবে।", + "by_posting_you_agree_to_tiktoks": "পোস্ট করার মাধ্যমে, আপনি TikTok-এর সাথে সম্মত হচ্ছেন", + "music_usage_confirmation": "সঙ্গীত ব্যবহারের নিশ্চিতকরণ", + "branded_content_policy": "ব্র্যান্ডেড কন্টেন্ট নীতি", + "select_1": "--নির্বাচন করুন--", + "select_flair": "--ফ্লেয়ার নির্বাচন করুন--", + "link": "লিংক", + "add_subreddit": "সাবরেডিট যোগ করুন", + "please_add_at_least_one_subreddit": "অনুগ্রহ করে কমপক্ষে একটি সাবরেডিট যোগ করুন", + "add_community": "কমিউনিটি যোগ করুন", + "select_post_type": "পোস্টের ধরন নির্বাচন করুন...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "আমরা আপনার LinkedIn পৃষ্ঠার সাথে সংযুক্ত কোনো ব্যবসা খুঁজে পাইনি।", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "অনুগ্রহ করে এই ডায়ালগটি বন্ধ করুন, একটি নতুন পৃষ্ঠা তৈরি করুন এবং আবার একটি নতুন চ্যানেল যোগ করুন।", + "select_linkedin_page": "LinkedIn পৃষ্ঠা নির্বাচন করুন:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "আমরা নির্বাচিত পৃষ্ঠাগুলির সাথে সংযুক্ত কোনো ব্যবসা খুঁজে পাইনি।", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "আমরা আপনাকে সব পৃষ্ঠা এবং সব ব্যবসা সংযুক্ত করার পরামর্শ দিই।", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "অনুগ্রহ করে এই ডায়ালগটি বন্ধ করুন, আপনার ইন্টিগ্রেশন মুছে ফেলুন এবং আবার একটি নতুন চ্যানেল যোগ করুন।", + "select_instagram_account": "Instagram অ্যাকাউন্ট নির্বাচন করুন:", + "select_page": "পৃষ্ঠা নির্বাচন করুন:", + "generate_image_with_ai": "AI দিয়ে ছবি তৈরি করুন", + "reconnect_channel": "চ্যানেল পুনরায় সংযুক্ত করুন", + "update_credentials": "শংসাপত্র আপডেট করুন", + "additional_settings": "অতিরিক্ত সেটিংস", + "change_bot": "বট পরিবর্তন করুন", + "move_add_to_customer": "গ্রাহকের কাছে স্থানান্তর / যোগ করুন", + "edit_time_slots": "সময় স্লট সম্পাদনা করুন", + "enable_channel": "চ্যানেল সক্রিয় করুন", + "disable_channel": "চ্যানেল নিষ্ক্রিয় করুন", + "add": "যোগ করুন", + "short_post": "সংক্ষিপ্ত পোস্ট", + "long_post": "দীর্ঘ পোস্ট", + "a_thread_with_short_posts": "সংক্ষিপ্ত পোস্টের একটি থ্রেড", + "a_thread_with_long_posts": "দীর্ঘ পোস্টের একটি থ্রেড", + "personal_voice_i_am_happy_to_announce": "ব্যক্তিগত কণ্ঠস্বর (\"আমি ঘোষণা করতে খুশি\")", + "company_voice_we_are_happy_to_announce": "কোম্পানির কণ্ঠস্বর (\"আমরা ঘোষণা করতে খুশি\")", + "generate": "তৈরি করুন", + "generate_posts": "পোস্ট তৈরি করুন", + "purchase_a_life_time_pro_account_with_sol_199": "SOL ($199) দিয়ে একটি লাইফ-টাইম PRO অ্যাকাউন্ট কিনুন, অনুগ্রহ করে মনে রাখবেন এই ক্রয়ের জন্য কোনো রিফান্ড নেই।", + "purchase_now": "এখনই কিনুন", + "pay_today": "আজই পেমেন্ট করুন", + "we_are_sorry_to_see_you_go": "আপনাকে যেতে দেখে আমরা দুঃখিত :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "আমরা কী আরও ভাল করতে পারতাম তা সংক্ষেপে বলতে কি আপনার আপত্তি আছে?", + "cancel_subscription": "সাবস্ক্রিপশন বাতিল করুন", + "plans": "পরিকল্পনা", + "monthly": "মাসিক", + "yearly": "বার্ষিক", + "reactivate_subscription": "সাবস্ক্রিপশন পুনরায় সক্রিয় করুন", + "update_payment_method_invoices_history": "পেমেন্ট পদ্ধতি / ইনভয়েস ইতিহাস আপডেট করুন", + "cancel_subscription_1": "সাবস্ক্রিপশন বাতিল করুন", + "your_subscription_will_be_canceled_at": "আপনার সাবস্ক্রিপশন বাতিল হবে", + "you_will_never_be_charged_again": "আপনার কাছ থেকে আর কখনো চার্জ নেওয়া হবে না", + "current_package": "বর্তমান প্যাকেজ:", + "next_package": "পরবর্তী প্যাকেজ:", + "claim": "দাবি করুন", + "frequently_asked_questions": "প্রায়শই জিজ্ঞাসিত প্রশ্ন", + "autopost": "অটোপোস্ট", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "অটোপোস্ট স্বয়ংক্রিয়ভাবে আপনার RSS নতুন আইটেমগুলি সোশ্যাল মিডিয়ায় পোস্ট করতে পারে", + "title": "শিরোনাম", + "add_an_autopost": "একটি অটোপোস্ট যোগ করুন", + "post_content": "পোস্ট বিষয়বস্তু", + "sign_up": "সাইন আপ", + "or": "অথবা", + "by_registering_you_agree_to_our": "নিবন্ধন করার মাধ্যমে আপনি আমাদের সাথে সম্মত হচ্ছেন", + "and": "এবং", + "terms_of_service": "সেবার শর্তাবলী", + "privacy_policy": "গোপনীয়তা নীতি", + "create_account": "অ্যাকাউন্ট তৈরি করুন", + "already_have_an_account": "ইতিমধ্যে একটি অ্যাকাউন্ট আছে?", + "sign_in": "সাইন ইন", + "sign_in_1": "সাইন ইন", + "don_t_have_an_account": "কোনো অ্যাকাউন্ট নেই?", + "forgot_password": "পাসওয়ার্ড ভুলে গেছেন", + "forgot_password_1": "পাসওয়ার্ড ভুলে গেছেন", + "send_password_reset_email": "পাসওয়ার্ড রিসেট ইমেইল পাঠান", + "go_back_to_login": "লগইনে ফিরে যান", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "আমরা আপনার পাসওয়ার্ড রিসেট করার জন্য একটি লিংক সহ একটি ইমেইল পাঠিয়েছি।", + "change_password": "পাসওয়ার্ড পরিবর্তন করুন", + "we_successfully_reset_your_password_you_can_now_login_with_your": "আমরা সফলভাবে আপনার পাসওয়ার্ড রিসেট করেছি। আপনি এখন আপনার সাথে লগইন করতে পারেন", + "click_here_to_go_back_to_login": "লগইনে ফিরে যেতে এখানে ক্লিক করুন", + "activate_your_account": "আপনার অ্যাকাউন্ট সক্রিয় করুন", + "thank_you_for_registering": "নিবন্ধনের জন্য ধন্যবাদ!", + "please_check_your_email_to_activate_your_account": "আপনার অ্যাকাউন্ট সক্রিয় করতে অনুগ্রহ করে আপনার ইমেইল চেক করুন।", + "sign_in_with": "এর সাথে সাইন ইন করুন", + "continue_with_google": "Google এর সাথে চালিয়ে যান", + "sign_in_with_github": "GitHub এর সাথে সাইন ইন করুন", + "continue_with_farcaster": "Farcaster দিয়ে চালিয়ে যান", + "continue_with_your_wallet": "আপনার ওয়ালেট দিয়ে চালিয়ে যান", + "stars_per_day": "প্রতিদিন স্টার", + "media": "মিডিয়া", + "check_launch": "লঞ্চ চেক করুন", + "load_your_github_repository_from_settings_to_see_analytics": "বিশ্লেষণ দেখতে সেটিংস থেকে আপনার GitHub রিপোজিটরি লোড করুন", + "stars": "স্টার", + "processing_stars": "স্টার প্রক্রিয়াকরণ", + "forks": "ফর্ক", + "registration_is_disabled": "নিবন্ধন নিষ্ক্রিয়", + "login_instead": "পরিবর্তে লগইন করুন", + "gitroom": "গিটরুম", + "select_a_conversation_and_chat_away": "একটি কথোপকথন নির্বাচন করুন এবং চ্যাট করুন", + "adding_channel_redirecting_you": "চ্যানেল যোগ করা হচ্ছে, আপনাকে রিডাইরেক্ট করা হচ্ছে", + "could_not_add_provider": "প্রদানকারী যোগ করা যায়নি", + "you_are_being_redirected_back": "আপনাকে ফিরিয়ে নিয়ে যাওয়া হচ্ছে", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "আমরা কিছু সমস্যার সম্মুখীন হচ্ছি, পৃষ্ঠা রিফ্রেশ করার চেষ্টা করুন", + "post_not_found": "পোস্ট পাওয়া যায়নি", + "publication_date": "প্রকাশনার তারিখ", + "analytics": "বিশ্লেষণ", + "launches": "লঞ্চ", + "plugs": "প্লাগ", + "billing": "বিলিং", + "affiliate": "অ্যাফিলিয়েট", + "monday": "সোমবার", + "tuesday": "মঙ্গলবার", + "wednesday": "বুধবার", + "thursday": "বৃহস্পতিবার", + "friday": "শুক্রবার", + "saturday": "শনিবার", + "sunday": "রবিবার", + "can_t_change_date_remove_post_from_publication": "তারিখ পরিবর্তন করা যাবে না, প্রকাশনা থেকে পোস্ট সরান", + "predicted_github_trending_change": "পূর্বাভাসিত GitHub ট্রেন্ডিং পরিবর্তন", + "duplicate_post": "পোস্ট ডুপ্লিকেট করুন", + "preview_post": "পোস্ট প্রিভিউ", + "post_statistics": "পোস্ট পরিসংখ্যান", + "draft": "খসড়া", + "week_number": "সপ্তাহ নম্বর", + "top_title_edit_webhook": "ওয়েবহুক সম্পাদনা", + "top_title_add_webhook": "ওয়েবহুক যোগ করুন", + "top_title_oh_no": "ওহ না", + "top_title_auto_plug": "অটো প্লাগ", + "top_title_edit_autopost": "অটোপোস্ট সম্পাদনা", + "top_title_add_autopost": "অটোপোস্ট যোগ করুন", + "top_title_send_a_new_offer": "একটি নতুন অফার পাঠান", + "top_title_media_library": "মিডিয়া লাইব্রেরি", + "top_title_add_signature": "স্বাক্ষর যোগ করুন", + "top_title_send_a_message_to": "একটি বার্তা পাঠান", + "top_title_configure_provider": "প্রদানকারী কনফিগার করুন", + "top_title_add_member": "সদস্য যোগ করুন", + "top_title_change_bot_picture": "বট ছবি পরিবর্তন করুন", + "top_title_create_a_new_tag": "একটি নতুন ট্যাগ তৈরি করুন", + "top_title_select_company": "কোম্পানি নির্বাচন করুন", + "top_title_additional_settings": "অতিরিক্ত সেটিংস", + "top_title_time_table_slots": "সময়সূচী স্লট", + "top_title_design_media": "মিডিয়া ডিজাইন", + "top_title_edit_post": "পোস্ট সম্পাদনা", + "top_title_create_post": "পোস্ট তৈরি করুন", + "top_title_move__add_to_customer": "গ্রাহকের কাছে স্থানান্তর/যোগ করুন", + "top_title_add_api_key_for": "এর জন্য API কী যোগ করুন", + "top_title_instance_url": "ইনস্ট্যান্স URL", + "top_title_custom_url": "কাস্টম URL", + "top_title_add_channel": "চ্যানেল যোগ করুন", + "top_title_add_telegram": "টেলিগ্রাম যোগ করুন", + "top_title_add_wrapcast": "Wrapcast যোগ করুন", + "top_title_comments_for": "{{date}}-এর জন্য মন্তব্যসমূহ", + "top_title_edit_signature": "স্বাক্ষর সম্পাদনা করুন", + "label_name": "নাম", + "label_url": "URL", + "label_title": "শিরোনাম", + "label_subtitle": "উপশিরোনাম", + "label_email": "ইমেইল", + "label_full_name": "পূর্ণ নাম", + "label_password": "পাসওয়ার্ড", + "label_confirm_password": "পাসওয়ার্ড নিশ্চিত করুন", + "label_api_key": "API কী", + "label_instance_url": "ইনস্ট্যান্স URL", + "label_custom_url": "কাস্টম URL", + "label_feedback": "প্রতিক্রিয়া", + "label_bio": "জীবনী", + "label_role": "ভূমিকা", + "label_country": "দেশ", + "label_audience_size": "দর্শক সংখ্যা", + "label_pick_time": "সময় নির্বাচন করুন", + "label_nickname": "ডাকনাম", + "label_write_anything": "যেকোনো কিছু লিখুন", + "label_output_format": "আউটপুট ফরম্যাট", + "label_add_pictures": "ছবি যোগ করুন", + "label_hour": "ঘন্টা", + "label_minutes": "মিনিট", + "label_select_publication": "প্রকাশনা নির্বাচন করুন", + "label_canonical_link": "ক্যানোনিক্যাল লিংক", + "label_cover_picture": "কভার ছবি", + "label_tags": "ট্যাগ", + "label_topics": "বিষয়", + "label_tags_maximum_4": "ট্যাগ (সর্বোচ্চ ৪টি)", + "label_attachments": "সংযুক্তি", + "label_type": "ধরন", + "label_thumbnail": "থাম্বনেইল", + "label_who_can_see_this_video": "কে এই ভিডিও দেখতে পারবে", + "label_content_posting_method": "কন্টেন্ট পোস্টিং পদ্ধতি", + "label_auto_add_music": "স্বয়ংক্রিয় সঙ্গীত যোগ", + "label_duet": "ডুয়েট", + "label_stitch": "স্টিচ", + "label_comments": "মন্তব্য", + "label_disclose_video_content": "ভিডিও কন্টেন্ট প্রকাশ করুন", + "label_your_brand": "আপনার ব্র্যান্ড", + "label_branded_content": "ব্র্যান্ডেড কন্টেন্ট", + "label_subreddit": "সাবরেডিট", + "label_flair": "ফ্লেয়ার", + "label_media": "মিডিয়া", + "label_search_subreddit": "সাবরেডিট অনুসন্ধান", + "label_delay": "বিলম্ব", + "label_post_type": "পোস্টের ধরন", + "label_collaborators": "সহযোগী", + "label_community": "কমিউনিটি", + "label_search_community": "কমিউনিটি অনুসন্ধান", + "label_channel": "চ্যানেল", + "label_search_channel": "চ্যানেল অনুসন্ধান", + "label_select_channel": "চ্যানেল নির্বাচন করুন", + "label_new_password": "নতুন পাসওয়ার্ড", + "label_repeat_password": "পাসওয়ার্ড পুনরায় দিন", + "label_platform": "প্ল্যাটফর্ম", + "label_price_per_post": "প্রতি পোস্টের মূল্য", + "label_integrations": "ইন্টিগ্রেশন", + "label_code": "কোড", + "label_should_sync_last_post": "শেষ পোস্ট সিঙ্ক করা উচিত", + "label_when_post": "পোস্ট সময়", + "label_autogenerate_content": "কন্টেন্ট স্বয়ংক্রিয় তৈরি করা", + "label_generate_picture": "ছবি তৈরি করা", + "label_company": "কমপানি", + "label_tag_color": "ট্যাগ কলর", + "label_select_board": "বোর্ড নির্বাচন করুন", + "label_select_organization": "সংস্থান নির্বাচন করুন", + "label_auto_add_signature": "স্বাক্ষর স্বয়ংক্রিয় তৈরি করা", + "enable_color_picker": "কলর পিকার সক্রিয় করা", + "cancel_the_color_picker": "কলর পিকার বাতিল করা", + "no_content_yet": "কন্টেন্ট নেই", + "write_your_reply": "আপনার পোস্ট লিখুন...", + "add_a_tag": "ট্যাগ যোগ করুন", + "add_to_calendar": "ক্যালেন্ডারে যোগ করুন", + "select_channels_from_circles": "বলার মাধ্যমে চ্যানেল নির্বাচন করুন", + "not_matching_order": "অর্ডার মিলনায়ন না", + "submit_for_order": "অর্ডারে জমা দিন", + "schedule": "সময়সূচী করা", + "update": "আপডেট করা", + "attachments": "সংযুক্তি", + "tags": "ট্যাগ", + "public_to_everyone": "সকলের জন্য পাবলিক", + "mutual_follow_friends": "সহযোগী ফ্রেন্ডস", + "follower_of_creator": "প্রকাশকের অন্তর্গত", + "self_only": "নিজের জন্য কেবল", + "post_content_directly_to_tiktok": "টিকটকে কন্টেন্ট পোস্ট করা", + "upload_content_to_tiktok_without_posting": "পোস্ট করার প্রয়োজন ছাড়া টিকটকে কন্টেন্ট আপলোড করা", + "choose_upload_without_posting_description": "পোস্ট করার প্রয়োজন ছাড়া টিকটকে কন্টেন্ট আপলোড করার বর্ণনা", + "faq_am_i_going_to_be_charged_by_postiz": "পোস্টিজ দ্বারা চার্জ করা হবে কি?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "পোস্টিজ ক্রেডিট কার্ড তথ্য নিশ্চিত করার জন্য কি?", + "faq_can_i_trust_postiz_gitroom": "আমি কি Postiz-এ বিশ্বাস করতে পারি?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz গর্বের সাথে ওপেন-সোর্স! আমরা নৈতিক ও স্বচ্ছ সংস্কৃতিতে বিশ্বাস করি, যার মানে Postiz চিরকাল টিকে থাকবে। আপনি চাইলে সম্পূর্ণ কোড দেখতে পারেন বা ব্যক্তিগত প্রকল্পে ব্যবহার করতে পারেন। ওপেন-সোর্স রিপোজিটরি দেখতে, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">এখানে ক্লিক করুন</a>।", + "faq_what_are_channels": "চ্যানেল কী?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz আপনাকে বিভিন্ন চ্যানেলের মধ্যে আপনার পোস্টগুলো সময়সূচি নির্ধারণ করতে দেয়।\nএকটি চ্যানেল হলো এমন একটি প্রকাশনা প্ল্যাটফর্ম যেখানে আপনি আপনার পোস্টগুলো সময়সূচি অনুযায়ী প্রকাশ করতে পারেন।\nউদাহরণস্বরূপ, আপনি X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads এবং Pinterest-এ আপনার পোস্টগুলো সময়সূচি অনুযায়ী প্রকাশ করতে পারেন।", + "faq_what_are_team_members": "টিম সদস্য কী?", + "faq_if_you_have_a_team_with_multiple_members": "যদি আপনার একাধিক সদস্যের একটি টিম থাকে", + "faq_what_is_ai_auto_complete": "AI অটো কমপ্লিট কী?", + "faq_we_automate_chatgpt_to_help_you_write": "আমরা আপনাকে লিখতে সাহায্য করার জন্য ChatGPT স্বয়ংক্রিয় করি", + "enter_email": "ইমেইল প্রবেশ করান", + "are_you_sure": "আপনি কি নিশ্চিত?", + "yes_delete_it": "হ্যাঁ, মুছে ফেলুন", + "no_cancel": "না, বাতিল করুন", + "are_you_sure_you_want_to_delete": "আপনি কি নিশ্চিত যে আপনি মুছে ফেলতে চান?", + "are_you_sure_you_want_to_delete_the_image": "আপনি কি নিশ্চিত যে আপনি ছবিটি মুছে ফেলতে চান?", + "are_you_sure_you_want_to_logout": "আপনি কি নিশ্চিত যে আপনি লগআউট করতে চান?", + "yes_logout": "হ্যাঁ, লগআউট করুন", + "are_you_sure_you_want_to_delete_this_slot": "আপনি কি নিশ্চিত যে আপনি এই স্লটটি মুছে ফেলতে চান?", + "are_you_sure_you_want_to_delete_this_subreddit": "আপনি কি নিশ্চিত যে আপনি এই সাবরেডিটটি মুছে ফেলতে চান?", + "are_you_sure_you_want_to_close_the_window": "আপনি কি নিশ্চিত যে আপনি উইন্ডোটি বন্ধ করতে চান?", + "yes_close": "হ্যাঁ, বন্ধ করুন", + "link_copied_to_clipboard": "লিংক ক্লিপবোর্ডে কপি করা হয়েছে", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "আপনি কি নিশ্চিত যে আপনি এই মোডালটি বন্ধ করতে চান? সমস্ত ডেটা হারিয়ে যাবে", + "yes_close_it": "হ্যাঁ, বন্ধ করুন", + "uploading_pictures": "ছবি আপলোড করা হচ্ছে", + "agent_starting": "এজেন্ট শুরু হচ্ছে", + "researching_your_content": "আপনার কন্টেন্ট গবেষণা করা হচ্ছে", + "understanding_the_category": "বিভাগ বোঝা হচ্ছে", + "finding_the_topic": "বিষয় খোঁজা হচ্ছে", + "finding_popular_posts_to_match_with": "মিলানোর জন্য জনপ্রিয় পোস্ট খোঁজা হচ্ছে", + "generating_hook": "হুক তৈরি করা হচ্ছে", + "generating_content": "কন্টেন্ট তৈরি করা হচ্ছে", + "generating_pictures": "ছবি তৈরি করা হচ্ছে", + "finding_time_to_post": "পোস্ট করার সময় খোঁজা হচ্ছে", + "write_anything": "যেকোনো কিছু লিখুন", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "আপনি যা চান তা লিখতে পারেন এবং লিংকও যোগ করতে পারেন, আমরা আপনার জন্য গবেষণা করব", + "output_format": "আউটপুট ফরম্যাট", + "add_pictures": "ছবি যোগ করুন", + "7_days": "৭ দিন", + "30_days": "৩০ দিন", + "90_days": "৯০ দিন", + "start_7_days_free_trial": "৭ দিনের বিনামূল্যে ট্রায়াল শুরু করুন", + "change_language": "ভাষা পরিবর্তন করুন", + "that_a_wrap": "এটাই শেষ!\n\nযদি আপনি এই থ্রেডটি উপভোগ করে থাকেন:\n\n১. আরও এমন পোস্টের জন্য আমাকে @{{username}} ফলো করুন\n২. আপনার অডিয়েন্সের সাথে এই থ্রেডটি শেয়ার করতে নিচের টুইটটি রিটুইট করুন\n", + "post_as_images_carousel": "ছবির ক্যারোসেল হিসেবে পোস্ট করুন", + "save_set": "সেট সংরক্ষণ করুন", + "separate_post": "একটি পোস্টকে একাধিক পোস্টে ভাগ করুন", + "label_who_can_reply_to_this_post": "এই পোস্টে কে উত্তর দিতে পারবে?", + "delete_integration": "ইন্টিগ্রেশন মুছে ফেলুন", + "start_writing_your_post": "প্রিভিউর জন্য আপনার পোস্ট লেখা শুরু করুন", + "billing_join_over": "যোগ দিন", + "billing_entrepreneurs_count": "১৮,০০০+ উদ্যোক্তা", + "billing_who_use": "যারা ব্যবহার করেন", + "billing_postiz_grow_social": "Postiz তাদের সামাজিক উপস্থিতি বাড়াতে", + "billing_no_risk_trial": "১০০% ঝুঁকিমুক্ত ফ্রি ট্রায়াল", + "billing_pay_nothing_7_days": "প্রথম ৭ দিন কিছুই দিতে হবে না", + "billing_cancel_anytime": "যেকোনো সময় বাতিল করুন, ঝামেলা ছাড়াই", + "billing_choose_plan": "একটি প্ল্যান বেছে নিন", + "billing_monthly": "মাসিক", + "billing_yearly": "বার্ষিক", + "billing_20_percent_off": "২০% ছাড়", + "billing_features": "বৈশিষ্ট্যাবলী", + "billing_channel": "চ্যানেল", + "billing_channels": "চ্যানেলসমূহ", + "billing_unlimited": "অসীম", + "billing_posts_per_month": "প্রতি মাসে পোস্ট", + "billing_unlimited_team_members": "অসীম টিম সদস্য", + "billing_ai_auto_complete": "এআই অটো-কমপ্লিট", + "billing_ai_copilots": "এআই কো-পাইলট", + "billing_ai_autocomplete": "এআই অটো-কমপ্লিট", + "billing_advanced_picture_editor": "উন্নত ছবি সম্পাদনা", + "billing_ai_images_per_month": "প্রতি মাসে এআই ছবি", + "billing_ai_videos_per_month": "প্রতি মাসে এআই ভিডিও", + "billing_billing_address": "বিলিং ঠিকানা", + "billing_payment": "পেমেন্ট", + "billing_powered_by_stripe": "স্ট্রাইপ দ্বারা পরিচালিত", + "billing_your_7_day_trial_is": "আপনার ৭ দিনের ট্রায়াল", + "billing_100_percent_free": "১০০% ফ্রি", + "billing_ending": "শেষ হচ্ছে", + "billing_cancel_anytime_short": "যেকোনো সময় বাতিল করুন।", + "billing_pay_0_start_trial": "আজ $0 দিন - আপনার ফ্রি ট্রায়াল শুরু করুন!", + "billing_pay_now": "এখনই পেমেন্ট করুন", + "billing_per_month": "/ মাস" } diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index 51529dc4..10ac1cc0 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -1,506 +1,539 @@ { - "calendar": "Kalender", - "webhooks": "Webhooks", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks sind eine Möglichkeit, benachrichtigt zu werden, wenn etwas in Postiz passiert,\n über eine HTTP-Anfrage.", - "name": "Name", - "url": "URL", - "edit": "Bearbeiten", - "delete": "Löschen", - "add_a_webhook": "Webhook hinzufügen", - "save": "Speichern", - "send_test": "Test senden", - "select_role": "Rolle auswählen", - "video_made_with_ai": "Video mit KI erstellt", - "please_add_at_least": "Bitte füge mindestens 20 Zeichen hinzu", - "send_invitation_via_email": "Einladung per E-Mail senden?", - "global_settings": "Globale Einstellungen", - "copy_id": "Kanal-ID kopieren", - "team_members": "Teammitglieder", - "invite_your_assistant_or_team_member_to_manage_your_account": "Laden Sie Ihren Assistenten oder Ihre Teammitglieder ein, um ihren Account zu managen", - "remove": "Entfernen", - "add_another_member": "Weiteres Mitglied hinzufügen", - "signatures": "Signaturen", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Sie können ihrem Konto Signaturen hinzufügen, die in Ihren Beiträgen verwendet werden können.", - "content": "Inhalt", - "auto_add": "Automatisch hinzufügen?", - "actions": "Aktionen", - "use_signature": "Signatur verwenden", - "add_a_signature": "Signatur hinzufügen", - "no": "Nein", - "yes": "Ja", - "your_git_repository": "Ihr Git-Repository", - "connect_your_github_repository_to_receive_updates_and_analytics": "Verbinden Sie Ihr GitHub-Repository, um Updates und Analysen zu erhalten", - "connected": "Verbunden:", - "disconnect": "Trennen", - "connect_your_repository": "Repository verbinden", - "cancel": "Abbrechen", - "connect": "Verbinden", - "public_api": "Öffentliche API", - "check_n8n": "Schau dir unseren benutzerdefinierten n8n-Knoten für Postiz an.", - "use_postiz_api_to_integrate_with_your_tools": "Verwenden Sie die Postiz-API, um sie mit Ihren Tools zu integrieren.", - "read_how_to_use_it_over_the_documentation": "Lesen Sie in der Dokumentation, wie Sie sie verwenden.", - "reveal": "Anzeigen", - "copy_key": "Schlüssel kopieren", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Verbinden Sie den Postiz MCP-Server mit Ihrem Client (HTTP-Streaming), um Ihre Beiträge schneller zu planen!", - "share_with_a_client": "Mit einem Kunden teilen", - "post": "Beitrag", - "comments": "Kommentare", - "user": "Benutzer", - "login_register_to_add_comments": "Anmelden / Registrieren, um Kommentare hinzuzufügen", - "status": "Status:", - "there_are_not_plugs_matching_your_channels": "Es gibt keine Plugs, die zu Ihren Kanälen passen", - "you_have_to_add_x_or_linkedin_or_threads": "Sie müssen eines hinzufügen: X, LinkedIn oder Threads", - "go_to_the_calendar_to_add_channels": "Gehen Sie zum Kalender, um Kanäle hinzuzufügen", - "channels": "Kanäle", - "activate": "Aktivieren", - "this_channel_needs_to_be_refreshed": "Dieser Kanal muss aktualisiert werden,", - "click_here_to_refresh": "Hier klicken, um zu aktualisieren", - "can_t_show_analytics_yet": "Analysen können noch nicht angezeigt werden", - "you_have_to_add_social_media_channels": "Du musst Social-Media-Kanäle hinzufügen", - "supported": "Unterstützt:", - "step": "SCHRITT", - "skip_onboarding": "Onboarding überspringen", - "onboarding": "Onboarding", - "next": "Weiter", - "you_are_done_from_here_you_can": "Du bist fertig, von hier aus kannst du:", - "view_analytics": "Analysen anzeigen", - "schedule_a_new_post": "Einen neuen Beitrag planen", - "to_sell_posts_you_would_have_to": "Um Beiträge zu verkaufen, musst du:", - "1_connect_at_least_one_channel": "1. Mindestens einen Kanal verbinden", - "2_connect_you_bank_account": "2. Dein Bankkonto verbinden", - "go_back_to_connect_channels": "Zurückgehen, um Kanäle zu verbinden", - "move_to_the_seller_page_to_connect_you_bank": "Zur Verkäuferseite wechseln, um dein Bankkonto zu verbinden", - "connect_channels": "Kanäle verbinden", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Verbinde deine Social-Media- und Publishing-Webseiten-Kanäle,\n um später Beiträge zu planen", - "social": "Soziale Netzwerke", - "publishing_platforms": "Publishing-Plattformen", - "no_channels": "Noch keine Kanäle", - "connect_your_accounts": "Verbinde deine sozialen Konten, um mit dem Planen, Veröffentlichen und Analysieren zu beginnen – alles an einem Ort.", - "notifications": "Benachrichtigungen", - "no_notifications": "Keine Benachrichtigungen", - "send_message": "Nachricht senden", - "mar_28": "28. März", - "there_are_no_messages_yet": "Es gibt noch keine Nachrichten.", - "checkout_the_marketplace": "Marktplatz ansehen", - "go_to_marketplace": "Zum Marktplatz gehen", - "all_messages": "Alle Nachrichten", - "previous": "Zurück", - "select_or_upload_pictures_maximum_5_at_a_time": "Bilder auswählen oder hochladen (maximal 5 auf einmal)", - "you_can_also_drag_drop_pictures": "Sie können Bilder auch per Drag & Drop hinzufügen", - "you_don_t_have_any_assets_yet": "Sie haben noch keine Assets.", - "click_the_button_below_to_upload_one": "Klicken Sie auf die Schaltfläche unten, um eines hochzuladen", - "click_the_button_below_to_upload_other": "Klicken Sie auf die Schaltfläche unten, um mehrere hochzuladen", - "add_selected_media": "Ausgewählte Medien hinzufügen", - "insert_media": "Medien einfügen", - "design_media": "Medien gestalten", - "select": "Auswählen", - "editor": "Editor", - "clear": "Löschen", - "order_completed": "Bestellung abgeschlossen", - "the_order_has_been_completed": "Die Bestellung wurde abgeschlossen", - "post_has_been_published": "Beitrag wurde veröffentlicht", - "url_1": "URL:", - "new_offer": "Neues Angebot", - "platform": "Plattform", - "posts": "Beiträge", - "pay_accept_offer": "Bezahlen & Angebot annehmen", - "accepted": "Angenommen", - "post_draft": "Beitragsentwurf", - "revision_needed": "Überarbeitung erforderlich", - "approve": "Genehmigen", - "preview": "Vorschau", - "revision_requested": "Überarbeitung angefordert", - "accepted_1": "AKZEPTIERT", - "cancelled_by_the_seller": "Vom Verkäufer storniert", - "please_select_your_country_where_your_business_is": "Bitte wählen Sie das Land aus, in dem sich Ihr Unternehmen befindet.", - "select_country": "--LAND AUSWÄHLEN--", - "connect_bank_account": "Bankkonto verbinden", - "seller_mode": "Verkäufermodus", - "active": "Aktiv", - "details": "Details", - "audience_size": "Zielgruppengröße", - "add_another_platform": "Weitere Plattform hinzufügen", - "send_an_offer_for": "Angebot senden für €", - "complete_order_and_pay_early": "Bestellung abschließen und frühzeitig bezahlen", - "order_in_progress": "Bestellung in Bearbeitung", - "create_a_new_offer": "Neues Angebot erstellen", - "orders": "Bestellungen", - "price": "Preis", - "state": "Status", - "showing": "Anzeigen", - "to": "bis", - "from": "von", - "results": "Ergebnisse", - "content_writer": "Texter", - "influencer": "Influencer", - "request_service": "Dienst anfordern", - "the_marketplace_is_not_opened_yet": "Der Marktplatz ist noch nicht geöffnet", - "check_again_soon": "Schau bald wieder vorbei!", - "filter": "Filter", - "result": "Ergebnis", - "seller": "Verkäufer", - "buyer": "Käufer", - "discord_support": "Discord-Support", - "teams": "Teams", - "webhooks_1": "Webhooks", - "auto_post": "Automatisches Posten", - "logout_from": "Abmelden von", - "join_10000_entrepreneurs_who_use_postiz": "Schließe dich über 10.000 Unternehmern an, die Postiz nutzen", - "to_manage_all_your_social_media_channels": "Um all deine Social-Media-Kanäle zu verwalten", - "100_no_risk_trial": "100% risikofreie Testphase", - "pay_nothing_for_the_first_7_days": "Zahle in den ersten 7 Tagen nichts", - "cancel_anytime_hassle_free": "Jederzeit kündbar, ganz ohne Aufwand", - "add_free_subscription": "-- KOSTENLOSES ABONNEMENT HINZUFÜGEN --", - "currently_impersonating": "Derzeit als jemand anderes angemeldet", - "user_1": "Benutzer:", - "drag_n_drop_some_files_here": "Dateien hierher ziehen und ablegen", - "add_time_slot": "Zeitfenster hinzufügen", - "add_slot": "Slot hinzufügen", - "cancel_publication": "Veröffentlichung abbrechen", - "statistics": "Statistiken", - "loading": "Lädt", - "short_link": "Kurzlink", - "original_link": "Originaler Link", - "clicks": "Klicks", - "selected_customer": "Ausgewählter Kunde", - "customer": "Kunde:", - "repeat_post_every": "Beitrag wiederholen alle...", - "use_this_media": "Diese Media verwenden", - "create_new_post": "Beitrag erstellen", - "update_post": "Vorhandenen Beitrag aktualisieren", - "merge_comments_into_one_post": "Kommentare zu einem Beitrag zusammenfassen", - "accounts_that_will_engage": "Konten, die interagieren werden:", - "day": "Tag", - "week": "Woche", - "month": "Monat", - "remove_from_customer": "Vom Kunden entfernen", - "show_more": "+ Mehr anzeigen", - "show_less": "- Weniger anzeigen", - "upload": "Hochladen", - "ai": "KI", - "add_channel": "Kanal hinzufügen", - "add_platform": "Plattform hinzufügen", - "articles": "Artikel", - "add_comment": "Kommentar hinzufügen", - "add_post": "Beitrag in einem Thread hinzufügen", - "add_comment_or_post": "Kommentar / Beitrag hinzufügen", - "you_are_in_global_editing_mode": "Sie befinden sich im globalen Bearbeitungsmodus", - "the_post_should_be_at_least_6_characters_long": "Der Beitrag sollte mindestens 6 Zeichen lang sein", - "are_you_sure_you_want_to_delete_post": "Bist du sicher, dass du diesen Beitrag löschen möchtest?", - "post_deleted_successfully": "Beitrag erfolgreich gelöscht", - "delete_post": "Beitrag löschen", - "save_as_draft": "Als Entwurf speichern", - "post_now": "Jetzt posten", - "please_add": "Bitte hinzufügen", - "to_your_telegram_group_channel_and_click_here": "zu Ihrer\n Telegram-Gruppe / Ihrem Kanal und klicken Sie hier:", - "connect_telegram": "Telegram verbinden", - "please_add_the_following_command_in_your_chat": "Bitte fügen Sie den folgenden Befehl in Ihrem Chat hinzu:", - "copy": "Kopieren", - "settings": "Einstellungen", - "integrations": "Integrationen", - "add_integration": "Integration hinzufügen", - "you_are_now_editing_only": "Sie bearbeiten jetzt nur", - "tag_a_company": "Unternehmen markieren", - "video_length_is_invalid_must_be_up_to": "Videolänge ist ungültig, sie darf maximal", - "seconds": "Sekunden", - "this_feature_available_only_for_photos": "Diese Funktion ist nur für Fotos verfügbar, es wird eine Standardmusik hinzugefügt, die Sie später ändern können.", - "allow_user_to": "Benutzer erlauben zu:", - "your_video_will_be_labeled_promotional": "Ihr Video wird als \"Werbeinhalte\" gekennzeichnet.", - "this_cannot_be_changed_once_posted": "Dies kann nicht mehr geändert werden, sobald Ihr Video gepostet wurde.", - "turn_on_to_disclose_video_promotes": "Aktivieren Sie diese Option, um offenzulegen, dass dieses Video Waren oder Dienstleistungen im Austausch für eine Gegenleistung bewirbt. Ihr Video könnte Sie selbst, eine dritte Partei oder beides bewerben.", - "you_are_promoting_yourself": "Sie bewerben sich selbst oder Ihre eigene Marke.", - "this_video_will_be_classified_brand_organic": "Dieses Video wird als Brand Organic eingestuft.", - "you_are_promoting_another_brand": "Sie bewerben eine andere Marke oder eine dritte Partei.", - "this_video_will_be_classified_branded_content": "Dieses Video wird als Markeninhalt eingestuft.", - "by_posting_you_agree_to_tiktoks": "Mit dem Posten stimmen Sie den TikTok-Bedingungen zu", - "music_usage_confirmation": "Bestätigung der Musiknutzung", - "branded_content_policy": "Richtlinie für Markeninhalte", - "select_1": "--Auswählen--", - "select_flair": "--Flair auswählen--", - "link": "Link", - "add_subreddit": "Subreddit hinzufügen", - "please_add_at_least_one_subreddit": "Bitte fügen Sie mindestens einen Subreddit hinzu", - "add_community": "Community hinzufügen", - "select_post_type": "Beitragstyp auswählen...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Wir konnten kein Unternehmen finden, das mit Ihrer LinkedIn-Seite verbunden ist.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Bitte schließen Sie diesen Dialog, erstellen Sie eine neue Seite und fügen Sie erneut einen neuen Kanal hinzu.", - "select_linkedin_page": "LinkedIn-Seite auswählen:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Wir konnten kein Unternehmen finden, das mit den ausgewählten Seiten verbunden ist.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Wir empfehlen Ihnen, alle Seiten und alle Unternehmen zu verbinden.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Bitte schließen Sie diesen Dialog, löschen Sie Ihre Integration und fügen Sie erneut einen neuen Kanal \n hinzu.", - "select_instagram_account": "Instagram-Konto auswählen:", - "select_page": "Seite auswählen:", - "generate_image_with_ai": "Bild mit KI generieren", - "reconnect_channel": "Kanal erneut verbinden", - "update_credentials": "Zugangsdaten aktualisieren", - "additional_settings": "Zusätzliche Einstellungen", - "change_bot": "Bot wechseln", - "move_add_to_customer": "Zu Kunde verschieben / hinzufügen", - "edit_time_slots": "Zeitfenster bearbeiten", - "enable_channel": "Kanal aktivieren", - "disable_channel": "Kanal deaktivieren", - "add": "Hinzufügen", - "short_post": "Kurzer Beitrag", - "long_post": "Langer Beitrag", - "a_thread_with_short_posts": "Ein Thread mit kurzen Beiträgen", - "a_thread_with_long_posts": "Ein Thread mit langen Beiträgen", - "personal_voice_i_am_happy_to_announce": "Persönlicher Ton (\"Ich freue mich, bekannt zu geben\")", - "company_voice_we_are_happy_to_announce": "Unternehmenston (\"Wir freuen uns, bekannt zu geben\")", - "generate": "Generieren", - "generate_posts": "Beiträge generieren", - "purchase_a_life_time_pro_account_with_sol_199": "Kaufe ein lebenslanges PRO-Konto mit SOL ($199). Bitte beachte, dass es für diesen Kauf keine Rückerstattung gibt.", - "purchase_now": "Jetzt kaufen", - "pay_today": "Heute bezahlen", - "we_are_sorry_to_see_you_go": "Es tut uns leid, dass Sie gehen :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Würden Sie uns kurz mitteilen, was wir hätten besser machen können?", - "cancel_subscription": "Abonnement kündigen", - "plans": "Pläne", - "monthly": "MONATLICH", - "yearly": "JÄHRLICH", - "reactivate_subscription": "Abonnement reaktivieren", - "update_payment_method_invoices_history": "Zahlungsmethode aktualisieren / Rechnungsverlauf", - "cancel_subscription_1": "Abonnement kündigen", - "your_subscription_will_be_canceled_at": "Ihr Abonnement wird gekündigt am", - "you_will_never_be_charged_again": "Ihnen werden keine weiteren Kosten berechnet", - "current_package": "Aktuelles Paket:", - "next_package": "Nächstes Paket:", - "claim": "Einlösen", - "frequently_asked_questions": "Häufig gestellte Fragen", - "autopost": "Automatisches Posten", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Autopost kann Ihre neuen RSS-Einträge automatisch in sozialen Medien veröffentlichen", - "title": "Titel", - "add_an_autopost": "Autopost hinzufügen", - "post_content": "Beitragsinhalt", - "sign_up": "Registrieren", - "or": "ODER", - "by_registering_you_agree_to_our": "Mit der Registrierung stimmen Sie unseren", - "and": "und", - "terms_of_service": "Nutzungsbedingungen", - "privacy_policy": "Datenschutzrichtlinien", - "create_account": "Konto erstellen", - "already_have_an_account": "Sie haben bereits ein Konto?", - "sign_in": "Anmelden", - "sign_in_1": "Anmelden", - "don_t_have_an_account": "Sie haben noch kein Konto?", - "forgot_password": "Passwort vergessen", - "forgot_password_1": "Passwort vergessen", - "send_password_reset_email": "E-Mail zum Zurücksetzen des Passworts senden", - "go_back_to_login": "Zurück zum Login", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Wir haben Ihnen eine E-Mail mit einem Link zum Zurücksetzen Ihres Passworts gesendet.", - "change_password": "Passwort ändern", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Wir haben Ihr Passwort erfolgreich zurückgesetzt. Sie können sich jetzt anmelden mit ihrem", - "click_here_to_go_back_to_login": "Klicken Sie hier, um zum Login zurückzukehren", - "activate_your_account": "Aktivieren Sie Ihr Konto", - "thank_you_for_registering": "Vielen Dank für Ihre Registrierung!", - "please_check_your_email_to_activate_your_account": "Bitte überprüfen Sie Ihre E-Mails, um Ihr Konto zu aktivieren.", - "sign_in_with": "Anmelden mit", - "continue_with_google": "Mit Google fortfahren", - "sign_in_with_github": "Mit GitHub anmelden", - "continue_with_farcaster": "Mit Farcaster fortfahren", - "continue_with_your_wallet": "Mit Ihrem Wallet fortfahren", - "stars_per_day": "Sterne pro Tag", - "media": "Medien", - "check_launch": "Launch prüfen", - "load_your_github_repository_from_settings_to_see_analytics": "Laden Sie Ihr GitHub-Repository in den Einstellungen, um Analysen zu sehen", - "stars": "Sterne", - "processing_stars": "Sterne werden verarbeitet...", - "forks": "Forks", - "registration_is_disabled": "Registrierung ist deaktiviert", - "login_instead": "Stattdessen anmelden", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Wählen Sie eine Unterhaltung und chatten Sie los.", - "adding_channel_redirecting_you": "Kanal wird hinzugefügt, Sie werden weitergeleitet", - "could_not_add_provider": "Anbieter konnte nicht hinzugefügt werden.", - "you_are_being_redirected_back": "Sie werden zurückgeleitet", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Wir haben derzeit Schwierigkeiten, bitte versuchen Sie, die Seite zu aktualisieren.", - "post_not_found": "Beitrag nicht gefunden", - "publication_date": "Veröffentlichungsdatum:", - "analytics": "Analytiken:", - "launches": "Veröffentlichungen", - "plugs": "Plugs", - "billing": "Abrechnung", - "affiliate": "Partnerprogramm", - "monday": "Montag", - "tuesday": "Dienstag", - "wednesday": "Mittwoch", - "thursday": "Donnerstag", - "friday": "Freitag", - "saturday": "Samstag", - "sunday": "Sonntag", - "can_t_change_date_remove_post_from_publication": "Datum kann nicht geändert werden, entfernen Sie den Beitrag aus der Veröffentlichung", - "predicted_github_trending_change": "Prognostizierte GitHub-Trendänderung", - "duplicate_post": "Dupliziere Beitrag", - "preview_post": "Beitragsvorschau", - "post_statistics": "Beitragsstatistiken", - "draft": "Entwurf", - "week_number": "Woche {{number}}", - "top_title_edit_webhook": "Webhook bearbeiten", - "top_title_add_webhook": "Webhook hinzufügen", - "top_title_oh_no": "Oh nein", - "top_title_auto_plug": "Auto Plug: {{title}}", - "top_title_edit_autopost": "Autopost bearbeiten", - "top_title_add_autopost": "Autopost hinzufügen", - "top_title_send_a_new_offer": "Neues Angebot senden", - "top_title_media_library": "Medienbibliothek", - "top_title_add_signature": "Signatur hinzufügen", - "top_title_send_a_message_to": "Nachricht an {{name}} senden", - "top_title_configure_provider": "Anbieter konfigurieren", - "top_title_add_member": "Mitglied hinzufügen", - "top_title_change_bot_picture": "Bot-Bild ändern", - "top_title_create_a_new_tag": "Neuen Tag erstellen", - "top_title_select_company": "Unternehmen auswählen", - "top_title_additional_settings": "Zusätzliche Einstellungen", - "top_title_time_table_slots": "Zeitplan-Slots", - "top_title_design_media": "Medien gestalten", - "top_title_edit_post": "Beitrag bearbeiten", - "top_title_create_post": "Beitrag erstellen", - "top_title_move__add_to_customer": "Zu Kunde verschieben / hinzufügen", - "top_title_add_api_key_for": "API-Schlüssel für {{name}} hinzufügen", - "top_title_instance_url": "Instanz-URL", - "top_title_custom_url": "Benutzerdefinierte URL", - "top_title_add_channel": "Kanal hinzufügen", - "top_title_add_telegram": "Telegram hinzufügen", - "top_title_add_wrapcast": "Wrapcast hinzufügen", - "top_title_comments_for": "Kommentare für {{date}}", - "top_title_edit_signature": "Signatur bearbeiten", - "label_name": "Name", - "label_url": "URL", - "label_title": "Titel", - "label_subtitle": "Untertitel", - "label_email": "E-Mail", - "label_full_name": "Vollständiger Name", - "label_password": "Passwort", - "label_confirm_password": "Passwort bestätigen", - "label_api_key": "API-Schlüssel", - "label_instance_url": "Instanz-URL", - "label_custom_url": "Benutzerdefinierte URL", - "label_feedback": "Feedback", - "label_bio": "Biografie", - "label_role": "Rolle", - "label_country": "Land", - "label_audience_size": "Publikumsgröße auf allen Plattformen", - "label_pick_time": "Zeit auswählen", - "label_nickname": "Spitzname", - "label_write_anything": "Schreibe irgendetwas", - "label_output_format": "Ausgabeformat", - "label_add_pictures": "Bilder hinzufügen?", - "label_hour": "Stunde", - "label_minutes": "Minuten", - "label_select_publication": "Publikation auswählen", - "label_canonical_link": "Kanonischer Link", - "label_cover_picture": "Titelbild", - "label_tags": "Tags", - "label_topics": "Themen", - "label_tags_maximum_4": "Tags (maximal 4)", - "label_attachments": "Anhänge", - "label_type": "Typ", - "label_thumbnail": "Vorschaubild", - "label_who_can_see_this_video": "Wer kann dieses Video sehen?", - "label_content_posting_method": "Methode zum Posten von Inhalten", - "label_auto_add_music": "Musik automatisch hinzufügen", - "label_duet": "Duett", - "label_stitch": "Stitch", - "label_comments": "Kommentare", - "label_disclose_video_content": "Videoinhalt offenlegen", - "label_your_brand": "Deine Marke", - "label_branded_content": "Markeninhalt", - "label_subreddit": "Subreddit", - "label_flair": "Flair", - "label_media": "Medien", - "label_search_subreddit": "Subreddit suchen", - "label_delay": "Verzögerung", - "label_post_type": "Beitragstyp", - "label_collaborators": "Kollaboratoren (max. 3) – Konten dürfen nicht privat sein", - "label_community": "Community", - "label_search_community": "Community durchsuchen", - "label_channel": "Kanal", - "label_search_channel": "Kanal durchsuchen", - "label_select_channel": "Kanal auswählen", - "label_new_password": "Neues Passwort", - "label_repeat_password": "Passwort wiederholen", - "label_platform": "Plattform", - "label_price_per_post": "Preis pro Beitrag", - "label_integrations": "Integrationen", - "label_code": "Code", - "label_should_sync_last_post": "Sollen wir den aktuellen letzten Beitrag synchronisieren?", - "label_when_post": "Wann sollen wir es posten?", - "label_autogenerate_content": "Inhalt automatisch generieren", - "label_generate_picture": "Bild generieren?", - "label_company": "Unternehmen", - "label_tag_color": "Tag-Farbe", - "label_select_board": "Board auswählen", - "label_select_organization": "Organisation auswählen", - "label_auto_add_signature": "Signatur automatisch hinzufügen?", - "enable_color_picker": "Farbwähler aktivieren", - "cancel_the_color_picker": "Farbwähler abbrechen", - "no_content_yet": "Noch kein Inhalt", - "write_your_reply": "Schreibe deinen Beitrag...", - "add_a_tag": "Tag hinzufügen", - "add_to_calendar": "Zum Kalender hinzufügen", - "select_channels_from_circles": "Wähle Kanäle aus den obigen Kreisen aus", - "not_matching_order": "Nicht übereinstimmende Reihenfolge", - "submit_for_order": "Zur Bestellung einreichen", - "schedule": "Planen", - "update": "Aktualisieren", - "attachments": "Anhänge", - "tags": "Tags", - "public_to_everyone": "Öffentlich für alle", - "mutual_follow_friends": "Gegenseitig gefolgte Freunde", - "follower_of_creator": "Follower des Erstellers", - "self_only": "Nur ich", - "post_content_directly_to_tiktok": "Inhalt direkt auf TikTok posten", - "upload_content_to_tiktok_without_posting": "Inhalt auf TikTok hochladen, ohne zu posten", - "choose_upload_without_posting_description": "Wähle 'Hochladen ohne Posten', wenn du deinen Inhalt in der TikTok-App vor der Veröffentlichung überprüfen und bearbeiten möchtest. So hast du Zugriff auf die integrierten Bearbeitungstools von TikTok und kannst vor dem Posten letzte Anpassungen vornehmen.", - "faq_am_i_going_to_be_charged_by_postiz": "Werde ich von Postiz Gebühren berechnet bekommen?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Um die Kreditkarteninformationen zu bestätigen, wird Postiz 2$ reservieren und diesen Betrag sofort wieder freigeben.", - "faq_can_i_trust_postiz_gitroom": "Kann ich Postiz vertrauen?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz ist stolz darauf, Open Source zu sein! Wir glauben an eine ethische und transparente Kultur, was bedeutet, dass Postiz für immer bestehen wird. Du kannst den gesamten Code einsehen oder für persönliche Projekte nutzen. Um das Open-Source-Repository anzusehen, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">klicke hier</a>.", - "faq_what_are_channels": "Was sind Kanäle?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz ermöglicht es dir, deine Beiträge auf verschiedenen Kanälen zu planen.\nEin Kanal ist eine Veröffentlichungsplattform, auf der du deine Beiträge planen kannst.\nZum Beispiel kannst du deine Beiträge auf X, Facebook, Instagram, TikTok, YouTube, Reddit, LinkedIn, Dribbble, Threads oder Pinterest planen.", - "faq_what_are_team_members": "Was sind Teammitglieder?", - "faq_if_you_have_a_team_with_multiple_members": "Wenn du ein Team mit mehreren Mitgliedern hast, kannst du sie in deinen Arbeitsbereich einladen, um gemeinsam an Beiträgen zu arbeiten und ihre persönlichen Kanäle hinzuzufügen.", - "faq_what_is_ai_auto_complete": "Was ist KI-Autovervollständigung?", - "faq_we_automate_chatgpt_to_help_you_write": "Wir automatisieren ChatGPT, um dir beim Schreiben von Social-Media-Beiträgen und Artikeln zu helfen.", - "enter_email": "E-Mail eingeben", - "are_you_sure": "Bist du sicher?", - "yes_delete_it": "Ja, löschen!", - "no_cancel": "Nein, abbrechen!", - "are_you_sure_you_want_to_delete": "Bist du sicher, dass du {{name}} löschen möchtest?", - "are_you_sure_you_want_to_delete_the_image": "Bist du sicher, dass du das Bild löschen möchtest?", - "are_you_sure_you_want_to_logout": "Bist du sicher, dass du dich abmelden möchtest?", - "yes_logout": "Ja, abmelden", - "are_you_sure_you_want_to_delete_this_slot": "Bist du sicher, dass du diesen Slot löschen möchtest?", - "are_you_sure_you_want_to_delete_this_subreddit": "Bist du sicher, dass du dieses Subreddit löschen möchtest?", - "are_you_sure_you_want_to_close_the_window": "Bist du sicher, dass du das Fenster schließen möchtest?", - "yes_close": "Ja, schließen", - "link_copied_to_clipboard": "Link in die Zwischenablage kopiert", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Bist du sicher, dass du dieses Fenster schließen möchtest? (Alle Daten gehen verloren)", - "yes_close_it": "Ja, schließe es!", - "uploading_pictures": "Bilder werden hochgeladen...", - "agent_starting": "Agent startet", - "researching_your_content": "Dein Inhalt wird recherchiert...", - "understanding_the_category": "Kategorie wird verstanden...", - "finding_the_topic": "Thema wird gefunden...", - "finding_popular_posts_to_match_with": "Beliebte Beiträge werden gesucht...", - "generating_hook": "Hook wird generiert...", - "generating_content": "Inhalt wird generiert...", - "generating_pictures": "Bilder werden generiert...", - "finding_time_to_post": "Zeit zum Posten wird gesucht...", - "write_anything": "Schreibe irgendetwas", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Sie können alles schreiben, was Sie möchten, und auch Links hinzufügen. Wir übernehmen die Recherche für Sie...", - "output_format": "Ausgabeformat", - "add_pictures": "Bilder hinzufügen?", - "7_days": "7 Tage", - "30_days": "30 Tage", - "90_days": "90 Tage", - "start_7_days_free_trial": "7-tägige kostenlose Testversion starten", - "change_language": "Sprache ändern", - "that_a_wrap": "Das war's!\n\nWenn dir dieser Thread gefallen hat:\n\n1. Folge mir @{{username}} für mehr davon\n2. Retweete den untenstehenden Tweet, um diesen Thread mit deinem Publikum zu teilen\n", - "post_as_images_carousel": "Als Bilderkarussell posten", - "save_set": "Set speichern", - "separate_post": "Beitrag in mehrere Beiträge aufteilen", - "label_who_can_reply_to_this_post": "Wer kann auf diesen Beitrag antworten?", - "delete_integration": "Integration löschen", - "start_writing_your_post": "Beginne, deinen Beitrag für eine Vorschau zu schreiben" + "calendar": "Kalender", + "webhooks": "Webhooks", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks sind eine Möglichkeit, benachrichtigt zu werden, wenn etwas in Postiz passiert,\n über eine HTTP-Anfrage.", + "name": "Name", + "url": "URL", + "edit": "Bearbeiten", + "delete": "Löschen", + "add_a_webhook": "Webhook hinzufügen", + "save": "Speichern", + "send_test": "Test senden", + "select_role": "Rolle auswählen", + "video_made_with_ai": "Video mit KI erstellt", + "please_add_at_least": "Bitte fügen Sie mindestens 20 Zeichen hinzu", + "send_invitation_via_email": "Einladung per E-Mail senden?", + "global_settings": "Globale Einstellungen", + "copy_id": "Kanal-ID kopieren", + "team_members": "Teammitglieder", + "invite_your_assistant_or_team_member_to_manage_your_account": "Laden Sie Ihren Assistenten oder Ihre Teammitglieder ein, um ihren Account zu managen", + "remove": "Entfernen", + "add_another_member": "Weiteres Mitglied hinzufügen", + "signatures": "Signaturen", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Sie können ihrem Konto Signaturen hinzufügen, die in Ihren Beiträgen verwendet werden können.", + "content": "Inhalt", + "auto_add": "Automatisch hinzufügen?", + "actions": "Aktionen", + "use_signature": "Signatur verwenden", + "add_a_signature": "Signatur hinzufügen", + "no": "Nein", + "yes": "Ja", + "your_git_repository": "Ihr Git-Repository", + "connect_your_github_repository_to_receive_updates_and_analytics": "Verbinden Sie Ihr GitHub-Repository, um Updates und Analysen zu erhalten", + "connected": "Verbunden:", + "disconnect": "Trennen", + "connect_your_repository": "Repository verbinden", + "cancel": "Abbrechen", + "connect": "Verbinden", + "public_api": "Öffentliche API", + "check_n8n": "Schau dir unseren benutzerdefinierten n8n-Knoten für Postiz an.", + "use_postiz_api_to_integrate_with_your_tools": "Verwenden Sie die Postiz-API, um sie mit Ihren Tools zu integrieren.", + "read_how_to_use_it_over_the_documentation": "Lesen Sie in der Dokumentation, wie Sie sie verwenden.", + "reveal": "Anzeigen", + "copy_key": "Schlüssel kopieren", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Verbinden Sie den Postiz MCP-Server mit Ihrem Client (HTTP-Streaming), um Ihre Beiträge schneller zu planen!", + "share_with_a_client": "Mit einem Kunden teilen", + "post": "Beitrag", + "comments": "Kommentare", + "user": "Benutzer", + "login_register_to_add_comments": "Anmelden / Registrieren, um Kommentare hinzuzufügen", + "status": "Status:", + "there_are_not_plugs_matching_your_channels": "Es gibt keine Plugs, die zu Ihren Kanälen passen", + "you_have_to_add_x_or_linkedin_or_threads": "Sie müssen eines hinzufügen: X, LinkedIn oder Threads", + "go_to_the_calendar_to_add_channels": "Gehen Sie zum Kalender, um Kanäle hinzuzufügen", + "channels": "Kanäle", + "activate": "Aktivieren", + "this_channel_needs_to_be_refreshed": "Dieser Kanal muss aktualisiert werden,", + "click_here_to_refresh": "Hier klicken, um zu aktualisieren", + "can_t_show_analytics_yet": "Analysen können noch nicht angezeigt werden", + "you_have_to_add_social_media_channels": "Du musst Social-Media-Kanäle hinzufügen", + "supported": "Unterstützt:", + "step": "SCHRITT", + "skip_onboarding": "Onboarding überspringen", + "onboarding": "Onboarding", + "next": "Weiter", + "you_are_done_from_here_you_can": "Du bist fertig, von hier aus kannst du:", + "view_analytics": "Analysen anzeigen", + "schedule_a_new_post": "Einen neuen Beitrag planen", + "to_sell_posts_you_would_have_to": "Um Beiträge zu verkaufen, musst du:", + "1_connect_at_least_one_channel": "1. Mindestens einen Kanal verbinden", + "2_connect_you_bank_account": "2. Dein Bankkonto verbinden", + "go_back_to_connect_channels": "Zurückgehen, um Kanäle zu verbinden", + "move_to_the_seller_page_to_connect_you_bank": "Zur Verkäuferseite wechseln, um dein Bankkonto zu verbinden", + "connect_channels": "Kanäle verbinden", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Verbinde deine Social-Media- und Publishing-Webseiten-Kanäle,\n um später Beiträge zu planen", + "social": "Soziale Netzwerke", + "publishing_platforms": "Publishing-Plattformen", + "no_channels": "Noch keine Kanäle", + "connect_your_accounts": "Verbinde deine sozialen Konten, um mit dem Planen, Veröffentlichen und Analysieren zu beginnen – alles an einem Ort.", + "notifications": "Benachrichtigungen", + "no_notifications": "Keine Benachrichtigungen", + "send_message": "Nachricht senden", + "mar_28": "28. März", + "there_are_no_messages_yet": "Es gibt noch keine Nachrichten.", + "checkout_the_marketplace": "Marktplatz ansehen", + "go_to_marketplace": "Zum Marktplatz gehen", + "all_messages": "Alle Nachrichten", + "previous": "Zurück", + "select_or_upload_pictures_maximum_5_at_a_time": "Bilder auswählen oder hochladen (maximal 5 auf einmal)", + "you_can_also_drag_drop_pictures": "Sie können Bilder auch per Drag & Drop hinzufügen", + "you_don_t_have_any_assets_yet": "Sie haben noch keine Assets.", + "click_the_button_below_to_upload_one": "Klicken Sie auf die Schaltfläche unten, um eines hochzuladen", + "click_the_button_below_to_upload_other": "Klicken Sie auf die Schaltfläche unten, um mehrere hochzuladen", + "add_selected_media": "Ausgewählte Medien hinzufügen", + "insert_media": "Medien einfügen", + "design_media": "Medien gestalten", + "select": "Auswählen", + "editor": "Editor", + "clear": "Löschen", + "order_completed": "Bestellung abgeschlossen", + "the_order_has_been_completed": "Die Bestellung wurde abgeschlossen", + "post_has_been_published": "Beitrag wurde veröffentlicht", + "url_1": "URL:", + "new_offer": "Neues Angebot", + "platform": "Plattform", + "posts": "Beiträge", + "pay_accept_offer": "Bezahlen & Angebot annehmen", + "accepted": "Angenommen", + "post_draft": "Beitragsentwurf", + "revision_needed": "Überarbeitung erforderlich", + "approve": "Genehmigen", + "preview": "Vorschau", + "revision_requested": "Überarbeitung angefordert", + "accepted_1": "AKZEPTIERT", + "cancelled_by_the_seller": "Vom Verkäufer storniert", + "please_select_your_country_where_your_business_is": "Bitte wählen Sie das Land aus, in dem sich Ihr Unternehmen befindet.", + "select_country": "--LAND AUSWÄHLEN--", + "connect_bank_account": "Bankkonto verbinden", + "seller_mode": "Verkäufermodus", + "active": "Aktiv", + "details": "Details", + "audience_size": "Zielgruppengröße", + "add_another_platform": "Weitere Plattform hinzufügen", + "send_an_offer_for": "Angebot senden für €", + "complete_order_and_pay_early": "Bestellung abschließen und frühzeitig bezahlen", + "order_in_progress": "Bestellung in Bearbeitung", + "create_a_new_offer": "Neues Angebot erstellen", + "orders": "Bestellungen", + "price": "Preis", + "state": "Status", + "showing": "Anzeigen", + "to": "bis", + "from": "von", + "results": "Ergebnisse", + "content_writer": "Texter", + "influencer": "Influencer", + "request_service": "Dienst anfordern", + "the_marketplace_is_not_opened_yet": "Der Marktplatz ist noch nicht geöffnet", + "check_again_soon": "Schau bald wieder vorbei!", + "filter": "Filter", + "result": "Ergebnis", + "seller": "Verkäufer", + "buyer": "Käufer", + "discord_support": "Discord-Support", + "teams": "Teams", + "webhooks_1": "Webhooks", + "auto_post": "Automatisches Posten", + "logout_from": "Abmelden von", + "join_10000_entrepreneurs_who_use_postiz": "Schließe dich über 10.000 Unternehmern an, die Postiz nutzen", + "to_manage_all_your_social_media_channels": "Um all deine Social-Media-Kanäle zu verwalten", + "100_no_risk_trial": "100% risikofreie Testphase", + "pay_nothing_for_the_first_7_days": "Zahle in den ersten 7 Tagen nichts", + "cancel_anytime_hassle_free": "Jederzeit kündbar, ganz ohne Aufwand", + "add_free_subscription": "-- KOSTENLOSES ABONNEMENT HINZUFÜGEN --", + "currently_impersonating": "Derzeit als jemand anderes angemeldet", + "user_1": "Benutzer:", + "drag_n_drop_some_files_here": "Dateien hierher ziehen und ablegen", + "add_time_slot": "Zeitfenster hinzufügen", + "add_slot": "Slot hinzufügen", + "cancel_publication": "Veröffentlichung abbrechen", + "statistics": "Statistiken", + "loading": "Lädt", + "short_link": "Kurzlink", + "original_link": "Originaler Link", + "clicks": "Klicks", + "selected_customer": "Ausgewählter Kunde", + "customer": "Kunde:", + "repeat_post_every": "Beitrag wiederholen alle...", + "use_this_media": "Diese Media verwenden", + "create_new_post": "Beitrag erstellen", + "update_post": "Vorhandenen Beitrag aktualisieren", + "merge_comments_into_one_post": "Kommentare zu einem Beitrag zusammenfassen", + "accounts_that_will_engage": "Konten, die interagieren werden:", + "day": "Tag", + "week": "Woche", + "month": "Monat", + "remove_from_customer": "Vom Kunden entfernen", + "show_more": "+ Mehr anzeigen", + "show_less": "- Weniger anzeigen", + "upload": "Hochladen", + "ai": "KI", + "add_channel": "Kanal hinzufügen", + "add_platform": "Plattform hinzufügen", + "articles": "Artikel", + "add_comment": "Kommentar hinzufügen", + "add_post": "Beitrag in einem Thread hinzufügen", + "add_comment_or_post": "Kommentar / Beitrag hinzufügen", + "you_are_in_global_editing_mode": "Sie befinden sich im globalen Bearbeitungsmodus", + "the_post_should_be_at_least_6_characters_long": "Der Beitrag sollte mindestens 6 Zeichen lang sein", + "are_you_sure_you_want_to_delete_post": "Bist du sicher, dass du diesen Beitrag löschen möchtest?", + "post_deleted_successfully": "Beitrag erfolgreich gelöscht", + "delete_post": "Beitrag löschen", + "save_as_draft": "Als Entwurf speichern", + "post_now": "Jetzt posten", + "please_add": "Bitte hinzufügen", + "to_your_telegram_group_channel_and_click_here": "zu Ihrer\n Telegram-Gruppe / Ihrem Kanal und klicken Sie hier:", + "connect_telegram": "Telegram verbinden", + "please_add_the_following_command_in_your_chat": "Bitte fügen Sie den folgenden Befehl in Ihrem Chat hinzu:", + "copy": "Kopieren", + "settings": "Einstellungen", + "integrations": "Integrationen", + "add_integration": "Integration hinzufügen", + "you_are_now_editing_only": "Sie bearbeiten jetzt nur", + "tag_a_company": "Unternehmen markieren", + "video_length_is_invalid_must_be_up_to": "Videolänge ist ungültig, sie darf maximal", + "seconds": "Sekunden", + "this_feature_available_only_for_photos": "Diese Funktion ist nur für Fotos verfügbar, es wird eine Standardmusik hinzugefügt, die Sie später ändern können.", + "allow_user_to": "Benutzer erlauben zu:", + "your_video_will_be_labeled_promotional": "Ihr Video wird als \"Werbeinhalte\" gekennzeichnet.", + "this_cannot_be_changed_once_posted": "Dies kann nicht mehr geändert werden, sobald Ihr Video gepostet wurde.", + "turn_on_to_disclose_video_promotes": "Aktivieren Sie diese Option, um offenzulegen, dass dieses Video Waren oder Dienstleistungen im Austausch für eine Gegenleistung bewirbt. Ihr Video könnte Sie selbst, eine dritte Partei oder beides bewerben.", + "you_are_promoting_yourself": "Sie bewerben sich selbst oder Ihre eigene Marke.", + "this_video_will_be_classified_brand_organic": "Dieses Video wird als Brand Organic eingestuft.", + "you_are_promoting_another_brand": "Sie bewerben eine andere Marke oder eine dritte Partei.", + "this_video_will_be_classified_branded_content": "Dieses Video wird als Markeninhalt eingestuft.", + "by_posting_you_agree_to_tiktoks": "Mit dem Posten stimmen Sie den TikTok-Bedingungen zu", + "music_usage_confirmation": "Bestätigung der Musiknutzung", + "branded_content_policy": "Richtlinie für Markeninhalte", + "select_1": "--Auswählen--", + "select_flair": "--Flair auswählen--", + "link": "Link", + "add_subreddit": "Subreddit hinzufügen", + "please_add_at_least_one_subreddit": "Bitte fügen Sie mindestens einen Subreddit hinzu", + "add_community": "Community hinzufügen", + "select_post_type": "Beitragstyp auswählen...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Wir konnten kein Unternehmen finden, das mit Ihrer LinkedIn-Seite verbunden ist.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Bitte schließen Sie diesen Dialog, erstellen Sie eine neue Seite und fügen Sie erneut einen neuen Kanal hinzu.", + "select_linkedin_page": "LinkedIn-Seite auswählen:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Wir konnten kein Unternehmen finden, das mit den ausgewählten Seiten verbunden ist.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Wir empfehlen Ihnen, alle Seiten und alle Unternehmen zu verbinden.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Bitte schließen Sie diesen Dialog, löschen Sie Ihre Integration und fügen Sie erneut einen neuen Kanal \n hinzu.", + "select_instagram_account": "Instagram-Konto auswählen:", + "select_page": "Seite auswählen:", + "generate_image_with_ai": "Bild mit KI generieren", + "reconnect_channel": "Kanal erneut verbinden", + "update_credentials": "Zugangsdaten aktualisieren", + "additional_settings": "Zusätzliche Einstellungen", + "change_bot": "Bot wechseln", + "move_add_to_customer": "Zu Kunde verschieben / hinzufügen", + "edit_time_slots": "Zeitfenster bearbeiten", + "enable_channel": "Kanal aktivieren", + "disable_channel": "Kanal deaktivieren", + "add": "Hinzufügen", + "short_post": "Kurzer Beitrag", + "long_post": "Langer Beitrag", + "a_thread_with_short_posts": "Ein Thread mit kurzen Beiträgen", + "a_thread_with_long_posts": "Ein Thread mit langen Beiträgen", + "personal_voice_i_am_happy_to_announce": "Persönlicher Ton (\"Ich freue mich, bekannt zu geben\")", + "company_voice_we_are_happy_to_announce": "Unternehmenston (\"Wir freuen uns, bekannt zu geben\")", + "generate": "Generieren", + "generate_posts": "Beiträge generieren", + "purchase_a_life_time_pro_account_with_sol_199": "Kaufe ein lebenslanges PRO-Konto mit SOL ($199). Bitte beachte, dass es für diesen Kauf keine Rückerstattung gibt.", + "purchase_now": "Jetzt kaufen", + "pay_today": "Heute bezahlen", + "we_are_sorry_to_see_you_go": "Es tut uns leid, dass Sie gehen :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Würden Sie uns kurz mitteilen, was wir hätten besser machen können?", + "cancel_subscription": "Abonnement kündigen", + "plans": "Pläne", + "monthly": "MONATLICH", + "yearly": "JÄHRLICH", + "reactivate_subscription": "Abonnement reaktivieren", + "update_payment_method_invoices_history": "Zahlungsmethode aktualisieren / Rechnungsverlauf", + "cancel_subscription_1": "Abonnement kündigen", + "your_subscription_will_be_canceled_at": "Ihr Abonnement wird gekündigt am", + "you_will_never_be_charged_again": "Ihnen werden keine weiteren Kosten berechnet", + "current_package": "Aktuelles Paket:", + "next_package": "Nächstes Paket:", + "claim": "Einlösen", + "frequently_asked_questions": "Häufig gestellte Fragen", + "autopost": "Automatisches Posten", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Autopost kann Ihre neuen RSS-Einträge automatisch in sozialen Medien veröffentlichen", + "title": "Titel", + "add_an_autopost": "Autopost hinzufügen", + "post_content": "Beitragsinhalt", + "sign_up": "Registrieren", + "or": "ODER", + "by_registering_you_agree_to_our": "Mit der Registrierung stimmen Sie unseren", + "and": "und", + "terms_of_service": "Nutzungsbedingungen", + "privacy_policy": "Datenschutzrichtlinien", + "create_account": "Konto erstellen", + "already_have_an_account": "Sie haben bereits ein Konto?", + "sign_in": "Anmelden", + "sign_in_1": "Anmelden", + "don_t_have_an_account": "Sie haben noch kein Konto?", + "forgot_password": "Passwort vergessen", + "forgot_password_1": "Passwort vergessen", + "send_password_reset_email": "E-Mail zum Zurücksetzen des Passworts senden", + "go_back_to_login": "Zurück zum Login", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Wir haben Ihnen eine E-Mail mit einem Link zum Zurücksetzen Ihres Passworts gesendet.", + "change_password": "Passwort ändern", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Wir haben Ihr Passwort erfolgreich zurückgesetzt. Sie können sich jetzt anmelden mit ihrem", + "click_here_to_go_back_to_login": "Klicken Sie hier, um zum Login zurückzukehren", + "activate_your_account": "Aktivieren Sie Ihr Konto", + "thank_you_for_registering": "Vielen Dank für Ihre Registrierung!", + "please_check_your_email_to_activate_your_account": "Bitte überprüfen Sie Ihre E-Mails, um Ihr Konto zu aktivieren.", + "sign_in_with": "Anmelden mit", + "continue_with_google": "Mit Google fortfahren", + "sign_in_with_github": "Mit GitHub anmelden", + "continue_with_farcaster": "Mit Farcaster fortfahren", + "continue_with_your_wallet": "Mit Ihrem Wallet fortfahren", + "stars_per_day": "Sterne pro Tag", + "media": "Medien", + "check_launch": "Launch prüfen", + "load_your_github_repository_from_settings_to_see_analytics": "Laden Sie Ihr GitHub-Repository in den Einstellungen, um Analysen zu sehen", + "stars": "Sterne", + "processing_stars": "Sterne werden verarbeitet...", + "forks": "Forks", + "registration_is_disabled": "Registrierung ist deaktiviert", + "login_instead": "Stattdessen anmelden", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Wählen Sie eine Unterhaltung und chatten Sie los.", + "adding_channel_redirecting_you": "Kanal wird hinzugefügt, Sie werden weitergeleitet", + "could_not_add_provider": "Anbieter konnte nicht hinzugefügt werden.", + "you_are_being_redirected_back": "Sie werden zurückgeleitet", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Wir haben derzeit Schwierigkeiten, bitte versuchen Sie, die Seite zu aktualisieren.", + "post_not_found": "Beitrag nicht gefunden", + "publication_date": "Veröffentlichungsdatum:", + "analytics": "Analytiken:", + "launches": "Veröffentlichungen", + "plugs": "Plugs", + "billing": "Abrechnung", + "affiliate": "Partnerprogramm", + "monday": "Montag", + "tuesday": "Dienstag", + "wednesday": "Mittwoch", + "thursday": "Donnerstag", + "friday": "Freitag", + "saturday": "Samstag", + "sunday": "Sonntag", + "can_t_change_date_remove_post_from_publication": "Datum kann nicht geändert werden, entfernen Sie den Beitrag aus der Veröffentlichung", + "predicted_github_trending_change": "Prognostizierte GitHub-Trendänderung", + "duplicate_post": "Dupliziere Beitrag", + "preview_post": "Beitragsvorschau", + "post_statistics": "Beitragsstatistiken", + "draft": "Entwurf", + "week_number": "Woche {{number}}", + "top_title_edit_webhook": "Webhook bearbeiten", + "top_title_add_webhook": "Webhook hinzufügen", + "top_title_oh_no": "Oh nein", + "top_title_auto_plug": "Auto Plug: {{title}}", + "top_title_edit_autopost": "Autopost bearbeiten", + "top_title_add_autopost": "Autopost hinzufügen", + "top_title_send_a_new_offer": "Neues Angebot senden", + "top_title_media_library": "Medienbibliothek", + "top_title_add_signature": "Signatur hinzufügen", + "top_title_send_a_message_to": "Nachricht an {{name}} senden", + "top_title_configure_provider": "Anbieter konfigurieren", + "top_title_add_member": "Mitglied hinzufügen", + "top_title_change_bot_picture": "Bot-Bild ändern", + "top_title_create_a_new_tag": "Neuen Tag erstellen", + "top_title_select_company": "Unternehmen auswählen", + "top_title_additional_settings": "Zusätzliche Einstellungen", + "top_title_time_table_slots": "Zeitplan-Slots", + "top_title_design_media": "Medien gestalten", + "top_title_edit_post": "Beitrag bearbeiten", + "top_title_create_post": "Beitrag erstellen", + "top_title_move__add_to_customer": "Zu Kunde verschieben / hinzufügen", + "top_title_add_api_key_for": "API-Schlüssel für {{name}} hinzufügen", + "top_title_instance_url": "Instanz-URL", + "top_title_custom_url": "Benutzerdefinierte URL", + "top_title_add_channel": "Kanal hinzufügen", + "top_title_add_telegram": "Telegram hinzufügen", + "top_title_add_wrapcast": "Wrapcast hinzufügen", + "top_title_comments_for": "Kommentare für {{date}}", + "top_title_edit_signature": "Signatur bearbeiten", + "label_name": "Name", + "label_url": "URL", + "label_title": "Titel", + "label_subtitle": "Untertitel", + "label_email": "E-Mail", + "label_full_name": "Vollständiger Name", + "label_password": "Passwort", + "label_confirm_password": "Passwort bestätigen", + "label_api_key": "API-Schlüssel", + "label_instance_url": "Instanz-URL", + "label_custom_url": "Benutzerdefinierte URL", + "label_feedback": "Feedback", + "label_bio": "Biografie", + "label_role": "Rolle", + "label_country": "Land", + "label_audience_size": "Publikumsgröße auf allen Plattformen", + "label_pick_time": "Zeit auswählen", + "label_nickname": "Spitzname", + "label_write_anything": "Schreibe irgendetwas", + "label_output_format": "Ausgabeformat", + "label_add_pictures": "Bilder hinzufügen?", + "label_hour": "Stunde", + "label_minutes": "Minuten", + "label_select_publication": "Publikation auswählen", + "label_canonical_link": "Kanonischer Link", + "label_cover_picture": "Titelbild", + "label_tags": "Tags", + "label_topics": "Themen", + "label_tags_maximum_4": "Tags (maximal 4)", + "label_attachments": "Anhänge", + "label_type": "Typ", + "label_thumbnail": "Vorschaubild", + "label_who_can_see_this_video": "Wer kann dieses Video sehen?", + "label_content_posting_method": "Methode zum Posten von Inhalten", + "label_auto_add_music": "Musik automatisch hinzufügen", + "label_duet": "Duett", + "label_stitch": "Stitch", + "label_comments": "Kommentare", + "label_disclose_video_content": "Videoinhalt offenlegen", + "label_your_brand": "Deine Marke", + "label_branded_content": "Markeninhalt", + "label_subreddit": "Subreddit", + "label_flair": "Flair", + "label_media": "Medien", + "label_search_subreddit": "Subreddit suchen", + "label_delay": "Verzögerung", + "label_post_type": "Beitragstyp", + "label_collaborators": "Kollaboratoren (max. 3) – Konten dürfen nicht privat sein", + "label_community": "Community", + "label_search_community": "Community durchsuchen", + "label_channel": "Kanal", + "label_search_channel": "Kanal durchsuchen", + "label_select_channel": "Kanal auswählen", + "label_new_password": "Neues Passwort", + "label_repeat_password": "Passwort wiederholen", + "label_platform": "Plattform", + "label_price_per_post": "Preis pro Beitrag", + "label_integrations": "Integrationen", + "label_code": "Code", + "label_should_sync_last_post": "Sollen wir den aktuellen letzten Beitrag synchronisieren?", + "label_when_post": "Wann sollen wir es posten?", + "label_autogenerate_content": "Inhalt automatisch generieren", + "label_generate_picture": "Bild generieren?", + "label_company": "Unternehmen", + "label_tag_color": "Tag-Farbe", + "label_select_board": "Board auswählen", + "label_select_organization": "Organisation auswählen", + "label_auto_add_signature": "Signatur automatisch hinzufügen?", + "enable_color_picker": "Farbwähler aktivieren", + "cancel_the_color_picker": "Farbwähler abbrechen", + "no_content_yet": "Noch kein Inhalt", + "write_your_reply": "Schreibe deinen Beitrag...", + "add_a_tag": "Tag hinzufügen", + "add_to_calendar": "Zum Kalender hinzufügen", + "select_channels_from_circles": "Wähle Kanäle aus den obigen Kreisen aus", + "not_matching_order": "Nicht übereinstimmende Reihenfolge", + "submit_for_order": "Zur Bestellung einreichen", + "schedule": "Planen", + "update": "Aktualisieren", + "attachments": "Anhänge", + "tags": "Tags", + "public_to_everyone": "Öffentlich für alle", + "mutual_follow_friends": "Gegenseitig gefolgte Freunde", + "follower_of_creator": "Follower des Erstellers", + "self_only": "Nur ich", + "post_content_directly_to_tiktok": "Inhalt direkt auf TikTok posten", + "upload_content_to_tiktok_without_posting": "Inhalt auf TikTok hochladen, ohne zu posten", + "choose_upload_without_posting_description": "Wähle 'Hochladen ohne Posten', wenn du deinen Inhalt in der TikTok-App vor der Veröffentlichung überprüfen und bearbeiten möchtest. So hast du Zugriff auf die integrierten Bearbeitungstools von TikTok und kannst vor dem Posten letzte Anpassungen vornehmen.", + "faq_am_i_going_to_be_charged_by_postiz": "Werde ich von Postiz Gebühren berechnet bekommen?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Um die Kreditkarteninformationen zu bestätigen, wird Postiz 2$ reservieren und diesen Betrag sofort wieder freigeben.", + "faq_can_i_trust_postiz_gitroom": "Kann ich Postiz vertrauen?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz ist stolz darauf, Open Source zu sein! Wir glauben an eine ethische und transparente Kultur, was bedeutet, dass Postiz für immer bestehen wird. Du kannst den gesamten Code einsehen oder für persönliche Projekte nutzen. Um das Open-Source-Repository anzusehen, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">klicke hier</a>.", + "faq_what_are_channels": "Was sind Kanäle?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz ermöglicht es dir, deine Beiträge auf verschiedenen Kanälen zu planen.\nEin Kanal ist eine Veröffentlichungsplattform, auf der du deine Beiträge planen kannst.\nZum Beispiel kannst du deine Beiträge auf X, Facebook, Instagram, TikTok, YouTube, Reddit, LinkedIn, Dribbble, Threads oder Pinterest planen.", + "faq_what_are_team_members": "Was sind Teammitglieder?", + "faq_if_you_have_a_team_with_multiple_members": "Wenn du ein Team mit mehreren Mitgliedern hast, kannst du sie in deinen Arbeitsbereich einladen, um gemeinsam an Beiträgen zu arbeiten und ihre persönlichen Kanäle hinzuzufügen.", + "faq_what_is_ai_auto_complete": "Was ist KI-Autovervollständigung?", + "faq_we_automate_chatgpt_to_help_you_write": "Wir automatisieren ChatGPT, um dir beim Schreiben von Social-Media-Beiträgen und Artikeln zu helfen.", + "enter_email": "E-Mail eingeben", + "are_you_sure": "Bist du sicher?", + "yes_delete_it": "Ja, löschen!", + "no_cancel": "Nein, abbrechen!", + "are_you_sure_you_want_to_delete": "Bist du sicher, dass du {{name}} löschen möchtest?", + "are_you_sure_you_want_to_delete_the_image": "Bist du sicher, dass du das Bild löschen möchtest?", + "are_you_sure_you_want_to_logout": "Bist du sicher, dass du dich abmelden möchtest?", + "yes_logout": "Ja, abmelden", + "are_you_sure_you_want_to_delete_this_slot": "Bist du sicher, dass du diesen Slot löschen möchtest?", + "are_you_sure_you_want_to_delete_this_subreddit": "Bist du sicher, dass du dieses Subreddit löschen möchtest?", + "are_you_sure_you_want_to_close_the_window": "Bist du sicher, dass du das Fenster schließen möchtest?", + "yes_close": "Ja, schließen", + "link_copied_to_clipboard": "Link in die Zwischenablage kopiert", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Bist du sicher, dass du dieses Fenster schließen möchtest? (Alle Daten gehen verloren)", + "yes_close_it": "Ja, schließe es!", + "uploading_pictures": "Bilder werden hochgeladen...", + "agent_starting": "Agent startet", + "researching_your_content": "Dein Inhalt wird recherchiert...", + "understanding_the_category": "Kategorie wird verstanden...", + "finding_the_topic": "Thema wird gefunden...", + "finding_popular_posts_to_match_with": "Beliebte Beiträge werden gesucht...", + "generating_hook": "Hook wird generiert...", + "generating_content": "Inhalt wird generiert...", + "generating_pictures": "Bilder werden generiert...", + "finding_time_to_post": "Zeit zum Posten wird gesucht...", + "write_anything": "Schreibe irgendetwas", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Sie können alles schreiben, was Sie möchten, und auch Links hinzufügen. Wir übernehmen die Recherche für Sie...", + "output_format": "Ausgabeformat", + "add_pictures": "Bilder hinzufügen?", + "7_days": "7 Tage", + "30_days": "30 Tage", + "90_days": "90 Tage", + "start_7_days_free_trial": "7-tägige kostenlose Testversion starten", + "change_language": "Sprache ändern", + "that_a_wrap": "Das war's!\n\nWenn dir dieser Thread gefallen hat:\n\n1. Folge mir @{{username}} für mehr davon\n2. Retweete den untenstehenden Tweet, um diesen Thread mit deinem Publikum zu teilen\n", + "post_as_images_carousel": "Als Bilderkarussell posten", + "save_set": "Set speichern", + "separate_post": "Beitrag in mehrere Beiträge aufteilen", + "label_who_can_reply_to_this_post": "Wer kann auf diesen Beitrag antworten?", + "delete_integration": "Integration löschen", + "start_writing_your_post": "Beginne, deinen Beitrag für eine Vorschau zu schreiben", + "billing_join_over": "Schließen Sie sich über", + "billing_entrepreneurs_count": "18.000+ Unternehmern an", + "billing_who_use": "die nutzen", + "billing_postiz_grow_social": "Postiz, um ihre Social-Media-Präsenz zu steigern", + "billing_no_risk_trial": "100% risikofreie Testphase", + "billing_pay_nothing_7_days": "Zahlen Sie NICHTS in den ersten 7 Tagen", + "billing_cancel_anytime": "Jederzeit kündbar, ganz ohne Aufwand", + "billing_choose_plan": "Wählen Sie einen Plan", + "billing_monthly": "Monatlich", + "billing_yearly": "Jährlich", + "billing_20_percent_off": "20% Rabatt", + "billing_features": "Funktionen", + "billing_channel": "Kanal", + "billing_channels": "Kanäle", + "billing_unlimited": "Unbegrenzt", + "billing_posts_per_month": "Beiträge pro Monat", + "billing_unlimited_team_members": "Unbegrenzte Teammitglieder", + "billing_ai_auto_complete": "KI-Autovervollständigung", + "billing_ai_copilots": "KI-Copiloten", + "billing_ai_autocomplete": "KI-Autovervollständigung", + "billing_advanced_picture_editor": "Erweiterter Bildeditor", + "billing_ai_images_per_month": "KI-Bilder pro Monat", + "billing_ai_videos_per_month": "KI-Videos pro Monat", + "billing_billing_address": "Rechnungsadresse", + "billing_payment": "Zahlung", + "billing_powered_by_stripe": "Bereitgestellt von Stripe", + "billing_your_7_day_trial_is": "Ihre 7-tägige Testphase ist", + "billing_100_percent_free": "100% kostenlos", + "billing_ending": "endet", + "billing_cancel_anytime_short": "Jederzeit kündbar.", + "billing_pay_0_start_trial": "Heute 0 $ zahlen – Starten Sie Ihre kostenlose Testphase!", + "billing_pay_now": "Jetzt bezahlen", + "billing_per_month": "/ Monat" } diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index fea34cc3..d1029414 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -502,5 +502,38 @@ "separate_post": "Separate post to multiple posts", "label_who_can_reply_to_this_post": "Who can reply to this post?", "delete_integration": "Delete Integration", - "start_writing_your_post": "Start writing your post for a preview" + "start_writing_your_post": "Start writing your post for a preview", + "billing_join_over": "Join Over", + "billing_entrepreneurs_count": "18,000+ Entrepreneurs", + "billing_who_use": "who use", + "billing_postiz_grow_social": "Postiz To Grow Their Social Presence", + "billing_no_risk_trial": "100% No-Risk Free Trial", + "billing_pay_nothing_7_days": "Pay NOTHING for the first 7-days", + "billing_cancel_anytime": "Cancel anytime, hassle-free", + "billing_choose_plan": "Choose a Plan", + "billing_monthly": "Monthly", + "billing_yearly": "Yearly", + "billing_20_percent_off": "20% Off", + "billing_features": "Features", + "billing_channel": "channel", + "billing_channels": "channels", + "billing_unlimited": "Unlimited", + "billing_posts_per_month": "posts per month", + "billing_unlimited_team_members": "Unlimited team members", + "billing_ai_auto_complete": "AI auto-complete", + "billing_ai_copilots": "AI copilots", + "billing_ai_autocomplete": "AI Autocomplete", + "billing_advanced_picture_editor": "Advanced Picture Editor", + "billing_ai_images_per_month": "AI Images per month", + "billing_ai_videos_per_month": "AI Videos per month", + "billing_billing_address": "Billing Address", + "billing_payment": "Payment", + "billing_powered_by_stripe": "Powered by Stripe", + "billing_your_7_day_trial_is": "Your 7-day trial is", + "billing_100_percent_free": "100% free", + "billing_ending": "ending", + "billing_cancel_anytime_short": "Cancel anytime.", + "billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!", + "billing_pay_now": "Pay Now", + "billing_per_month": "/ month" } diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index 0b19f055..ab1f97a3 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -1,507 +1,539 @@ { - "calendar": "Calendario", - "webhooks": "Webhooks", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Los webhooks son una forma de recibir notificaciones cuando algo sucede en Postiz mediante una solicitud HTTP.", - "name": "Nombre", - "url": "URL", - "edit": "Editar", - "delete": "Eliminar", - "add_a_webhook": "Agregar un webhook", - "save": "Guardar", - "send_test": "Enviar prueba", - "select_role": "Seleccionar rol", - "video_made_with_ai": "Video hecho con IA", - "please_add_at_least": "Por favor, añade al menos 20 caracteres", - "send_invitation_via_email": "¿Enviar invitación por correo electrónico?", - "global_settings": "Configuración global", - "copy_id": "Copiar ID del canal", - "team_members": "Miembros del equipo", - "invite_your_assistant_or_team_member_to_manage_your_account": "Invita a tu asistente o miembro del equipo para que gestione tu cuenta", - "remove": "Eliminar", - "add_another_member": "Agregar otro miembro", - "signatures": "Firmas", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Puedes agregar firmas a tu cuenta para usarlas en tus publicaciones.", - "content": "Contenido", - "auto_add": "¿Agregar automáticamente?", - "actions": "Acciones", - "use_signature": "Usar firma", - "add_a_signature": "Agregar una firma", - "no": "No", - "yes": "Sí", - "your_git_repository": "Tu repositorio Git", - "connect_your_github_repository_to_receive_updates_and_analytics": "Conecta tu repositorio de GitHub para recibir actualizaciones y análisis", - "connected": "Conectado:", - "disconnect": "Desconectar", - "connect_your_repository": "Conecta tu repositorio", - "cancel": "Cancelar", - "connect": "Conectar", - "public_api": "API pública", - "check_n8n": "Echa un vistazo a nuestro nodo personalizado de N8N para Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Usa la API de Postiz para integrarla con tus herramientas.", - "read_how_to_use_it_over_the_documentation": "Lee cómo usarla en la documentación.", - "reveal": "Revelar", - "copy_key": "Copiar clave", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "¡Conecta el servidor MCP de Postiz a tu cliente (transmisión HTTP) para programar tus publicaciones más rápido!", - "share_with_a_client": "Compartir con un cliente", - "post": "Publicar", - "comments": "Comentarios", - "user": "Usuario", - "login_register_to_add_comments": "Inicia sesión / Regístrate para agregar comentarios", - "status": "Estado:", - "there_are_not_plugs_matching_your_channels": "No hay conectores que coincidan con tus canales", - "you_have_to_add_x_or_linkedin_or_threads": "Debes agregar: X o LinkedIn o Threads", - "go_to_the_calendar_to_add_channels": "Ve al calendario para agregar canales", - "channels": "Canales", - "activate": "Activar", - "this_channel_needs_to_be_refreshed": "Este canal necesita ser actualizado,", - "click_here_to_refresh": "haz clic aquí para actualizar", - "can_t_show_analytics_yet": "Aún no se pueden mostrar las analíticas", - "you_have_to_add_social_media_channels": "Tienes que añadir canales de redes sociales", - "supported": "Soportado:", - "step": "PASO", - "skip_onboarding": "Omitir introducción", - "onboarding": "Introducción", - "next": "Siguiente", - "you_are_done_from_here_you_can": "Has terminado, desde aquí puedes:", - "view_analytics": "Ver analíticas", - "schedule_a_new_post": "Programar una nueva publicación", - "to_sell_posts_you_would_have_to": "Para vender publicaciones debes:", - "1_connect_at_least_one_channel": "1. Conectar al menos un canal", - "2_connect_you_bank_account": "2. Conectar tu cuenta bancaria", - "go_back_to_connect_channels": "Volver para conectar canales", - "move_to_the_seller_page_to_connect_you_bank": "Ir a la página de vendedor para conectar tu banco", - "connect_channels": "Conectar canales", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Conecta tus canales de redes sociales y sitios de publicación para\n programar publicaciones más tarde", - "social": "Social", - "publishing_platforms": "Plataformas de publicación", - "no_channels": "Aún no hay canales", - "connect_your_accounts": "Conecta tus cuentas sociales para empezar a programar, publicar y analizar, todo en un solo lugar.", - "notifications": "Notificaciones", - "no_notifications": "Sin notificaciones", - "send_message": "Enviar mensaje", - "mar_28": "28 de mar", - "there_are_no_messages_yet": "Aún no hay mensajes.", - "checkout_the_marketplace": "Explora el Marketplace", - "go_to_marketplace": "Ir al Marketplace", - "all_messages": "Todos los mensajes", - "previous": "Anterior", - "select_or_upload_pictures_maximum_5_at_a_time": "Selecciona o sube imágenes (máximo 5 a la vez)", - "you_can_also_drag_drop_pictures": "También puedes arrastrar y soltar imágenes", - "you_don_t_have_any_assets_yet": "Aún no tienes ningún recurso.", - "click_the_button_below_to_upload_one": "Haz clic en el botón de abajo para subir uno", - "click_the_button_below_to_upload_many": "", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "Agregar medios seleccionados", - "insert_media": "Insertar medios", - "design_media": "Diseñar medios", - "select": "Seleccionar", - "editor": "Editor", - "clear": "Limpiar", - "order_completed": "Pedido completado", - "the_order_has_been_completed": "El pedido ha sido completado", - "post_has_been_published": "La publicación ha sido publicada", - "url_1": "URL:", - "new_offer": "Nueva oferta", - "platform": "Plataforma", - "posts": "Publicaciones", - "pay_accept_offer": "Pagar y aceptar oferta", - "accepted": "Aceptado", - "post_draft": "Borrador de publicación", - "revision_needed": "Revisión necesaria", - "approve": "Aprobar", - "preview": "Vista previa", - "revision_requested": "Revisión solicitada", - "accepted_1": "ACEPTADO", - "cancelled_by_the_seller": "Cancelado por el vendedor", - "please_select_your_country_where_your_business_is": "Por favor, selecciona el país donde se encuentra tu negocio.", - "select_country": "--SELECCIONA PAÍS--", - "connect_bank_account": "Conectar cuenta bancaria", - "seller_mode": "Modo vendedor", - "active": "Activo", - "details": "Detalles", - "audience_size": "Tamaño de la audiencia", - "add_another_platform": "Agregar otra plataforma", - "send_an_offer_for": "Enviar una oferta por $", - "complete_order_and_pay_early": "Completar pedido y pagar anticipadamente", - "order_in_progress": "Pedido en curso", - "create_a_new_offer": "Crear una nueva oferta", - "orders": "Pedidos", - "price": "Precio", - "state": "Estado", - "showing": "Mostrando", - "to": "a", - "from": "de", - "results": "Resultados", - "content_writer": "Redactor de contenido", - "influencer": "Influencer", - "request_service": "Solicitar servicio", - "the_marketplace_is_not_opened_yet": "El mercado aún no está abierto", - "check_again_soon": "¡Vuelve a comprobar pronto!", - "filter": "Filtrar", - "result": "Resultado", - "seller": "Vendedor", - "buyer": "Comprador", - "discord_support": "Soporte de Discord", - "teams": "Equipos", - "webhooks_1": "Webhooks", - "auto_post": "Publicación automática", - "logout_from": "Cerrar sesión de", - "join_10000_entrepreneurs_who_use_postiz": "Únete a más de 10,000 emprendedores que usan Postiz", - "to_manage_all_your_social_media_channels": "Para gestionar todos tus canales de redes sociales", - "100_no_risk_trial": "Prueba sin riesgo 100%", - "pay_nothing_for_the_first_7_days": "No pagues nada durante los primeros 7 días", - "cancel_anytime_hassle_free": "Cancela en cualquier momento, sin complicaciones", - "add_free_subscription": "-- AÑADIR SUSCRIPCIÓN GRATUITA --", - "currently_impersonating": "Actualmente suplantando", - "user_1": "usuario:", - "drag_n_drop_some_files_here": "Arrastra y suelta algunos archivos aquí", - "add_time_slot": "Agregar franja horaria", - "add_slot": "Agregar espacio", - "cancel_publication": "Cancelar publicación", - "statistics": "Estadísticas", - "loading": "Cargando", - "short_link": "Enlace corto", - "original_link": "Enlace original", - "clicks": "Clics", - "selected_customer": "Cliente seleccionado", - "customer": "Cliente:", - "repeat_post_every": "Repetir publicación cada...", - "use_this_media": "Usar este medio", - "create_new_post": "Crear publicación", - "update_post": "Actualizar publicación existente", - "merge_comments_into_one_post": "Unir comentarios en una sola publicación", - "accounts_that_will_engage": "Cuentas que interactuarán:", - "day": "Día", - "week": "Semana", - "month": "Mes", - "remove_from_customer": "Eliminar del cliente", - "show_more": "+ Mostrar más", - "show_less": "- Mostrar menos", - "upload": "Subir", - "ai": "IA", - "add_channel": "Agregar canal", - "add_platform": "Agregar plataforma", - "articles": "Artículos", - "add_comment": "Agregar comentario", - "add_post": "Agregar publicación en un hilo", - "add_comment_or_post": "Agregar comentario / publicación", - "you_are_in_global_editing_mode": "Estás en modo de edición global", - "the_post_should_be_at_least_6_characters_long": "La publicación debe tener al menos 6 caracteres", - "are_you_sure_you_want_to_delete_post": "¿Estás seguro de que deseas eliminar esta publicación?", - "post_deleted_successfully": "Publicación eliminada con éxito", - "delete_post": "Eliminar publicación", - "save_as_draft": "Guardar como borrador", - "post_now": "Publicar ahora", - "please_add": "Por favor agrega", - "to_your_telegram_group_channel_and_click_here": "a tu\ngrupo/canal de Telegram y haz clic aquí:", - "connect_telegram": "Conectar Telegram", - "please_add_the_following_command_in_your_chat": "Por favor agrega el siguiente comando en tu chat:", - "copy": "Copiar", - "settings": "Configuración", - "integrations": "Integraciones", - "add_integration": "Agregar integración", - "you_are_now_editing_only": "Ahora solo estás editando", - "tag_a_company": "Etiquetar una empresa", - "video_length_is_invalid_must_be_up_to": "La duración del video no es válida, debe ser de hasta", - "seconds": "segundos", - "this_feature_available_only_for_photos": "Esta función está disponible solo para fotos, agregará una música predeterminada que podrás cambiar después.", - "allow_user_to": "Permitir al usuario:", - "your_video_will_be_labeled_promotional": "Tu video será etiquetado como \"Contenido promocional\".", - "this_cannot_be_changed_once_posted": "Esto no se puede cambiar una vez que tu video sea publicado.", - "turn_on_to_disclose_video_promotes": "Activa para revelar que este video promociona bienes o servicios a cambio de algo de valor. Tu video podría promocionarte a ti, a un tercero o a ambos.", - "you_are_promoting_yourself": "Te estás promocionando a ti mismo o a tu propia marca.", - "this_video_will_be_classified_brand_organic": "Este video será clasificado como Marca Orgánica.", - "you_are_promoting_another_brand": "Estás promocionando otra marca o un tercero.", - "this_video_will_be_classified_branded_content": "Este video será clasificado como Contenido de Marca.", - "by_posting_you_agree_to_tiktoks": "Al publicar, aceptas las condiciones de TikTok", - "music_usage_confirmation": "Confirmación de uso de música", - "branded_content_policy": "Política de contenido de marca", - "select_1": "--Seleccionar--", - "select_flair": "--Seleccionar distintivo--", - "link": "Enlace", - "add_subreddit": "Agregar Subreddit", - "please_add_at_least_one_subreddit": "Por favor, agrega al menos un Subreddit", - "add_community": "Agregar comunidad", - "select_post_type": "Seleccionar tipo de publicación...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "No pudimos encontrar ningún negocio conectado a tu página de LinkedIn.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Por favor, cierra este diálogo, crea una nueva página y agrega un nuevo canal nuevamente.", - "select_linkedin_page": "Selecciona la página de LinkedIn:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "No pudimos encontrar ningún negocio conectado a las páginas seleccionadas.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Te recomendamos conectar todas las páginas y todos los negocios.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Por favor, cierra este diálogo, elimina tu integración y agrega un nuevo canal nuevamente.", - "select_instagram_account": "Selecciona la cuenta de Instagram:", - "select_page": "Selecciona la página:", - "generate_image_with_ai": "Generar imagen con IA", - "reconnect_channel": "Reconectar canal", - "update_credentials": "Actualizar credenciales", - "additional_settings": "Configuraciones adicionales", - "change_bot": "Cambiar bot", - "move_add_to_customer": "Mover / agregar al cliente", - "edit_time_slots": "Editar franjas horarias", - "enable_channel": "Habilitar canal", - "disable_channel": "Deshabilitar canal", - "add": "Agregar", - "short_post": "Publicación corta", - "long_post": "Publicación larga", - "a_thread_with_short_posts": "Un hilo con publicaciones cortas", - "a_thread_with_long_posts": "Un hilo con publicaciones largas", - "personal_voice_i_am_happy_to_announce": "Voz personal (\"Me complace anunciar\")", - "company_voice_we_are_happy_to_announce": "Voz de la empresa (\"Nos complace anunciar\")", - "generate": "Generar", - "generate_posts": "Generar publicaciones", - "purchase_a_life_time_pro_account_with_sol_199": "Compra una cuenta PRO de por vida con SOL ($199). Ten en cuenta que no hay reembolso para esta compra.", - "purchase_now": "Comprar ahora", - "pay_today": "Pagar hoy", - "we_are_sorry_to_see_you_go": "Lamentamos verte partir :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "¿Podrías decirnos brevemente qué podríamos haber hecho mejor?", - "cancel_subscription": "Cancelar suscripción", - "plans": "Planes", - "monthly": "MENSUAL", - "yearly": "ANUAL", - "reactivate_subscription": "Reactivar suscripción", - "update_payment_method_invoices_history": "Actualizar método de pago / Historial de facturas", - "cancel_subscription_1": "Cancelar suscripción", - "your_subscription_will_be_canceled_at": "Tu suscripción se cancelará el", - "you_will_never_be_charged_again": "Nunca se te volverá a cobrar", - "current_package": "Paquete actual:", - "next_package": "Próximo paquete:", - "claim": "Reclamar", - "frequently_asked_questions": "Preguntas frecuentes", - "autopost": "Autopublicar", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "La autopublicación puede publicar automáticamente tus nuevos elementos RSS en las redes sociales", - "title": "Título", - "add_an_autopost": "Agregar una autopublicación", - "post_content": "Contenido de la publicación", - "sign_up": "Registrarse", - "or": "O", - "by_registering_you_agree_to_our": "Al registrarte, aceptas nuestros", - "and": "y", - "terms_of_service": "Términos de servicio", - "privacy_policy": "Política de privacidad", - "create_account": "Crear cuenta", - "already_have_an_account": "¿Ya tienes una cuenta?", - "sign_in": "Iniciar sesión", - "sign_in_1": "Iniciar sesión", - "don_t_have_an_account": "¿No tienes una cuenta?", - "forgot_password": "¿Olvidaste tu contraseña?", - "forgot_password_1": "¿Olvidaste tu contraseña?", - "send_password_reset_email": "Enviar correo para restablecer contraseña", - "go_back_to_login": "Volver al inicio de sesión", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Te hemos enviado un correo electrónico con un enlace para restablecer tu contraseña.", - "change_password": "Cambiar contraseña", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Hemos restablecido tu contraseña correctamente. Ahora puedes iniciar sesión con tu", - "click_here_to_go_back_to_login": "Haz clic aquí para volver a iniciar sesión", - "activate_your_account": "Activa tu cuenta", - "thank_you_for_registering": "¡Gracias por registrarte!", - "please_check_your_email_to_activate_your_account": "Por favor, revisa tu correo electrónico para activar tu cuenta.", - "sign_in_with": "Iniciar sesión con", - "continue_with_google": "Continuar con Google", - "sign_in_with_github": "Iniciar sesión con GitHub", - "continue_with_farcaster": "Continuar con Farcaster", - "continue_with_your_wallet": "Continuar con tu Wallet", - "stars_per_day": "Estrellas por día", - "media": "Medios", - "check_launch": "Verificar lanzamiento", - "load_your_github_repository_from_settings_to_see_analytics": "Carga tu repositorio de GitHub desde la configuración para ver las analíticas", - "stars": "Estrellas", - "processing_stars": "Procesando estrellas...", - "forks": "Forks", - "registration_is_disabled": "El registro está deshabilitado", - "login_instead": "Inicia sesión en su lugar", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Selecciona una conversación y comienza a chatear.", - "adding_channel_redirecting_you": "Agregando canal, redirigiéndote", - "could_not_add_provider": "No se pudo agregar el proveedor.", - "you_are_being_redirected_back": "Estás siendo redirigido de vuelta", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Estamos experimentando algunas dificultades, intenta actualizar la página", - "post_not_found": "Publicación no encontrada", - "publication_date": "Fecha de publicación:", - "analytics": "Analíticas", - "launches": "Lanzamientos", - "plugs": "Enchufes", - "billing": "Facturación", - "affiliate": "Afiliado", - "monday": "Lunes", - "tuesday": "Martes", - "wednesday": "Miércoles", - "thursday": "Jueves", - "friday": "Viernes", - "saturday": "Sábado", - "sunday": "Domingo", - "can_t_change_date_remove_post_from_publication": "No se puede cambiar la fecha, elimina la publicación de la publicación", - "predicted_github_trending_change": "Cambio de tendencia de GitHub previsto", - "duplicate_post": "Publicación duplicada", - "preview_post": "Vista previa de la publicación", - "post_statistics": "Estadísticas de la publicación", - "draft": "Borrador", - "week_number": "Semana {{number}}", - "top_title_edit_webhook": "Editar webhook", - "top_title_add_webhook": "Agregar webhook", - "top_title_oh_no": "Oh no", - "top_title_auto_plug": "Auto Plug: {{title}}", - "top_title_edit_autopost": "Editar autopublicación", - "top_title_add_autopost": "Agregar autopublicación", - "top_title_send_a_new_offer": "Enviar una nueva oferta", - "top_title_media_library": "Biblioteca de medios", - "top_title_add_signature": "Agregar firma", - "top_title_send_a_message_to": "Enviar un mensaje a {{name}}", - "top_title_configure_provider": "Configurar proveedor", - "top_title_add_member": "Agregar miembro", - "top_title_change_bot_picture": "Cambiar imagen del bot", - "top_title_create_a_new_tag": "Crear una nueva etiqueta", - "top_title_select_company": "Seleccionar empresa", - "top_title_additional_settings": "Configuraciones adicionales", - "top_title_time_table_slots": "Intervalos de horario", - "top_title_design_media": "Diseñar medios", - "top_title_edit_post": "Editar publicación", - "top_title_create_post": "Crear publicación", - "top_title_move__add_to_customer": "Mover / Agregar al cliente", - "top_title_add_api_key_for": "Agregar clave API para {{name}}", - "top_title_instance_url": "URL de la instancia", - "top_title_custom_url": "URL personalizada", - "top_title_add_channel": "Agregar canal", - "top_title_add_telegram": "Agregar Telegram", - "top_title_add_wrapcast": "Agregar Wrapcast", - "top_title_comments_for": "Comentarios para {{date}}", - "top_title_edit_signature": "Editar firma", - "label_name": "Nombre", - "label_url": "URL", - "label_title": "Título", - "label_subtitle": "Subtítulo", - "label_email": "Correo electrónico", - "label_full_name": "Nombre completo", - "label_password": "Contraseña", - "label_confirm_password": "Confirmar contraseña", - "label_api_key": "Clave API", - "label_instance_url": "URL de la instancia", - "label_custom_url": "URL personalizada", - "label_feedback": "Comentarios", - "label_bio": "Biografía", - "label_role": "Rol", - "label_country": "País", - "label_audience_size": "Tamaño de la audiencia en todas las plataformas", - "label_pick_time": "Seleccionar hora", - "label_nickname": "Apodo", - "label_write_anything": "Escribe cualquier cosa", - "label_output_format": "Formato de salida", - "label_add_pictures": "¿Agregar imágenes?", - "label_hour": "Hora", - "label_minutes": "Minutos", - "label_select_publication": "Seleccionar publicación", - "label_canonical_link": "Enlace canónico", - "label_cover_picture": "Imagen de portada", - "label_tags": "Etiquetas", - "label_topics": "Temas", - "label_tags_maximum_4": "Etiquetas (máximo 4)", - "label_attachments": "Archivos adjuntos", - "label_type": "Tipo", - "label_thumbnail": "Miniatura", - "label_who_can_see_this_video": "¿Quién puede ver este video?", - "label_content_posting_method": "Método de publicación de contenido", - "label_auto_add_music": "Agregar música automáticamente", - "label_duet": "Dúo", - "label_stitch": "Stitch", - "label_comments": "Comentarios", - "label_disclose_video_content": "Revelar contenido del video", - "label_your_brand": "Tu marca", - "label_branded_content": "Contenido de marca", - "label_subreddit": "Subreddit", - "label_flair": "Distintivo", - "label_media": "Medios", - "label_search_subreddit": "Buscar Subreddit", - "label_delay": "Retraso", - "label_post_type": "Tipo de publicación", - "label_collaborators": "Colaboradores (máx. 3) - las cuentas no pueden ser privadas", - "label_community": "Comunidad", - "label_search_community": "Buscar comunidad", - "label_channel": "Canal", - "label_search_channel": "Buscar canal", - "label_select_channel": "Seleccionar canal", - "label_new_password": "Nueva contraseña", - "label_repeat_password": "Repetir contraseña", - "label_platform": "Plataforma", - "label_price_per_post": "Precio por publicación", - "label_integrations": "Integraciones", - "label_code": "Código", - "label_should_sync_last_post": "¿Deberíamos sincronizar la última publicación actual?", - "label_when_post": "¿Cuándo deberíamos publicarlo?", - "label_autogenerate_content": "Autogenerar contenido", - "label_generate_picture": "¿Generar imagen?", - "label_company": "Empresa", - "label_tag_color": "Color de la etiqueta", - "label_select_board": "Seleccionar tablero", - "label_select_organization": "Seleccionar organización", - "label_auto_add_signature": "¿Agregar firma automáticamente?", - "enable_color_picker": "Habilitar selector de color", - "cancel_the_color_picker": "Cancelar el selector de color", - "no_content_yet": "Aún no hay contenido", - "write_your_reply": "Escribe tu publicación...", - "add_a_tag": "Agregar una etiqueta", - "add_to_calendar": "Agregar al calendario", - "select_channels_from_circles": "Selecciona canales de los círculos de arriba", - "not_matching_order": "El orden no coincide", - "submit_for_order": "Enviar para pedido", - "schedule": "Programar", - "update": "Actualizar", - "attachments": "Archivos adjuntos", - "tags": "Etiquetas", - "public_to_everyone": "Público para todos", - "mutual_follow_friends": "Amigos con seguimiento mutuo", - "follower_of_creator": "Seguidor del creador", - "self_only": "Solo yo", - "post_content_directly_to_tiktok": "Publicar contenido directamente en TikTok", - "upload_content_to_tiktok_without_posting": "Subir contenido a TikTok sin publicarlo", - "choose_upload_without_posting_description": "Elige subir sin publicar si quieres revisar y editar tu contenido en la aplicación de TikTok antes de publicarlo. Esto te da acceso a las herramientas de edición integradas de TikTok y te permite hacer ajustes finales antes de publicar.", - "faq_am_i_going_to_be_charged_by_postiz": "¿Me va a cobrar Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar la información de la tarjeta de crédito, Postiz retendrá $2 y los liberará inmediatamente", - "faq_can_i_trust_postiz_gitroom": "¿Puedo confiar en Postiz?", - "faq_postiz_gitroom_is_proudly_open_source": "¡Postiz es orgullosamente de código abierto! Creemos en una cultura ética y transparente, lo que significa que Postiz vivirá para siempre. Puedes revisar todo el código o usarlo para proyectos personales. Para ver el repositorio de código abierto, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">haz clic aquí</a>.", - "faq_what_are_channels": "¿Qué son los canales?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz te permite programar tus publicaciones entre diferentes canales.\nUn canal es una plataforma de publicación donde puedes programar tus publicaciones.\nPor ejemplo, puedes programar tus publicaciones en X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads y Pinterest.", - "faq_what_are_team_members": "¿Qué son los miembros del equipo?", - "faq_if_you_have_a_team_with_multiple_members": "Si tienes un equipo con varios miembros, puedes invitarlos a tu espacio de trabajo para colaborar en tus publicaciones y agregar sus canales personales", - "faq_what_is_ai_auto_complete": "¿Qué es la autocompletación con IA?", - "faq_we_automate_chatgpt_to_help_you_write": "Automatizamos ChatGPT para ayudarte a escribir publicaciones sociales y artículos.", - "enter_email": "Introduce el correo electrónico", - "are_you_sure": "¿Estás seguro?", - "yes_delete_it": "¡Sí, bórralo!", - "no_cancel": "No, cancelar", - "are_you_sure_you_want_to_delete": "¿Estás seguro de que quieres eliminar {{name}}?", - "are_you_sure_you_want_to_delete_the_image": "¿Estás seguro de que quieres eliminar la imagen?", - "are_you_sure_you_want_to_logout": "¿Estás seguro de que quieres cerrar sesión?", - "yes_logout": "Sí, cerrar sesión", - "are_you_sure_you_want_to_delete_this_slot": "¿Estás seguro de que quieres eliminar este espacio?", - "are_you_sure_you_want_to_delete_this_subreddit": "¿Estás seguro de que quieres eliminar este Subreddit?", - "are_you_sure_you_want_to_close_the_window": "¿Estás seguro de que quieres cerrar la ventana?", - "yes_close": "Sí, cerrar", - "link_copied_to_clipboard": "Enlace copiado al portapapeles", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "¿Estás seguro de que quieres cerrar este modal? (todos los datos se perderán)", - "yes_close_it": "¡Sí, ciérralo!", - "uploading_pictures": "Subiendo imágenes...", - "agent_starting": "Iniciando agente", - "researching_your_content": "Investigando tu contenido...", - "understanding_the_category": "Entendiendo la categoría...", - "finding_the_topic": "Buscando el tema...", - "finding_popular_posts_to_match_with": "Buscando publicaciones populares para emparejar...", - "generating_hook": "Generando gancho...", - "generating_content": "Generando contenido...", - "generating_pictures": "Generando imágenes...", - "finding_time_to_post": "Buscando el mejor momento para publicar...", - "write_anything": "Escribe lo que sea", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Puedes escribir lo que quieras y también añadir enlaces, nosotros haremos la investigación por ti...", - "output_format": "Formato de salida", - "add_pictures": "¿Agregar imágenes?", - "7_days": "7 días", - "30_days": "30 días", - "90_days": "90 días", - "start_7_days_free_trial": "Comienza la prueba gratuita de 7 días", - "change_language": "Cambiar idioma", - "that_a_wrap": "¡Eso es todo!\n\nSi te gustó este hilo:\n\n1. Sígueme en @{{username}} para más contenido como este\n2. Haz RT al tuit de abajo para compartir este hilo con tu audiencia\n", - "post_as_images_carousel": "Publicar como carrusel de imágenes", - "save_set": "Guardar conjunto", - "separate_post": "Separar publicación en varias publicaciones", - "label_who_can_reply_to_this_post": "¿Quién puede responder a esta publicación?", - "delete_integration": "Eliminar integración", - "start_writing_your_post": "Comienza a escribir tu publicación para obtener una vista previa" + "calendar": "Calendario", + "webhooks": "Webhooks", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Los webhooks son una forma de recibir notificaciones cuando algo sucede en Postiz mediante una solicitud HTTP.", + "name": "Nombre", + "url": "URL", + "edit": "Editar", + "delete": "Eliminar", + "add_a_webhook": "Agregar un webhook", + "save": "Guardar", + "send_test": "Enviar prueba", + "select_role": "Seleccionar rol", + "video_made_with_ai": "Video hecho con IA", + "please_add_at_least": "Por favor, añade al menos 20 caracteres", + "send_invitation_via_email": "¿Enviar invitación por correo electrónico?", + "global_settings": "Configuración global", + "copy_id": "Copiar ID del canal", + "team_members": "Miembros del equipo", + "invite_your_assistant_or_team_member_to_manage_your_account": "Invita a tu asistente o miembro del equipo para que gestione tu cuenta", + "remove": "Eliminar", + "add_another_member": "Agregar otro miembro", + "signatures": "Firmas", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Puedes agregar firmas a tu cuenta para usarlas en tus publicaciones.", + "content": "Contenido", + "auto_add": "¿Agregar automáticamente?", + "actions": "Acciones", + "use_signature": "Usar firma", + "add_a_signature": "Agregar una firma", + "no": "No", + "yes": "Sí", + "your_git_repository": "Tu repositorio Git", + "connect_your_github_repository_to_receive_updates_and_analytics": "Conecta tu repositorio de GitHub para recibir actualizaciones y análisis", + "connected": "Conectado:", + "disconnect": "Desconectar", + "connect_your_repository": "Conecta tu repositorio", + "cancel": "Cancelar", + "connect": "Conectar", + "public_api": "API pública", + "check_n8n": "Echa un vistazo a nuestro nodo personalizado de N8N para Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Usa la API de Postiz para integrarla con tus herramientas.", + "read_how_to_use_it_over_the_documentation": "Lee cómo usarla en la documentación.", + "reveal": "Revelar", + "copy_key": "Copiar clave", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "¡Conecta el servidor MCP de Postiz a tu cliente (transmisión HTTP) para programar tus publicaciones más rápido!", + "share_with_a_client": "Compartir con un cliente", + "post": "Publicar", + "comments": "Comentarios", + "user": "Usuario", + "login_register_to_add_comments": "Inicia sesión / Regístrate para agregar comentarios", + "status": "Estado:", + "there_are_not_plugs_matching_your_channels": "No hay conectores que coincidan con tus canales", + "you_have_to_add_x_or_linkedin_or_threads": "Debes agregar: X o LinkedIn o Threads", + "go_to_the_calendar_to_add_channels": "Ve al calendario para agregar canales", + "channels": "Canales", + "activate": "Activar", + "this_channel_needs_to_be_refreshed": "Este canal necesita ser actualizado,", + "click_here_to_refresh": "haz clic aquí para actualizar", + "can_t_show_analytics_yet": "Aún no se pueden mostrar las analíticas", + "you_have_to_add_social_media_channels": "Tienes que añadir canales de redes sociales", + "supported": "Soportado:", + "step": "PASO", + "skip_onboarding": "Omitir introducción", + "onboarding": "Introducción", + "next": "Siguiente", + "you_are_done_from_here_you_can": "Has terminado, desde aquí puedes:", + "view_analytics": "Ver analíticas", + "schedule_a_new_post": "Programar una nueva publicación", + "to_sell_posts_you_would_have_to": "Para vender publicaciones debes:", + "1_connect_at_least_one_channel": "1. Conectar al menos un canal", + "2_connect_you_bank_account": "2. Conectar tu cuenta bancaria", + "go_back_to_connect_channels": "Volver para conectar canales", + "move_to_the_seller_page_to_connect_you_bank": "Ir a la página de vendedor para conectar tu banco", + "connect_channels": "Conectar canales", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Conecta tus canales de redes sociales y sitios de publicación para\n programar publicaciones más tarde", + "social": "Social", + "publishing_platforms": "Plataformas de publicación", + "no_channels": "Aún no hay canales", + "connect_your_accounts": "Conecta tus cuentas sociales para empezar a programar, publicar y analizar, todo en un solo lugar.", + "notifications": "Notificaciones", + "no_notifications": "Sin notificaciones", + "send_message": "Enviar mensaje", + "mar_28": "28 de mar", + "there_are_no_messages_yet": "Aún no hay mensajes.", + "checkout_the_marketplace": "Explora el Marketplace", + "go_to_marketplace": "Ir al Marketplace", + "all_messages": "Todos los mensajes", + "previous": "Anterior", + "select_or_upload_pictures_maximum_5_at_a_time": "Selecciona o sube imágenes (máximo 5 a la vez)", + "you_can_also_drag_drop_pictures": "También puedes arrastrar y soltar imágenes", + "you_don_t_have_any_assets_yet": "Aún no tienes ningún recurso.", + "click_the_button_below_to_upload_one": "Haz clic en el botón de abajo para subir uno", + "click_the_button_below_to_upload_other": "Haz clic en el botón de abajo para subir varios", + "add_selected_media": "Agregar medios seleccionados", + "insert_media": "Insertar medios", + "design_media": "Diseñar medios", + "select": "Seleccionar", + "editor": "Editor", + "clear": "Limpiar", + "order_completed": "Pedido completado", + "the_order_has_been_completed": "El pedido ha sido completado", + "post_has_been_published": "La publicación ha sido publicada", + "url_1": "URL:", + "new_offer": "Nueva oferta", + "platform": "Plataforma", + "posts": "Publicaciones", + "pay_accept_offer": "Pagar y aceptar oferta", + "accepted": "Aceptado", + "post_draft": "Borrador de publicación", + "revision_needed": "Revisión necesaria", + "approve": "Aprobar", + "preview": "Vista previa", + "revision_requested": "Revisión solicitada", + "accepted_1": "ACEPTADO", + "cancelled_by_the_seller": "Cancelado por el vendedor", + "please_select_your_country_where_your_business_is": "Por favor, selecciona el país donde se encuentra tu negocio.", + "select_country": "--SELECCIONA PAÍS--", + "connect_bank_account": "Conectar cuenta bancaria", + "seller_mode": "Modo vendedor", + "active": "Activo", + "details": "Detalles", + "audience_size": "Tamaño de la audiencia", + "add_another_platform": "Agregar otra plataforma", + "send_an_offer_for": "Enviar una oferta por $", + "complete_order_and_pay_early": "Completar pedido y pagar anticipadamente", + "order_in_progress": "Pedido en curso", + "create_a_new_offer": "Crear una nueva oferta", + "orders": "Pedidos", + "price": "Precio", + "state": "Estado", + "showing": "Mostrando", + "to": "a", + "from": "de", + "results": "Resultados", + "content_writer": "Redactor de contenido", + "influencer": "Influencer", + "request_service": "Solicitar servicio", + "the_marketplace_is_not_opened_yet": "El mercado aún no está abierto", + "check_again_soon": "¡Vuelve a comprobar pronto!", + "filter": "Filtrar", + "result": "Resultado", + "seller": "Vendedor", + "buyer": "Comprador", + "discord_support": "Soporte de Discord", + "teams": "Equipos", + "webhooks_1": "Webhooks", + "auto_post": "Publicación automática", + "logout_from": "Cerrar sesión de", + "join_10000_entrepreneurs_who_use_postiz": "Únete a más de 10,000 emprendedores que usan Postiz", + "to_manage_all_your_social_media_channels": "Para gestionar todos tus canales de redes sociales", + "100_no_risk_trial": "Prueba sin riesgo 100%", + "pay_nothing_for_the_first_7_days": "No pagues nada durante los primeros 7 días", + "cancel_anytime_hassle_free": "Cancela en cualquier momento, sin complicaciones", + "add_free_subscription": "-- AÑADIR SUSCRIPCIÓN GRATUITA --", + "currently_impersonating": "Actualmente suplantando", + "user_1": "usuario:", + "drag_n_drop_some_files_here": "Arrastra y suelta algunos archivos aquí", + "add_time_slot": "Agregar franja horaria", + "add_slot": "Agregar espacio", + "cancel_publication": "Cancelar publicación", + "statistics": "Estadísticas", + "loading": "Cargando", + "short_link": "Enlace corto", + "original_link": "Enlace original", + "clicks": "Clics", + "selected_customer": "Cliente seleccionado", + "customer": "Cliente:", + "repeat_post_every": "Repetir publicación cada...", + "use_this_media": "Usar este medio", + "create_new_post": "Crear publicación", + "update_post": "Actualizar publicación existente", + "merge_comments_into_one_post": "Unir comentarios en una sola publicación", + "accounts_that_will_engage": "Cuentas que interactuarán:", + "day": "Día", + "week": "Semana", + "month": "Mes", + "remove_from_customer": "Eliminar del cliente", + "show_more": "+ Mostrar más", + "show_less": "- Mostrar menos", + "upload": "Subir", + "ai": "IA", + "add_channel": "Agregar canal", + "add_platform": "Agregar plataforma", + "articles": "Artículos", + "add_comment": "Agregar comentario", + "add_post": "Agregar publicación en un hilo", + "add_comment_or_post": "Agregar comentario / publicación", + "you_are_in_global_editing_mode": "Estás en modo de edición global", + "the_post_should_be_at_least_6_characters_long": "La publicación debe tener al menos 6 caracteres", + "are_you_sure_you_want_to_delete_post": "¿Estás seguro de que deseas eliminar esta publicación?", + "post_deleted_successfully": "Publicación eliminada con éxito", + "delete_post": "Eliminar publicación", + "save_as_draft": "Guardar como borrador", + "post_now": "Publicar ahora", + "please_add": "Por favor agrega", + "to_your_telegram_group_channel_and_click_here": "a tu\ngrupo/canal de Telegram y haz clic aquí:", + "connect_telegram": "Conectar Telegram", + "please_add_the_following_command_in_your_chat": "Por favor agrega el siguiente comando en tu chat:", + "copy": "Copiar", + "settings": "Configuración", + "integrations": "Integraciones", + "add_integration": "Agregar integración", + "you_are_now_editing_only": "Ahora solo estás editando", + "tag_a_company": "Etiquetar una empresa", + "video_length_is_invalid_must_be_up_to": "La duración del video no es válida, debe ser de hasta", + "seconds": "segundos", + "this_feature_available_only_for_photos": "Esta función está disponible solo para fotos, agregará una música predeterminada que podrás cambiar después.", + "allow_user_to": "Permitir al usuario:", + "your_video_will_be_labeled_promotional": "Tu video será etiquetado como \"Contenido promocional\".", + "this_cannot_be_changed_once_posted": "Esto no se puede cambiar una vez que tu video sea publicado.", + "turn_on_to_disclose_video_promotes": "Activa para revelar que este video promociona bienes o servicios a cambio de algo de valor. Tu video podría promocionarte a ti, a un tercero o a ambos.", + "you_are_promoting_yourself": "Te estás promocionando a ti mismo o a tu propia marca.", + "this_video_will_be_classified_brand_organic": "Este video será clasificado como Marca Orgánica.", + "you_are_promoting_another_brand": "Estás promocionando otra marca o un tercero.", + "this_video_will_be_classified_branded_content": "Este video será clasificado como Contenido de Marca.", + "by_posting_you_agree_to_tiktoks": "Al publicar, aceptas las condiciones de TikTok", + "music_usage_confirmation": "Confirmación de uso de música", + "branded_content_policy": "Política de contenido de marca", + "select_1": "--Seleccionar--", + "select_flair": "--Seleccionar distintivo--", + "link": "Enlace", + "add_subreddit": "Agregar Subreddit", + "please_add_at_least_one_subreddit": "Por favor, agrega al menos un Subreddit", + "add_community": "Agregar comunidad", + "select_post_type": "Seleccionar tipo de publicación...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "No pudimos encontrar ningún negocio conectado a tu página de LinkedIn.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Por favor, cierra este diálogo, crea una nueva página y agrega un nuevo canal nuevamente.", + "select_linkedin_page": "Selecciona la página de LinkedIn:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "No pudimos encontrar ningún negocio conectado a las páginas seleccionadas.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Te recomendamos conectar todas las páginas y todos los negocios.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Por favor, cierra este diálogo, elimina tu integración y agrega un nuevo canal nuevamente.", + "select_instagram_account": "Selecciona la cuenta de Instagram:", + "select_page": "Selecciona la página:", + "generate_image_with_ai": "Generar imagen con IA", + "reconnect_channel": "Reconectar canal", + "update_credentials": "Actualizar credenciales", + "additional_settings": "Configuraciones adicionales", + "change_bot": "Cambiar bot", + "move_add_to_customer": "Mover / agregar al cliente", + "edit_time_slots": "Editar franjas horarias", + "enable_channel": "Habilitar canal", + "disable_channel": "Deshabilitar canal", + "add": "Agregar", + "short_post": "Publicación corta", + "long_post": "Publicación larga", + "a_thread_with_short_posts": "Un hilo con publicaciones cortas", + "a_thread_with_long_posts": "Un hilo con publicaciones largas", + "personal_voice_i_am_happy_to_announce": "Voz personal (\"Me complace anunciar\")", + "company_voice_we_are_happy_to_announce": "Voz de la empresa (\"Nos complace anunciar\")", + "generate": "Generar", + "generate_posts": "Generar publicaciones", + "purchase_a_life_time_pro_account_with_sol_199": "Compra una cuenta PRO de por vida con SOL ($199). Ten en cuenta que no hay reembolso para esta compra.", + "purchase_now": "Comprar ahora", + "pay_today": "Pagar hoy", + "we_are_sorry_to_see_you_go": "Lamentamos verte partir :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "¿Podrías decirnos brevemente qué podríamos haber hecho mejor?", + "cancel_subscription": "Cancelar suscripción", + "plans": "Planes", + "monthly": "MENSUAL", + "yearly": "ANUAL", + "reactivate_subscription": "Reactivar suscripción", + "update_payment_method_invoices_history": "Actualizar método de pago / Historial de facturas", + "cancel_subscription_1": "Cancelar suscripción", + "your_subscription_will_be_canceled_at": "Tu suscripción se cancelará el", + "you_will_never_be_charged_again": "Nunca se te volverá a cobrar", + "current_package": "Paquete actual:", + "next_package": "Próximo paquete:", + "claim": "Reclamar", + "frequently_asked_questions": "Preguntas frecuentes", + "autopost": "Autopublicar", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "La autopublicación puede publicar automáticamente tus nuevos elementos RSS en las redes sociales", + "title": "Título", + "add_an_autopost": "Agregar una autopublicación", + "post_content": "Contenido de la publicación", + "sign_up": "Registrarse", + "or": "O", + "by_registering_you_agree_to_our": "Al registrarte, aceptas nuestros", + "and": "y", + "terms_of_service": "Términos de servicio", + "privacy_policy": "Política de privacidad", + "create_account": "Crear cuenta", + "already_have_an_account": "¿Ya tienes una cuenta?", + "sign_in": "Iniciar sesión", + "sign_in_1": "Iniciar sesión", + "don_t_have_an_account": "¿No tienes una cuenta?", + "forgot_password": "¿Olvidaste tu contraseña?", + "forgot_password_1": "¿Olvidaste tu contraseña?", + "send_password_reset_email": "Enviar correo para restablecer contraseña", + "go_back_to_login": "Volver al inicio de sesión", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Te hemos enviado un correo electrónico con un enlace para restablecer tu contraseña.", + "change_password": "Cambiar contraseña", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Hemos restablecido tu contraseña correctamente. Ahora puedes iniciar sesión con tu", + "click_here_to_go_back_to_login": "Haz clic aquí para volver a iniciar sesión", + "activate_your_account": "Activa tu cuenta", + "thank_you_for_registering": "¡Gracias por registrarte!", + "please_check_your_email_to_activate_your_account": "Por favor, revisa tu correo electrónico para activar tu cuenta.", + "sign_in_with": "Iniciar sesión con", + "continue_with_google": "Continuar con Google", + "sign_in_with_github": "Iniciar sesión con GitHub", + "continue_with_farcaster": "Continuar con Farcaster", + "continue_with_your_wallet": "Continuar con tu Wallet", + "stars_per_day": "Estrellas por día", + "media": "Medios", + "check_launch": "Verificar lanzamiento", + "load_your_github_repository_from_settings_to_see_analytics": "Carga tu repositorio de GitHub desde la configuración para ver las analíticas", + "stars": "Estrellas", + "processing_stars": "Procesando estrellas...", + "forks": "Forks", + "registration_is_disabled": "El registro está deshabilitado", + "login_instead": "Inicia sesión en su lugar", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Selecciona una conversación y comienza a chatear.", + "adding_channel_redirecting_you": "Agregando canal, redirigiéndote", + "could_not_add_provider": "No se pudo agregar el proveedor.", + "you_are_being_redirected_back": "Estás siendo redirigido de vuelta", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Estamos experimentando algunas dificultades, intenta actualizar la página", + "post_not_found": "Publicación no encontrada", + "publication_date": "Fecha de publicación:", + "analytics": "Analíticas", + "launches": "Lanzamientos", + "plugs": "Enchufes", + "billing": "Facturación", + "affiliate": "Afiliado", + "monday": "Lunes", + "tuesday": "Martes", + "wednesday": "Miércoles", + "thursday": "Jueves", + "friday": "Viernes", + "saturday": "Sábado", + "sunday": "Domingo", + "can_t_change_date_remove_post_from_publication": "No se puede cambiar la fecha, elimina la publicación de la publicación", + "predicted_github_trending_change": "Cambio de tendencia de GitHub previsto", + "duplicate_post": "Publicación duplicada", + "preview_post": "Vista previa de la publicación", + "post_statistics": "Estadísticas de la publicación", + "draft": "Borrador", + "week_number": "Semana {{number}}", + "top_title_edit_webhook": "Editar webhook", + "top_title_add_webhook": "Agregar webhook", + "top_title_oh_no": "Oh no", + "top_title_auto_plug": "Auto Plug: {{title}}", + "top_title_edit_autopost": "Editar autopublicación", + "top_title_add_autopost": "Agregar autopublicación", + "top_title_send_a_new_offer": "Enviar una nueva oferta", + "top_title_media_library": "Biblioteca de medios", + "top_title_add_signature": "Agregar firma", + "top_title_send_a_message_to": "Enviar un mensaje a {{name}}", + "top_title_configure_provider": "Configurar proveedor", + "top_title_add_member": "Agregar miembro", + "top_title_change_bot_picture": "Cambiar imagen del bot", + "top_title_create_a_new_tag": "Crear una nueva etiqueta", + "top_title_select_company": "Seleccionar empresa", + "top_title_additional_settings": "Configuraciones adicionales", + "top_title_time_table_slots": "Intervalos de horario", + "top_title_design_media": "Diseñar medios", + "top_title_edit_post": "Editar publicación", + "top_title_create_post": "Crear publicación", + "top_title_move__add_to_customer": "Mover / Agregar al cliente", + "top_title_add_api_key_for": "Agregar clave API para {{name}}", + "top_title_instance_url": "URL de la instancia", + "top_title_custom_url": "URL personalizada", + "top_title_add_channel": "Agregar canal", + "top_title_add_telegram": "Agregar Telegram", + "top_title_add_wrapcast": "Agregar Wrapcast", + "top_title_comments_for": "Comentarios para {{date}}", + "top_title_edit_signature": "Editar firma", + "label_name": "Nombre", + "label_url": "URL", + "label_title": "Título", + "label_subtitle": "Subtítulo", + "label_email": "Correo electrónico", + "label_full_name": "Nombre completo", + "label_password": "Contraseña", + "label_confirm_password": "Confirmar contraseña", + "label_api_key": "Clave API", + "label_instance_url": "URL de la instancia", + "label_custom_url": "URL personalizada", + "label_feedback": "Comentarios", + "label_bio": "Biografía", + "label_role": "Rol", + "label_country": "País", + "label_audience_size": "Tamaño de la audiencia en todas las plataformas", + "label_pick_time": "Seleccionar hora", + "label_nickname": "Apodo", + "label_write_anything": "Escribe cualquier cosa", + "label_output_format": "Formato de salida", + "label_add_pictures": "¿Agregar imágenes?", + "label_hour": "Hora", + "label_minutes": "Minutos", + "label_select_publication": "Seleccionar publicación", + "label_canonical_link": "Enlace canónico", + "label_cover_picture": "Imagen de portada", + "label_tags": "Etiquetas", + "label_topics": "Temas", + "label_tags_maximum_4": "Etiquetas (máximo 4)", + "label_attachments": "Archivos adjuntos", + "label_type": "Tipo", + "label_thumbnail": "Miniatura", + "label_who_can_see_this_video": "¿Quién puede ver este video?", + "label_content_posting_method": "Método de publicación de contenido", + "label_auto_add_music": "Agregar música automáticamente", + "label_duet": "Dúo", + "label_stitch": "Stitch", + "label_comments": "Comentarios", + "label_disclose_video_content": "Revelar contenido del video", + "label_your_brand": "Tu marca", + "label_branded_content": "Contenido de marca", + "label_subreddit": "Subreddit", + "label_flair": "Distintivo", + "label_media": "Medios", + "label_search_subreddit": "Buscar Subreddit", + "label_delay": "Retraso", + "label_post_type": "Tipo de publicación", + "label_collaborators": "Colaboradores (máx. 3) - las cuentas no pueden ser privadas", + "label_community": "Comunidad", + "label_search_community": "Buscar comunidad", + "label_channel": "Canal", + "label_search_channel": "Buscar canal", + "label_select_channel": "Seleccionar canal", + "label_new_password": "Nueva contraseña", + "label_repeat_password": "Repetir contraseña", + "label_platform": "Plataforma", + "label_price_per_post": "Precio por publicación", + "label_integrations": "Integraciones", + "label_code": "Código", + "label_should_sync_last_post": "¿Deberíamos sincronizar la última publicación actual?", + "label_when_post": "¿Cuándo deberíamos publicarlo?", + "label_autogenerate_content": "Autogenerar contenido", + "label_generate_picture": "¿Generar imagen?", + "label_company": "Empresa", + "label_tag_color": "Color de la etiqueta", + "label_select_board": "Seleccionar tablero", + "label_select_organization": "Seleccionar organización", + "label_auto_add_signature": "¿Agregar firma automáticamente?", + "enable_color_picker": "Habilitar selector de color", + "cancel_the_color_picker": "Cancelar el selector de color", + "no_content_yet": "Aún no hay contenido", + "write_your_reply": "Escribe tu publicación...", + "add_a_tag": "Agregar una etiqueta", + "add_to_calendar": "Agregar al calendario", + "select_channels_from_circles": "Selecciona canales de los círculos de arriba", + "not_matching_order": "El orden no coincide", + "submit_for_order": "Enviar para pedido", + "schedule": "Programar", + "update": "Actualizar", + "attachments": "Archivos adjuntos", + "tags": "Etiquetas", + "public_to_everyone": "Público para todos", + "mutual_follow_friends": "Amigos con seguimiento mutuo", + "follower_of_creator": "Seguidor del creador", + "self_only": "Solo yo", + "post_content_directly_to_tiktok": "Publicar contenido directamente en TikTok", + "upload_content_to_tiktok_without_posting": "Subir contenido a TikTok sin publicarlo", + "choose_upload_without_posting_description": "Elige subir sin publicar si quieres revisar y editar tu contenido en la aplicación de TikTok antes de publicarlo. Esto te da acceso a las herramientas de edición integradas de TikTok y te permite hacer ajustes finales antes de publicar.", + "faq_am_i_going_to_be_charged_by_postiz": "¿Me va a cobrar Postiz?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar la información de la tarjeta de crédito, Postiz retendrá $2 y los liberará inmediatamente", + "faq_can_i_trust_postiz_gitroom": "¿Puedo confiar en Postiz?", + "faq_postiz_gitroom_is_proudly_open_source": "¡Postiz es orgullosamente de código abierto! Creemos en una cultura ética y transparente, lo que significa que Postiz vivirá para siempre. Puedes revisar todo el código o usarlo para proyectos personales. Para ver el repositorio de código abierto, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">haz clic aquí</a>.", + "faq_what_are_channels": "¿Qué son los canales?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz te permite programar tus publicaciones entre diferentes canales.\nUn canal es una plataforma de publicación donde puedes programar tus publicaciones.\nPor ejemplo, puedes programar tus publicaciones en X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads y Pinterest.", + "faq_what_are_team_members": "¿Qué son los miembros del equipo?", + "faq_if_you_have_a_team_with_multiple_members": "Si tienes un equipo con varios miembros, puedes invitarlos a tu espacio de trabajo para colaborar en tus publicaciones y agregar sus canales personales", + "faq_what_is_ai_auto_complete": "¿Qué es la autocompletación con IA?", + "faq_we_automate_chatgpt_to_help_you_write": "Automatizamos ChatGPT para ayudarte a escribir publicaciones sociales y artículos.", + "enter_email": "Introduce el correo electrónico", + "are_you_sure": "¿Estás seguro?", + "yes_delete_it": "¡Sí, bórralo!", + "no_cancel": "No, cancelar", + "are_you_sure_you_want_to_delete": "¿Estás seguro de que quieres eliminar {{name}}?", + "are_you_sure_you_want_to_delete_the_image": "¿Estás seguro de que quieres eliminar la imagen?", + "are_you_sure_you_want_to_logout": "¿Estás seguro de que quieres cerrar sesión?", + "yes_logout": "Sí, cerrar sesión", + "are_you_sure_you_want_to_delete_this_slot": "¿Estás seguro de que quieres eliminar este espacio?", + "are_you_sure_you_want_to_delete_this_subreddit": "¿Estás seguro de que quieres eliminar este Subreddit?", + "are_you_sure_you_want_to_close_the_window": "¿Estás seguro de que quieres cerrar la ventana?", + "yes_close": "Sí, cerrar", + "link_copied_to_clipboard": "Enlace copiado al portapapeles", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "¿Estás seguro de que quieres cerrar este modal? (todos los datos se perderán)", + "yes_close_it": "¡Sí, ciérralo!", + "uploading_pictures": "Subiendo imágenes...", + "agent_starting": "Iniciando agente", + "researching_your_content": "Investigando tu contenido...", + "understanding_the_category": "Entendiendo la categoría...", + "finding_the_topic": "Buscando el tema...", + "finding_popular_posts_to_match_with": "Buscando publicaciones populares para emparejar...", + "generating_hook": "Generando gancho...", + "generating_content": "Generando contenido...", + "generating_pictures": "Generando imágenes...", + "finding_time_to_post": "Buscando el mejor momento para publicar...", + "write_anything": "Escribe lo que sea", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Puedes escribir lo que quieras y también añadir enlaces, nosotros haremos la investigación por ti...", + "output_format": "Formato de salida", + "add_pictures": "¿Agregar imágenes?", + "7_days": "7 días", + "30_days": "30 días", + "90_days": "90 días", + "start_7_days_free_trial": "Comienza la prueba gratuita de 7 días", + "change_language": "Cambiar idioma", + "that_a_wrap": "¡Eso es todo!\n\nSi te gustó este hilo:\n\n1. Sígueme en @{{username}} para más contenido como este\n2. Haz RT al tuit de abajo para compartir este hilo con tu audiencia\n", + "post_as_images_carousel": "Publicar como carrusel de imágenes", + "save_set": "Guardar conjunto", + "separate_post": "Separar publicación en varias publicaciones", + "label_who_can_reply_to_this_post": "¿Quién puede responder a esta publicación?", + "delete_integration": "Eliminar integración", + "start_writing_your_post": "Comienza a escribir tu publicación para obtener una vista previa", + "billing_join_over": "Únete a más de", + "billing_entrepreneurs_count": "18,000+ emprendedores", + "billing_who_use": "que usan", + "billing_postiz_grow_social": "Postiz para hacer crecer su presencia en redes sociales", + "billing_no_risk_trial": "Prueba gratuita 100% sin riesgo", + "billing_pay_nothing_7_days": "No pagues NADA durante los primeros 7 días", + "billing_cancel_anytime": "Cancela en cualquier momento, sin complicaciones", + "billing_choose_plan": "Elige un plan", + "billing_monthly": "Mensual", + "billing_yearly": "Anual", + "billing_20_percent_off": "20% de descuento", + "billing_features": "Características", + "billing_channel": "canal", + "billing_channels": "canales", + "billing_unlimited": "Ilimitado", + "billing_posts_per_month": "publicaciones por mes", + "billing_unlimited_team_members": "Miembros de equipo ilimitados", + "billing_ai_auto_complete": "Autocompletado por IA", + "billing_ai_copilots": "Copilotos de IA", + "billing_ai_autocomplete": "Autocompletado por IA", + "billing_advanced_picture_editor": "Editor de imágenes avanzado", + "billing_ai_images_per_month": "Imágenes de IA por mes", + "billing_ai_videos_per_month": "Videos de IA por mes", + "billing_billing_address": "Dirección de facturación", + "billing_payment": "Pago", + "billing_powered_by_stripe": "Desarrollado por Stripe", + "billing_your_7_day_trial_is": "Tu prueba de 7 días es", + "billing_100_percent_free": "100% gratis", + "billing_ending": "finaliza", + "billing_cancel_anytime_short": "Cancela en cualquier momento.", + "billing_pay_0_start_trial": "¡Paga $0 hoy - Comienza tu prueba gratis!", + "billing_pay_now": "Pagar ahora", + "billing_per_month": "/ mes" } diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index 9e69af1b..6de13f4b 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -1,507 +1,539 @@ { - "calendar": "Calendrier", - "webhooks": "Webhooks", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Les webhooks sont un moyen d'être notifié lorsqu'il se passe quelque chose dans Postiz via une requête HTTP.", - "name": "Nom", - "url": "URL", - "edit": "Modifier", - "delete": "Supprimer", - "add_a_webhook": "Ajouter un webhook", - "save": "Enregistrer", - "send_test": "Envoyer un test", - "select_role": "Sélectionner un rôle", - "video_made_with_ai": "Vidéo réalisée avec l'IA", - "please_add_at_least": "Veuillez ajouter au moins 20 caractères.", - "send_invitation_via_email": "Envoyer l'invitation par e-mail ?", - "global_settings": "Paramètres globaux", - "copy_id": "Copier l'identifiant de la chaîne", - "team_members": "Membres de l'équipe", - "invite_your_assistant_or_team_member_to_manage_your_account": "Invitez votre assistant ou un membre de votre équipe à gérer votre compte", - "remove": "Retirer", - "add_another_member": "Ajouter un autre membre", - "signatures": "Signatures", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Vous pouvez ajouter des signatures à votre compte pour les utiliser dans vos publications.", - "content": "Contenu", - "auto_add": "Ajout automatique ?", - "actions": "Actions", - "use_signature": "Utiliser la signature", - "add_a_signature": "Ajouter une signature", - "no": "Non", - "yes": "Oui", - "your_git_repository": "Votre dépôt Git", - "connect_your_github_repository_to_receive_updates_and_analytics": "Connectez votre dépôt GitHub pour recevoir des mises à jour et des analyses", - "connected": "Connecté :", - "disconnect": "Déconnecter", - "connect_your_repository": "Connectez votre dépôt", - "cancel": "Annuler", - "connect": "Connecter", - "public_api": "API publique", - "check_n8n": "Découvrez notre nœud personnalisé N8N pour Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Utilisez l'API Postiz pour l'intégrer à vos outils.", - "read_how_to_use_it_over_the_documentation": "Lisez comment l'utiliser dans la documentation.", - "reveal": "Révéler", - "copy_key": "Copier la clé", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Connectez le serveur MCP de Postiz à votre client (streaming HTTP) pour programmer vos publications plus rapidement !", - "share_with_a_client": "Partager avec un client", - "post": "Publication", - "comments": "Commentaires", - "user": "Utilisateur", - "login_register_to_add_comments": "Connectez-vous / Inscrivez-vous pour ajouter des commentaires", - "status": "Statut :", - "there_are_not_plugs_matching_your_channels": "Il n'y a pas de connecteurs correspondant à vos canaux", - "you_have_to_add_x_or_linkedin_or_threads": "Vous devez ajouter : X ou LinkedIn ou Threads", - "go_to_the_calendar_to_add_channels": "Allez dans le calendrier pour ajouter des canaux", - "channels": "Canaux", - "activate": "Activer", - "this_channel_needs_to_be_refreshed": "Ce canal doit être actualisé,", - "click_here_to_refresh": "cliquez ici pour actualiser", - "can_t_show_analytics_yet": "Impossible d'afficher les analyses pour le moment", - "you_have_to_add_social_media_channels": "Vous devez ajouter des canaux de réseaux sociaux", - "supported": "Pris en charge :", - "step": "ÉTAPE", - "skip_onboarding": "Passer l'intégration", - "onboarding": "Intégration", - "next": "Suivant", - "you_are_done_from_here_you_can": "C'est terminé, à partir d'ici vous pouvez :", - "view_analytics": "Voir les analyses", - "schedule_a_new_post": "Programmer une nouvelle publication", - "to_sell_posts_you_would_have_to": "Pour vendre des publications, vous devez :", - "1_connect_at_least_one_channel": "1. Connecter au moins un canal", - "2_connect_you_bank_account": "2. Connecter votre compte bancaire", - "go_back_to_connect_channels": "Revenir pour connecter des canaux", - "move_to_the_seller_page_to_connect_you_bank": "Aller à la page vendeur pour connecter votre banque", - "connect_channels": "Connecter des canaux", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Connectez vos canaux de réseaux sociaux et de sites de publication pour\n programmer des publications ultérieurement", - "social": "Réseaux sociaux", - "publishing_platforms": "Plateformes de publication", - "no_channels": "Aucun canal pour le moment", - "connect_your_accounts": "Connectez vos comptes sociaux pour commencer à planifier, publier et analyser — tout en un seul endroit.", - "notifications": "Notifications", - "no_notifications": "Aucune notification", - "send_message": "Envoyer un message", - "mar_28": "28 mars", - "there_are_no_messages_yet": "Il n'y a pas encore de messages.", - "checkout_the_marketplace": "Découvrir la place de marché", - "go_to_marketplace": "Aller à la place de marché", - "all_messages": "Tous les messages", - "previous": "Précédent", - "select_or_upload_pictures_maximum_5_at_a_time": "Sélectionnez ou téléversez des images (maximum 5 à la fois)", - "you_can_also_drag_drop_pictures": "Vous pouvez aussi glisser-déposer des images", - "you_don_t_have_any_assets_yet": "Vous n'avez pas encore d'actifs.", - "click_the_button_below_to_upload_one": "Cliquez sur le bouton ci-dessous pour en téléverser un", - "click_the_button_below_to_upload_many": "", - "click_the_button_below_to_upload_other": "Cliquez sur le bouton ci-dessous pour télécharger plusieurs fichiers.", - "add_selected_media": "Ajouter les médias sélectionnés", - "insert_media": "Insérer un média", - "design_media": "Média de conception", - "select": "Sélectionner", - "editor": "Éditeur", - "clear": "Effacer", - "order_completed": "Commande terminée", - "the_order_has_been_completed": "La commande a été terminée", - "post_has_been_published": "La publication a été publiée", - "url_1": "URL :", - "new_offer": "Nouvelle offre", - "platform": "Plateforme", - "posts": "Publications", - "pay_accept_offer": "Payer et accepter l'offre", - "accepted": "Accepté", - "post_draft": "Brouillon de publication", - "revision_needed": "Révision nécessaire", - "approve": "Approuver", - "preview": "Aperçu", - "revision_requested": "Révision demandée", - "accepted_1": "ACCEPTÉ", - "cancelled_by_the_seller": "Annulé par le vendeur", - "please_select_your_country_where_your_business_is": "Veuillez sélectionner le pays où se trouve votre entreprise.", - "select_country": "--SÉLECTIONNER UN PAYS--", - "connect_bank_account": "Connecter un compte bancaire", - "seller_mode": "Mode vendeur", - "active": "Actif", - "details": "Détails", - "audience_size": "Taille de l'audience", - "add_another_platform": "Ajouter une autre plateforme", - "send_an_offer_for": "Envoyer une offre pour $", - "complete_order_and_pay_early": "Terminer la commande et payer à l'avance", - "order_in_progress": "Commande en cours", - "create_a_new_offer": "Créer une nouvelle offre", - "orders": "Commandes", - "price": "Prix", - "state": "État", - "showing": "Affichage", - "to": "à", - "from": "de", - "results": "Résultats", - "content_writer": "Rédacteur de contenu", - "influencer": "Influenceur", - "request_service": "Demander un service", - "the_marketplace_is_not_opened_yet": "La place de marché n'est pas encore ouverte", - "check_again_soon": "Revenez bientôt !", - "filter": "Filtrer", - "result": "Résultat", - "seller": "Vendeur", - "buyer": "Acheteur", - "discord_support": "Support Discord", - "teams": "Équipes", - "webhooks_1": "Webhooks", - "auto_post": "Publication automatique", - "logout_from": "Se déconnecter de", - "join_10000_entrepreneurs_who_use_postiz": "Rejoignez plus de 10 000 entrepreneurs qui utilisent Postiz", - "to_manage_all_your_social_media_channels": "Pour gérer tous vos réseaux sociaux", - "100_no_risk_trial": "Essai 100% sans risque", - "pay_nothing_for_the_first_7_days": "Ne payez rien pendant les 7 premiers jours", - "cancel_anytime_hassle_free": "Annulez à tout moment, sans tracas", - "add_free_subscription": "-- AJOUTER UN ABONNEMENT GRATUIT --", - "currently_impersonating": "Actuellement en mode usurpation", - "user_1": "utilisateur :", - "drag_n_drop_some_files_here": "Glissez-déposez des fichiers ici", - "add_time_slot": "Ajouter un créneau horaire", - "add_slot": "Ajouter un créneau", - "cancel_publication": "Annuler la publication", - "statistics": "Statistiques", - "loading": "Chargement", - "short_link": "Lien court", - "original_link": "Lien original", - "clicks": "Clics", - "selected_customer": "Client sélectionné", - "customer": "Client :", - "repeat_post_every": "Répéter la publication tous les...", - "use_this_media": "Utiliser ce média", - "create_new_post": "Créer une publication", - "update_post": "Mettre à jour le post existant", - "merge_comments_into_one_post": "Fusionner les commentaires en une seule publication", - "accounts_that_will_engage": "Comptes qui vont interagir :", - "day": "Jour", - "week": "Semaine", - "month": "Mois", - "remove_from_customer": "Retirer du client", - "show_more": "+ Afficher plus", - "show_less": "- Afficher moins", - "upload": "Téléverser", - "ai": "IA", - "add_channel": "Ajouter un canal", - "add_platform": "Ajouter une plateforme", - "articles": "Articles", - "add_comment": "Ajouter un commentaire", - "add_post": "Ajouter une publication dans un fil de discussion", - "add_comment_or_post": "Ajouter un commentaire / une publication", - "you_are_in_global_editing_mode": "Vous êtes en mode d'édition global", - "the_post_should_be_at_least_6_characters_long": "Le post doit contenir au moins 6 caractères", - "are_you_sure_you_want_to_delete_post": "Êtes-vous sûr de vouloir supprimer cette publication ?", - "post_deleted_successfully": "Publication supprimée avec succès", - "delete_post": "Supprimer le post", - "save_as_draft": "Enregistrer comme brouillon", - "post_now": "Publier maintenant", - "please_add": "Veuillez ajouter", - "to_your_telegram_group_channel_and_click_here": "à votre groupe / canal Telegram et cliquez ici :", - "connect_telegram": "Connecter Telegram", - "please_add_the_following_command_in_your_chat": "Veuillez ajouter la commande suivante dans votre chat :", - "copy": "Copier", - "settings": "Paramètres", - "integrations": "Intégrations", - "add_integration": "Ajouter une intégration", - "you_are_now_editing_only": "Vous modifiez maintenant uniquement", - "tag_a_company": "Taguer une entreprise", - "video_length_is_invalid_must_be_up_to": "La durée de la vidéo est invalide, elle doit être au maximum de", - "seconds": "secondes", - "this_feature_available_only_for_photos": "Cette fonctionnalité est disponible uniquement pour les photos, elle ajoutera une musique par défaut que vous pourrez changer plus tard.", - "allow_user_to": "Autoriser l'utilisateur à :", - "your_video_will_be_labeled_promotional": "Votre vidéo sera étiquetée « Contenu promotionnel ».", - "this_cannot_be_changed_once_posted": "Ceci ne pourra plus être modifié une fois votre vidéo publiée.", - "turn_on_to_disclose_video_promotes": "Activez pour indiquer que cette vidéo fait la promotion de biens ou services en échange d'une contrepartie. Votre vidéo peut faire la promotion de vous-même, d'un tiers, ou des deux.", - "you_are_promoting_yourself": "Vous faites la promotion de vous-même ou de votre propre marque.", - "this_video_will_be_classified_brand_organic": "Cette vidéo sera classée comme contenu de marque organique.", - "you_are_promoting_another_brand": "Vous faites la promotion d'une autre marque ou d'un tiers.", - "this_video_will_be_classified_branded_content": "Cette vidéo sera classée comme contenu de marque.", - "by_posting_you_agree_to_tiktoks": "En publiant, vous acceptez les conditions de TikTok", - "music_usage_confirmation": "Confirmation d'utilisation de la musique", - "branded_content_policy": "Politique sur le contenu de marque", - "select_1": "--Sélectionner--", - "select_flair": "--Sélectionner un flair--", - "link": "Lien", - "add_subreddit": "Ajouter un subreddit", - "please_add_at_least_one_subreddit": "Veuillez ajouter au moins un subreddit", - "add_community": "Ajouter une communauté", - "select_post_type": "Sélectionner le type de publication...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Nous n'avons trouvé aucune entreprise liée à votre page LinkedIn.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Veuillez fermer cette fenêtre, créer une nouvelle page et ajouter à nouveau un nouveau canal.", - "select_linkedin_page": "Sélectionnez la page LinkedIn :", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Nous n'avons trouvé aucune entreprise liée aux pages sélectionnées.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Nous vous recommandons de connecter toutes les pages et toutes les entreprises.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Veuillez fermer cette fenêtre, supprimer votre intégration et ajouter à nouveau un nouveau canal.", - "select_instagram_account": "Sélectionnez le compte Instagram :", - "select_page": "Sélectionnez la page :", - "generate_image_with_ai": "Générer une image avec l'IA", - "reconnect_channel": "Reconnecter le canal", - "update_credentials": "Mettre à jour les identifiants", - "additional_settings": "Paramètres supplémentaires", - "change_bot": "Changer de bot", - "move_add_to_customer": "Déplacer / ajouter au client", - "edit_time_slots": "Modifier les créneaux horaires", - "enable_channel": "Activer le canal", - "disable_channel": "Désactiver le canal", - "add": "Ajouter", - "short_post": "Message court", - "long_post": "Message long", - "a_thread_with_short_posts": "Un fil avec des messages courts", - "a_thread_with_long_posts": "Un fil avec des messages longs", - "personal_voice_i_am_happy_to_announce": "Voix personnelle (« Je suis heureux d'annoncer »)", - "company_voice_we_are_happy_to_announce": "Voix d'entreprise (« Nous sommes heureux d'annoncer »)", - "generate": "Générer", - "generate_posts": "Générer des messages", - "purchase_a_life_time_pro_account_with_sol_199": "Achetez un compte PRO à vie avec SOL (199 $). Veuillez noter qu’aucun remboursement n’est possible pour cet achat.", - "purchase_now": "Acheter maintenant", - "pay_today": "Payer aujourd'hui", - "we_are_sorry_to_see_you_go": "Nous sommes désolés de vous voir partir :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Pourriez-vous nous dire brièvement ce que nous aurions pu mieux faire ?", - "cancel_subscription": "Annuler l'abonnement", - "plans": "Formules", - "monthly": "MENSUEL", - "yearly": "ANNUEL", - "reactivate_subscription": "Réactiver l'abonnement", - "update_payment_method_invoices_history": "Mettre à jour le mode de paiement / Historique des factures", - "cancel_subscription_1": "Annuler l'abonnement", - "your_subscription_will_be_canceled_at": "Votre abonnement sera annulé le", - "you_will_never_be_charged_again": "Vous ne serez plus jamais facturé", - "current_package": "Forfait actuel :", - "next_package": "Forfait suivant :", - "claim": "Réclamer", - "frequently_asked_questions": "Foire aux questions", - "autopost": "Publication automatique", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "La publication automatique peut publier vos nouveaux éléments RSS sur les réseaux sociaux.", - "title": "Titre", - "add_an_autopost": "Ajouter une publication automatique", - "post_content": "Contenu de la publication", - "sign_up": "S'inscrire", - "or": "OU", - "by_registering_you_agree_to_our": "En vous inscrivant, vous acceptez nos", - "and": "et", - "terms_of_service": "Conditions d'utilisation", - "privacy_policy": "Politique de confidentialité", - "create_account": "Créer un compte", - "already_have_an_account": "Vous avez déjà un compte ?", - "sign_in": "Se connecter", - "sign_in_1": "Se connecter", - "don_t_have_an_account": "Vous n'avez pas de compte ?", - "forgot_password": "Mot de passe oublié", - "forgot_password_1": "Mot de passe oublié", - "send_password_reset_email": "Envoyer l'e-mail de réinitialisation du mot de passe", - "go_back_to_login": "Retour à la connexion", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Nous vous avons envoyé un e-mail avec un lien pour réinitialiser votre mot de passe.", - "change_password": "Changer le mot de passe", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Nous avons réinitialisé votre mot de passe avec succès. Vous pouvez maintenant vous connecter avec votre", - "click_here_to_go_back_to_login": "Cliquez ici pour revenir à la connexion", - "activate_your_account": "Activez votre compte", - "thank_you_for_registering": "Merci pour votre inscription !", - "please_check_your_email_to_activate_your_account": "Veuillez vérifier votre e-mail pour activer votre compte.", - "sign_in_with": "Se connecter avec", - "continue_with_google": "Continuer avec Google", - "sign_in_with_github": "Se connecter avec GitHub", - "continue_with_farcaster": "Continuer avec Farcaster", - "continue_with_your_wallet": "Continuer avec votre portefeuille", - "stars_per_day": "Étoiles par jour", - "media": "Médias", - "check_launch": "Vérifier le lancement", - "load_your_github_repository_from_settings_to_see_analytics": "Chargez votre dépôt GitHub depuis les paramètres pour voir les analyses", - "stars": "Étoiles", - "processing_stars": "Traitement des étoiles...", - "forks": "Forks", - "registration_is_disabled": "L'inscription est désactivée", - "login_instead": "Connectez-vous à la place", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Sélectionnez une conversation et discutez.", - "adding_channel_redirecting_you": "Ajout du canal, redirection en cours", - "could_not_add_provider": "Impossible d'ajouter le fournisseur.", - "you_are_being_redirected_back": "Vous êtes en cours de redirection", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Nous rencontrons quelques difficultés, essayez de rafraîchir la page", - "post_not_found": "Publication introuvable", - "publication_date": "Date de publication :", - "analytics": "Analyses", - "launches": "Lancements", - "plugs": "Plugs", - "billing": "Facturation", - "affiliate": "Affiliation", - "monday": "Lundi", - "tuesday": "Mardi", - "wednesday": "Mercredi", - "thursday": "Jeudi", - "friday": "Vendredi", - "saturday": "Samedi", - "sunday": "Dimanche", - "can_t_change_date_remove_post_from_publication": "Impossible de changer la date, retirez la publication de la publication", - "predicted_github_trending_change": "Changement de tendance GitHub prédit", - "duplicate_post": "Publication en double", - "preview_post": "Aperçu de la publication", - "post_statistics": "Statistiques de la publication", - "draft": "Brouillon", - "week_number": "Semaine {{number}}", - "top_title_edit_webhook": "Modifier le webhook", - "top_title_add_webhook": "Ajouter un webhook", - "top_title_oh_no": "Oh non", - "top_title_auto_plug": "Auto Plug : {{title}}", - "top_title_edit_autopost": "Modifier l'autopost", - "top_title_add_autopost": "Ajouter un autopost", - "top_title_send_a_new_offer": "Envoyer une nouvelle offre", - "top_title_media_library": "Médiathèque", - "top_title_add_signature": "Ajouter une signature", - "top_title_send_a_message_to": "Envoyer un message à {{name}}", - "top_title_configure_provider": "Configurer le fournisseur", - "top_title_add_member": "Ajouter un membre", - "top_title_change_bot_picture": "Changer l'image du bot", - "top_title_create_a_new_tag": "Créer un nouveau tag", - "top_title_select_company": "Sélectionner une entreprise", - "top_title_additional_settings": "Paramètres supplémentaires", - "top_title_time_table_slots": "Créneaux horaires", - "top_title_design_media": "Concevoir un média", - "top_title_edit_post": "Modifier la publication", - "top_title_create_post": "Créer une publication", - "top_title_move__add_to_customer": "Déplacer / Ajouter au client", - "top_title_add_api_key_for": "Ajouter une clé API pour {{name}}", - "top_title_instance_url": "URL de l'instance", - "top_title_custom_url": "URL personnalisée", - "top_title_add_channel": "Ajouter un canal", - "top_title_add_telegram": "Ajouter Telegram", - "top_title_add_wrapcast": "Ajouter Wrapcast", - "top_title_comments_for": "Commentaires pour {{date}}", - "top_title_edit_signature": "Modifier la signature", - "label_name": "Nom", - "label_url": "URL", - "label_title": "Titre", - "label_subtitle": "Sous-titre", - "label_email": "E-mail", - "label_full_name": "Nom complet", - "label_password": "Mot de passe", - "label_confirm_password": "Confirmer le mot de passe", - "label_api_key": "Clé API", - "label_instance_url": "URL de l'instance", - "label_custom_url": "URL personnalisée", - "label_feedback": "Retour d'information", - "label_bio": "Bio", - "label_role": "Rôle", - "label_country": "Pays", - "label_audience_size": "Taille de l'audience sur toutes les plateformes", - "label_pick_time": "Choisir l'heure", - "label_nickname": "Surnom", - "label_write_anything": "Écrire n'importe quoi", - "label_output_format": "Format de sortie", - "label_add_pictures": "Ajouter des images ?", - "label_hour": "Heure", - "label_minutes": "Minutes", - "label_select_publication": "Sélectionner la publication", - "label_canonical_link": "Lien canonique", - "label_cover_picture": "Image de couverture", - "label_tags": "Tags", - "label_topics": "Sujets", - "label_tags_maximum_4": "Tags (maximum 4)", - "label_attachments": "Pièces jointes", - "label_type": "Type", - "label_thumbnail": "Vignette", - "label_who_can_see_this_video": "Qui peut voir cette vidéo ?", - "label_content_posting_method": "Méthode de publication du contenu", - "label_auto_add_music": "Ajouter automatiquement de la musique", - "label_duet": "Duet", - "label_stitch": "Stitch", - "label_comments": "Commentaires", - "label_disclose_video_content": "Divulguer le contenu de la vidéo", - "label_your_brand": "Votre marque", - "label_branded_content": "Contenu de marque", - "label_subreddit": "Subreddit", - "label_flair": "Flair", - "label_media": "Média", - "label_search_subreddit": "Rechercher un subreddit", - "label_delay": "Délai", - "label_post_type": "Type de publication", - "label_collaborators": "Collaborateurs (max 3) - les comptes ne peuvent pas être privés", - "label_community": "Communauté", - "label_search_community": "Rechercher une communauté", - "label_channel": "Canal", - "label_search_channel": "Rechercher un canal", - "label_select_channel": "Sélectionner un canal", - "label_new_password": "Nouveau mot de passe", - "label_repeat_password": "Répéter le mot de passe", - "label_platform": "Plateforme", - "label_price_per_post": "Prix par publication", - "label_integrations": "Intégrations", - "label_code": "Code", - "label_should_sync_last_post": "Faut-il synchroniser la dernière publication actuelle ?", - "label_when_post": "Quand devons-nous la publier ?", - "label_autogenerate_content": "Générer automatiquement le contenu", - "label_generate_picture": "Générer une image ?", - "label_company": "Entreprise", - "label_tag_color": "Couleur de l'étiquette", - "label_select_board": "Sélectionner un tableau", - "label_select_organization": "Sélectionner une organisation", - "label_auto_add_signature": "Ajouter automatiquement une signature ?", - "enable_color_picker": "Activer le sélecteur de couleurs", - "cancel_the_color_picker": "Annuler le sélecteur de couleurs", - "no_content_yet": "Pas encore de contenu", - "write_your_reply": "Écrivez votre post...", - "add_a_tag": "Ajouter une étiquette", - "add_to_calendar": "Ajouter au calendrier", - "select_channels_from_circles": "Sélectionnez des canaux à partir des cercles ci-dessus", - "not_matching_order": "Ordre non correspondant", - "submit_for_order": "Soumettre la commande", - "schedule": "Planifier", - "update": "Mettre à jour", - "attachments": "Pièces jointes", - "tags": "Tags", - "public_to_everyone": "Public pour tous", - "mutual_follow_friends": "Amis avec abonnement mutuel", - "follower_of_creator": "Abonné du créateur", - "self_only": "Moi uniquement", - "post_content_directly_to_tiktok": "Publier le contenu directement sur TikTok", - "upload_content_to_tiktok_without_posting": "Télécharger le contenu sur TikTok sans le publier", - "choose_upload_without_posting_description": "Choisissez de télécharger sans publier si vous souhaitez revoir et modifier votre contenu dans l’application TikTok avant de le publier. Cela vous donne accès aux outils d’édition intégrés de TikTok et vous permet de faire des ajustements finaux avant la publication.", - "faq_am_i_going_to_be_charged_by_postiz": "Vais-je être facturé par Postiz ?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Pour confirmer les informations de carte de crédit, Postiz retiendra 2 $ et les libérera immédiatement", - "faq_can_i_trust_postiz_gitroom": "Puis-je faire confiance à Postiz ?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz est fièrement open source ! Nous croyons en une culture éthique et transparente, ce qui signifie que Postiz existera toujours. Vous pouvez consulter l'intégralité du code ou l'utiliser pour des projets personnels. Pour voir le dépôt open source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">cliquez ici</a>.", - "faq_what_are_channels": "Que sont les canaux ?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz vous permet de planifier vos publications sur différents canaux.\nUn canal est une plateforme de publication où vous pouvez programmer vos publications.\nPar exemple, vous pouvez planifier vos publications sur X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads et Pinterest.", - "faq_what_are_team_members": "Que sont les membres de l’équipe ?", - "faq_if_you_have_a_team_with_multiple_members": "Si vous avez une équipe avec plusieurs membres, vous pouvez les inviter dans votre espace de travail pour collaborer sur vos publications et ajouter leurs canaux personnels", - "faq_what_is_ai_auto_complete": "Qu’est-ce que la saisie automatique par IA ?", - "faq_we_automate_chatgpt_to_help_you_write": "Nous automatisons ChatGPT pour vous aider à rédiger des publications sociales et des articles.", - "enter_email": "Saisissez l’e-mail", - "are_you_sure": "Êtes-vous sûr ?", - "yes_delete_it": "Oui, supprimez-le !", - "no_cancel": "Non, annuler !", - "are_you_sure_you_want_to_delete": "Êtes-vous sûr de vouloir supprimer {{name}} ?", - "are_you_sure_you_want_to_delete_the_image": "Êtes-vous sûr de vouloir supprimer l'image ?", - "are_you_sure_you_want_to_logout": "Êtes-vous sûr de vouloir vous déconnecter ?", - "yes_logout": "Oui, se déconnecter", - "are_you_sure_you_want_to_delete_this_slot": "Êtes-vous sûr de vouloir supprimer cet emplacement ?", - "are_you_sure_you_want_to_delete_this_subreddit": "Êtes-vous sûr de vouloir supprimer ce Subreddit ?", - "are_you_sure_you_want_to_close_the_window": "Êtes-vous sûr de vouloir fermer la fenêtre ?", - "yes_close": "Oui, fermer", - "link_copied_to_clipboard": "Lien copié dans le presse-papiers", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Êtes-vous sûr de vouloir fermer cette fenêtre ? (toutes les données seront perdues)", - "yes_close_it": "Oui, fermez-la !", - "uploading_pictures": "Téléchargement des images...", - "agent_starting": "Agent en cours de démarrage", - "researching_your_content": "Recherche de votre contenu...", - "understanding_the_category": "Compréhension de la catégorie...", - "finding_the_topic": "Recherche du sujet...", - "finding_popular_posts_to_match_with": "Recherche de publications populaires correspondantes...", - "generating_hook": "Génération du crochet...", - "generating_content": "Génération du contenu...", - "generating_pictures": "Génération des images...", - "finding_time_to_post": "Recherche du meilleur moment pour publier...", - "write_anything": "Écrivez n'importe quoi", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Vous pouvez écrire tout ce que vous voulez, et aussi ajouter des liens, nous ferons les recherches pour vous...", - "output_format": "Format de sortie", - "add_pictures": "Ajouter des images ?", - "7_days": "7 jours", - "30_days": "30 jours", - "90_days": "90 jours", - "start_7_days_free_trial": "Commencez l’essai gratuit de 7 jours", - "change_language": "Changer de langue", - "that_a_wrap": "C'est terminé !\n\nSi vous avez aimé ce fil :\n\n1. Suivez-moi @{{username}} pour en voir d'autres\n2. Retweetez le tweet ci-dessous pour partager ce fil avec votre audience\n", - "post_as_images_carousel": "Publier en carrousel d’images", - "save_set": "Enregistrer l'ensemble", - "separate_post": "Séparer le post en plusieurs publications", - "label_who_can_reply_to_this_post": "Qui peut répondre à ce post ?", - "delete_integration": "Supprimer l'intégration", - "start_writing_your_post": "Commencez à écrire votre post pour un aperçu" + "calendar": "Calendrier", + "webhooks": "Webhooks", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Les webhooks sont un moyen d'être notifié lorsqu'il se passe quelque chose dans Postiz via une requête HTTP.", + "name": "Nom", + "url": "URL", + "edit": "Modifier", + "delete": "Supprimer", + "add_a_webhook": "Ajouter un webhook", + "save": "Enregistrer", + "send_test": "Envoyer un test", + "select_role": "Sélectionner un rôle", + "video_made_with_ai": "Vidéo réalisée avec l'IA", + "please_add_at_least": "Veuillez ajouter au moins 20 caractères", + "send_invitation_via_email": "Envoyer l'invitation par e-mail ?", + "global_settings": "Paramètres globaux", + "copy_id": "Copier l'identifiant de la chaîne", + "team_members": "Membres de l'équipe", + "invite_your_assistant_or_team_member_to_manage_your_account": "Invitez votre assistant ou un membre de votre équipe à gérer votre compte", + "remove": "Retirer", + "add_another_member": "Ajouter un autre membre", + "signatures": "Signatures", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Vous pouvez ajouter des signatures à votre compte pour les utiliser dans vos publications.", + "content": "Contenu", + "auto_add": "Ajout automatique ?", + "actions": "Actions", + "use_signature": "Utiliser la signature", + "add_a_signature": "Ajouter une signature", + "no": "Non", + "yes": "Oui", + "your_git_repository": "Votre dépôt Git", + "connect_your_github_repository_to_receive_updates_and_analytics": "Connectez votre dépôt GitHub pour recevoir des mises à jour et des analyses", + "connected": "Connecté :", + "disconnect": "Déconnecter", + "connect_your_repository": "Connectez votre dépôt", + "cancel": "Annuler", + "connect": "Connecter", + "public_api": "API publique", + "check_n8n": "Découvrez notre nœud personnalisé N8N pour Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Utilisez l'API Postiz pour l'intégrer à vos outils.", + "read_how_to_use_it_over_the_documentation": "Lisez comment l'utiliser dans la documentation.", + "reveal": "Révéler", + "copy_key": "Copier la clé", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Connectez le serveur MCP de Postiz à votre client (streaming HTTP) pour programmer vos publications plus rapidement !", + "share_with_a_client": "Partager avec un client", + "post": "Publication", + "comments": "Commentaires", + "user": "Utilisateur", + "login_register_to_add_comments": "Connectez-vous / Inscrivez-vous pour ajouter des commentaires", + "status": "Statut :", + "there_are_not_plugs_matching_your_channels": "Il n'y a pas de connecteurs correspondant à vos canaux", + "you_have_to_add_x_or_linkedin_or_threads": "Vous devez ajouter : X ou LinkedIn ou Threads", + "go_to_the_calendar_to_add_channels": "Allez dans le calendrier pour ajouter des canaux", + "channels": "Canaux", + "activate": "Activer", + "this_channel_needs_to_be_refreshed": "Ce canal doit être actualisé,", + "click_here_to_refresh": "cliquez ici pour actualiser", + "can_t_show_analytics_yet": "Impossible d'afficher les analyses pour le moment", + "you_have_to_add_social_media_channels": "Vous devez ajouter des canaux de réseaux sociaux", + "supported": "Pris en charge :", + "step": "ÉTAPE", + "skip_onboarding": "Passer l'intégration", + "onboarding": "Intégration", + "next": "Suivant", + "you_are_done_from_here_you_can": "C'est terminé, à partir d'ici vous pouvez :", + "view_analytics": "Voir les analyses", + "schedule_a_new_post": "Programmer une nouvelle publication", + "to_sell_posts_you_would_have_to": "Pour vendre des publications, vous devez :", + "1_connect_at_least_one_channel": "1. Connecter au moins un canal", + "2_connect_you_bank_account": "2. Connecter votre compte bancaire", + "go_back_to_connect_channels": "Revenir pour connecter des canaux", + "move_to_the_seller_page_to_connect_you_bank": "Aller à la page vendeur pour connecter votre banque", + "connect_channels": "Connecter des canaux", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Connectez vos canaux de réseaux sociaux et de sites de publication pour\n programmer des publications ultérieurement", + "social": "Réseaux sociaux", + "publishing_platforms": "Plateformes de publication", + "no_channels": "Aucun canal pour le moment", + "connect_your_accounts": "Connectez vos comptes sociaux pour commencer à planifier, publier et analyser — tout en un seul endroit.", + "notifications": "Notifications", + "no_notifications": "Aucune notification", + "send_message": "Envoyer un message", + "mar_28": "28 mars", + "there_are_no_messages_yet": "Il n'y a pas encore de messages.", + "checkout_the_marketplace": "Découvrir la place de marché", + "go_to_marketplace": "Aller à la place de marché", + "all_messages": "Tous les messages", + "previous": "Précédent", + "select_or_upload_pictures_maximum_5_at_a_time": "Sélectionnez ou téléversez des images (maximum 5 à la fois)", + "you_can_also_drag_drop_pictures": "Vous pouvez aussi glisser-déposer des images", + "you_don_t_have_any_assets_yet": "Vous n'avez pas encore d'actifs.", + "click_the_button_below_to_upload_one": "Cliquez sur le bouton ci-dessous pour en téléverser un", + "click_the_button_below_to_upload_other": "Cliquez sur le bouton ci-dessous pour télécharger plusieurs fichiers.", + "add_selected_media": "Ajouter les médias sélectionnés", + "insert_media": "Insérer un média", + "design_media": "Média de conception", + "select": "Sélectionner", + "editor": "Éditeur", + "clear": "Effacer", + "order_completed": "Commande terminée", + "the_order_has_been_completed": "La commande a été terminée", + "post_has_been_published": "La publication a été publiée", + "url_1": "URL :", + "new_offer": "Nouvelle offre", + "platform": "Plateforme", + "posts": "Publications", + "pay_accept_offer": "Payer et accepter l'offre", + "accepted": "Accepté", + "post_draft": "Brouillon de publication", + "revision_needed": "Révision nécessaire", + "approve": "Approuver", + "preview": "Aperçu", + "revision_requested": "Révision demandée", + "accepted_1": "ACCEPTÉ", + "cancelled_by_the_seller": "Annulé par le vendeur", + "please_select_your_country_where_your_business_is": "Veuillez sélectionner le pays où se trouve votre entreprise.", + "select_country": "--SÉLECTIONNER UN PAYS--", + "connect_bank_account": "Connecter un compte bancaire", + "seller_mode": "Mode vendeur", + "active": "Actif", + "details": "Détails", + "audience_size": "Taille de l'audience", + "add_another_platform": "Ajouter une autre plateforme", + "send_an_offer_for": "Envoyer une offre pour $", + "complete_order_and_pay_early": "Terminer la commande et payer à l'avance", + "order_in_progress": "Commande en cours", + "create_a_new_offer": "Créer une nouvelle offre", + "orders": "Commandes", + "price": "Prix", + "state": "État", + "showing": "Affichage", + "to": "à", + "from": "de", + "results": "Résultats", + "content_writer": "Rédacteur de contenu", + "influencer": "Influenceur", + "request_service": "Demander un service", + "the_marketplace_is_not_opened_yet": "La place de marché n'est pas encore ouverte", + "check_again_soon": "Revenez bientôt !", + "filter": "Filtrer", + "result": "Résultat", + "seller": "Vendeur", + "buyer": "Acheteur", + "discord_support": "Support Discord", + "teams": "Équipes", + "webhooks_1": "Webhooks", + "auto_post": "Publication automatique", + "logout_from": "Se déconnecter de", + "join_10000_entrepreneurs_who_use_postiz": "Rejoignez plus de 10 000 entrepreneurs qui utilisent Postiz", + "to_manage_all_your_social_media_channels": "Pour gérer tous vos réseaux sociaux", + "100_no_risk_trial": "Essai 100% sans risque", + "pay_nothing_for_the_first_7_days": "Ne payez rien pendant les 7 premiers jours", + "cancel_anytime_hassle_free": "Annulez à tout moment, sans tracas", + "add_free_subscription": "-- AJOUTER UN ABONNEMENT GRATUIT --", + "currently_impersonating": "Actuellement en mode usurpation", + "user_1": "utilisateur :", + "drag_n_drop_some_files_here": "Glissez-déposez des fichiers ici", + "add_time_slot": "Ajouter un créneau horaire", + "add_slot": "Ajouter un créneau", + "cancel_publication": "Annuler la publication", + "statistics": "Statistiques", + "loading": "Chargement", + "short_link": "Lien court", + "original_link": "Lien original", + "clicks": "Clics", + "selected_customer": "Client sélectionné", + "customer": "Client :", + "repeat_post_every": "Répéter la publication tous les...", + "use_this_media": "Utiliser ce média", + "create_new_post": "Créer une publication", + "update_post": "Mettre à jour le post existant", + "merge_comments_into_one_post": "Fusionner les commentaires en une seule publication", + "accounts_that_will_engage": "Comptes qui vont interagir :", + "day": "Jour", + "week": "Semaine", + "month": "Mois", + "remove_from_customer": "Retirer du client", + "show_more": "+ Afficher plus", + "show_less": "- Afficher moins", + "upload": "Téléverser", + "ai": "IA", + "add_channel": "Ajouter un canal", + "add_platform": "Ajouter une plateforme", + "articles": "Articles", + "add_comment": "Ajouter un commentaire", + "add_post": "Ajouter une publication dans un fil de discussion", + "add_comment_or_post": "Ajouter un commentaire / une publication", + "you_are_in_global_editing_mode": "Vous êtes en mode d'édition global", + "the_post_should_be_at_least_6_characters_long": "Le post doit contenir au moins 6 caractères", + "are_you_sure_you_want_to_delete_post": "Êtes-vous sûr de vouloir supprimer cette publication ?", + "post_deleted_successfully": "Publication supprimée avec succès", + "delete_post": "Supprimer le post", + "save_as_draft": "Enregistrer comme brouillon", + "post_now": "Publier maintenant", + "please_add": "Veuillez ajouter", + "to_your_telegram_group_channel_and_click_here": "à votre groupe / canal Telegram et cliquez ici :", + "connect_telegram": "Connecter Telegram", + "please_add_the_following_command_in_your_chat": "Veuillez ajouter la commande suivante dans votre chat :", + "copy": "Copier", + "settings": "Paramètres", + "integrations": "Intégrations", + "add_integration": "Ajouter une intégration", + "you_are_now_editing_only": "Vous modifiez maintenant uniquement", + "tag_a_company": "Taguer une entreprise", + "video_length_is_invalid_must_be_up_to": "La durée de la vidéo est invalide, elle doit être au maximum de", + "seconds": "secondes", + "this_feature_available_only_for_photos": "Cette fonctionnalité est disponible uniquement pour les photos, elle ajoutera une musique par défaut que vous pourrez changer plus tard.", + "allow_user_to": "Autoriser l'utilisateur à :", + "your_video_will_be_labeled_promotional": "Votre vidéo sera étiquetée « Contenu promotionnel ».", + "this_cannot_be_changed_once_posted": "Ceci ne pourra plus être modifié une fois votre vidéo publiée.", + "turn_on_to_disclose_video_promotes": "Activez pour indiquer que cette vidéo fait la promotion de biens ou services en échange d'une contrepartie. Votre vidéo peut faire la promotion de vous-même, d'un tiers, ou des deux.", + "you_are_promoting_yourself": "Vous faites la promotion de vous-même ou de votre propre marque.", + "this_video_will_be_classified_brand_organic": "Cette vidéo sera classée comme contenu de marque organique.", + "you_are_promoting_another_brand": "Vous faites la promotion d'une autre marque ou d'un tiers.", + "this_video_will_be_classified_branded_content": "Cette vidéo sera classée comme contenu de marque.", + "by_posting_you_agree_to_tiktoks": "En publiant, vous acceptez les conditions de TikTok", + "music_usage_confirmation": "Confirmation d'utilisation de la musique", + "branded_content_policy": "Politique sur le contenu de marque", + "select_1": "--Sélectionner--", + "select_flair": "--Sélectionner un flair--", + "link": "Lien", + "add_subreddit": "Ajouter un subreddit", + "please_add_at_least_one_subreddit": "Veuillez ajouter au moins un subreddit", + "add_community": "Ajouter une communauté", + "select_post_type": "Sélectionner le type de publication...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Nous n'avons trouvé aucune entreprise liée à votre page LinkedIn.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Veuillez fermer cette fenêtre, créer une nouvelle page et ajouter à nouveau un nouveau canal.", + "select_linkedin_page": "Sélectionnez la page LinkedIn :", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Nous n'avons trouvé aucune entreprise liée aux pages sélectionnées.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Nous vous recommandons de connecter toutes les pages et toutes les entreprises.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Veuillez fermer cette fenêtre, supprimer votre intégration et ajouter à nouveau un nouveau canal.", + "select_instagram_account": "Sélectionnez le compte Instagram :", + "select_page": "Sélectionnez la page :", + "generate_image_with_ai": "Générer une image avec l'IA", + "reconnect_channel": "Reconnecter le canal", + "update_credentials": "Mettre à jour les identifiants", + "additional_settings": "Paramètres supplémentaires", + "change_bot": "Changer de bot", + "move_add_to_customer": "Déplacer / ajouter au client", + "edit_time_slots": "Modifier les créneaux horaires", + "enable_channel": "Activer le canal", + "disable_channel": "Désactiver le canal", + "add": "Ajouter", + "short_post": "Message court", + "long_post": "Message long", + "a_thread_with_short_posts": "Un fil avec des messages courts", + "a_thread_with_long_posts": "Un fil avec des messages longs", + "personal_voice_i_am_happy_to_announce": "Voix personnelle (« Je suis heureux d'annoncer »)", + "company_voice_we_are_happy_to_announce": "Voix d'entreprise (« Nous sommes heureux d'annoncer »)", + "generate": "Générer", + "generate_posts": "Générer des messages", + "purchase_a_life_time_pro_account_with_sol_199": "Achetez un compte PRO à vie avec SOL (199 $). Veuillez noter qu’aucun remboursement n’est possible pour cet achat.", + "purchase_now": "Acheter maintenant", + "pay_today": "Payer aujourd'hui", + "we_are_sorry_to_see_you_go": "Nous sommes désolés de vous voir partir :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Pourriez-vous nous dire brièvement ce que nous aurions pu mieux faire ?", + "cancel_subscription": "Annuler l'abonnement", + "plans": "Formules", + "monthly": "MENSUEL", + "yearly": "ANNUEL", + "reactivate_subscription": "Réactiver l'abonnement", + "update_payment_method_invoices_history": "Mettre à jour le mode de paiement / Historique des factures", + "cancel_subscription_1": "Annuler l'abonnement", + "your_subscription_will_be_canceled_at": "Votre abonnement sera annulé le", + "you_will_never_be_charged_again": "Vous ne serez plus jamais facturé", + "current_package": "Forfait actuel :", + "next_package": "Forfait suivant :", + "claim": "Réclamer", + "frequently_asked_questions": "Foire aux questions", + "autopost": "Publication automatique", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "La publication automatique peut publier vos nouveaux éléments RSS sur les réseaux sociaux.", + "title": "Titre", + "add_an_autopost": "Ajouter une publication automatique", + "post_content": "Contenu de la publication", + "sign_up": "S'inscrire", + "or": "OU", + "by_registering_you_agree_to_our": "En vous inscrivant, vous acceptez nos", + "and": "et", + "terms_of_service": "Conditions d'utilisation", + "privacy_policy": "Politique de confidentialité", + "create_account": "Créer un compte", + "already_have_an_account": "Vous avez déjà un compte ?", + "sign_in": "Se connecter", + "sign_in_1": "Se connecter", + "don_t_have_an_account": "Vous n'avez pas de compte ?", + "forgot_password": "Mot de passe oublié", + "forgot_password_1": "Mot de passe oublié", + "send_password_reset_email": "Envoyer l'e-mail de réinitialisation du mot de passe", + "go_back_to_login": "Retour à la connexion", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Nous vous avons envoyé un e-mail avec un lien pour réinitialiser votre mot de passe.", + "change_password": "Changer le mot de passe", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Nous avons réinitialisé votre mot de passe avec succès. Vous pouvez maintenant vous connecter avec votre", + "click_here_to_go_back_to_login": "Cliquez ici pour revenir à la connexion", + "activate_your_account": "Activez votre compte", + "thank_you_for_registering": "Merci pour votre inscription !", + "please_check_your_email_to_activate_your_account": "Veuillez vérifier votre e-mail pour activer votre compte.", + "sign_in_with": "Se connecter avec", + "continue_with_google": "Continuer avec Google", + "sign_in_with_github": "Se connecter avec GitHub", + "continue_with_farcaster": "Continuer avec Farcaster", + "continue_with_your_wallet": "Continuer avec votre portefeuille", + "stars_per_day": "Étoiles par jour", + "media": "Médias", + "check_launch": "Vérifier le lancement", + "load_your_github_repository_from_settings_to_see_analytics": "Chargez votre dépôt GitHub depuis les paramètres pour voir les analyses", + "stars": "Étoiles", + "processing_stars": "Traitement des étoiles...", + "forks": "Forks", + "registration_is_disabled": "L'inscription est désactivée", + "login_instead": "Connectez-vous à la place", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Sélectionnez une conversation et discutez.", + "adding_channel_redirecting_you": "Ajout du canal, redirection en cours", + "could_not_add_provider": "Impossible d'ajouter le fournisseur.", + "you_are_being_redirected_back": "Vous êtes en cours de redirection", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Nous rencontrons quelques difficultés, essayez de rafraîchir la page", + "post_not_found": "Publication introuvable", + "publication_date": "Date de publication :", + "analytics": "Analyses", + "launches": "Lancements", + "plugs": "Plugs", + "billing": "Facturation", + "affiliate": "Affiliation", + "monday": "Lundi", + "tuesday": "Mardi", + "wednesday": "Mercredi", + "thursday": "Jeudi", + "friday": "Vendredi", + "saturday": "Samedi", + "sunday": "Dimanche", + "can_t_change_date_remove_post_from_publication": "Impossible de changer la date, retirez la publication de la publication", + "predicted_github_trending_change": "Changement de tendance GitHub prédit", + "duplicate_post": "Publication en double", + "preview_post": "Aperçu de la publication", + "post_statistics": "Statistiques de la publication", + "draft": "Brouillon", + "week_number": "Semaine {{number}}", + "top_title_edit_webhook": "Modifier le webhook", + "top_title_add_webhook": "Ajouter un webhook", + "top_title_oh_no": "Oh non", + "top_title_auto_plug": "Auto Plug : {{title}}", + "top_title_edit_autopost": "Modifier l'autopost", + "top_title_add_autopost": "Ajouter un autopost", + "top_title_send_a_new_offer": "Envoyer une nouvelle offre", + "top_title_media_library": "Médiathèque", + "top_title_add_signature": "Ajouter une signature", + "top_title_send_a_message_to": "Envoyer un message à {{name}}", + "top_title_configure_provider": "Configurer le fournisseur", + "top_title_add_member": "Ajouter un membre", + "top_title_change_bot_picture": "Changer l'image du bot", + "top_title_create_a_new_tag": "Créer un nouveau tag", + "top_title_select_company": "Sélectionner une entreprise", + "top_title_additional_settings": "Paramètres supplémentaires", + "top_title_time_table_slots": "Créneaux horaires", + "top_title_design_media": "Concevoir un média", + "top_title_edit_post": "Modifier la publication", + "top_title_create_post": "Créer une publication", + "top_title_move__add_to_customer": "Déplacer / Ajouter au client", + "top_title_add_api_key_for": "Ajouter une clé API pour {{name}}", + "top_title_instance_url": "URL de l'instance", + "top_title_custom_url": "URL personnalisée", + "top_title_add_channel": "Ajouter un canal", + "top_title_add_telegram": "Ajouter Telegram", + "top_title_add_wrapcast": "Ajouter Wrapcast", + "top_title_comments_for": "Commentaires pour {{date}}", + "top_title_edit_signature": "Modifier la signature", + "label_name": "Nom", + "label_url": "URL", + "label_title": "Titre", + "label_subtitle": "Sous-titre", + "label_email": "E-mail", + "label_full_name": "Nom complet", + "label_password": "Mot de passe", + "label_confirm_password": "Confirmer le mot de passe", + "label_api_key": "Clé API", + "label_instance_url": "URL de l'instance", + "label_custom_url": "URL personnalisée", + "label_feedback": "Retour d'information", + "label_bio": "Bio", + "label_role": "Rôle", + "label_country": "Pays", + "label_audience_size": "Taille de l'audience sur toutes les plateformes", + "label_pick_time": "Choisir l'heure", + "label_nickname": "Surnom", + "label_write_anything": "Écrire n'importe quoi", + "label_output_format": "Format de sortie", + "label_add_pictures": "Ajouter des images ?", + "label_hour": "Heure", + "label_minutes": "Minutes", + "label_select_publication": "Sélectionner la publication", + "label_canonical_link": "Lien canonique", + "label_cover_picture": "Image de couverture", + "label_tags": "Tags", + "label_topics": "Sujets", + "label_tags_maximum_4": "Tags (maximum 4)", + "label_attachments": "Pièces jointes", + "label_type": "Type", + "label_thumbnail": "Vignette", + "label_who_can_see_this_video": "Qui peut voir cette vidéo ?", + "label_content_posting_method": "Méthode de publication du contenu", + "label_auto_add_music": "Ajouter automatiquement de la musique", + "label_duet": "Duet", + "label_stitch": "Stitch", + "label_comments": "Commentaires", + "label_disclose_video_content": "Divulguer le contenu de la vidéo", + "label_your_brand": "Votre marque", + "label_branded_content": "Contenu de marque", + "label_subreddit": "Subreddit", + "label_flair": "Flair", + "label_media": "Média", + "label_search_subreddit": "Rechercher un subreddit", + "label_delay": "Délai", + "label_post_type": "Type de publication", + "label_collaborators": "Collaborateurs (max 3) - les comptes ne peuvent pas être privés", + "label_community": "Communauté", + "label_search_community": "Rechercher une communauté", + "label_channel": "Canal", + "label_search_channel": "Rechercher un canal", + "label_select_channel": "Sélectionner un canal", + "label_new_password": "Nouveau mot de passe", + "label_repeat_password": "Répéter le mot de passe", + "label_platform": "Plateforme", + "label_price_per_post": "Prix par publication", + "label_integrations": "Intégrations", + "label_code": "Code", + "label_should_sync_last_post": "Faut-il synchroniser la dernière publication actuelle ?", + "label_when_post": "Quand devons-nous la publier ?", + "label_autogenerate_content": "Générer automatiquement le contenu", + "label_generate_picture": "Générer une image ?", + "label_company": "Entreprise", + "label_tag_color": "Couleur de l'étiquette", + "label_select_board": "Sélectionner un tableau", + "label_select_organization": "Sélectionner une organisation", + "label_auto_add_signature": "Ajouter automatiquement une signature ?", + "enable_color_picker": "Activer le sélecteur de couleurs", + "cancel_the_color_picker": "Annuler le sélecteur de couleurs", + "no_content_yet": "Pas encore de contenu", + "write_your_reply": "Écrivez votre post...", + "add_a_tag": "Ajouter une étiquette", + "add_to_calendar": "Ajouter au calendrier", + "select_channels_from_circles": "Sélectionnez des canaux à partir des cercles ci-dessus", + "not_matching_order": "Ordre non correspondant", + "submit_for_order": "Soumettre la commande", + "schedule": "Planifier", + "update": "Mettre à jour", + "attachments": "Pièces jointes", + "tags": "Tags", + "public_to_everyone": "Public pour tous", + "mutual_follow_friends": "Amis avec abonnement mutuel", + "follower_of_creator": "Abonné du créateur", + "self_only": "Moi uniquement", + "post_content_directly_to_tiktok": "Publier le contenu directement sur TikTok", + "upload_content_to_tiktok_without_posting": "Télécharger le contenu sur TikTok sans le publier", + "choose_upload_without_posting_description": "Choisissez de télécharger sans publier si vous souhaitez revoir et modifier votre contenu dans l’application TikTok avant de le publier. Cela vous donne accès aux outils d’édition intégrés de TikTok et vous permet de faire des ajustements finaux avant la publication.", + "faq_am_i_going_to_be_charged_by_postiz": "Vais-je être facturé par Postiz ?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Pour confirmer les informations de carte de crédit, Postiz retiendra 2 $ et les libérera immédiatement", + "faq_can_i_trust_postiz_gitroom": "Puis-je faire confiance à Postiz ?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz est fièrement open source ! Nous croyons en une culture éthique et transparente, ce qui signifie que Postiz existera toujours. Vous pouvez consulter l'intégralité du code ou l'utiliser pour des projets personnels. Pour voir le dépôt open source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">cliquez ici</a>.", + "faq_what_are_channels": "Que sont les canaux ?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz vous permet de planifier vos publications sur différents canaux.\nUn canal est une plateforme de publication où vous pouvez programmer vos publications.\nPar exemple, vous pouvez planifier vos publications sur X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads et Pinterest.", + "faq_what_are_team_members": "Que sont les membres de l’équipe ?", + "faq_if_you_have_a_team_with_multiple_members": "Si vous avez une équipe avec plusieurs membres, vous pouvez les inviter dans votre espace de travail pour collaborer sur vos publications et ajouter leurs canaux personnels", + "faq_what_is_ai_auto_complete": "Qu’est-ce que la saisie automatique par IA ?", + "faq_we_automate_chatgpt_to_help_you_write": "Nous automatisons ChatGPT pour vous aider à rédiger des publications sociales et des articles.", + "enter_email": "Saisissez l’e-mail", + "are_you_sure": "Êtes-vous sûr ?", + "yes_delete_it": "Oui, supprimez-le !", + "no_cancel": "Non, annuler !", + "are_you_sure_you_want_to_delete": "Êtes-vous sûr de vouloir supprimer {{name}} ?", + "are_you_sure_you_want_to_delete_the_image": "Êtes-vous sûr de vouloir supprimer l'image ?", + "are_you_sure_you_want_to_logout": "Êtes-vous sûr de vouloir vous déconnecter ?", + "yes_logout": "Oui, se déconnecter", + "are_you_sure_you_want_to_delete_this_slot": "Êtes-vous sûr de vouloir supprimer cet emplacement ?", + "are_you_sure_you_want_to_delete_this_subreddit": "Êtes-vous sûr de vouloir supprimer ce Subreddit ?", + "are_you_sure_you_want_to_close_the_window": "Êtes-vous sûr de vouloir fermer la fenêtre ?", + "yes_close": "Oui, fermer", + "link_copied_to_clipboard": "Lien copié dans le presse-papiers", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Êtes-vous sûr de vouloir fermer cette fenêtre ? (toutes les données seront perdues)", + "yes_close_it": "Oui, fermez-la !", + "uploading_pictures": "Téléchargement des images...", + "agent_starting": "Agent en cours de démarrage", + "researching_your_content": "Recherche de votre contenu...", + "understanding_the_category": "Compréhension de la catégorie...", + "finding_the_topic": "Recherche du sujet...", + "finding_popular_posts_to_match_with": "Recherche de publications populaires correspondantes...", + "generating_hook": "Génération du crochet...", + "generating_content": "Génération du contenu...", + "generating_pictures": "Génération des images...", + "finding_time_to_post": "Recherche du meilleur moment pour publier...", + "write_anything": "Écrivez n'importe quoi", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Vous pouvez écrire tout ce que vous voulez, et aussi ajouter des liens, nous ferons les recherches pour vous...", + "output_format": "Format de sortie", + "add_pictures": "Ajouter des images ?", + "7_days": "7 jours", + "30_days": "30 jours", + "90_days": "90 jours", + "start_7_days_free_trial": "Commencez l’essai gratuit de 7 jours", + "change_language": "Changer de langue", + "that_a_wrap": "C'est terminé !\n\nSi vous avez aimé ce fil :\n\n1. Suivez-moi @{{username}} pour en voir d'autres\n2. Retweetez le tweet ci-dessous pour partager ce fil avec votre audience\n", + "post_as_images_carousel": "Publier en carrousel d’images", + "save_set": "Enregistrer l'ensemble", + "separate_post": "Séparer le post en plusieurs publications", + "label_who_can_reply_to_this_post": "Qui peut répondre à ce post ?", + "delete_integration": "Supprimer l'intégration", + "start_writing_your_post": "Commencez à écrire votre post pour un aperçu", + "billing_join_over": "Rejoignez plus de", + "billing_entrepreneurs_count": "18 000+ entrepreneurs", + "billing_who_use": "qui utilisent", + "billing_postiz_grow_social": "Postiz pour développer leur présence sur les réseaux sociaux", + "billing_no_risk_trial": "Essai gratuit 100% sans risque", + "billing_pay_nothing_7_days": "Ne payez RIEN pendant les 7 premiers jours", + "billing_cancel_anytime": "Annulez à tout moment, sans tracas", + "billing_choose_plan": "Choisissez un forfait", + "billing_monthly": "Mensuel", + "billing_yearly": "Annuel", + "billing_20_percent_off": "20% de réduction", + "billing_features": "Fonctionnalités", + "billing_channel": "canal", + "billing_channels": "canaux", + "billing_unlimited": "Illimité", + "billing_posts_per_month": "publications par mois", + "billing_unlimited_team_members": "Membres d'équipe illimités", + "billing_ai_auto_complete": "Saisie automatique par IA", + "billing_ai_copilots": "Copilotes IA", + "billing_ai_autocomplete": "Saisie automatique IA", + "billing_advanced_picture_editor": "Éditeur d'images avancé", + "billing_ai_images_per_month": "Images IA par mois", + "billing_ai_videos_per_month": "Vidéos IA par mois", + "billing_billing_address": "Adresse de facturation", + "billing_payment": "Paiement", + "billing_powered_by_stripe": "Propulsé par Stripe", + "billing_your_7_day_trial_is": "Votre essai de 7 jours est", + "billing_100_percent_free": "100% gratuit", + "billing_ending": "se termine", + "billing_cancel_anytime_short": "Annulez à tout moment.", + "billing_pay_0_start_trial": "Payez 0 $ aujourd'hui - Commencez votre essai gratuit !", + "billing_pay_now": "Payer maintenant", + "billing_per_month": "/ mois" } diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index 2dd89494..db8266e9 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -1,507 +1,539 @@ { - "calendar": "לוח שנה", - "webhooks": "וובהוקים", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "וובהוקים הם דרך לקבל התראה כאשר משהו קורה ב-Postiz באמצעות בקשת HTTP.", - "name": "שם", - "url": "כתובת URL", - "edit": "ערוך", - "delete": "מחק", - "add_a_webhook": "הוסף וובהוק", - "save": "שמור", - "send_test": "שלח בדיקה", - "select_role": "בחר תפקיד", - "video_made_with_ai": "וידאו שנוצר באמצעות בינה מלאכותית", - "please_add_at_least": "אנא הוסף לפחות 20 תווים", - "send_invitation_via_email": "לשלוח הזמנה בדוא\"ל?", - "global_settings": "הגדרות כלליות", - "copy_id": "העתק מזהה ערוץ", - "team_members": "חברי צוות", - "invite_your_assistant_or_team_member_to_manage_your_account": "הזמן את העוזר או חבר הצוות שלך לנהל את החשבון שלך", - "remove": "הסר", - "add_another_member": "הוסף חבר נוסף", - "signatures": "חתימות", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "ניתן להוסיף חתימות לחשבונך לשימוש בפוסטים שלך.", - "content": "תוכן", - "auto_add": "הוספה אוטומטית?", - "actions": "פעולות", - "use_signature": "השתמש בחתימה", - "add_a_signature": "הוסף חתימה", - "no": "לא", - "yes": "כן", - "your_git_repository": "מאגר ה-Git שלך", - "connect_your_github_repository_to_receive_updates_and_analytics": "חבר את מאגר ה-GitHub שלך כדי לקבל עדכונים וניתוחים", - "connected": "מחובר:", - "disconnect": "נתק", - "connect_your_repository": "חבר את המאגר שלך", - "cancel": "ביטול", - "connect": "חיבור", - "public_api": "API ציבורי", - "check_n8n": "בדקו את הצומת המותאם שלנו ל-N8N עבור Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "השתמש ב-API של Postiz כדי להשתלב עם הכלים שלך.", - "read_how_to_use_it_over_the_documentation": "קרא כיצד להשתמש בזה בתיעוד.", - "reveal": "הצג", - "copy_key": "העתק מפתח", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "חבר את שרת ה-MCP של Postiz ללקוח שלך (הזרמת Http) כדי לתזמן את הפוסטים שלך מהר יותר!", - "share_with_a_client": "שתף עם לקוח", - "post": "פוסט", - "comments": "תגובות", - "user": "משתמש", - "login_register_to_add_comments": "התחבר / הירשם כדי להוסיף תגובות", - "status": "סטטוס:", - "there_are_not_plugs_matching_your_channels": "אין פלאגים התואמים לערוצים שלך", - "you_have_to_add_x_or_linkedin_or_threads": "עליך להוסיף: X או LinkedIn או Threads", - "go_to_the_calendar_to_add_channels": "עבור ללוח השנה כדי להוסיף ערוצים", - "channels": "ערוצים", - "activate": "הפעל", - "this_channel_needs_to_be_refreshed": "הערוץ הזה צריך לרענון,", - "click_here_to_refresh": "לחץ כאן לרענון", - "can_t_show_analytics_yet": "לא ניתן להציג נתוני אנליטיקה עדיין", - "you_have_to_add_social_media_channels": "עליך להוסיף ערוצי מדיה חברתית", - "supported": "נתמך:", - "step": "שלב", - "skip_onboarding": "דלג על תהליך ההתחלה", - "onboarding": "תהליך התחלה", - "next": "הבא", - "you_are_done_from_here_you_can": "סיימת, מכאן תוכל:", - "view_analytics": "הצג אנליטיקה", - "schedule_a_new_post": "תזמן פוסט חדש", - "to_sell_posts_you_would_have_to": "כדי למכור פוסטים עליך:", - "1_connect_at_least_one_channel": "1. לחבר לפחות ערוץ אחד", - "2_connect_you_bank_account": "2. לחבר את חשבון הבנק שלך", - "go_back_to_connect_channels": "חזור לחיבור ערוצים", - "move_to_the_seller_page_to_connect_you_bank": "עבור לעמוד המוכר כדי לחבר את חשבון הבנק שלך", - "connect_channels": "חבר ערוצים", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "חבר את ערוצי המדיה החברתית ואתרי הפרסום שלך כדי\n לתזמן פוסטים מאוחר יותר", - "social": "חברתי", - "publishing_platforms": "פלטפורמות פרסום", - "no_channels": "אין ערוצים עדיין", - "connect_your_accounts": "חברו את החשבונות החברתיים שלכם כדי להתחיל לתזמן, לפרסם ולנתח — הכל במקום אחד.", - "notifications": "התראות", - "no_notifications": "אין התראות", - "send_message": "שלח הודעה", - "mar_28": "28 במרץ", - "there_are_no_messages_yet": "עדיין אין הודעות.", - "checkout_the_marketplace": "בדוק את השוק", - "go_to_marketplace": "עבור לשוק", - "all_messages": "כל ההודעות", - "previous": "הקודם", - "select_or_upload_pictures_maximum_5_at_a_time": "בחר או העלה תמונות (מקסימום 5 בכל פעם)", - "you_can_also_drag_drop_pictures": "ניתן גם לגרור ולשחרר תמונות", - "you_don_t_have_any_assets_yet": "עדיין אין לך נכסים.", - "click_the_button_below_to_upload_one": "לחץ על הכפתור למטה כדי להעלות אחד", - "click_the_button_below_to_upload_two": "", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "הוסף מדיה שנבחרה", - "insert_media": "הוסף מדיה", - "design_media": "עצב מדיה", - "select": "בחר", - "editor": "עורך", - "clear": "נקה", - "order_completed": "ההזמנה הושלמה", - "the_order_has_been_completed": "ההזמנה הושלמה", - "post_has_been_published": "הפוסט פורסם", - "url_1": "כתובת URL:", - "new_offer": "הצעה חדשה", - "platform": "פלטפורמה", - "posts": "פוסטים", - "pay_accept_offer": "שלם וקבל הצעה", - "accepted": "התקבל", - "post_draft": "טיוטת פוסט", - "revision_needed": "נדרשת עריכה", - "approve": "אשר", - "preview": "תצוגה מקדימה", - "revision_requested": "בקשת עריכה", - "accepted_1": "התקבל", - "cancelled_by_the_seller": "בוטל על ידי המוכר", - "please_select_your_country_where_your_business_is": "אנא בחר את המדינה שבה העסק שלך נמצא.", - "select_country": "--בחר מדינה--", - "connect_bank_account": "חבר חשבון בנק", - "seller_mode": "מצב מוכר", - "active": "פעיל", - "details": "פרטים", - "audience_size": "גודל קהל", - "add_another_platform": "הוסף פלטפורמה נוספת", - "send_an_offer_for": "שלח הצעה עבור $", - "complete_order_and_pay_early": "השלם הזמנה ושלם מראש", - "order_in_progress": "הזמנה בתהליך", - "create_a_new_offer": "צור הצעה חדשה", - "orders": "הזמנות", - "price": "מחיר", - "state": "מדינה", - "showing": "מציג", - "to": "עד", - "from": "מ", - "results": "תוצאות", - "content_writer": "כותב תוכן", - "influencer": "משפיען", - "request_service": "בקש שירות", - "the_marketplace_is_not_opened_yet": "המרקטפלייס עדיין לא נפתח", - "check_again_soon": "בדוק שוב בקרוב!", - "filter": "סנן", - "result": "תוצאה", - "seller": "מוכר", - "buyer": "קונה", - "discord_support": "תמיכה בדיסקורד", - "teams": "צוותים", - "webhooks_1": "וובהוקים", - "auto_post": "פוסט אוטומטי", - "logout_from": "התנתק מ", - "join_10000_entrepreneurs_who_use_postiz": "הצטרפו ל-10,000+ יזמים שמשתמשים ב-Postiz", - "to_manage_all_your_social_media_channels": "לניהול כל ערוצי המדיה החברתית שלך", - "100_no_risk_trial": "100% ניסיון ללא סיכון", - "pay_nothing_for_the_first_7_days": "לא תשלם כלום ב-7 הימים הראשונים", - "cancel_anytime_hassle_free": "ביטול בכל עת, ללא טרחה", - "add_free_subscription": "-- הוסף מנוי חינמי --", - "currently_impersonating": "מתחזה כרגע", - "user_1": "משתמש:", - "drag_n_drop_some_files_here": "גרור ושחרר קבצים כאן", - "add_time_slot": "הוסף משבצת זמן", - "add_slot": "הוסף משבצת", - "cancel_publication": "בטל פרסום", - "statistics": "סטטיסטיקות", - "loading": "טוען", - "short_link": "קישור מקוצר", - "original_link": "קישור מקורי", - "clicks": "קליקים", - "selected_customer": "לקוח נבחר", - "customer": "לקוח:", - "repeat_post_every": "חזור על הפוסט כל...", - "use_this_media": "השתמש במדיה זו", - "create_new_post": "צור פוסט", - "update_post": "עדכן פוסט קיים", - "merge_comments_into_one_post": "מזג תגובות לפוסט אחד", - "accounts_that_will_engage": "חשבונות שיתקשרו:", - "day": "יום", - "week": "שבוע", - "month": "חודש", - "remove_from_customer": "הסר מהלקוח", - "show_more": "+ הצג עוד", - "show_less": "- הצג פחות", - "upload": "העלה", - "ai": "בינה מלאכותית", - "add_channel": "הוסף ערוץ", - "add_platform": "הוסף פלטפורמה", - "articles": "מאמרים", - "add_comment": "הוסף תגובה", - "add_post": "הוסף פוסט בדיון", - "add_comment_or_post": "הוסף תגובה / פוסט", - "you_are_in_global_editing_mode": "אתה נמצא במצב עריכה גלובלי", - "the_post_should_be_at_least_6_characters_long": "הפוסט צריך להיות באורך של לפחות 6 תווים", - "are_you_sure_you_want_to_delete_post": "האם אתה בטוח שברצונך למחוק את הפוסט הזה?", - "post_deleted_successfully": "הפוסט נמחק בהצלחה", - "delete_post": "מחק פוסט", - "save_as_draft": "שמור כטיוטה", - "post_now": "פרסם עכשיו", - "please_add": "אנא הוסף", - "to_your_telegram_group_channel_and_click_here": "לקבוצת / ערוץ הטלגרם שלך ולחץ כאן:", - "connect_telegram": "חבר טלגרם", - "please_add_the_following_command_in_your_chat": "אנא הוסף את הפקודה הבאה בצ'אט שלך:", - "copy": "העתק", - "settings": "הגדרות", - "integrations": "אינטגרציות", - "add_integration": "הוסף אינטגרציה", - "you_are_now_editing_only": "אתה עורך כעת רק", - "tag_a_company": "תייג חברה", - "video_length_is_invalid_must_be_up_to": "אורך הווידאו לא תקין, חייב להיות עד", - "seconds": "שניות", - "this_feature_available_only_for_photos": "פונקציה זו זמינה רק לתמונות, היא תוסיף מוזיקה ברירת מחדל שתוכל לשנות מאוחר יותר.", - "allow_user_to": "אפשר למשתמש ל-", - "your_video_will_be_labeled_promotional": "הווידאו שלך יסומן כ\"תוכן פרסומי\".", - "this_cannot_be_changed_once_posted": "לא ניתן לשנות זאת לאחר שהווידאו פורסם.", - "turn_on_to_disclose_video_promotes": "הפעל כדי לחשוף שהווידאו הזה מקדם מוצרים או שירותים בתמורה למשהו בעל ערך. הווידאו שלך יכול לקדם את עצמך, צד שלישי, או שניהם.", - "you_are_promoting_yourself": "אתה מקדם את עצמך או את המותג שלך.", - "this_video_will_be_classified_brand_organic": "הווידאו הזה יסווג כמותג אורגני.", - "you_are_promoting_another_brand": "אתה מקדם מותג אחר או צד שלישי.", - "this_video_will_be_classified_branded_content": "הווידאו הזה יסווג כתוכן ממותג.", - "by_posting_you_agree_to_tiktoks": "על ידי פרסום, אתה מסכים לתנאי השימוש של TikTok", - "music_usage_confirmation": "אישור שימוש במוזיקה", - "branded_content_policy": "מדיניות תוכן ממותג", - "select_1": "--בחר--", - "select_flair": "--בחר תיוג--", - "link": "קישור", - "add_subreddit": "הוסף סאב-רדיט", - "please_add_at_least_one_subreddit": "אנא הוסף לפחות סאב-רדיט אחד", - "add_community": "הוסף קהילה", - "select_post_type": "בחר סוג פוסט...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "לא הצלחנו למצוא עסק שמחובר לעמוד הלינקדאין שלך.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "אנא סגור את החלון הזה, צור עמוד חדש והוסף ערוץ חדש שוב.", - "select_linkedin_page": "בחר עמוד לינקדאין:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "לא הצלחנו למצוא עסק שמחובר לעמודים שנבחרו.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "אנו ממליצים לחבר את כל העמודים וכל העסקים.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "אנא סגור את החלון הזה, מחק את האינטגרציה שלך והוסף ערוץ חדש שוב.", - "select_instagram_account": "בחר חשבון אינסטגרם:", - "select_page": "בחר עמוד:", - "generate_image_with_ai": "צור תמונה עם בינה מלאכותית", - "reconnect_channel": "חבר ערוץ מחדש", - "update_credentials": "עדכן פרטי גישה", - "additional_settings": "הגדרות נוספות", - "change_bot": "החלף בוט", - "move_add_to_customer": "העבר / הוסף ללקוח", - "edit_time_slots": "ערוך חלונות זמן", - "enable_channel": "הפעל ערוץ", - "disable_channel": "השבת ערוץ", - "add": "הוסף", - "short_post": "פוסט קצר", - "long_post": "פוסט ארוך", - "a_thread_with_short_posts": "שרשור עם פוסטים קצרים", - "a_thread_with_long_posts": "שרשור עם פוסטים ארוכים", - "personal_voice_i_am_happy_to_announce": "קול אישי (\"אני שמח להודיע\")", - "company_voice_we_are_happy_to_announce": "קול חברה (\"אנחנו שמחים להודיע\")", - "generate": "צור", - "generate_posts": "צור פוסטים", - "purchase_a_life_time_pro_account_with_sol_199": "רכוש חשבון PRO לכל החיים עם SOL ($199). לידיעתך, אין החזר כספי עבור רכישה זו.", - "purchase_now": "רכוש עכשיו", - "pay_today": "שלם היום", - "we_are_sorry_to_see_you_go": "מצטערים לראות אותך עוזב :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "האם תוכל לספר לנו בקצרה מה יכולנו לעשות טוב יותר?", - "cancel_subscription": "בטל מנוי", - "plans": "תוכניות", - "monthly": "חודשי", - "yearly": "שנתי", - "reactivate_subscription": "הפעל מחדש את המנוי", - "update_payment_method_invoices_history": "עדכן אמצעי תשלום / היסטוריית חשבוניות", - "cancel_subscription_1": "בטל מנוי", - "your_subscription_will_be_canceled_at": "המנוי שלך יבוטל בתאריך", - "you_will_never_be_charged_again": "לא תחויב שוב לעולם", - "current_package": "חבילה נוכחית:", - "next_package": "החבילה הבאה:", - "claim": "תבע", - "frequently_asked_questions": "שאלות נפוצות", - "autopost": "פרסום אוטומטי", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "פרסום אוטומטי יכול לפרסם פריטים חדשים מה-RSS שלך לרשתות החברתיות באופן אוטומטי", - "title": "כותרת", - "add_an_autopost": "הוסף פרסום אוטומטי", - "post_content": "תוכן הפוסט", - "sign_up": "הרשמה", - "or": "או", - "by_registering_you_agree_to_our": "בהרשמתך אתה מסכים ל", - "and": "ו", - "terms_of_service": "תנאי השירות", - "privacy_policy": "מדיניות פרטיות", - "create_account": "צור חשבון", - "already_have_an_account": "כבר יש לך חשבון?", - "sign_in": "התחבר", - "sign_in_1": "התחבר", - "don_t_have_an_account": "אין לך חשבון?", - "forgot_password": "שכחת סיסמה", - "forgot_password_1": "שכחת סיסמה", - "send_password_reset_email": "שלח אימייל לאיפוס סיסמה", - "go_back_to_login": "חזור להתחברות", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "שלחנו לך אימייל עם קישור לאיפוס הסיסמה שלך.", - "change_password": "שנה סיסמה", - "we_successfully_reset_your_password_you_can_now_login_with_your": "איפסנו את הסיסמה שלך בהצלחה. כעת תוכל להתחבר עם", - "click_here_to_go_back_to_login": "לחץ כאן כדי לחזור להתחברות", - "activate_your_account": "הפעל את החשבון שלך", - "thank_you_for_registering": "תודה שנרשמת!", - "please_check_your_email_to_activate_your_account": "אנא בדוק את האימייל שלך כדי להפעיל את החשבון.", - "sign_in_with": "התחבר באמצעות", - "continue_with_google": "המשך עם Google", - "sign_in_with_github": "התחבר עם GitHub", - "continue_with_farcaster": "המשך עם Farcaster", - "continue_with_your_wallet": "המשך עם הארנק שלך", - "stars_per_day": "כוכבים ליום", - "media": "מדיה", - "check_launch": "בדוק השקה", - "load_your_github_repository_from_settings_to_see_analytics": "טען את מאגר ה-GitHub שלך מההגדרות כדי לראות ניתוחים", - "stars": "כוכבים", - "processing_stars": "מעבד כוכבים...", - "forks": "פיצולים", - "registration_is_disabled": "ההרשמה מושבתת", - "login_instead": "התחבר במקום", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "בחר שיחה והתחל לצ'וטט.", - "adding_channel_redirecting_you": "מוסיף ערוץ, מפנה אותך", - "could_not_add_provider": "לא ניתן להוסיף ספק.", - "you_are_being_redirected_back": "אתה מנותב חזרה", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "אנו חווים קושי, נסה לרענן את הדף", - "post_not_found": "הפוסט לא נמצא", - "publication_date": "תאריך פרסום:", - "analytics": "אנליטיקה", - "launches": "השקות", - "plugs": "פלאגים", - "billing": "חיוב", - "affiliate": "שותפים", - "monday": "יום שני", - "tuesday": "יום שלישי", - "wednesday": "יום רביעי", - "thursday": "יום חמישי", - "friday": "יום שישי", - "saturday": "יום שבת", - "sunday": "יום ראשון", - "can_t_change_date_remove_post_from_publication": "לא ניתן לשנות את התאריך, הסר את הפוסט מהפרסום", - "predicted_github_trending_change": "שינוי מגמה חזוי ב-GitHub", - "duplicate_post": "פוסט כפול", - "preview_post": "תצוגה מקדימה של הפוסט", - "post_statistics": "סטטיסטיקות פוסט", - "draft": "טיוטה", - "week_number": "שבוע {{number}}", - "top_title_edit_webhook": "ערוך Webhook", - "top_title_add_webhook": "הוסף Webhook", - "top_title_oh_no": "אוי לא", - "top_title_auto_plug": "Auto Plug: {{title}}", - "top_title_edit_autopost": "ערוך פוסט אוטומטי", - "top_title_add_autopost": "הוסף פוסט אוטומטי", - "top_title_send_a_new_offer": "שלח הצעה חדשה", - "top_title_media_library": "ספריית מדיה", - "top_title_add_signature": "הוסף חתימה", - "top_title_send_a_message_to": "שלח הודעה אל {{name}}", - "top_title_configure_provider": "הגדר ספק", - "top_title_add_member": "הוסף חבר", - "top_title_change_bot_picture": "שנה תמונת בוט", - "top_title_create_a_new_tag": "צור תג חדש", - "top_title_select_company": "בחר חברה", - "top_title_additional_settings": "הגדרות נוספות", - "top_title_time_table_slots": "משבצות לוח זמנים", - "top_title_design_media": "עיצוב מדיה", - "top_title_edit_post": "ערוך פוסט", - "top_title_create_post": "צור פוסט", - "top_title_move__add_to_customer": "העבר / הוסף ללקוח", - "top_title_add_api_key_for": "הוסף מפתח API עבור {{name}}", - "top_title_instance_url": "כתובת מופע (Instance URL)", - "top_title_custom_url": "כתובת מותאמת אישית", - "top_title_add_channel": "הוסף ערוץ", - "top_title_add_telegram": "הוסף טלגרם", - "top_title_add_wrapcast": "הוסף Wrapcast", - "top_title_comments_for": "הערות עבור {{date}}", - "top_title_edit_signature": "ערוך חתימה", - "label_name": "שם", - "label_url": "כתובת URL", - "label_title": "כותרת", - "label_subtitle": "כותרת משנה", - "label_email": "אימייל", - "label_full_name": "שם מלא", - "label_password": "סיסמה", - "label_confirm_password": "אישור סיסמה", - "label_api_key": "מפתח API", - "label_instance_url": "כתובת מופע", - "label_custom_url": "כתובת מותאמת אישית", - "label_feedback": "משוב", - "label_bio": "ביוגרפיה", - "label_role": "תפקיד", - "label_country": "מדינה", - "label_audience_size": "גודל קהל בכל הפלטפורמות", - "label_pick_time": "בחר זמן", - "label_nickname": "כינוי", - "label_write_anything": "כתוב כל דבר", - "label_output_format": "פורמט פלט", - "label_add_pictures": "להוסיף תמונות?", - "label_hour": "שעה", - "label_minutes": "דקות", - "label_select_publication": "בחר פרסום", - "label_canonical_link": "קישור קנוני", - "label_cover_picture": "תמונת שער", - "label_tags": "תגיות", - "label_topics": "נושאים", - "label_tags_maximum_4": "תגיות (מקסימום 4)", - "label_attachments": "קבצים מצורפים", - "label_type": "סוג", - "label_thumbnail": "תמונה ממוזערת", - "label_who_can_see_this_video": "מי יכול לראות את הסרטון הזה?", - "label_content_posting_method": "שיטת פרסום תוכן", - "label_auto_add_music": "הוספת מוזיקה אוטומטית", - "label_duet": "דואט", - "label_stitch": "סטיץ'", - "label_comments": "תגובות", - "label_disclose_video_content": "חשיפת תוכן וידאו", - "label_your_brand": "המותג שלך", - "label_branded_content": "תוכן ממותג", - "label_subreddit": "סאב-רדיט", - "label_flair": "פלייר", - "label_media": "מדיה", - "label_search_subreddit": "חיפוש סאב-רדיט", - "label_delay": "השהיה", - "label_post_type": "סוג פוסט", - "label_collaborators": "משתפי פעולה (מקסימום 3) - חשבונות לא יכולים להיות פרטיים", - "label_community": "קהילה", - "label_search_community": "חפש קהילה", - "label_channel": "ערוץ", - "label_search_channel": "חפש ערוץ", - "label_select_channel": "בחר ערוץ", - "label_new_password": "סיסמה חדשה", - "label_repeat_password": "חזור על הסיסמה", - "label_platform": "פלטפורמה", - "label_price_per_post": "מחיר לפוסט", - "label_integrations": "אינטגרציות", - "label_code": "קוד", - "label_should_sync_last_post": "האם לסנכרן את הפוסט האחרון הנוכחי?", - "label_when_post": "מתי לפרסם את זה?", - "label_autogenerate_content": "צור תוכן אוטומטית", - "label_generate_picture": "להפיק תמונה?", - "label_company": "חברה", - "label_tag_color": "צבע תגית", - "label_select_board": "בחר לוח", - "label_select_organization": "בחר ארגון", - "label_auto_add_signature": "להוסיף חתימה אוטומטית?", - "enable_color_picker": "הפעל בורר צבעים", - "cancel_the_color_picker": "בטל את בורר הצבעים", - "no_content_yet": "אין תוכן עדיין", - "write_your_reply": "כתוב את הפוסט שלך...", - "add_a_tag": "הוסף תגית", - "add_to_calendar": "הוסף ליומן", - "select_channels_from_circles": "בחר ערוצים מהעיגולים למעלה", - "not_matching_order": "הסדר לא תואם", - "submit_for_order": "שלח להזמנה", - "schedule": "תזמון", - "update": "עדכן", - "attachments": "קבצים מצורפים", - "tags": "תגיות", - "public_to_everyone": "פומבי לכולם", - "mutual_follow_friends": "חברים עם מעקב הדדי", - "follower_of_creator": "עוקב של היוצר", - "self_only": "לעצמי בלבד", - "post_content_directly_to_tiktok": "פרסם תוכן ישירות ל-TikTok", - "upload_content_to_tiktok_without_posting": "העלה תוכן ל-TikTok מבלי לפרסם אותו", - "choose_upload_without_posting_description": "בחר העלאה ללא פרסום אם ברצונך לעיין ולערוך את התוכן שלך באפליקציית TikTok לפני הפרסום. זה יאפשר לך להשתמש בכלי העריכה המובנים של TikTok ולבצע התאמות אחרונות לפני הפרסום.", - "faq_am_i_going_to_be_charged_by_postiz": "האם אחויב על ידי Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "כדי לאמת את פרטי כרטיס האשראי, Postiz תחזיק $2 ותשחרר אותם מיד", - "faq_can_i_trust_postiz_gitroom": "האם אפשר לסמוך על Postiz?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz היא קוד פתוח בגאווה! אנו מאמינים בתרבות אתית ושקופה, מה שאומר ש-Postiz תמשיך להתקיים לנצח. אתם יכולים לעיין בכל הקוד או להשתמש בו לפרויקטים אישיים. לצפייה במאגר הקוד הפתוח, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">לחצו כאן</a>.", - "faq_what_are_channels": "מהם ערוצים?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz מאפשרת לכם לתזמן פוסטים בין ערוצים שונים.\nערוץ הוא פלטפורמת פרסום שבה ניתן לתזמן את הפוסטים שלכם.\nלדוגמה, תוכלו לתזמן פוסטים ב-X, פייסבוק, אינסטגרם, טיקטוק, יוטיוב, רדיט, לינקדאין, Dribbble, Threads ופינטרסט.", - "faq_what_are_team_members": "מהם חברי צוות?", - "faq_if_you_have_a_team_with_multiple_members": "אם יש לך צוות עם מספר חברים, תוכל להזמין אותם לסביבת העבודה שלך כדי לשתף פעולה על הפוסטים ולהוסיף את הערוצים האישיים שלהם", - "faq_what_is_ai_auto_complete": "מהו השלמה אוטומטית בינה מלאכותית?", - "faq_we_automate_chatgpt_to_help_you_write": "אנו משתמשים ב-ChatGPT כדי לעזור לך לכתוב פוסטים ומאמרים לרשתות החברתיות.", - "enter_email": "הזן אימייל", - "are_you_sure": "האם אתה בטוח?", - "yes_delete_it": "כן, מחק את זה!", - "no_cancel": "לא, בטל!", - "are_you_sure_you_want_to_delete": "האם אתה בטוח שברצונך למחוק את {{name}}?", - "are_you_sure_you_want_to_delete_the_image": "האם אתה בטוח שברצונך למחוק את התמונה?", - "are_you_sure_you_want_to_logout": "האם אתה בטוח שברצונך להתנתק?", - "yes_logout": "כן, התנתק", - "are_you_sure_you_want_to_delete_this_slot": "האם אתה בטוח שברצונך למחוק את המשבצת הזו?", - "are_you_sure_you_want_to_delete_this_subreddit": "האם אתה בטוח שברצונך למחוק את הסאב-רדיט הזה?", - "are_you_sure_you_want_to_close_the_window": "האם אתה בטוח שברצונך לסגור את החלון?", - "yes_close": "כן, סגור", - "link_copied_to_clipboard": "הקישור הועתק ללוח", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "האם אתה בטוח שברצונך לסגור את החלון הזה? (כל המידע יאבד)", - "yes_close_it": "כן, סגור את זה!", - "uploading_pictures": "מעלה תמונות...", - "agent_starting": "הסוכן מתחיל", - "researching_your_content": "חוקר את התוכן שלך...", - "understanding_the_category": "מבין את הקטגוריה...", - "finding_the_topic": "מאתר את הנושא...", - "finding_popular_posts_to_match_with": "מאתר פוסטים פופולריים להתאים אליהם...", - "generating_hook": "יוצר הוק...", - "generating_content": "יוצר תוכן...", - "generating_pictures": "יוצר תמונות...", - "finding_time_to_post": "מאתר זמן לפרסום...", - "write_anything": "כתוב כל דבר", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "אתה יכול לכתוב כל מה שתרצה, וגם להוסיף קישורים, אנחנו נעשה את המחקר בשבילך...", - "output_format": "פורמט פלט", - "add_pictures": "להוסיף תמונות?", - "7_days": "7 ימים", - "30_days": "30 ימים", - "90_days": "90 ימים", - "start_7_days_free_trial": "התחל תקופת ניסיון חינם ל-7 ימים", - "change_language": "שנה שפה", - "that_a_wrap": "זה הסוף!\n\nאם נהנית מהשרשור הזה:\n\n1. עקוב אחרי @{{username}} לעוד תכנים כאלה\n2. רטווט את הציוץ למטה כדי לשתף את השרשור עם הקהל שלך\n", - "post_as_images_carousel": "פרסם כתמונות בגלריה", - "save_set": "שמור סט", - "separate_post": "פצל פוסט למספר פוסטים", - "label_who_can_reply_to_this_post": "מי יכול להגיב לפוסט הזה?", - "delete_integration": "מחק אינטגרציה", - "start_writing_your_post": "התחל לכתוב את הפוסט שלך לתצוגה מקדימה" + "calendar": "לוח שנה", + "webhooks": "וובהוקים", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "וובהוקים הם דרך לקבל התראה כאשר משהו קורה ב-Postiz באמצעות בקשת HTTP.", + "name": "שם", + "url": "כתובת URL", + "edit": "ערוך", + "delete": "מחק", + "add_a_webhook": "הוסף וובהוק", + "save": "שמור", + "send_test": "שלח בדיקה", + "select_role": "בחר תפקיד", + "video_made_with_ai": "וידאו שנוצר באמצעות בינה מלאכותית", + "please_add_at_least": "אנא הוסף לפחות 20 תווים", + "send_invitation_via_email": "לשלוח הזמנה בדוא\"ל?", + "global_settings": "הגדרות כלליות", + "copy_id": "העתק מזהה ערוץ", + "team_members": "חברי צוות", + "invite_your_assistant_or_team_member_to_manage_your_account": "הזמן את העוזר או חבר הצוות שלך לנהל את החשבון שלך", + "remove": "הסר", + "add_another_member": "הוסף חבר נוסף", + "signatures": "חתימות", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "ניתן להוסיף חתימות לחשבונך לשימוש בפוסטים שלך.", + "content": "תוכן", + "auto_add": "הוספה אוטומטית?", + "actions": "פעולות", + "use_signature": "השתמש בחתימה", + "add_a_signature": "הוסף חתימה", + "no": "לא", + "yes": "כן", + "your_git_repository": "מאגר ה-Git שלך", + "connect_your_github_repository_to_receive_updates_and_analytics": "חבר את מאגר ה-GitHub שלך כדי לקבל עדכונים וניתוחים", + "connected": "מחובר:", + "disconnect": "נתק", + "connect_your_repository": "חבר את המאגר שלך", + "cancel": "ביטול", + "connect": "חיבור", + "public_api": "API ציבורי", + "check_n8n": "בדקו את הצומת המותאם שלנו ל-N8N עבור Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "השתמש ב-API של Postiz כדי להשתלב עם הכלים שלך.", + "read_how_to_use_it_over_the_documentation": "קרא כיצד להשתמש בזה בתיעוד.", + "reveal": "הצג", + "copy_key": "העתק מפתח", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "חבר את שרת ה-MCP של Postiz ללקוח שלך (הזרמת Http) כדי לתזמן את הפוסטים שלך מהר יותר!", + "share_with_a_client": "שתף עם לקוח", + "post": "פוסט", + "comments": "תגובות", + "user": "משתמש", + "login_register_to_add_comments": "התחבר / הירשם כדי להוסיף תגובות", + "status": "סטטוס:", + "there_are_not_plugs_matching_your_channels": "אין פלאגים התואמים לערוצים שלך", + "you_have_to_add_x_or_linkedin_or_threads": "עליך להוסיף: X או LinkedIn או Threads", + "go_to_the_calendar_to_add_channels": "עבור ללוח השנה כדי להוסיף ערוצים", + "channels": "ערוצים", + "activate": "הפעל", + "this_channel_needs_to_be_refreshed": "הערוץ הזה צריך לרענון,", + "click_here_to_refresh": "לחץ כאן לרענון", + "can_t_show_analytics_yet": "לא ניתן להציג נתוני אנליטיקה עדיין", + "you_have_to_add_social_media_channels": "עליך להוסיף ערוצי מדיה חברתית", + "supported": "נתמך:", + "step": "שלב", + "skip_onboarding": "דלג על תהליך ההתחלה", + "onboarding": "תהליך התחלה", + "next": "הבא", + "you_are_done_from_here_you_can": "סיימת, מכאן תוכל:", + "view_analytics": "הצג אנליטיקה", + "schedule_a_new_post": "תזמן פוסט חדש", + "to_sell_posts_you_would_have_to": "כדי למכור פוסטים עליך:", + "1_connect_at_least_one_channel": "1. לחבר לפחות ערוץ אחד", + "2_connect_you_bank_account": "2. לחבר את חשבון הבנק שלך", + "go_back_to_connect_channels": "חזור לחיבור ערוצים", + "move_to_the_seller_page_to_connect_you_bank": "עבור לעמוד המוכר כדי לחבר את חשבון הבנק שלך", + "connect_channels": "חבר ערוצים", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "חבר את ערוצי המדיה החברתית ואתרי הפרסום שלך כדי\n לתזמן פוסטים מאוחר יותר", + "social": "חברתי", + "publishing_platforms": "פלטפורמות פרסום", + "no_channels": "אין ערוצים עדיין", + "connect_your_accounts": "חברו את החשבונות החברתיים שלכם כדי להתחיל לתזמן, לפרסם ולנתח — הכל במקום אחד.", + "notifications": "התראות", + "no_notifications": "אין התראות", + "send_message": "שלח הודעה", + "mar_28": "28 במרץ", + "there_are_no_messages_yet": "עדיין אין הודעות.", + "checkout_the_marketplace": "בדוק את השוק", + "go_to_marketplace": "עבור לשוק", + "all_messages": "כל ההודעות", + "previous": "הקודם", + "select_or_upload_pictures_maximum_5_at_a_time": "בחר או העלה תמונות (מקסימום 5 בכל פעם)", + "you_can_also_drag_drop_pictures": "ניתן גם לגרור ולשחרר תמונות", + "you_don_t_have_any_assets_yet": "עדיין אין לך נכסים.", + "click_the_button_below_to_upload_one": "לחץ על הכפתור למטה כדי להעלות אחד", + "click_the_button_below_to_upload_other": "לחץ על הכפתור למטה כדי להעלות קבצים מרובים", + "add_selected_media": "הוסף מדיה שנבחרה", + "insert_media": "הוסף מדיה", + "design_media": "עצב מדיה", + "select": "בחר", + "editor": "עורך", + "clear": "נקה", + "order_completed": "ההזמנה הושלמה", + "the_order_has_been_completed": "ההזמנה הושלמה", + "post_has_been_published": "הפוסט פורסם", + "url_1": "כתובת URL:", + "new_offer": "הצעה חדשה", + "platform": "פלטפורמה", + "posts": "פוסטים", + "pay_accept_offer": "שלם וקבל הצעה", + "accepted": "התקבל", + "post_draft": "טיוטת פוסט", + "revision_needed": "נדרשת עריכה", + "approve": "אשר", + "preview": "תצוגה מקדימה", + "revision_requested": "בקשת עריכה", + "accepted_1": "התקבל", + "cancelled_by_the_seller": "בוטל על ידי המוכר", + "please_select_your_country_where_your_business_is": "אנא בחר את המדינה שבה העסק שלך נמצא.", + "select_country": "--בחר מדינה--", + "connect_bank_account": "חבר חשבון בנק", + "seller_mode": "מצב מוכר", + "active": "פעיל", + "details": "פרטים", + "audience_size": "גודל קהל", + "add_another_platform": "הוסף פלטפורמה נוספת", + "send_an_offer_for": "שלח הצעה עבור $", + "complete_order_and_pay_early": "השלם הזמנה ושלם מראש", + "order_in_progress": "הזמנה בתהליך", + "create_a_new_offer": "צור הצעה חדשה", + "orders": "הזמנות", + "price": "מחיר", + "state": "מדינה", + "showing": "מציג", + "to": "עד", + "from": "מ", + "results": "תוצאות", + "content_writer": "כותב תוכן", + "influencer": "משפיען", + "request_service": "בקש שירות", + "the_marketplace_is_not_opened_yet": "המרקטפלייס עדיין לא נפתח", + "check_again_soon": "בדוק שוב בקרוב!", + "filter": "סנן", + "result": "תוצאה", + "seller": "מוכר", + "buyer": "קונה", + "discord_support": "תמיכה בדיסקורד", + "teams": "צוותים", + "webhooks_1": "וובהוקים", + "auto_post": "פוסט אוטומטי", + "logout_from": "התנתק מ", + "join_10000_entrepreneurs_who_use_postiz": "הצטרפו ל-10,000+ יזמים שמשתמשים ב-Postiz", + "to_manage_all_your_social_media_channels": "לניהול כל ערוצי המדיה החברתית שלך", + "100_no_risk_trial": "100% ניסיון ללא סיכון", + "pay_nothing_for_the_first_7_days": "לא תשלם כלום ב-7 הימים הראשונים", + "cancel_anytime_hassle_free": "ביטול בכל עת, ללא טרחה", + "add_free_subscription": "-- הוסף מנוי חינמי --", + "currently_impersonating": "מתחזה כרגע", + "user_1": "משתמש:", + "drag_n_drop_some_files_here": "גרור ושחרר קבצים כאן", + "add_time_slot": "הוסף משבצת זמן", + "add_slot": "הוסף משבצת", + "cancel_publication": "בטל פרסום", + "statistics": "סטטיסטיקות", + "loading": "טוען", + "short_link": "קישור מקוצר", + "original_link": "קישור מקורי", + "clicks": "קליקים", + "selected_customer": "לקוח נבחר", + "customer": "לקוח:", + "repeat_post_every": "חזור על הפוסט כל...", + "use_this_media": "השתמש במדיה זו", + "create_new_post": "צור פוסט", + "update_post": "עדכן פוסט קיים", + "merge_comments_into_one_post": "מזג תגובות לפוסט אחד", + "accounts_that_will_engage": "חשבונות שיתקשרו:", + "day": "יום", + "week": "שבוע", + "month": "חודש", + "remove_from_customer": "הסר מהלקוח", + "show_more": "+ הצג עוד", + "show_less": "- הצג פחות", + "upload": "העלה", + "ai": "בינה מלאכותית", + "add_channel": "הוסף ערוץ", + "add_platform": "הוסף פלטפורמה", + "articles": "מאמרים", + "add_comment": "הוסף תגובה", + "add_post": "הוסף פוסט בדיון", + "add_comment_or_post": "הוסף תגובה / פוסט", + "you_are_in_global_editing_mode": "אתה נמצא במצב עריכה גלובלי", + "the_post_should_be_at_least_6_characters_long": "הפוסט צריך להיות באורך של לפחות 6 תווים", + "are_you_sure_you_want_to_delete_post": "האם אתה בטוח שברצונך למחוק את הפוסט הזה?", + "post_deleted_successfully": "הפוסט נמחק בהצלחה", + "delete_post": "מחק פוסט", + "save_as_draft": "שמור כטיוטה", + "post_now": "פרסם עכשיו", + "please_add": "אנא הוסף", + "to_your_telegram_group_channel_and_click_here": "לקבוצת / ערוץ הטלגרם שלך ולחץ כאן:", + "connect_telegram": "חבר טלגרם", + "please_add_the_following_command_in_your_chat": "אנא הוסף את הפקודה הבאה בצ'אט שלך:", + "copy": "העתק", + "settings": "הגדרות", + "integrations": "אינטגרציות", + "add_integration": "הוסף אינטגרציה", + "you_are_now_editing_only": "אתה עורך כעת רק", + "tag_a_company": "תייג חברה", + "video_length_is_invalid_must_be_up_to": "אורך הווידאו לא תקין, חייב להיות עד", + "seconds": "שניות", + "this_feature_available_only_for_photos": "פונקציה זו זמינה רק לתמונות, היא תוסיף מוזיקה ברירת מחדל שתוכל לשנות מאוחר יותר.", + "allow_user_to": "אפשר למשתמש ל-", + "your_video_will_be_labeled_promotional": "הווידאו שלך יסומן כ\"תוכן פרסומי\".", + "this_cannot_be_changed_once_posted": "לא ניתן לשנות זאת לאחר שהווידאו פורסם.", + "turn_on_to_disclose_video_promotes": "הפעל כדי לחשוף שהווידאו הזה מקדם מוצרים או שירותים בתמורה למשהו בעל ערך. הווידאו שלך יכול לקדם את עצמך, צד שלישי, או שניהם.", + "you_are_promoting_yourself": "אתה מקדם את עצמך או את המותג שלך.", + "this_video_will_be_classified_brand_organic": "הווידאו הזה יסווג כמותג אורגני.", + "you_are_promoting_another_brand": "אתה מקדם מותג אחר או צד שלישי.", + "this_video_will_be_classified_branded_content": "הווידאו הזה יסווג כתוכן ממותג.", + "by_posting_you_agree_to_tiktoks": "על ידי פרסום, אתה מסכים לתנאי השימוש של TikTok", + "music_usage_confirmation": "אישור שימוש במוזיקה", + "branded_content_policy": "מדיניות תוכן ממותג", + "select_1": "--בחר--", + "select_flair": "--בחר תיוג--", + "link": "קישור", + "add_subreddit": "הוסף סאב-רדיט", + "please_add_at_least_one_subreddit": "אנא הוסף לפחות סאב-רדיט אחד", + "add_community": "הוסף קהילה", + "select_post_type": "בחר סוג פוסט...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "לא הצלחנו למצוא עסק שמחובר לעמוד הלינקדאין שלך.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "אנא סגור את החלון הזה, צור עמוד חדש והוסף ערוץ חדש שוב.", + "select_linkedin_page": "בחר עמוד לינקדאין:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "לא הצלחנו למצוא עסק שמחובר לעמודים שנבחרו.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "אנו ממליצים לחבר את כל העמודים וכל העסקים.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "אנא סגור את החלון הזה, מחק את האינטגרציה שלך והוסף ערוץ חדש שוב.", + "select_instagram_account": "בחר חשבון אינסטגרם:", + "select_page": "בחר עמוד:", + "generate_image_with_ai": "צור תמונה עם בינה מלאכותית", + "reconnect_channel": "חבר ערוץ מחדש", + "update_credentials": "עדכן פרטי גישה", + "additional_settings": "הגדרות נוספות", + "change_bot": "החלף בוט", + "move_add_to_customer": "העבר / הוסף ללקוח", + "edit_time_slots": "ערוך חלונות זמן", + "enable_channel": "הפעל ערוץ", + "disable_channel": "השבת ערוץ", + "add": "הוסף", + "short_post": "פוסט קצר", + "long_post": "פוסט ארוך", + "a_thread_with_short_posts": "שרשור עם פוסטים קצרים", + "a_thread_with_long_posts": "שרשור עם פוסטים ארוכים", + "personal_voice_i_am_happy_to_announce": "קול אישי (\"אני שמח להודיע\")", + "company_voice_we_are_happy_to_announce": "קול חברה (\"אנחנו שמחים להודיע\")", + "generate": "צור", + "generate_posts": "צור פוסטים", + "purchase_a_life_time_pro_account_with_sol_199": "רכוש חשבון PRO לכל החיים עם SOL ($199). לידיעתך, אין החזר כספי עבור רכישה זו.", + "purchase_now": "רכוש עכשיו", + "pay_today": "שלם היום", + "we_are_sorry_to_see_you_go": "מצטערים לראות אותך עוזב :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "האם תוכל לספר לנו בקצרה מה יכולנו לעשות טוב יותר?", + "cancel_subscription": "בטל מנוי", + "plans": "תוכניות", + "monthly": "חודשי", + "yearly": "שנתי", + "reactivate_subscription": "הפעל מחדש את המנוי", + "update_payment_method_invoices_history": "עדכן אמצעי תשלום / היסטוריית חשבוניות", + "cancel_subscription_1": "בטל מנוי", + "your_subscription_will_be_canceled_at": "המנוי שלך יבוטל בתאריך", + "you_will_never_be_charged_again": "לא תחויב שוב לעולם", + "current_package": "חבילה נוכחית:", + "next_package": "החבילה הבאה:", + "claim": "תבע", + "frequently_asked_questions": "שאלות נפוצות", + "autopost": "פרסום אוטומטי", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "פרסום אוטומטי יכול לפרסם פריטים חדשים מה-RSS שלך לרשתות החברתיות באופן אוטומטי", + "title": "כותרת", + "add_an_autopost": "הוסף פרסום אוטומטי", + "post_content": "תוכן הפוסט", + "sign_up": "הרשמה", + "or": "או", + "by_registering_you_agree_to_our": "בהרשמתך אתה מסכים ל", + "and": "ו", + "terms_of_service": "תנאי השירות", + "privacy_policy": "מדיניות פרטיות", + "create_account": "צור חשבון", + "already_have_an_account": "כבר יש לך חשבון?", + "sign_in": "התחבר", + "sign_in_1": "התחבר", + "don_t_have_an_account": "אין לך חשבון?", + "forgot_password": "שכחת סיסמה", + "forgot_password_1": "שכחת סיסמה", + "send_password_reset_email": "שלח אימייל לאיפוס סיסמה", + "go_back_to_login": "חזור להתחברות", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "שלחנו לך אימייל עם קישור לאיפוס הסיסמה שלך.", + "change_password": "שנה סיסמה", + "we_successfully_reset_your_password_you_can_now_login_with_your": "איפסנו את הסיסמה שלך בהצלחה. כעת תוכל להתחבר עם", + "click_here_to_go_back_to_login": "לחץ כאן כדי לחזור להתחברות", + "activate_your_account": "הפעל את החשבון שלך", + "thank_you_for_registering": "תודה שנרשמת!", + "please_check_your_email_to_activate_your_account": "אנא בדוק את האימייל שלך כדי להפעיל את החשבון.", + "sign_in_with": "התחבר באמצעות", + "continue_with_google": "המשך עם Google", + "sign_in_with_github": "התחבר עם GitHub", + "continue_with_farcaster": "המשך עם Farcaster", + "continue_with_your_wallet": "המשך עם הארנק שלך", + "stars_per_day": "כוכבים ליום", + "media": "מדיה", + "check_launch": "בדוק השקה", + "load_your_github_repository_from_settings_to_see_analytics": "טען את מאגר ה-GitHub שלך מההגדרות כדי לראות ניתוחים", + "stars": "כוכבים", + "processing_stars": "מעבד כוכבים...", + "forks": "פיצולים", + "registration_is_disabled": "ההרשמה מושבתת", + "login_instead": "התחבר במקום", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "בחר שיחה והתחל לצ'וטט.", + "adding_channel_redirecting_you": "מוסיף ערוץ, מפנה אותך", + "could_not_add_provider": "לא ניתן להוסיף ספק.", + "you_are_being_redirected_back": "אתה מנותב חזרה", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "אנו חווים קושי, נסה לרענן את הדף", + "post_not_found": "הפוסט לא נמצא", + "publication_date": "תאריך פרסום:", + "analytics": "אנליטיקה", + "launches": "השקות", + "plugs": "פלאגים", + "billing": "חיוב", + "affiliate": "שותפים", + "monday": "יום שני", + "tuesday": "יום שלישי", + "wednesday": "יום רביעי", + "thursday": "יום חמישי", + "friday": "יום שישי", + "saturday": "יום שבת", + "sunday": "יום ראשון", + "can_t_change_date_remove_post_from_publication": "לא ניתן לשנות את התאריך, הסר את הפוסט מהפרסום", + "predicted_github_trending_change": "שינוי מגמה חזוי ב-GitHub", + "duplicate_post": "פוסט כפול", + "preview_post": "תצוגה מקדימה של הפוסט", + "post_statistics": "סטטיסטיקות פוסט", + "draft": "טיוטה", + "week_number": "שבוע {{number}}", + "top_title_edit_webhook": "ערוך Webhook", + "top_title_add_webhook": "הוסף Webhook", + "top_title_oh_no": "אוי לא", + "top_title_auto_plug": "Auto Plug: {{title}}", + "top_title_edit_autopost": "ערוך פוסט אוטומטי", + "top_title_add_autopost": "הוסף פוסט אוטומטי", + "top_title_send_a_new_offer": "שלח הצעה חדשה", + "top_title_media_library": "ספריית מדיה", + "top_title_add_signature": "הוסף חתימה", + "top_title_send_a_message_to": "שלח הודעה אל {{name}}", + "top_title_configure_provider": "הגדר ספק", + "top_title_add_member": "הוסף חבר", + "top_title_change_bot_picture": "שנה תמונת בוט", + "top_title_create_a_new_tag": "צור תג חדש", + "top_title_select_company": "בחר חברה", + "top_title_additional_settings": "הגדרות נוספות", + "top_title_time_table_slots": "משבצות לוח זמנים", + "top_title_design_media": "עיצוב מדיה", + "top_title_edit_post": "ערוך פוסט", + "top_title_create_post": "צור פוסט", + "top_title_move__add_to_customer": "העבר / הוסף ללקוח", + "top_title_add_api_key_for": "הוסף מפתח API עבור {{name}}", + "top_title_instance_url": "כתובת מופע (Instance URL)", + "top_title_custom_url": "כתובת מותאמת אישית", + "top_title_add_channel": "הוסף ערוץ", + "top_title_add_telegram": "הוסף טלגרם", + "top_title_add_wrapcast": "הוסף Wrapcast", + "top_title_comments_for": "הערות עבור {{date}}", + "top_title_edit_signature": "ערוך חתימה", + "label_name": "שם", + "label_url": "כתובת URL", + "label_title": "כותרת", + "label_subtitle": "כותרת משנה", + "label_email": "אימייל", + "label_full_name": "שם מלא", + "label_password": "סיסמה", + "label_confirm_password": "אישור סיסמה", + "label_api_key": "מפתח API", + "label_instance_url": "כתובת מופע", + "label_custom_url": "כתובת מותאמת אישית", + "label_feedback": "משוב", + "label_bio": "ביוגרפיה", + "label_role": "תפקיד", + "label_country": "מדינה", + "label_audience_size": "גודל קהל בכל הפלטפורמות", + "label_pick_time": "בחר זמן", + "label_nickname": "כינוי", + "label_write_anything": "כתוב כל דבר", + "label_output_format": "פורמט פלט", + "label_add_pictures": "להוסיף תמונות?", + "label_hour": "שעה", + "label_minutes": "דקות", + "label_select_publication": "בחר פרסום", + "label_canonical_link": "קישור קנוני", + "label_cover_picture": "תמונת שער", + "label_tags": "תגיות", + "label_topics": "נושאים", + "label_tags_maximum_4": "תגיות (מקסימום 4)", + "label_attachments": "קבצים מצורפים", + "label_type": "סוג", + "label_thumbnail": "תמונה ממוזערת", + "label_who_can_see_this_video": "מי יכול לראות את הסרטון הזה?", + "label_content_posting_method": "שיטת פרסום תוכן", + "label_auto_add_music": "הוספת מוזיקה אוטומטית", + "label_duet": "דואט", + "label_stitch": "סטיץ'", + "label_comments": "תגובות", + "label_disclose_video_content": "חשיפת תוכן וידאו", + "label_your_brand": "המותג שלך", + "label_branded_content": "תוכן ממותג", + "label_subreddit": "סאב-רדיט", + "label_flair": "פלייר", + "label_media": "מדיה", + "label_search_subreddit": "חיפוש סאב-רדיט", + "label_delay": "השהיה", + "label_post_type": "סוג פוסט", + "label_collaborators": "משתפי פעולה (מקסימום 3) - חשבונות לא יכולים להיות פרטיים", + "label_community": "קהילה", + "label_search_community": "חפש קהילה", + "label_channel": "ערוץ", + "label_search_channel": "חפש ערוץ", + "label_select_channel": "בחר ערוץ", + "label_new_password": "סיסמה חדשה", + "label_repeat_password": "חזור על הסיסמה", + "label_platform": "פלטפורמה", + "label_price_per_post": "מחיר לפוסט", + "label_integrations": "אינטגרציות", + "label_code": "קוד", + "label_should_sync_last_post": "האם לסנכרן את הפוסט האחרון הנוכחי?", + "label_when_post": "מתי לפרסם את זה?", + "label_autogenerate_content": "צור תוכן אוטומטית", + "label_generate_picture": "להפיק תמונה?", + "label_company": "חברה", + "label_tag_color": "צבע תגית", + "label_select_board": "בחר לוח", + "label_select_organization": "בחר ארגון", + "label_auto_add_signature": "להוסיף חתימה אוטומטית?", + "enable_color_picker": "הפעל בורר צבעים", + "cancel_the_color_picker": "בטל את בורר הצבעים", + "no_content_yet": "אין תוכן עדיין", + "write_your_reply": "כתוב את הפוסט שלך...", + "add_a_tag": "הוסף תגית", + "add_to_calendar": "הוסף ליומן", + "select_channels_from_circles": "בחר ערוצים מהעיגולים למעלה", + "not_matching_order": "הסדר לא תואם", + "submit_for_order": "שלח להזמנה", + "schedule": "תזמון", + "update": "עדכן", + "attachments": "קבצים מצורפים", + "tags": "תגיות", + "public_to_everyone": "פומבי לכולם", + "mutual_follow_friends": "חברים עם מעקב הדדי", + "follower_of_creator": "עוקב של היוצר", + "self_only": "לעצמי בלבד", + "post_content_directly_to_tiktok": "פרסם תוכן ישירות ל-TikTok", + "upload_content_to_tiktok_without_posting": "העלה תוכן ל-TikTok מבלי לפרסם אותו", + "choose_upload_without_posting_description": "בחר העלאה ללא פרסום אם ברצונך לעיין ולערוך את התוכן שלך באפליקציית TikTok לפני הפרסום. זה יאפשר לך להשתמש בכלי העריכה המובנים של TikTok ולבצע התאמות אחרונות לפני הפרסום.", + "faq_am_i_going_to_be_charged_by_postiz": "האם אחויב על ידי Postiz?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "כדי לאמת את פרטי כרטיס האשראי, Postiz תחזיק $2 ותשחרר אותם מיד", + "faq_can_i_trust_postiz_gitroom": "האם אפשר לסמוך על Postiz?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz היא קוד פתוח בגאווה! אנו מאמינים בתרבות אתית ושקופה, מה שאומר ש-Postiz תמשיך להתקיים לנצח. אתם יכולים לעיין בכל הקוד או להשתמש בו לפרויקטים אישיים. לצפייה במאגר הקוד הפתוח, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">לחצו כאן</a>.", + "faq_what_are_channels": "מהם ערוצים?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz מאפשרת לכם לתזמן פוסטים בין ערוצים שונים.\nערוץ הוא פלטפורמת פרסום שבה ניתן לתזמן את הפוסטים שלכם.\nלדוגמה, תוכלו לתזמן פוסטים ב-X, פייסבוק, אינסטגרם, טיקטוק, יוטיוב, רדיט, לינקדאין, Dribbble, Threads ופינטרסט.", + "faq_what_are_team_members": "מהם חברי צוות?", + "faq_if_you_have_a_team_with_multiple_members": "אם יש לך צוות עם מספר חברים, תוכל להזמין אותם לסביבת העבודה שלך כדי לשתף פעולה על הפוסטים ולהוסיף את הערוצים האישיים שלהם", + "faq_what_is_ai_auto_complete": "מהו השלמה אוטומטית בינה מלאכותית?", + "faq_we_automate_chatgpt_to_help_you_write": "אנו משתמשים ב-ChatGPT כדי לעזור לך לכתוב פוסטים ומאמרים לרשתות החברתיות.", + "enter_email": "הזן אימייל", + "are_you_sure": "האם אתה בטוח?", + "yes_delete_it": "כן, מחק את זה!", + "no_cancel": "לא, בטל!", + "are_you_sure_you_want_to_delete": "האם אתה בטוח שברצונך למחוק את {{name}}?", + "are_you_sure_you_want_to_delete_the_image": "האם אתה בטוח שברצונך למחוק את התמונה?", + "are_you_sure_you_want_to_logout": "האם אתה בטוח שברצונך להתנתק?", + "yes_logout": "כן, התנתק", + "are_you_sure_you_want_to_delete_this_slot": "האם אתה בטוח שברצונך למחוק את המשבצת הזו?", + "are_you_sure_you_want_to_delete_this_subreddit": "האם אתה בטוח שברצונך למחוק את הסאב-רדיט הזה?", + "are_you_sure_you_want_to_close_the_window": "האם אתה בטוח שברצונך לסגור את החלון?", + "yes_close": "כן, סגור", + "link_copied_to_clipboard": "הקישור הועתק ללוח", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "האם אתה בטוח שברצונך לסגור את החלון הזה? (כל המידע יאבד)", + "yes_close_it": "כן, סגור את זה!", + "uploading_pictures": "מעלה תמונות...", + "agent_starting": "הסוכן מתחיל", + "researching_your_content": "חוקר את התוכן שלך...", + "understanding_the_category": "מבין את הקטגוריה...", + "finding_the_topic": "מאתר את הנושא...", + "finding_popular_posts_to_match_with": "מאתר פוסטים פופולריים להתאים אליהם...", + "generating_hook": "יוצר הוק...", + "generating_content": "יוצר תוכן...", + "generating_pictures": "יוצר תמונות...", + "finding_time_to_post": "מאתר זמן לפרסום...", + "write_anything": "כתוב כל דבר", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "אתה יכול לכתוב כל מה שתרצה, וגם להוסיף קישורים, אנחנו נעשה את המחקר בשבילך...", + "output_format": "פורמט פלט", + "add_pictures": "להוסיף תמונות?", + "7_days": "7 ימים", + "30_days": "30 ימים", + "90_days": "90 ימים", + "start_7_days_free_trial": "התחל תקופת ניסיון חינם ל-7 ימים", + "change_language": "שנה שפה", + "that_a_wrap": "זה הסוף!\n\nאם נהנית מהשרשור הזה:\n\n1. עקוב אחרי @{{username}} לעוד תכנים כאלה\n2. רטווט את הציוץ למטה כדי לשתף את השרשור עם הקהל שלך\n", + "post_as_images_carousel": "פרסם כתמונות בגלריה", + "save_set": "שמור סט", + "separate_post": "פצל פוסט למספר פוסטים", + "label_who_can_reply_to_this_post": "מי יכול להגיב לפוסט הזה?", + "delete_integration": "מחק אינטגרציה", + "start_writing_your_post": "התחל לכתוב את הפוסט שלך לתצוגה מקדימה", + "billing_join_over": "הצטרפו ליותר מ-", + "billing_entrepreneurs_count": "18,000+ יזמים", + "billing_who_use": "שמשתמשים ב-", + "billing_postiz_grow_social": "Postiz כדי להגדיל את הנוכחות החברתית שלהם", + "billing_no_risk_trial": "ניסיון חינם ללא סיכון ב-100%", + "billing_pay_nothing_7_days": "לא משלמים כלום ב-7 הימים הראשונים", + "billing_cancel_anytime": "ניתן לבטל בכל עת, ללא טרחה", + "billing_choose_plan": "בחרו תוכנית", + "billing_monthly": "חודשי", + "billing_yearly": "שנתי", + "billing_20_percent_off": "20% הנחה", + "billing_features": "תכונות", + "billing_channel": "ערוץ", + "billing_channels": "ערוצים", + "billing_unlimited": "ללא הגבלה", + "billing_posts_per_month": "פוסטים בחודש", + "billing_unlimited_team_members": "חברי צוות ללא הגבלה", + "billing_ai_auto_complete": "השלמה אוטומטית בינה מלאכותית", + "billing_ai_copilots": "קופיילוטים מבוססי בינה מלאכותית", + "billing_ai_autocomplete": "השלמה אוטומטית בינה מלאכותית", + "billing_advanced_picture_editor": "עורך תמונות מתקדם", + "billing_ai_images_per_month": "תמונות בינה מלאכותית בחודש", + "billing_ai_videos_per_month": "סרטוני בינה מלאכותית בחודש", + "billing_billing_address": "כתובת לחיוב", + "billing_payment": "תשלום", + "billing_powered_by_stripe": "מופעל על ידי Stripe", + "billing_your_7_day_trial_is": "תקופת הניסיון שלך ל-7 ימים היא", + "billing_100_percent_free": "100% חינם", + "billing_ending": "מסתיימת", + "billing_cancel_anytime_short": "ניתן לבטל בכל עת.", + "billing_pay_0_start_trial": "שלם 0$ היום - התחל את תקופת הניסיון שלך!", + "billing_pay_now": "שלם עכשיו", + "billing_per_month": "/ חודש" } diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index cce7c1ca..d16c070d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -1,507 +1,539 @@ { - "calendar": "Calendario", - "webhooks": "Webhook", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "I webhook sono un modo per ricevere notifiche quando succede qualcosa in Postiz tramite una richiesta HTTP.", - "name": "Nome", - "url": "URL", - "edit": "Modifica", - "delete": "Elimina", - "add_a_webhook": "Aggiungi un webhook", - "save": "Salva", - "send_test": "Invia test", - "select_role": "Seleziona ruolo", - "video_made_with_ai": "Video realizzato con l'IA", - "please_add_at_least": "Per favore aggiungi almeno 20 caratteri", - "send_invitation_via_email": "Inviare l'invito via email?", - "global_settings": "Impostazioni globali", - "copy_id": "Copia ID canale", - "team_members": "Membri del team", - "invite_your_assistant_or_team_member_to_manage_your_account": "Invita il tuo assistente o un membro del team a gestire il tuo account", - "remove": "Rimuovi", - "add_another_member": "Aggiungi un altro membro", - "signatures": "Firme", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Puoi aggiungere firme al tuo account da utilizzare nei tuoi post.", - "content": "Contenuto", - "auto_add": "Aggiunta automatica?", - "actions": "Azioni", - "use_signature": "Usa firma", - "add_a_signature": "Aggiungi una firma", - "no": "No", - "yes": "Sì", - "your_git_repository": "Il tuo repository Git", - "connect_your_github_repository_to_receive_updates_and_analytics": "Collega il tuo repository GitHub per ricevere aggiornamenti e analisi", - "connected": "Connesso:", - "disconnect": "Disconnetti", - "connect_your_repository": "Collega il tuo repository", - "cancel": "Annulla", - "connect": "Collega", - "public_api": "API pubblica", - "check_n8n": "Scopri il nostro nodo personalizzato N8N per Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Usa l'API di Postiz per integrarla con i tuoi strumenti.", - "read_how_to_use_it_over_the_documentation": "Leggi come usarla nella documentazione.", - "reveal": "Mostra", - "copy_key": "Copia chiave", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Collega il server MCP di Postiz al tuo client (streaming Http) per programmare i tuoi post più velocemente!", - "share_with_a_client": "Condividi con un cliente", - "post": "Post", - "comments": "Commenti", - "user": "Utente", - "login_register_to_add_comments": "Accedi / Registrati per aggiungere commenti", - "status": "Stato:", - "there_are_not_plugs_matching_your_channels": "Non ci sono plug compatibili con i tuoi canali", - "you_have_to_add_x_or_linkedin_or_threads": "Devi aggiungere: X oppure LinkedIn oppure Threads", - "go_to_the_calendar_to_add_channels": "Vai al calendario per aggiungere canali", - "channels": "Canali", - "activate": "Attiva", - "this_channel_needs_to_be_refreshed": "Questo canale deve essere aggiornato,", - "click_here_to_refresh": "clicca qui per aggiornare", - "can_t_show_analytics_yet": "Impossibile mostrare le analisi al momento", - "you_have_to_add_social_media_channels": "Devi aggiungere i canali dei social media", - "supported": "Supportato:", - "step": "PASSO", - "skip_onboarding": "Salta onboarding", - "onboarding": "Onboarding", - "next": "Avanti", - "you_are_done_from_here_you_can": "Hai finito, da qui puoi:", - "view_analytics": "Visualizza Analisi", - "schedule_a_new_post": "Programma un nuovo post", - "to_sell_posts_you_would_have_to": "Per vendere post dovresti:", - "1_connect_at_least_one_channel": "1. Collegare almeno un canale", - "2_connect_you_bank_account": "2. Collegare il tuo conto bancario", - "go_back_to_connect_channels": "Torna a collegare i canali", - "move_to_the_seller_page_to_connect_you_bank": "Vai alla pagina venditore per collegare il tuo conto bancario", - "connect_channels": "Collega Canali", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Collega i tuoi canali social e le piattaforme di pubblicazione per\n programmare i post in seguito", - "social": "Social", - "publishing_platforms": "Piattaforme di pubblicazione", - "no_channels": "Nessun canale ancora", - "connect_your_accounts": "Collega i tuoi account social per iniziare a programmare, pubblicare e analizzare — tutto in un unico posto.", - "notifications": "Notifiche", - "no_notifications": "Nessuna notifica", - "send_message": "Invia messaggio", - "mar_28": "28 mar", - "there_are_no_messages_yet": "Non ci sono ancora messaggi.", - "checkout_the_marketplace": "Visita il Marketplace", - "go_to_marketplace": "Vai al marketplace", - "all_messages": "Tutti i messaggi", - "previous": "Precedente", - "select_or_upload_pictures_maximum_5_at_a_time": "Seleziona o carica immagini (massimo 5 alla volta)", - "you_can_also_drag_drop_pictures": "Puoi anche trascinare e rilasciare le immagini", - "you_don_t_have_any_assets_yet": "Non hai ancora nessuna risorsa.", - "click_the_button_below_to_upload_one": "Clicca sul pulsante qui sotto per caricarne una", - "click_the_button_below_to_upload_many": "", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "Aggiungi i media selezionati", - "insert_media": "Inserisci media", - "design_media": "Progetta media", - "select": "Seleziona", - "editor": "Editor", - "clear": "Cancella", - "order_completed": "Ordine completato", - "the_order_has_been_completed": "L'ordine è stato completato", - "post_has_been_published": "Il post è stato pubblicato", - "url_1": "URL:", - "new_offer": "Nuova offerta", - "platform": "Piattaforma", - "posts": "Post", - "pay_accept_offer": "Paga e accetta l'offerta", - "accepted": "Accettato", - "post_draft": "Bozza del post", - "revision_needed": "Revisione necessaria", - "approve": "Approva", - "preview": "Anteprima", - "revision_requested": "Revisione richiesta", - "accepted_1": "ACCETTATO", - "cancelled_by_the_seller": "Annullato dal venditore", - "please_select_your_country_where_your_business_is": "Seleziona il paese in cui si trova la tua attività.", - "select_country": "--SELEZIONA PAESE--", - "connect_bank_account": "Collega conto bancario", - "seller_mode": "Modalità venditore", - "active": "Attivo", - "details": "Dettagli", - "audience_size": "Dimensione pubblico", - "add_another_platform": "Aggiungi un'altra piattaforma", - "send_an_offer_for": "Invia un'offerta per $", - "complete_order_and_pay_early": "Completa l'ordine e paga in anticipo", - "order_in_progress": "Ordine in corso", - "create_a_new_offer": "Crea una nuova offerta", - "orders": "Ordini", - "price": "Prezzo", - "state": "Stato", - "showing": "Visualizzazione", - "to": "a", - "from": "da", - "results": "Risultati", - "content_writer": "Scrittore di contenuti", - "influencer": "Influencer", - "request_service": "Richiedi servizio", - "the_marketplace_is_not_opened_yet": "Il marketplace non è ancora aperto", - "check_again_soon": "Ricontrolla presto!", - "filter": "Filtro", - "result": "Risultato", - "seller": "Venditore", - "buyer": "Acquirente", - "discord_support": "Supporto Discord", - "teams": "Team", - "webhooks_1": "Webhook", - "auto_post": "Pubblicazione automatica", - "logout_from": "Disconnetti da", - "join_10000_entrepreneurs_who_use_postiz": "Unisciti a oltre 10.000 imprenditori che usano Postiz", - "to_manage_all_your_social_media_channels": "Per gestire tutti i tuoi canali social", - "100_no_risk_trial": "Prova senza rischi al 100%", - "pay_nothing_for_the_first_7_days": "Non paghi nulla per i primi 7 giorni", - "cancel_anytime_hassle_free": "Annulla in qualsiasi momento, senza problemi", - "add_free_subscription": "-- AGGIUNGI ABBONAMENTO GRATUITO --", - "currently_impersonating": "Attualmente stai impersonando", - "user_1": "utente:", - "drag_n_drop_some_files_here": "Trascina qui alcuni file", - "add_time_slot": "Aggiungi fascia oraria", - "add_slot": "Aggiungi fascia oraria", - "cancel_publication": "Annulla pubblicazione", - "statistics": "Statistiche", - "loading": "Caricamento in corso", - "short_link": "Link breve", - "original_link": "Link originale", - "clicks": "Click", - "selected_customer": "Cliente selezionato", - "customer": "Cliente:", - "repeat_post_every": "Ripeti post ogni...", - "use_this_media": "Usa questo media", - "create_new_post": "Crea post", - "update_post": "Aggiorna post esistente", - "merge_comments_into_one_post": "Unisci i commenti in un unico post", - "accounts_that_will_engage": "Account che interagiranno:", - "day": "Giorno", - "week": "Settimana", - "month": "Mese", - "remove_from_customer": "Rimuovi dal cliente", - "show_more": "+ Mostra di più", - "show_less": "- Mostra di meno", - "upload": "Carica", - "ai": "AI", - "add_channel": "Aggiungi canale", - "add_platform": "Aggiungi piattaforma", - "articles": "Articoli", - "add_comment": "Aggiungi commento", - "add_post": "Aggiungi post in una discussione", - "add_comment_or_post": "Aggiungi commento / post", - "you_are_in_global_editing_mode": "Sei in modalità di modifica globale", - "the_post_should_be_at_least_6_characters_long": "Il post deve contenere almeno 6 caratteri", - "are_you_sure_you_want_to_delete_post": "Sei sicuro di voler eliminare questo post?", - "post_deleted_successfully": "Post eliminato con successo", - "delete_post": "Elimina post", - "save_as_draft": "Salva come bozza", - "post_now": "Pubblica ora", - "please_add": "Per favore aggiungi", - "to_your_telegram_group_channel_and_click_here": "al tuo\ngruppo/canale Telegram e clicca qui:", - "connect_telegram": "Collega Telegram", - "please_add_the_following_command_in_your_chat": "Per favore aggiungi il seguente comando nella tua chat:", - "copy": "Copia", - "settings": "Impostazioni", - "integrations": "Integrazioni", - "add_integration": "Aggiungi integrazione", - "you_are_now_editing_only": "Ora stai modificando solo", - "tag_a_company": "Tagga un'azienda", - "video_length_is_invalid_must_be_up_to": "La durata del video non è valida, deve essere fino a", - "seconds": "secondi", - "this_feature_available_only_for_photos": "Questa funzione è disponibile solo per le foto, verrà aggiunta una musica predefinita che potrai cambiare in seguito.", - "allow_user_to": "Consenti all'utente di:", - "your_video_will_be_labeled_promotional": "Il tuo video sarà etichettato come \"Contenuto promozionale\".", - "this_cannot_be_changed_once_posted": "Questo non può essere modificato una volta che il video è stato pubblicato.", - "turn_on_to_disclose_video_promotes": "Attiva per dichiarare che questo video promuove beni o servizi in cambio di qualcosa di valore. Il tuo video potrebbe promuovere te stesso, una terza parte o entrambi.", - "you_are_promoting_yourself": "Stai promuovendo te stesso o il tuo marchio.", - "this_video_will_be_classified_brand_organic": "Questo video sarà classificato come Brand Organico.", - "you_are_promoting_another_brand": "Stai promuovendo un altro marchio o una terza parte.", - "this_video_will_be_classified_branded_content": "Questo video sarà classificato come Contenuto Brandizzato.", - "by_posting_you_agree_to_tiktoks": "Pubblicando, accetti le condizioni di TikTok", - "music_usage_confirmation": "Conferma utilizzo musica", - "branded_content_policy": "Politica sui contenuti sponsorizzati", - "select_1": "--Seleziona--", - "select_flair": "--Seleziona Flair--", - "link": "Link", - "add_subreddit": "Aggiungi Subreddit", - "please_add_at_least_one_subreddit": "Per favore aggiungi almeno un Subreddit", - "add_community": "Aggiungi Comunità", - "select_post_type": "Seleziona tipo di post...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Non siamo riusciti a trovare nessuna azienda collegata alla tua pagina LinkedIn.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Per favore chiudi questa finestra di dialogo, crea una nuova pagina e aggiungi nuovamente un nuovo canale.", - "select_linkedin_page": "Seleziona pagina LinkedIn:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Non siamo riusciti a trovare nessuna azienda collegata alle pagine selezionate.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Ti consigliamo di collegare tutte le pagine e tutte le aziende.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Per favore chiudi questa finestra di dialogo, elimina la tua integrazione e aggiungi nuovamente un nuovo canale.", - "select_instagram_account": "Seleziona account Instagram:", - "select_page": "Seleziona pagina:", - "generate_image_with_ai": "Genera immagine con IA", - "reconnect_channel": "Riconnetti canale", - "update_credentials": "Aggiorna credenziali", - "additional_settings": "Impostazioni aggiuntive", - "change_bot": "Cambia Bot", - "move_add_to_customer": "Sposta / aggiungi al cliente", - "edit_time_slots": "Modifica fasce orarie", - "enable_channel": "Abilita canale", - "disable_channel": "Disabilita canale", - "add": "Aggiungi", - "short_post": "Post breve", - "long_post": "Post lungo", - "a_thread_with_short_posts": "Una discussione con post brevi", - "a_thread_with_long_posts": "Una discussione con post lunghi", - "personal_voice_i_am_happy_to_announce": "Voce personale (\"Sono felice di annunciare\")", - "company_voice_we_are_happy_to_announce": "Voce aziendale (\"Siamo felici di annunciare\")", - "generate": "Genera", - "generate_posts": "Genera post", - "purchase_a_life_time_pro_account_with_sol_199": "Acquista un account PRO a vita con SOL ($199). Ti informiamo che non è previsto alcun rimborso per questo acquisto.", - "purchase_now": "Acquista ora", - "pay_today": "Paga oggi", - "we_are_sorry_to_see_you_go": "Ci dispiace vederti andare :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Ti andrebbe di dirci brevemente cosa avremmo potuto fare meglio?", - "cancel_subscription": "Annulla abbonamento", - "plans": "Piani", - "monthly": "MENSILE", - "yearly": "ANNUALE", - "reactivate_subscription": "Riattiva abbonamento", - "update_payment_method_invoices_history": "Aggiorna metodo di pagamento / Storico fatture", - "cancel_subscription_1": "Annulla abbonamento", - "your_subscription_will_be_canceled_at": "Il tuo abbonamento sarà annullato il", - "you_will_never_be_charged_again": "Non ti verrà mai più addebitato nulla", - "current_package": "Pacchetto attuale:", - "next_package": "Prossimo pacchetto:", - "claim": "Richiedi", - "frequently_asked_questions": "Domande frequenti", - "autopost": "Autopost", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Autopost può pubblicare automaticamente i nuovi elementi RSS sui social media", - "title": "Titolo", - "add_an_autopost": "Aggiungi un autopost", - "post_content": "Contenuto del post", - "sign_up": "Registrati", - "or": "OPPURE", - "by_registering_you_agree_to_our": "Registrandoti accetti i nostri", - "and": "e", - "terms_of_service": "Termini di servizio", - "privacy_policy": "Informativa sulla privacy", - "create_account": "Crea account", - "already_have_an_account": "Hai già un account?", - "sign_in": "Accedi", - "sign_in_1": "Accedi", - "don_t_have_an_account": "Non hai un account?", - "forgot_password": "Password dimenticata", - "forgot_password_1": "Password dimenticata", - "send_password_reset_email": "Invia email per reimpostare la password", - "go_back_to_login": "Torna al login", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Ti abbiamo inviato un'email con un link per reimpostare la password.", - "change_password": "Cambia password", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Abbiamo reimpostato con successo la tua password. Ora puoi accedere con la tua", - "click_here_to_go_back_to_login": "Clicca qui per tornare al login", - "activate_your_account": "Attiva il tuo account", - "thank_you_for_registering": "Grazie per esserti registrato!", - "please_check_your_email_to_activate_your_account": "Controlla la tua email per attivare il tuo account.", - "sign_in_with": "Accedi con", - "continue_with_google": "Continua con Google", - "sign_in_with_github": "Accedi con GitHub", - "continue_with_farcaster": "Continua con Farcaster", - "continue_with_your_wallet": "Continua con il tuo Wallet", - "stars_per_day": "Stelle al giorno", - "media": "Media", - "check_launch": "Verifica lancio", - "load_your_github_repository_from_settings_to_see_analytics": "Carica il tuo repository GitHub dalle impostazioni per vedere le analisi", - "stars": "Stelle", - "processing_stars": "Elaborazione delle stelle...", - "forks": "Fork", - "registration_is_disabled": "La registrazione è disabilitata", - "login_instead": "Accedi invece", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Seleziona una conversazione e inizia a chattare.", - "adding_channel_redirecting_you": "Aggiunta canale, ti stiamo reindirizzando", - "could_not_add_provider": "Impossibile aggiungere il provider.", - "you_are_being_redirected_back": "Stai venendo reindirizzato indietro", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Stiamo riscontrando alcune difficoltà, prova ad aggiornare la pagina", - "post_not_found": "Post non trovato", - "publication_date": "Data di pubblicazione:", - "analytics": "Analisi", - "launches": "Lanci", - "plugs": "Promozioni", - "billing": "Fatturazione", - "affiliate": "Affiliazione", - "monday": "Lunedì", - "tuesday": "Martedì", - "wednesday": "Mercoledì", - "thursday": "Giovedì", - "friday": "Venerdì", - "saturday": "Sabato", - "sunday": "Domenica", - "can_t_change_date_remove_post_from_publication": "Impossibile cambiare la data, rimuovi il post dalla pubblicazione", - "predicted_github_trending_change": "Variazione prevista delle tendenze di GitHub", - "duplicate_post": "Post duplicato", - "preview_post": "Anteprima post", - "post_statistics": "Statistiche del post", - "draft": "Bozza", - "week_number": "Settimana {{number}}", - "top_title_edit_webhook": "Modifica webhook", - "top_title_add_webhook": "Aggiungi webhook", - "top_title_oh_no": "Oh no", - "top_title_auto_plug": "Auto Plug: {{title}}", - "top_title_edit_autopost": "Modifica autopost", - "top_title_add_autopost": "Aggiungi autopost", - "top_title_send_a_new_offer": "Invia una nuova offerta", - "top_title_media_library": "Libreria multimediale", - "top_title_add_signature": "Aggiungi firma", - "top_title_send_a_message_to": "Invia un messaggio a {{name}}", - "top_title_configure_provider": "Configura provider", - "top_title_add_member": "Aggiungi membro", - "top_title_change_bot_picture": "Cambia immagine bot", - "top_title_create_a_new_tag": "Crea un nuovo tag", - "top_title_select_company": "Seleziona azienda", - "top_title_additional_settings": "Impostazioni aggiuntive", - "top_title_time_table_slots": "Fasce orarie", - "top_title_design_media": "Progetta media", - "top_title_edit_post": "Modifica post", - "top_title_create_post": "Crea post", - "top_title_move__add_to_customer": "Sposta / Aggiungi al cliente", - "top_title_add_api_key_for": "Aggiungi chiave API per {{name}}", - "top_title_instance_url": "URL istanza", - "top_title_custom_url": "URL personalizzato", - "top_title_add_channel": "Aggiungi canale", - "top_title_add_telegram": "Aggiungi Telegram", - "top_title_add_wrapcast": "Aggiungi Wrapcast", - "top_title_comments_for": "Commenti per {{date}}", - "top_title_edit_signature": "Modifica firma", - "label_name": "Nome", - "label_url": "URL", - "label_title": "Titolo", - "label_subtitle": "Sottotitolo", - "label_email": "Email", - "label_full_name": "Nome completo", - "label_password": "Password", - "label_confirm_password": "Conferma password", - "label_api_key": "Chiave API", - "label_instance_url": "URL istanza", - "label_custom_url": "URL personalizzato", - "label_feedback": "Feedback", - "label_bio": "Biografia", - "label_role": "Ruolo", - "label_country": "Paese", - "label_audience_size": "Dimensione del pubblico su tutte le piattaforme", - "label_pick_time": "Scegli l'orario", - "label_nickname": "Soprannome", - "label_write_anything": "Scrivi qualsiasi cosa", - "label_output_format": "Formato di output", - "label_add_pictures": "Aggiungere immagini?", - "label_hour": "Ora", - "label_minutes": "Minuti", - "label_select_publication": "Seleziona pubblicazione", - "label_canonical_link": "Link canonico", - "label_cover_picture": "Immagine di copertina", - "label_tags": "Tag", - "label_topics": "Argomenti", - "label_tags_maximum_4": "Tag (Massimo 4)", - "label_attachments": "Allegati", - "label_type": "Tipo", - "label_thumbnail": "Miniatura", - "label_who_can_see_this_video": "Chi può vedere questo video?", - "label_content_posting_method": "Metodo di pubblicazione del contenuto", - "label_auto_add_music": "Aggiungi musica automaticamente", - "label_duet": "Duetto", - "label_stitch": "Stitch", - "label_comments": "Commenti", - "label_disclose_video_content": "Divulga il contenuto del video", - "label_your_brand": "Il tuo brand", - "label_branded_content": "Contenuto sponsorizzato", - "label_subreddit": "Subreddit", - "label_flair": "Flair", - "label_media": "Media", - "label_search_subreddit": "Cerca Subreddit", - "label_delay": "Ritardo", - "label_post_type": "Tipo di post", - "label_collaborators": "Collaboratori (max 3) - gli account non possono essere privati", - "label_community": "Community", - "label_search_community": "Cerca nella community", - "label_channel": "Canale", - "label_search_channel": "Cerca canale", - "label_select_channel": "Seleziona canale", - "label_new_password": "Nuova password", - "label_repeat_password": "Ripeti password", - "label_platform": "Piattaforma", - "label_price_per_post": "Prezzo per post", - "label_integrations": "Integrazioni", - "label_code": "Codice", - "label_should_sync_last_post": "Dobbiamo sincronizzare l'ultimo post attuale?", - "label_when_post": "Quando dobbiamo pubblicarlo?", - "label_autogenerate_content": "Genera automaticamente il contenuto", - "label_generate_picture": "Generare un'immagine?", - "label_company": "Azienda", - "label_tag_color": "Colore etichetta", - "label_select_board": "Seleziona bacheca", - "label_select_organization": "Seleziona organizzazione", - "label_auto_add_signature": "Aggiungere automaticamente la firma?", - "enable_color_picker": "Abilita selettore colore", - "cancel_the_color_picker": "Annulla il selettore colore", - "no_content_yet": "Nessun contenuto ancora", - "write_your_reply": "Scrivi il tuo post...", - "add_a_tag": "Aggiungi un'etichetta", - "add_to_calendar": "Aggiungi al calendario", - "select_channels_from_circles": "Seleziona i canali dai cerchi sopra", - "not_matching_order": "Ordine non corrispondente", - "submit_for_order": "Invia per l'ordine", - "schedule": "Programma", - "update": "Aggiorna", - "attachments": "Allegati", - "tags": "Tag", - "public_to_everyone": "Pubblico per tutti", - "mutual_follow_friends": "Amici con follow reciproco", - "follower_of_creator": "Follower del creatore", - "self_only": "Solo io", - "post_content_directly_to_tiktok": "Pubblica contenuto direttamente su TikTok", - "upload_content_to_tiktok_without_posting": "Carica contenuto su TikTok senza pubblicarlo", - "choose_upload_without_posting_description": "Scegli 'carica senza pubblicare' se vuoi rivedere e modificare il tuo contenuto nell'app di TikTok prima di pubblicarlo. Questo ti dà accesso agli strumenti di modifica integrati di TikTok e ti permette di fare le ultime modifiche prima della pubblicazione.", - "faq_am_i_going_to_be_charged_by_postiz": "Mi verrà addebitato qualcosa da Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Per confermare le informazioni della carta di credito, Postiz tratterrà 2$ e li rilascerà immediatamente", - "faq_can_i_trust_postiz_gitroom": "Posso fidarmi di Postiz?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz è orgogliosamente open-source! Crediamo in una cultura etica e trasparente, il che significa che Postiz vivrà per sempre. Puoi consultare tutto il codice o usarlo per progetti personali. Per visualizzare il repository open-source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">clicca qui</a>.", - "faq_what_are_channels": "Cosa sono i canali?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz ti permette di programmare i tuoi post su diversi canali.\nUn canale è una piattaforma di pubblicazione dove puoi programmare i tuoi post.\nAd esempio, puoi programmare i tuoi post su X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads e Pinterest.", - "faq_what_are_team_members": "Chi sono i membri del team?", - "faq_if_you_have_a_team_with_multiple_members": "Se hai un team con più membri, puoi invitarli nel tuo workspace per collaborare ai tuoi post e aggiungere i loro canali personali", - "faq_what_is_ai_auto_complete": "Cos'è il completamento automatico AI?", - "faq_we_automate_chatgpt_to_help_you_write": "Automatizziamo ChatGPT per aiutarti a scrivere post social e articoli.", - "enter_email": "Inserisci email", - "are_you_sure": "Sei sicuro?", - "yes_delete_it": "Sì, eliminalo!", - "no_cancel": "No, annulla!", - "are_you_sure_you_want_to_delete": "Sei sicuro di voler eliminare {{name}}?", - "are_you_sure_you_want_to_delete_the_image": "Sei sicuro di voler eliminare l'immagine?", - "are_you_sure_you_want_to_logout": "Sei sicuro di voler uscire?", - "yes_logout": "Sì, esci", - "are_you_sure_you_want_to_delete_this_slot": "Sei sicuro di voler eliminare questo slot?", - "are_you_sure_you_want_to_delete_this_subreddit": "Sei sicuro di voler eliminare questo Subreddit?", - "are_you_sure_you_want_to_close_the_window": "Sei sicuro di voler chiudere la finestra?", - "yes_close": "Sì, chiudi", - "link_copied_to_clipboard": "Link copiato negli appunti", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Sei sicuro di voler chiudere questa finestra? (tutti i dati andranno persi)", - "yes_close_it": "Sì, chiudila!", - "uploading_pictures": "Caricamento immagini...", - "agent_starting": "Avvio agente", - "researching_your_content": "Analisi del tuo contenuto...", - "understanding_the_category": "Comprensione della categoria...", - "finding_the_topic": "Ricerca dell'argomento...", - "finding_popular_posts_to_match_with": "Ricerca di post popolari da abbinare...", - "generating_hook": "Generazione hook...", - "generating_content": "Generazione contenuto...", - "generating_pictures": "Generazione immagini...", - "finding_time_to_post": "Ricerca del momento migliore per pubblicare...", - "write_anything": "Scrivi qualsiasi cosa", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Puoi scrivere tutto quello che vuoi e anche aggiungere link, faremo noi la ricerca per te...", - "output_format": "Formato di output", - "add_pictures": "Aggiungere immagini?", - "7_days": "7 giorni", - "30_days": "30 giorni", - "90_days": "90 giorni", - "start_7_days_free_trial": "Inizia la prova gratuita di 7 giorni", - "change_language": "Cambia lingua", - "that_a_wrap": "È tutto!\n\nSe ti è piaciuto questo thread:\n\n1. Seguimi su @{{username}} per altri contenuti come questo\n2. Ritwitta il tweet qui sotto per condividere questo thread con il tuo pubblico\n", - "post_as_images_carousel": "Pubblica come carosello di immagini", - "save_set": "Salva set", - "separate_post": "Separa il post in più post", - "label_who_can_reply_to_this_post": "Chi può rispondere a questo post?", - "delete_integration": "Elimina integrazione", - "start_writing_your_post": "Inizia a scrivere il tuo post per un'anteprima" + "calendar": "Calendario", + "webhooks": "Webhook", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "I webhook sono un modo per ricevere notifiche quando succede qualcosa in Postiz tramite una richiesta HTTP.", + "name": "Nome", + "url": "URL", + "edit": "Modifica", + "delete": "Elimina", + "add_a_webhook": "Aggiungi un webhook", + "save": "Salva", + "send_test": "Invia test", + "select_role": "Seleziona ruolo", + "video_made_with_ai": "Video realizzato con l'IA", + "please_add_at_least": "Per favore aggiungi almeno 20 caratteri", + "send_invitation_via_email": "Inviare l'invito via email?", + "global_settings": "Impostazioni globali", + "copy_id": "Copia ID canale", + "team_members": "Membri del team", + "invite_your_assistant_or_team_member_to_manage_your_account": "Invita il tuo assistente o un membro del team a gestire il tuo account", + "remove": "Rimuovi", + "add_another_member": "Aggiungi un altro membro", + "signatures": "Firme", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Puoi aggiungere firme al tuo account da utilizzare nei tuoi post.", + "content": "Contenuto", + "auto_add": "Aggiunta automatica?", + "actions": "Azioni", + "use_signature": "Usa firma", + "add_a_signature": "Aggiungi una firma", + "no": "No", + "yes": "Sì", + "your_git_repository": "Il tuo repository Git", + "connect_your_github_repository_to_receive_updates_and_analytics": "Collega il tuo repository GitHub per ricevere aggiornamenti e analisi", + "connected": "Connesso:", + "disconnect": "Disconnetti", + "connect_your_repository": "Collega il tuo repository", + "cancel": "Annulla", + "connect": "Collega", + "public_api": "API pubblica", + "check_n8n": "Scopri il nostro nodo personalizzato N8N per Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Usa l'API di Postiz per integrarla con i tuoi strumenti.", + "read_how_to_use_it_over_the_documentation": "Leggi come usarla nella documentazione.", + "reveal": "Mostra", + "copy_key": "Copia chiave", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Collega il server MCP di Postiz al tuo client (streaming Http) per programmare i tuoi post più velocemente!", + "share_with_a_client": "Condividi con un cliente", + "post": "Post", + "comments": "Commenti", + "user": "Utente", + "login_register_to_add_comments": "Accedi / Registrati per aggiungere commenti", + "status": "Stato:", + "there_are_not_plugs_matching_your_channels": "Non ci sono plug compatibili con i tuoi canali", + "you_have_to_add_x_or_linkedin_or_threads": "Devi aggiungere: X oppure LinkedIn oppure Threads", + "go_to_the_calendar_to_add_channels": "Vai al calendario per aggiungere canali", + "channels": "Canali", + "activate": "Attiva", + "this_channel_needs_to_be_refreshed": "Questo canale deve essere aggiornato,", + "click_here_to_refresh": "clicca qui per aggiornare", + "can_t_show_analytics_yet": "Impossibile mostrare le analisi al momento", + "you_have_to_add_social_media_channels": "Devi aggiungere i canali dei social media", + "supported": "Supportato:", + "step": "PASSO", + "skip_onboarding": "Salta onboarding", + "onboarding": "Onboarding", + "next": "Avanti", + "you_are_done_from_here_you_can": "Hai finito, da qui puoi:", + "view_analytics": "Visualizza Analisi", + "schedule_a_new_post": "Programma un nuovo post", + "to_sell_posts_you_would_have_to": "Per vendere post dovresti:", + "1_connect_at_least_one_channel": "1. Collegare almeno un canale", + "2_connect_you_bank_account": "2. Collegare il tuo conto bancario", + "go_back_to_connect_channels": "Torna a collegare i canali", + "move_to_the_seller_page_to_connect_you_bank": "Vai alla pagina venditore per collegare il tuo conto bancario", + "connect_channels": "Collega Canali", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Collega i tuoi canali social e le piattaforme di pubblicazione per\n programmare i post in seguito", + "social": "Social", + "publishing_platforms": "Piattaforme di pubblicazione", + "no_channels": "Nessun canale ancora", + "connect_your_accounts": "Collega i tuoi account social per iniziare a programmare, pubblicare e analizzare — tutto in un unico posto.", + "notifications": "Notifiche", + "no_notifications": "Nessuna notifica", + "send_message": "Invia messaggio", + "mar_28": "28 mar", + "there_are_no_messages_yet": "Non ci sono ancora messaggi.", + "checkout_the_marketplace": "Visita il Marketplace", + "go_to_marketplace": "Vai al marketplace", + "all_messages": "Tutti i messaggi", + "previous": "Precedente", + "select_or_upload_pictures_maximum_5_at_a_time": "Seleziona o carica immagini (massimo 5 alla volta)", + "you_can_also_drag_drop_pictures": "Puoi anche trascinare e rilasciare le immagini", + "you_don_t_have_any_assets_yet": "Non hai ancora nessuna risorsa.", + "click_the_button_below_to_upload_one": "Clicca sul pulsante qui sotto per caricarne una", + "click_the_button_below_to_upload_other": "Clicca il pulsante qui sotto per caricare più file", + "add_selected_media": "Aggiungi i media selezionati", + "insert_media": "Inserisci media", + "design_media": "Progetta media", + "select": "Seleziona", + "editor": "Editor", + "clear": "Cancella", + "order_completed": "Ordine completato", + "the_order_has_been_completed": "L'ordine è stato completato", + "post_has_been_published": "Il post è stato pubblicato", + "url_1": "URL:", + "new_offer": "Nuova offerta", + "platform": "Piattaforma", + "posts": "Post", + "pay_accept_offer": "Paga e accetta l'offerta", + "accepted": "Accettato", + "post_draft": "Bozza del post", + "revision_needed": "Revisione necessaria", + "approve": "Approva", + "preview": "Anteprima", + "revision_requested": "Revisione richiesta", + "accepted_1": "ACCETTATO", + "cancelled_by_the_seller": "Annullato dal venditore", + "please_select_your_country_where_your_business_is": "Seleziona il paese in cui si trova la tua attività.", + "select_country": "--SELEZIONA PAESE--", + "connect_bank_account": "Collega conto bancario", + "seller_mode": "Modalità venditore", + "active": "Attivo", + "details": "Dettagli", + "audience_size": "Dimensione pubblico", + "add_another_platform": "Aggiungi un'altra piattaforma", + "send_an_offer_for": "Invia un'offerta per $", + "complete_order_and_pay_early": "Completa l'ordine e paga in anticipo", + "order_in_progress": "Ordine in corso", + "create_a_new_offer": "Crea una nuova offerta", + "orders": "Ordini", + "price": "Prezzo", + "state": "Stato", + "showing": "Visualizzazione", + "to": "a", + "from": "da", + "results": "Risultati", + "content_writer": "Scrittore di contenuti", + "influencer": "Influencer", + "request_service": "Richiedi servizio", + "the_marketplace_is_not_opened_yet": "Il marketplace non è ancora aperto", + "check_again_soon": "Ricontrolla presto!", + "filter": "Filtro", + "result": "Risultato", + "seller": "Venditore", + "buyer": "Acquirente", + "discord_support": "Supporto Discord", + "teams": "Team", + "webhooks_1": "Webhook", + "auto_post": "Pubblicazione automatica", + "logout_from": "Disconnetti da", + "join_10000_entrepreneurs_who_use_postiz": "Unisciti a oltre 10.000 imprenditori che usano Postiz", + "to_manage_all_your_social_media_channels": "Per gestire tutti i tuoi canali social", + "100_no_risk_trial": "Prova senza rischi al 100%", + "pay_nothing_for_the_first_7_days": "Non paghi nulla per i primi 7 giorni", + "cancel_anytime_hassle_free": "Annulla in qualsiasi momento, senza problemi", + "add_free_subscription": "-- AGGIUNGI ABBONAMENTO GRATUITO --", + "currently_impersonating": "Attualmente stai impersonando", + "user_1": "utente:", + "drag_n_drop_some_files_here": "Trascina qui alcuni file", + "add_time_slot": "Aggiungi fascia oraria", + "add_slot": "Aggiungi fascia oraria", + "cancel_publication": "Annulla pubblicazione", + "statistics": "Statistiche", + "loading": "Caricamento in corso", + "short_link": "Link breve", + "original_link": "Link originale", + "clicks": "Click", + "selected_customer": "Cliente selezionato", + "customer": "Cliente:", + "repeat_post_every": "Ripeti post ogni...", + "use_this_media": "Usa questo media", + "create_new_post": "Crea post", + "update_post": "Aggiorna post esistente", + "merge_comments_into_one_post": "Unisci i commenti in un unico post", + "accounts_that_will_engage": "Account che interagiranno:", + "day": "Giorno", + "week": "Settimana", + "month": "Mese", + "remove_from_customer": "Rimuovi dal cliente", + "show_more": "+ Mostra di più", + "show_less": "- Mostra di meno", + "upload": "Carica", + "ai": "AI", + "add_channel": "Aggiungi canale", + "add_platform": "Aggiungi piattaforma", + "articles": "Articoli", + "add_comment": "Aggiungi commento", + "add_post": "Aggiungi post in una discussione", + "add_comment_or_post": "Aggiungi commento / post", + "you_are_in_global_editing_mode": "Sei in modalità di modifica globale", + "the_post_should_be_at_least_6_characters_long": "Il post deve contenere almeno 6 caratteri", + "are_you_sure_you_want_to_delete_post": "Sei sicuro di voler eliminare questo post?", + "post_deleted_successfully": "Post eliminato con successo", + "delete_post": "Elimina post", + "save_as_draft": "Salva come bozza", + "post_now": "Pubblica ora", + "please_add": "Per favore aggiungi", + "to_your_telegram_group_channel_and_click_here": "al tuo\ngruppo/canale Telegram e clicca qui:", + "connect_telegram": "Collega Telegram", + "please_add_the_following_command_in_your_chat": "Per favore aggiungi il seguente comando nella tua chat:", + "copy": "Copia", + "settings": "Impostazioni", + "integrations": "Integrazioni", + "add_integration": "Aggiungi integrazione", + "you_are_now_editing_only": "Ora stai modificando solo", + "tag_a_company": "Tagga un'azienda", + "video_length_is_invalid_must_be_up_to": "La durata del video non è valida, deve essere fino a", + "seconds": "secondi", + "this_feature_available_only_for_photos": "Questa funzione è disponibile solo per le foto, verrà aggiunta una musica predefinita che potrai cambiare in seguito.", + "allow_user_to": "Consenti all'utente di:", + "your_video_will_be_labeled_promotional": "Il tuo video sarà etichettato come \"Contenuto promozionale\".", + "this_cannot_be_changed_once_posted": "Questo non può essere modificato una volta che il video è stato pubblicato.", + "turn_on_to_disclose_video_promotes": "Attiva per dichiarare che questo video promuove beni o servizi in cambio di qualcosa di valore. Il tuo video potrebbe promuovere te stesso, una terza parte o entrambi.", + "you_are_promoting_yourself": "Stai promuovendo te stesso o il tuo marchio.", + "this_video_will_be_classified_brand_organic": "Questo video sarà classificato come Brand Organico.", + "you_are_promoting_another_brand": "Stai promuovendo un altro marchio o una terza parte.", + "this_video_will_be_classified_branded_content": "Questo video sarà classificato come Contenuto Brandizzato.", + "by_posting_you_agree_to_tiktoks": "Pubblicando, accetti le condizioni di TikTok", + "music_usage_confirmation": "Conferma utilizzo musica", + "branded_content_policy": "Politica sui contenuti sponsorizzati", + "select_1": "--Seleziona--", + "select_flair": "--Seleziona Flair--", + "link": "Link", + "add_subreddit": "Aggiungi Subreddit", + "please_add_at_least_one_subreddit": "Per favore aggiungi almeno un Subreddit", + "add_community": "Aggiungi Comunità", + "select_post_type": "Seleziona tipo di post...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Non siamo riusciti a trovare nessuna azienda collegata alla tua pagina LinkedIn.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Per favore chiudi questa finestra di dialogo, crea una nuova pagina e aggiungi nuovamente un nuovo canale.", + "select_linkedin_page": "Seleziona pagina LinkedIn:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Non siamo riusciti a trovare nessuna azienda collegata alle pagine selezionate.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Ti consigliamo di collegare tutte le pagine e tutte le aziende.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Per favore chiudi questa finestra di dialogo, elimina la tua integrazione e aggiungi nuovamente un nuovo canale.", + "select_instagram_account": "Seleziona account Instagram:", + "select_page": "Seleziona pagina:", + "generate_image_with_ai": "Genera immagine con IA", + "reconnect_channel": "Riconnetti canale", + "update_credentials": "Aggiorna credenziali", + "additional_settings": "Impostazioni aggiuntive", + "change_bot": "Cambia Bot", + "move_add_to_customer": "Sposta / aggiungi al cliente", + "edit_time_slots": "Modifica fasce orarie", + "enable_channel": "Abilita canale", + "disable_channel": "Disabilita canale", + "add": "Aggiungi", + "short_post": "Post breve", + "long_post": "Post lungo", + "a_thread_with_short_posts": "Una discussione con post brevi", + "a_thread_with_long_posts": "Una discussione con post lunghi", + "personal_voice_i_am_happy_to_announce": "Voce personale (\"Sono felice di annunciare\")", + "company_voice_we_are_happy_to_announce": "Voce aziendale (\"Siamo felici di annunciare\")", + "generate": "Genera", + "generate_posts": "Genera post", + "purchase_a_life_time_pro_account_with_sol_199": "Acquista un account PRO a vita con SOL ($199). Ti informiamo che non è previsto alcun rimborso per questo acquisto.", + "purchase_now": "Acquista ora", + "pay_today": "Paga oggi", + "we_are_sorry_to_see_you_go": "Ci dispiace vederti andare :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Ti andrebbe di dirci brevemente cosa avremmo potuto fare meglio?", + "cancel_subscription": "Annulla abbonamento", + "plans": "Piani", + "monthly": "MENSILE", + "yearly": "ANNUALE", + "reactivate_subscription": "Riattiva abbonamento", + "update_payment_method_invoices_history": "Aggiorna metodo di pagamento / Storico fatture", + "cancel_subscription_1": "Annulla abbonamento", + "your_subscription_will_be_canceled_at": "Il tuo abbonamento sarà annullato il", + "you_will_never_be_charged_again": "Non ti verrà mai più addebitato nulla", + "current_package": "Pacchetto attuale:", + "next_package": "Prossimo pacchetto:", + "claim": "Richiedi", + "frequently_asked_questions": "Domande frequenti", + "autopost": "Autopost", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Autopost può pubblicare automaticamente i nuovi elementi RSS sui social media", + "title": "Titolo", + "add_an_autopost": "Aggiungi un autopost", + "post_content": "Contenuto del post", + "sign_up": "Registrati", + "or": "OPPURE", + "by_registering_you_agree_to_our": "Registrandoti accetti i nostri", + "and": "e", + "terms_of_service": "Termini di servizio", + "privacy_policy": "Informativa sulla privacy", + "create_account": "Crea account", + "already_have_an_account": "Hai già un account?", + "sign_in": "Accedi", + "sign_in_1": "Accedi", + "don_t_have_an_account": "Non hai un account?", + "forgot_password": "Password dimenticata", + "forgot_password_1": "Password dimenticata", + "send_password_reset_email": "Invia email per reimpostare la password", + "go_back_to_login": "Torna al login", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Ti abbiamo inviato un'email con un link per reimpostare la password.", + "change_password": "Cambia password", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Abbiamo reimpostato con successo la tua password. Ora puoi accedere con la tua", + "click_here_to_go_back_to_login": "Clicca qui per tornare al login", + "activate_your_account": "Attiva il tuo account", + "thank_you_for_registering": "Grazie per esserti registrato!", + "please_check_your_email_to_activate_your_account": "Controlla la tua email per attivare il tuo account.", + "sign_in_with": "Accedi con", + "continue_with_google": "Continua con Google", + "sign_in_with_github": "Accedi con GitHub", + "continue_with_farcaster": "Continua con Farcaster", + "continue_with_your_wallet": "Continua con il tuo Wallet", + "stars_per_day": "Stelle al giorno", + "media": "Media", + "check_launch": "Verifica lancio", + "load_your_github_repository_from_settings_to_see_analytics": "Carica il tuo repository GitHub dalle impostazioni per vedere le analisi", + "stars": "Stelle", + "processing_stars": "Elaborazione delle stelle...", + "forks": "Fork", + "registration_is_disabled": "La registrazione è disabilitata", + "login_instead": "Accedi invece", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Seleziona una conversazione e inizia a chattare.", + "adding_channel_redirecting_you": "Aggiunta canale, ti stiamo reindirizzando", + "could_not_add_provider": "Impossibile aggiungere il provider.", + "you_are_being_redirected_back": "Stai venendo reindirizzato indietro", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Stiamo riscontrando alcune difficoltà, prova ad aggiornare la pagina", + "post_not_found": "Post non trovato", + "publication_date": "Data di pubblicazione:", + "analytics": "Analisi", + "launches": "Lanci", + "plugs": "Promozioni", + "billing": "Fatturazione", + "affiliate": "Affiliazione", + "monday": "Lunedì", + "tuesday": "Martedì", + "wednesday": "Mercoledì", + "thursday": "Giovedì", + "friday": "Venerdì", + "saturday": "Sabato", + "sunday": "Domenica", + "can_t_change_date_remove_post_from_publication": "Impossibile cambiare la data, rimuovi il post dalla pubblicazione", + "predicted_github_trending_change": "Variazione prevista delle tendenze di GitHub", + "duplicate_post": "Post duplicato", + "preview_post": "Anteprima post", + "post_statistics": "Statistiche del post", + "draft": "Bozza", + "week_number": "Settimana {{number}}", + "top_title_edit_webhook": "Modifica webhook", + "top_title_add_webhook": "Aggiungi webhook", + "top_title_oh_no": "Oh no", + "top_title_auto_plug": "Auto Plug: {{title}}", + "top_title_edit_autopost": "Modifica autopost", + "top_title_add_autopost": "Aggiungi autopost", + "top_title_send_a_new_offer": "Invia una nuova offerta", + "top_title_media_library": "Libreria multimediale", + "top_title_add_signature": "Aggiungi firma", + "top_title_send_a_message_to": "Invia un messaggio a {{name}}", + "top_title_configure_provider": "Configura provider", + "top_title_add_member": "Aggiungi membro", + "top_title_change_bot_picture": "Cambia immagine bot", + "top_title_create_a_new_tag": "Crea un nuovo tag", + "top_title_select_company": "Seleziona azienda", + "top_title_additional_settings": "Impostazioni aggiuntive", + "top_title_time_table_slots": "Fasce orarie", + "top_title_design_media": "Progetta media", + "top_title_edit_post": "Modifica post", + "top_title_create_post": "Crea post", + "top_title_move__add_to_customer": "Sposta / Aggiungi al cliente", + "top_title_add_api_key_for": "Aggiungi chiave API per {{name}}", + "top_title_instance_url": "URL istanza", + "top_title_custom_url": "URL personalizzato", + "top_title_add_channel": "Aggiungi canale", + "top_title_add_telegram": "Aggiungi Telegram", + "top_title_add_wrapcast": "Aggiungi Wrapcast", + "top_title_comments_for": "Commenti per {{date}}", + "top_title_edit_signature": "Modifica firma", + "label_name": "Nome", + "label_url": "URL", + "label_title": "Titolo", + "label_subtitle": "Sottotitolo", + "label_email": "Email", + "label_full_name": "Nome completo", + "label_password": "Password", + "label_confirm_password": "Conferma password", + "label_api_key": "Chiave API", + "label_instance_url": "URL istanza", + "label_custom_url": "URL personalizzato", + "label_feedback": "Feedback", + "label_bio": "Biografia", + "label_role": "Ruolo", + "label_country": "Paese", + "label_audience_size": "Dimensione del pubblico su tutte le piattaforme", + "label_pick_time": "Scegli l'orario", + "label_nickname": "Soprannome", + "label_write_anything": "Scrivi qualsiasi cosa", + "label_output_format": "Formato di output", + "label_add_pictures": "Aggiungere immagini?", + "label_hour": "Ora", + "label_minutes": "Minuti", + "label_select_publication": "Seleziona pubblicazione", + "label_canonical_link": "Link canonico", + "label_cover_picture": "Immagine di copertina", + "label_tags": "Tag", + "label_topics": "Argomenti", + "label_tags_maximum_4": "Tag (Massimo 4)", + "label_attachments": "Allegati", + "label_type": "Tipo", + "label_thumbnail": "Miniatura", + "label_who_can_see_this_video": "Chi può vedere questo video?", + "label_content_posting_method": "Metodo di pubblicazione del contenuto", + "label_auto_add_music": "Aggiungi musica automaticamente", + "label_duet": "Duetto", + "label_stitch": "Stitch", + "label_comments": "Commenti", + "label_disclose_video_content": "Divulga il contenuto del video", + "label_your_brand": "Il tuo brand", + "label_branded_content": "Contenuto sponsorizzato", + "label_subreddit": "Subreddit", + "label_flair": "Flair", + "label_media": "Media", + "label_search_subreddit": "Cerca Subreddit", + "label_delay": "Ritardo", + "label_post_type": "Tipo di post", + "label_collaborators": "Collaboratori (max 3) - gli account non possono essere privati", + "label_community": "Community", + "label_search_community": "Cerca nella community", + "label_channel": "Canale", + "label_search_channel": "Cerca canale", + "label_select_channel": "Seleziona canale", + "label_new_password": "Nuova password", + "label_repeat_password": "Ripeti password", + "label_platform": "Piattaforma", + "label_price_per_post": "Prezzo per post", + "label_integrations": "Integrazioni", + "label_code": "Codice", + "label_should_sync_last_post": "Dobbiamo sincronizzare l'ultimo post attuale?", + "label_when_post": "Quando dobbiamo pubblicarlo?", + "label_autogenerate_content": "Genera automaticamente il contenuto", + "label_generate_picture": "Generare un'immagine?", + "label_company": "Azienda", + "label_tag_color": "Colore etichetta", + "label_select_board": "Seleziona bacheca", + "label_select_organization": "Seleziona organizzazione", + "label_auto_add_signature": "Aggiungere automaticamente la firma?", + "enable_color_picker": "Abilita selettore colore", + "cancel_the_color_picker": "Annulla il selettore colore", + "no_content_yet": "Nessun contenuto ancora", + "write_your_reply": "Scrivi il tuo post...", + "add_a_tag": "Aggiungi un'etichetta", + "add_to_calendar": "Aggiungi al calendario", + "select_channels_from_circles": "Seleziona i canali dai cerchi sopra", + "not_matching_order": "Ordine non corrispondente", + "submit_for_order": "Invia per l'ordine", + "schedule": "Programma", + "update": "Aggiorna", + "attachments": "Allegati", + "tags": "Tag", + "public_to_everyone": "Pubblico per tutti", + "mutual_follow_friends": "Amici con follow reciproco", + "follower_of_creator": "Follower del creatore", + "self_only": "Solo io", + "post_content_directly_to_tiktok": "Pubblica contenuto direttamente su TikTok", + "upload_content_to_tiktok_without_posting": "Carica contenuto su TikTok senza pubblicarlo", + "choose_upload_without_posting_description": "Scegli 'carica senza pubblicare' se vuoi rivedere e modificare il tuo contenuto nell'app di TikTok prima di pubblicarlo. Questo ti dà accesso agli strumenti di modifica integrati di TikTok e ti permette di fare le ultime modifiche prima della pubblicazione.", + "faq_am_i_going_to_be_charged_by_postiz": "Mi verrà addebitato qualcosa da Postiz?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Per confermare le informazioni della carta di credito, Postiz tratterrà 2$ e li rilascerà immediatamente", + "faq_can_i_trust_postiz_gitroom": "Posso fidarmi di Postiz?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz è orgogliosamente open-source! Crediamo in una cultura etica e trasparente, il che significa che Postiz vivrà per sempre. Puoi consultare tutto il codice o usarlo per progetti personali. Per visualizzare il repository open-source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">clicca qui</a>.", + "faq_what_are_channels": "Cosa sono i canali?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz ti permette di programmare i tuoi post su diversi canali.\nUn canale è una piattaforma di pubblicazione dove puoi programmare i tuoi post.\nAd esempio, puoi programmare i tuoi post su X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads e Pinterest.", + "faq_what_are_team_members": "Chi sono i membri del team?", + "faq_if_you_have_a_team_with_multiple_members": "Se hai un team con più membri, puoi invitarli nel tuo workspace per collaborare ai tuoi post e aggiungere i loro canali personali", + "faq_what_is_ai_auto_complete": "Cos'è il completamento automatico AI?", + "faq_we_automate_chatgpt_to_help_you_write": "Automatizziamo ChatGPT per aiutarti a scrivere post social e articoli.", + "enter_email": "Inserisci email", + "are_you_sure": "Sei sicuro?", + "yes_delete_it": "Sì, eliminalo!", + "no_cancel": "No, annulla!", + "are_you_sure_you_want_to_delete": "Sei sicuro di voler eliminare {{name}}?", + "are_you_sure_you_want_to_delete_the_image": "Sei sicuro di voler eliminare l'immagine?", + "are_you_sure_you_want_to_logout": "Sei sicuro di voler uscire?", + "yes_logout": "Sì, esci", + "are_you_sure_you_want_to_delete_this_slot": "Sei sicuro di voler eliminare questo slot?", + "are_you_sure_you_want_to_delete_this_subreddit": "Sei sicuro di voler eliminare questo Subreddit?", + "are_you_sure_you_want_to_close_the_window": "Sei sicuro di voler chiudere la finestra?", + "yes_close": "Sì, chiudi", + "link_copied_to_clipboard": "Link copiato negli appunti", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Sei sicuro di voler chiudere questa finestra? (tutti i dati andranno persi)", + "yes_close_it": "Sì, chiudila!", + "uploading_pictures": "Caricamento immagini...", + "agent_starting": "Avvio agente", + "researching_your_content": "Analisi del tuo contenuto...", + "understanding_the_category": "Comprensione della categoria...", + "finding_the_topic": "Ricerca dell'argomento...", + "finding_popular_posts_to_match_with": "Ricerca di post popolari da abbinare...", + "generating_hook": "Generazione hook...", + "generating_content": "Generazione contenuto...", + "generating_pictures": "Generazione immagini...", + "finding_time_to_post": "Ricerca del momento migliore per pubblicare...", + "write_anything": "Scrivi qualsiasi cosa", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Puoi scrivere tutto quello che vuoi e anche aggiungere link, faremo noi la ricerca per te...", + "output_format": "Formato di output", + "add_pictures": "Aggiungere immagini?", + "7_days": "7 giorni", + "30_days": "30 giorni", + "90_days": "90 giorni", + "start_7_days_free_trial": "Inizia la prova gratuita di 7 giorni", + "change_language": "Cambia lingua", + "that_a_wrap": "È tutto!\n\nSe ti è piaciuto questo thread:\n\n1. Seguimi su @{{username}} per altri contenuti come questo\n2. Ritwitta il tweet qui sotto per condividere questo thread con il tuo pubblico\n", + "post_as_images_carousel": "Pubblica come carosello di immagini", + "save_set": "Salva set", + "separate_post": "Separa il post in più post", + "label_who_can_reply_to_this_post": "Chi può rispondere a questo post?", + "delete_integration": "Elimina integrazione", + "start_writing_your_post": "Inizia a scrivere il tuo post per un'anteprima", + "billing_join_over": "Unisciti a oltre", + "billing_entrepreneurs_count": "18.000+ imprenditori", + "billing_who_use": "che usano", + "billing_postiz_grow_social": "Postiz per far crescere la loro presenza sui social", + "billing_no_risk_trial": "Prova gratuita senza rischi al 100%", + "billing_pay_nothing_7_days": "Non paghi NULLA per i primi 7 giorni", + "billing_cancel_anytime": "Annulla in qualsiasi momento, senza problemi", + "billing_choose_plan": "Scegli un piano", + "billing_monthly": "Mensile", + "billing_yearly": "Annuale", + "billing_20_percent_off": "20% di sconto", + "billing_features": "Funzionalità", + "billing_channel": "canale", + "billing_channels": "canali", + "billing_unlimited": "Illimitato", + "billing_posts_per_month": "post al mese", + "billing_unlimited_team_members": "Membri del team illimitati", + "billing_ai_auto_complete": "Completamento automatico AI", + "billing_ai_copilots": "Copiloti AI", + "billing_ai_autocomplete": "Completamento automatico AI", + "billing_advanced_picture_editor": "Editor di immagini avanzato", + "billing_ai_images_per_month": "Immagini AI al mese", + "billing_ai_videos_per_month": "Video AI al mese", + "billing_billing_address": "Indirizzo di fatturazione", + "billing_payment": "Pagamento", + "billing_powered_by_stripe": "Offerto da Stripe", + "billing_your_7_day_trial_is": "La tua prova di 7 giorni è", + "billing_100_percent_free": "100% gratuita", + "billing_ending": "in scadenza", + "billing_cancel_anytime_short": "Annulla in qualsiasi momento.", + "billing_pay_0_start_trial": "Paga 0€ oggi - Inizia la tua prova gratuita!", + "billing_pay_now": "Paga ora", + "billing_per_month": "/ mese" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index 656bbd61..fd28c538 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -1,505 +1,539 @@ { - "calendar": "カレンダー", - "webhooks": "ウェブフック", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "ウェブフックは、Postizで何かが起こったときにHTTPリクエストを通じて通知を受け取る方法です。", - "name": "名前", - "url": "URL", - "edit": "編集", - "delete": "削除", - "add_a_webhook": "ウェブフックを追加", - "save": "保存", - "send_test": "テスト送信", - "select_role": "役割を選択", - "video_made_with_ai": "AIで作成された動画", - "please_add_at_least": "少なくとも20文字を追加してください", - "send_invitation_via_email": "メールで招待を送信しますか?", - "global_settings": "グローバル設定", - "copy_id": "チャンネルIDをコピー", - "team_members": "チームメンバー", - "invite_your_assistant_or_team_member_to_manage_your_account": "アシスタントやチームメンバーを招待してアカウントを管理してもらいましょう", - "remove": "削除", - "add_another_member": "メンバーを追加", - "signatures": "署名", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "投稿で使用するための署名をアカウントに追加できます。", - "content": "内容", - "auto_add": "自動追加?", - "actions": "操作", - "use_signature": "署名を使用", - "add_a_signature": "署名を追加", - "no": "いいえ", - "yes": "はい", - "your_git_repository": "あなたのGitリポジトリ", - "connect_your_github_repository_to_receive_updates_and_analytics": "アップデートと分析を受け取るためにGitHubリポジトリを接続してください", - "connected": "接続済み:", - "disconnect": "切断", - "connect_your_repository": "リポジトリを接続", - "cancel": "キャンセル", - "connect": "接続", - "public_api": "パブリックAPI", - "check_n8n": "Postiz用のカスタムN8Nノードをご覧ください。", - "use_postiz_api_to_integrate_with_your_tools": "Postiz APIを使ってあなたのツールと連携しましょう。", - "read_how_to_use_it_over_the_documentation": "ドキュメントで使い方を確認してください。", - "reveal": "表示", - "copy_key": "キーをコピー", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "投稿をより迅速にスケジュールするために、Postiz MCPサーバーをクライアント(HTTPストリーミング)に接続しましょう!", - "share_with_a_client": "クライアントと共有", - "post": "投稿", - "comments": "コメント", - "user": "ユーザー", - "login_register_to_add_comments": "コメントを追加するにはログイン/登録してください", - "status": "ステータス:", - "there_are_not_plugs_matching_your_channels": "あなたのチャンネルに一致するプラグはありません", - "you_have_to_add_x_or_linkedin_or_threads": "XまたはLinkedInまたはThreadsを追加する必要があります", - "go_to_the_calendar_to_add_channels": "チャンネルを追加するにはカレンダーに移動してください", - "channels": "チャンネル", - "activate": "有効化", - "this_channel_needs_to_be_refreshed": "このチャンネルは更新が必要です。", - "click_here_to_refresh": "ここをクリックして更新", - "can_t_show_analytics_yet": "まだ分析情報を表示できません", - "you_have_to_add_social_media_channels": "ソーシャルメディアチャンネルを追加する必要があります", - "supported": "対応:", - "step": "ステップ", - "skip_onboarding": "オンボーディングをスキップ", - "onboarding": "オンボーディング", - "next": "次へ", - "you_are_done_from_here_you_can": "完了しました。ここから次のことができます:", - "view_analytics": "分析情報を見る", - "schedule_a_new_post": "新しい投稿を予約する", - "to_sell_posts_you_would_have_to": "投稿を販売するには、次のことが必要です:", - "1_connect_at_least_one_channel": "1. 少なくとも1つのチャンネルを接続する", - "2_connect_you_bank_account": "2. 銀行口座を接続する", - "go_back_to_connect_channels": "チャンネル接続に戻る", - "move_to_the_seller_page_to_connect_you_bank": "販売者ページに移動して銀行口座を接続する", - "connect_channels": "チャンネルを接続", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "ソーシャルメディアや出版ウェブサイトのチャンネルを接続して、\n後で投稿を予約しましょう", - "social": "ソーシャル", - "publishing_platforms": "出版プラットフォーム", - "no_channels": "まだチャンネルがありません", - "connect_your_accounts": "ソーシャルアカウントを連携して、スケジューリング、投稿、分析をすべて一か所で始めましょう。", - "notifications": "通知", - "no_notifications": "通知はありません", - "send_message": "メッセージを送信", - "mar_28": "3月28日", - "there_are_no_messages_yet": "まだメッセージはありません。", - "checkout_the_marketplace": "マーケットプレイスをチェック", - "go_to_marketplace": "マーケットプレイスへ行く", - "all_messages": "すべてのメッセージ", - "previous": "前へ", - "select_or_upload_pictures_maximum_5_at_a_time": "画像を選択またはアップロード(最大5枚まで同時に)", - "you_can_also_drag_drop_pictures": "画像をドラッグ&ドロップすることもできます", - "you_don_t_have_any_assets_yet": "まだアセットがありません。", - "click_the_button_below_to_upload_other": "下のボタンをクリックして複数アップロード", - "add_selected_media": "選択したメディアを追加", - "insert_media": "メディアを挿入", - "design_media": "メディアをデザイン", - "select": "選択", - "editor": "エディター", - "clear": "クリア", - "order_completed": "注文完了", - "the_order_has_been_completed": "注文が完了しました", - "post_has_been_published": "投稿が公開されました", - "url_1": "URL:", - "new_offer": "新しいオファー", - "platform": "プラットフォーム", - "posts": "投稿", - "pay_accept_offer": "支払い&オファーを承諾", - "accepted": "承諾済み", - "post_draft": "下書き投稿", - "revision_needed": "修正が必要", - "approve": "承認する", - "preview": "プレビュー", - "revision_requested": "修正依頼済み", - "accepted_1": "承認済み", - "cancelled_by_the_seller": "出品者によってキャンセルされました", - "please_select_your_country_where_your_business_is": "ビジネスを行っている国を選択してください。", - "select_country": "--国を選択--", - "connect_bank_account": "銀行口座を接続", - "seller_mode": "出品者モード", - "active": "有効", - "details": "詳細", - "audience_size": "オーディエンス規模", - "add_another_platform": "別のプラットフォームを追加", - "send_an_offer_for": "$でオファーを送信", - "complete_order_and_pay_early": "注文を完了して早期支払い", - "order_in_progress": "注文進行中", - "create_a_new_offer": "新しいオファーを作成", - "orders": "注文", - "price": "価格", - "state": "状態", - "showing": "表示中", - "to": "から", - "from": "まで", - "results": "結果", - "content_writer": "コンテンツライター", - "influencer": "インフルエンサー", - "request_service": "サービスを依頼する", - "the_marketplace_is_not_opened_yet": "マーケットプレイスはまだオープンしていません", - "check_again_soon": "またすぐにご確認ください!", - "filter": "フィルター", - "result": "結果", - "seller": "販売者", - "buyer": "購入者", - "discord_support": "Discordサポート", - "teams": "チーム", - "webhooks_1": "ウェブフック", - "auto_post": "自動投稿", - "logout_from": "ログアウト:", - "join_10000_entrepreneurs_who_use_postiz": "Postizを利用している10,000人以上の起業家に参加しましょう", - "to_manage_all_your_social_media_channels": "すべてのソーシャルメディアチャンネルを管理するために", - "100_no_risk_trial": "100%リスクなしのトライアル", - "pay_nothing_for_the_first_7_days": "最初の7日間は無料", - "cancel_anytime_hassle_free": "いつでも簡単にキャンセル可能", - "add_free_subscription": "-- 無料サブスクリプションを追加 --", - "currently_impersonating": "現在なりすまし中", - "user_1": "ユーザー:", - "drag_n_drop_some_files_here": "ここにファイルをドラッグ&ドロップしてください", - "add_time_slot": "時間枠を追加", - "add_slot": "スロットを追加", - "cancel_publication": "公開をキャンセル", - "statistics": "統計", - "loading": "読み込み中", - "short_link": "短縮リンク", - "original_link": "元のリンク", - "clicks": "クリック数", - "selected_customer": "選択された顧客", - "customer": "顧客:", - "repeat_post_every": "投稿を繰り返す間隔...", - "use_this_media": "このメディアを使用", - "create_new_post": "投稿を作成", - "update_post": "既存の投稿を更新", - "merge_comments_into_one_post": "コメントを1つの投稿にまとめる", - "accounts_that_will_engage": "エンゲージするアカウント:", - "day": "日", - "week": "週", - "month": "月", - "remove_from_customer": "顧客から削除", - "show_more": "+もっと表示", - "show_less": "-表示を減らす", - "upload": "アップロード", - "ai": "AI", - "add_channel": "チャンネルを追加", - "add_platform": "プラットフォームを追加", - "articles": "記事", - "add_comment": "コメントを追加", - "add_post": "スレッドに投稿を追加", - "add_comment_or_post": "コメント/投稿を追加", - "you_are_in_global_editing_mode": "グローバル編集モードになっています", - "the_post_should_be_at_least_6_characters_long": "投稿は6文字以上である必要があります", - "are_you_sure_you_want_to_delete_post": "この投稿を本当に削除しますか?", - "post_deleted_successfully": "投稿が正常に削除されました", - "delete_post": "投稿を削除", - "save_as_draft": "下書きとして保存", - "post_now": "今すぐ投稿", - "please_add": "追加してください", - "to_your_telegram_group_channel_and_click_here": "あなたのTelegramグループ/チャンネルに追加し、ここをクリックしてください:", - "connect_telegram": "Telegramを接続", - "please_add_the_following_command_in_your_chat": "チャットに次のコマンドを追加してください:", - "copy": "コピー", - "settings": "設定", - "integrations": "連携", - "add_integration": "連携を追加", - "you_are_now_editing_only": "現在、次のみを編集しています", - "tag_a_company": "会社をタグ付け", - "video_length_is_invalid_must_be_up_to": "動画の長さが無効です。最大", - "seconds": "秒までです", - "this_feature_available_only_for_photos": "この機能は写真のみで利用可能です。デフォルトの音楽が追加され、後で変更できます。", - "allow_user_to": "ユーザーに許可する:", - "your_video_will_be_labeled_promotional": "あなたの動画には「プロモーションコンテンツ」とラベルが付きます。", - "this_cannot_be_changed_once_posted": "一度投稿すると、これは変更できません。", - "turn_on_to_disclose_video_promotes": "この動画が何らかの価値と引き換えに商品やサービスを宣伝していることを開示するにはオンにしてください。動画は自分自身、第三者、またはその両方を宣伝している場合があります。", - "you_are_promoting_yourself": "あなた自身またはあなたのブランドを宣伝しています。", - "this_video_will_be_classified_brand_organic": "この動画はブランドオーガニックとして分類されます。", - "you_are_promoting_another_brand": "他のブランドまたは第三者を宣伝しています。", - "this_video_will_be_classified_branded_content": "この動画はブランデッドコンテンツとして分類されます。", - "by_posting_you_agree_to_tiktoks": "投稿することで、TikTokの規約に同意したことになります", - "music_usage_confirmation": "音楽使用確認", - "branded_content_policy": "ブランドコンテンツポリシー", - "select_1": "--選択--", - "select_flair": "--フレアを選択--", - "link": "リンク", - "add_subreddit": "サブレディットを追加", - "please_add_at_least_one_subreddit": "少なくとも1つのサブレディットを追加してください", - "add_community": "コミュニティを追加", - "select_post_type": "投稿タイプを選択...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "あなたのLinkedInページに接続されているビジネスが見つかりませんでした。", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "このダイアログを閉じて、新しいページを作成し、再度新しいチャンネルを追加してください。", - "select_linkedin_page": "LinkedInページを選択:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "選択したページに接続されているビジネスが見つかりませんでした。", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "すべてのページとすべてのビジネスを接続することをおすすめします。", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "このダイアログを閉じて、連携を削除し、再度新しいチャンネルを追加してください。", - "select_instagram_account": "Instagramアカウントを選択:", - "select_page": "ページを選択:", - "generate_image_with_ai": "AIで画像を生成", - "reconnect_channel": "チャンネルを再接続", - "update_credentials": "認証情報を更新", - "additional_settings": "追加設定", - "change_bot": "ボットを変更", - "move_add_to_customer": "顧客に移動/追加", - "edit_time_slots": "時間枠を編集", - "enable_channel": "チャンネルを有効にする", - "disable_channel": "チャンネルを無効にする", - "add": "追加", - "short_post": "短い投稿", - "long_post": "長い投稿", - "a_thread_with_short_posts": "短い投稿のスレッド", - "a_thread_with_long_posts": "長い投稿のスレッド", - "personal_voice_i_am_happy_to_announce": "個人の声(「お知らせできて嬉しいです」)", - "company_voice_we_are_happy_to_announce": "会社の声(「お知らせできて嬉しいです」)", - "generate": "生成", - "generate_posts": "投稿を生成", - "purchase_a_life_time_pro_account_with_sol_199": "SOL($199)で生涯PROアカウントを購入できます。この購入には返金がないことをご承知おきください。", - "purchase_now": "今すぐ購入", - "pay_today": "今日支払う", - "we_are_sorry_to_see_you_go": "ご利用いただけなくなり残念です :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "どのように改善できたか、簡単に教えていただけますか?", - "cancel_subscription": "サブスクリプションを解約", - "plans": "プラン", - "monthly": "月額", - "yearly": "年額", - "reactivate_subscription": "サブスクリプションを再開", - "update_payment_method_invoices_history": "支払い方法の更新 / 請求履歴", - "cancel_subscription_1": "サブスクリプションを解約", - "your_subscription_will_be_canceled_at": "サブスクリプションは次の日付で解約されます:", - "you_will_never_be_charged_again": "今後、料金が請求されることはありません", - "current_package": "現在のパッケージ:", - "next_package": "次のパッケージ:", - "claim": "請求", - "frequently_asked_questions": "よくある質問", - "autopost": "自動投稿", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "自動投稿は、RSSの新しいアイテムを自動的にソーシャルメディアに投稿できます", - "title": "タイトル", - "add_an_autopost": "自動投稿を追加", - "post_content": "投稿内容", - "sign_up": "サインアップ", - "or": "または", - "by_registering_you_agree_to_our": "登録することで、あなたは当社の", - "and": "および", - "terms_of_service": "利用規約", - "privacy_policy": "プライバシーポリシー", - "create_account": "アカウントを作成", - "already_have_an_account": "すでにアカウントをお持ちですか?", - "sign_in": "サインイン", - "sign_in_1": "サインイン", - "don_t_have_an_account": "アカウントをお持ちでないですか?", - "forgot_password": "パスワードをお忘れですか", - "forgot_password_1": "パスワードをお忘れですか", - "send_password_reset_email": "パスワードリセットメールを送信", - "go_back_to_login": "ログイン画面に戻る", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "パスワードをリセットするためのリンクを記載したメールを送信しました。", - "change_password": "パスワードを変更", - "we_successfully_reset_your_password_you_can_now_login_with_your": "パスワードのリセットが完了しました。新しいパスワードでログインできます。", - "click_here_to_go_back_to_login": "ログイン画面に戻るにはこちらをクリックしてください", - "activate_your_account": "アカウントを有効化", - "thank_you_for_registering": "ご登録ありがとうございます!", - "please_check_your_email_to_activate_your_account": "アカウントを有効化するためにメールをご確認ください。", - "sign_in_with": "でサインイン", - "continue_with_google": "Googleで続行", - "sign_in_with_github": "GitHubでサインイン", - "continue_with_farcaster": "Farcasterで続行", - "continue_with_your_wallet": "ウォレットで続行", - "stars_per_day": "1日あたりのスター数", - "media": "メディア", - "check_launch": "ローンチを確認", - "load_your_github_repository_from_settings_to_see_analytics": "設定からGitHubリポジトリを読み込むと分析が表示されます", - "stars": "スター", - "processing_stars": "スターを処理中...", - "forks": "フォーク", - "registration_is_disabled": "登録は無効になっています", - "login_instead": "代わりにログイン", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "会話を選択してチャットを始めましょう。", - "adding_channel_redirecting_you": "チャンネルを追加中、リダイレクトしています", - "could_not_add_provider": "プロバイダーを追加できませんでした。", - "you_are_being_redirected_back": "元のページにリダイレクトしています", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "問題が発生しています。ページをリフレッシュしてみてください。", - "post_not_found": "投稿が見つかりません", - "publication_date": "公開日:", - "analytics": "アナリティクス", - "launches": "ローンチ", - "plugs": "プラグ", - "billing": "請求", - "affiliate": "アフィリエイト", - "monday": "月曜日", - "tuesday": "火曜日", - "wednesday": "水曜日", - "thursday": "木曜日", - "friday": "金曜日", - "saturday": "土曜日", - "sunday": "日曜日", - "can_t_change_date_remove_post_from_publication": "日付を変更できません。投稿を公開から削除してください。", - "predicted_github_trending_change": "GitHubトレンド予測の変化", - "duplicate_post": "重複した投稿", - "preview_post": "投稿をプレビュー", - "post_statistics": "投稿の統計", - "draft": "下書き", - "week_number": "第{{number}}週", - "top_title_edit_webhook": "Webhookを編集", - "top_title_add_webhook": "Webhookを追加", - "top_title_oh_no": "おっと", - "top_title_auto_plug": "自動プラグ:{{title}}", - "top_title_edit_autopost": "自動投稿を編集", - "top_title_add_autopost": "自動投稿を追加", - "top_title_send_a_new_offer": "新しいオファーを送信", - "top_title_media_library": "メディアライブラリ", - "top_title_add_signature": "署名を追加", - "top_title_send_a_message_to": "{{name}}にメッセージを送信", - "top_title_configure_provider": "プロバイダーを設定", - "top_title_add_member": "メンバーを追加", - "top_title_change_bot_picture": "ボットの画像を変更", - "top_title_create_a_new_tag": "新しいタグを作成", - "top_title_select_company": "会社を選択", - "top_title_additional_settings": "追加設定", - "top_title_time_table_slots": "タイムテーブル枠", - "top_title_design_media": "メディアをデザイン", - "top_title_edit_post": "投稿を編集", - "top_title_create_post": "投稿を作成", - "top_title_move__add_to_customer": "顧客に移動/追加", - "top_title_add_api_key_for": "{{name}}のAPIキーを追加", - "top_title_instance_url": "インスタンスURL", - "top_title_custom_url": "カスタムURL", - "top_title_add_channel": "チャンネルを追加", - "top_title_add_telegram": "Telegramを追加", - "top_title_add_wrapcast": "Wrapcastを追加", - "top_title_comments_for": "{{date}}のコメント", - "top_title_edit_signature": "署名を編集", - "label_name": "名前", - "label_url": "URL", - "label_title": "タイトル", - "label_subtitle": "サブタイトル", - "label_email": "メールアドレス", - "label_full_name": "氏名", - "label_password": "パスワード", - "label_confirm_password": "パスワードの確認", - "label_api_key": "APIキー", - "label_instance_url": "インスタンスURL", - "label_custom_url": "カスタムURL", - "label_feedback": "フィードバック", - "label_bio": "自己紹介", - "label_role": "役割", - "label_country": "国", - "label_audience_size": "全プラットフォームでのオーディエンス数", - "label_pick_time": "時間を選択", - "label_nickname": "ニックネーム", - "label_write_anything": "何でも書いてください", - "label_output_format": "出力形式", - "label_add_pictures": "画像を追加しますか?", - "label_hour": "時", - "label_minutes": "分", - "label_select_publication": "公開先を選択", - "label_canonical_link": "正規リンク", - "label_cover_picture": "カバー画像", - "label_tags": "タグ", - "label_topics": "トピック", - "label_tags_maximum_4": "タグ(最大4つ)", - "label_attachments": "添付ファイル", - "label_type": "タイプ", - "label_thumbnail": "サムネイル", - "label_who_can_see_this_video": "この動画を見られる人", - "label_content_posting_method": "コンテンツ投稿方法", - "label_auto_add_music": "自動で音楽を追加", - "label_duet": "デュエット", - "label_stitch": "ステッチ", - "label_comments": "コメント", - "label_disclose_video_content": "動画内容を公開する", - "label_your_brand": "あなたのブランド", - "label_branded_content": "ブランドコンテンツ", - "label_subreddit": "サブレディット", - "label_flair": "フレア", - "label_media": "メディア", - "label_search_subreddit": "サブレディットを検索", - "label_delay": "遅延", - "label_post_type": "投稿タイプ", - "label_collaborators": "共同作成者(最大3名)- アカウントは非公開にできません", - "label_community": "コミュニティ", - "label_search_community": "コミュニティを検索", - "label_channel": "チャンネル", - "label_search_channel": "チャンネルを検索", - "label_select_channel": "チャンネルを選択", - "label_new_password": "新しいパスワード", - "label_repeat_password": "パスワードを再入力", - "label_platform": "プラットフォーム", - "label_price_per_post": "投稿ごとの価格", - "label_integrations": "連携", - "label_code": "コード", - "label_should_sync_last_post": "現在の最新投稿を同期しますか?", - "label_when_post": "いつ投稿しますか?", - "label_autogenerate_content": "コンテンツを自動生成", - "label_generate_picture": "画像を生成しますか?", - "label_company": "会社", - "label_tag_color": "タグの色", - "label_select_board": "ボードを選択", - "label_select_organization": "組織を選択", - "label_auto_add_signature": "署名を自動追加しますか?", - "enable_color_picker": "カラーピッカーを有効にする", - "cancel_the_color_picker": "カラーピッカーをキャンセル", - "no_content_yet": "まだコンテンツがありません", - "write_your_reply": "投稿内容を入力してください...", - "add_a_tag": "タグを追加", - "add_to_calendar": "カレンダーに追加", - "select_channels_from_circles": "上のサークルからチャンネルを選択してください", - "not_matching_order": "順序が一致しません", - "submit_for_order": "注文を送信", - "schedule": "スケジュール", - "update": "更新", - "attachments": "添付ファイル", - "tags": "タグ", - "public_to_everyone": "全員に公開", - "mutual_follow_friends": "相互フォローの友達", - "follower_of_creator": "クリエイターのフォロワー", - "self_only": "自分のみ", - "post_content_directly_to_tiktok": "コンテンツを直接TikTokに投稿する", - "upload_content_to_tiktok_without_posting": "投稿せずにコンテンツをTikTokにアップロードする", - "choose_upload_without_posting_description": "公開前にTikTokアプリ内でコンテンツを確認・編集したい場合は「投稿せずにアップロード」を選択してください。これにより、TikTokの編集ツールを利用でき、投稿前に最終調整が可能です。", - "faq_am_i_going_to_be_charged_by_postiz": "Postizから料金が請求されますか?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "クレジットカード情報の確認のため、Postizは2ドルを一時的に保持し、すぐに解除します", - "faq_can_i_trust_postiz_gitroom": "Postizは信頼できますか?", - "faq_postiz_gitroom_is_proudly_open_source": "Postizは誇りを持ってオープンソースです!私たちは倫理的で透明性のある文化を信じており、Postizは永遠に存続します。全てのコードを確認したり、個人プロジェクトで利用したりできます。オープンソースリポジトリを見るには、<a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">こちらをクリック</a>してください。", - "faq_what_are_channels": "チャンネルとは何ですか?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postizでは、さまざまなチャンネル間で投稿のスケジューリングが可能です。\nチャンネルとは、投稿をスケジュールできる配信プラットフォームのことです。\n例えば、X、Facebook、Instagram、TikTok、YouTube、Reddit、Linkedin、Dribbble、Threads、Pinterestなどで投稿をスケジュールできます。", - "faq_what_are_team_members": "チームメンバーとは何ですか?", - "faq_if_you_have_a_team_with_multiple_members": "複数のメンバーがいるチームの場合、ワークスペースに招待して投稿の共同作業や個人チャンネルの追加ができます", - "faq_what_is_ai_auto_complete": "AI自動補完とは何ですか?", - "faq_we_automate_chatgpt_to_help_you_write": "ChatGPTを自動化して、SNS投稿や記事の作成をサポートします。", - "enter_email": "メールアドレスを入力", - "are_you_sure": "本当に宜しいですか?", - "yes_delete_it": "はい、削除します!", - "no_cancel": "いいえ、キャンセルします", - "are_you_sure_you_want_to_delete": "{{name}} を削除してもよろしいですか?", - "are_you_sure_you_want_to_delete_the_image": "この画像を削除してもよろしいですか?", - "are_you_sure_you_want_to_logout": "本当にログアウトしますか?", - "yes_logout": "はい、ログアウトします", - "are_you_sure_you_want_to_delete_this_slot": "このスロットを削除してもよろしいですか?", - "are_you_sure_you_want_to_delete_this_subreddit": "このSubredditを削除してもよろしいですか?", - "are_you_sure_you_want_to_close_the_window": "ウィンドウを閉じてもよろしいですか?", - "yes_close": "はい、閉じます", - "link_copied_to_clipboard": "リンクがクリップボードにコピーされました", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "このモーダルを閉じてもよろしいですか?(すべてのデータが失われます)", - "yes_close_it": "はい、閉じます!", - "uploading_pictures": "画像をアップロード中...", - "agent_starting": "エージェントを起動中", - "researching_your_content": "コンテンツを調査中...", - "understanding_the_category": "カテゴリを理解中...", - "finding_the_topic": "トピックを検索中...", - "finding_popular_posts_to_match_with": "マッチする人気投稿を検索中...", - "generating_hook": "フックを生成中...", - "generating_content": "コンテンツを生成中...", - "generating_pictures": "画像を生成中...", - "finding_time_to_post": "投稿する時間を検索中...", - "write_anything": "何でも書いてください", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "ご自由に何でも書くことができ、リンクも追加できます。リサーチは私たちが行います…", - "output_format": "出力形式", - "add_pictures": "画像を追加しますか?", - "7_days": "7日間", - "30_days": "30日間", - "90_days": "90日間", - "start_7_days_free_trial": "7日間の無料トライアルを開始", - "change_language": "言語を変更", - "that_a_wrap": "以上で終了です!\n\nこのスレッドを楽しんでいただけたなら:\n\n1. @{{username}} をフォローして、さらに多くの投稿をご覧ください\n2. 下のツイートをリツイートして、このスレッドをあなたのフォロワーと共有してください\n", - "post_as_images_carousel": "画像カルーセルとして投稿", - "save_set": "セットを保存", - "separate_post": "投稿を複数に分割", - "label_who_can_reply_to_this_post": "この投稿に返信できる人", - "delete_integration": "連携を削除", - "start_writing_your_post": "プレビュー用に投稿を書き始めてください" + "calendar": "カレンダー", + "webhooks": "ウェブフック", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "ウェブフックは、Postizで何かが起こったときにHTTPリクエストを通じて通知を受け取る方法です。", + "name": "名前", + "url": "URL", + "edit": "編集", + "delete": "削除", + "add_a_webhook": "ウェブフックを追加", + "save": "保存", + "send_test": "テスト送信", + "select_role": "役割を選択", + "video_made_with_ai": "AIで作成された動画", + "please_add_at_least": "少なくとも20文字を追加してください", + "send_invitation_via_email": "メールで招待を送信しますか?", + "global_settings": "グローバル設定", + "copy_id": "チャンネルIDをコピー", + "team_members": "チームメンバー", + "invite_your_assistant_or_team_member_to_manage_your_account": "アシスタントやチームメンバーを招待してアカウントを管理してもらいましょう", + "remove": "削除", + "add_another_member": "メンバーを追加", + "signatures": "署名", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "投稿で使用するための署名をアカウントに追加できます。", + "content": "内容", + "auto_add": "自動追加?", + "actions": "操作", + "use_signature": "署名を使用", + "add_a_signature": "署名を追加", + "no": "いいえ", + "yes": "はい", + "your_git_repository": "あなたのGitリポジトリ", + "connect_your_github_repository_to_receive_updates_and_analytics": "アップデートと分析を受け取るためにGitHubリポジトリを接続してください", + "connected": "接続済み:", + "disconnect": "切断", + "connect_your_repository": "リポジトリを接続", + "cancel": "キャンセル", + "connect": "接続", + "public_api": "パブリックAPI", + "check_n8n": "Postiz用のカスタムN8Nノードをご覧ください。", + "use_postiz_api_to_integrate_with_your_tools": "Postiz APIを使ってあなたのツールと連携しましょう。", + "read_how_to_use_it_over_the_documentation": "ドキュメントで使い方を確認してください。", + "reveal": "表示", + "copy_key": "キーをコピー", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "投稿をより迅速にスケジュールするために、Postiz MCPサーバーをクライアント(HTTPストリーミング)に接続しましょう!", + "share_with_a_client": "クライアントと共有", + "post": "投稿", + "comments": "コメント", + "user": "ユーザー", + "login_register_to_add_comments": "コメントを追加するにはログイン/登録してください", + "status": "ステータス:", + "there_are_not_plugs_matching_your_channels": "あなたのチャンネルに一致するプラグはありません", + "you_have_to_add_x_or_linkedin_or_threads": "XまたはLinkedInまたはThreadsを追加する必要があります", + "go_to_the_calendar_to_add_channels": "チャンネルを追加するにはカレンダーに移動してください", + "channels": "チャンネル", + "activate": "有効化", + "this_channel_needs_to_be_refreshed": "このチャンネルは更新が必要です。", + "click_here_to_refresh": "ここをクリックして更新", + "can_t_show_analytics_yet": "まだ分析情報を表示できません", + "you_have_to_add_social_media_channels": "ソーシャルメディアチャンネルを追加する必要があります", + "supported": "対応:", + "step": "ステップ", + "skip_onboarding": "オンボーディングをスキップ", + "onboarding": "オンボーディング", + "next": "次へ", + "you_are_done_from_here_you_can": "完了しました。ここから次のことができます:", + "view_analytics": "分析情報を見る", + "schedule_a_new_post": "新しい投稿を予約する", + "to_sell_posts_you_would_have_to": "投稿を販売するには、次のことが必要です:", + "1_connect_at_least_one_channel": "1. 少なくとも1つのチャンネルを接続する", + "2_connect_you_bank_account": "2. 銀行口座を接続する", + "go_back_to_connect_channels": "チャンネル接続に戻る", + "move_to_the_seller_page_to_connect_you_bank": "販売者ページに移動して銀行口座を接続する", + "connect_channels": "チャンネルを接続", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "ソーシャルメディアや出版ウェブサイトのチャンネルを接続して、\n後で投稿を予約しましょう", + "social": "ソーシャル", + "publishing_platforms": "出版プラットフォーム", + "no_channels": "まだチャンネルがありません", + "connect_your_accounts": "ソーシャルアカウントを連携して、スケジューリング、投稿、分析をすべて一か所で始めましょう。", + "notifications": "通知", + "no_notifications": "通知はありません", + "send_message": "メッセージを送信", + "mar_28": "3月28日", + "there_are_no_messages_yet": "まだメッセージはありません。", + "checkout_the_marketplace": "マーケットプレイスをチェック", + "go_to_marketplace": "マーケットプレイスへ行く", + "all_messages": "すべてのメッセージ", + "previous": "前へ", + "select_or_upload_pictures_maximum_5_at_a_time": "画像を選択またはアップロード(最大5枚まで同時に)", + "you_can_also_drag_drop_pictures": "画像をドラッグ&ドロップすることもできます", + "you_don_t_have_any_assets_yet": "まだアセットがありません。", + "click_the_button_below_to_upload_one": "下のボタンをクリックしてアップロードしてください", + "click_the_button_below_to_upload_other": "下のボタンをクリックして複数アップロード", + "add_selected_media": "選択したメディアを追加", + "insert_media": "メディアを挿入", + "design_media": "メディアをデザイン", + "select": "選択", + "editor": "エディター", + "clear": "クリア", + "order_completed": "注文完了", + "the_order_has_been_completed": "注文が完了しました", + "post_has_been_published": "投稿が公開されました", + "url_1": "URL:", + "new_offer": "新しいオファー", + "platform": "プラットフォーム", + "posts": "投稿", + "pay_accept_offer": "支払い&オファーを承諾", + "accepted": "承諾済み", + "post_draft": "下書き投稿", + "revision_needed": "修正が必要", + "approve": "承認する", + "preview": "プレビュー", + "revision_requested": "修正依頼済み", + "accepted_1": "承認済み", + "cancelled_by_the_seller": "出品者によってキャンセルされました", + "please_select_your_country_where_your_business_is": "ビジネスを行っている国を選択してください。", + "select_country": "--国を選択--", + "connect_bank_account": "銀行口座を接続", + "seller_mode": "出品者モード", + "active": "有効", + "details": "詳細", + "audience_size": "オーディエンス規模", + "add_another_platform": "別のプラットフォームを追加", + "send_an_offer_for": "$でオファーを送信", + "complete_order_and_pay_early": "注文を完了して早期支払い", + "order_in_progress": "注文進行中", + "create_a_new_offer": "新しいオファーを作成", + "orders": "注文", + "price": "価格", + "state": "状態", + "showing": "表示中", + "to": "から", + "from": "まで", + "results": "結果", + "content_writer": "コンテンツライター", + "influencer": "インフルエンサー", + "request_service": "サービスを依頼する", + "the_marketplace_is_not_opened_yet": "マーケットプレイスはまだオープンしていません", + "check_again_soon": "またすぐにご確認ください!", + "filter": "フィルター", + "result": "結果", + "seller": "販売者", + "buyer": "購入者", + "discord_support": "Discordサポート", + "teams": "チーム", + "webhooks_1": "ウェブフック", + "auto_post": "自動投稿", + "logout_from": "ログアウト:", + "join_10000_entrepreneurs_who_use_postiz": "Postizを利用している10,000人以上の起業家に参加しましょう", + "to_manage_all_your_social_media_channels": "すべてのソーシャルメディアチャンネルを管理するために", + "100_no_risk_trial": "100%リスクなしのトライアル", + "pay_nothing_for_the_first_7_days": "最初の7日間は無料", + "cancel_anytime_hassle_free": "いつでも簡単にキャンセル可能", + "add_free_subscription": "-- 無料サブスクリプションを追加 --", + "currently_impersonating": "現在なりすまし中", + "user_1": "ユーザー:", + "drag_n_drop_some_files_here": "ここにファイルをドラッグ&ドロップしてください", + "add_time_slot": "時間枠を追加", + "add_slot": "スロットを追加", + "cancel_publication": "公開をキャンセル", + "statistics": "統計", + "loading": "読み込み中", + "short_link": "短縮リンク", + "original_link": "元のリンク", + "clicks": "クリック数", + "selected_customer": "選択された顧客", + "customer": "顧客:", + "repeat_post_every": "投稿を繰り返す間隔...", + "use_this_media": "このメディアを使用", + "create_new_post": "投稿を作成", + "update_post": "既存の投稿を更新", + "merge_comments_into_one_post": "コメントを1つの投稿にまとめる", + "accounts_that_will_engage": "エンゲージするアカウント:", + "day": "日", + "week": "週", + "month": "月", + "remove_from_customer": "顧客から削除", + "show_more": "+もっと表示", + "show_less": "-表示を減らす", + "upload": "アップロード", + "ai": "AI", + "add_channel": "チャンネルを追加", + "add_platform": "プラットフォームを追加", + "articles": "記事", + "add_comment": "コメントを追加", + "add_post": "スレッドに投稿を追加", + "add_comment_or_post": "コメント/投稿を追加", + "you_are_in_global_editing_mode": "グローバル編集モードになっています", + "the_post_should_be_at_least_6_characters_long": "投稿は6文字以上である必要があります", + "are_you_sure_you_want_to_delete_post": "この投稿を本当に削除しますか?", + "post_deleted_successfully": "投稿が正常に削除されました", + "delete_post": "投稿を削除", + "save_as_draft": "下書きとして保存", + "post_now": "今すぐ投稿", + "please_add": "追加してください", + "to_your_telegram_group_channel_and_click_here": "あなたのTelegramグループ/チャンネルに追加し、ここをクリックしてください:", + "connect_telegram": "Telegramを接続", + "please_add_the_following_command_in_your_chat": "チャットに次のコマンドを追加してください:", + "copy": "コピー", + "settings": "設定", + "integrations": "連携", + "add_integration": "連携を追加", + "you_are_now_editing_only": "現在、次のみを編集しています", + "tag_a_company": "会社をタグ付け", + "video_length_is_invalid_must_be_up_to": "動画の長さが無効です。最大", + "seconds": "秒までです", + "this_feature_available_only_for_photos": "この機能は写真のみで利用可能です。デフォルトの音楽が追加され、後で変更できます。", + "allow_user_to": "ユーザーに許可する:", + "your_video_will_be_labeled_promotional": "あなたの動画には「プロモーションコンテンツ」とラベルが付きます。", + "this_cannot_be_changed_once_posted": "一度投稿すると、これは変更できません。", + "turn_on_to_disclose_video_promotes": "この動画が何らかの価値と引き換えに商品やサービスを宣伝していることを開示するにはオンにしてください。動画は自分自身、第三者、またはその両方を宣伝している場合があります。", + "you_are_promoting_yourself": "あなた自身またはあなたのブランドを宣伝しています。", + "this_video_will_be_classified_brand_organic": "この動画はブランドオーガニックとして分類されます。", + "you_are_promoting_another_brand": "他のブランドまたは第三者を宣伝しています。", + "this_video_will_be_classified_branded_content": "この動画はブランデッドコンテンツとして分類されます。", + "by_posting_you_agree_to_tiktoks": "投稿することで、TikTokの規約に同意したことになります", + "music_usage_confirmation": "音楽使用確認", + "branded_content_policy": "ブランドコンテンツポリシー", + "select_1": "--選択--", + "select_flair": "--フレアを選択--", + "link": "リンク", + "add_subreddit": "サブレディットを追加", + "please_add_at_least_one_subreddit": "少なくとも1つのサブレディットを追加してください", + "add_community": "コミュニティを追加", + "select_post_type": "投稿タイプを選択...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "あなたのLinkedInページに接続されているビジネスが見つかりませんでした。", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "このダイアログを閉じて、新しいページを作成し、再度新しいチャンネルを追加してください。", + "select_linkedin_page": "LinkedInページを選択:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "選択したページに接続されているビジネスが見つかりませんでした。", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "すべてのページとすべてのビジネスを接続することをおすすめします。", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "このダイアログを閉じて、連携を削除し、再度新しいチャンネルを追加してください。", + "select_instagram_account": "Instagramアカウントを選択:", + "select_page": "ページを選択:", + "generate_image_with_ai": "AIで画像を生成", + "reconnect_channel": "チャンネルを再接続", + "update_credentials": "認証情報を更新", + "additional_settings": "追加設定", + "change_bot": "ボットを変更", + "move_add_to_customer": "顧客に移動/追加", + "edit_time_slots": "時間枠を編集", + "enable_channel": "チャンネルを有効にする", + "disable_channel": "チャンネルを無効にする", + "add": "追加", + "short_post": "短い投稿", + "long_post": "長い投稿", + "a_thread_with_short_posts": "短い投稿のスレッド", + "a_thread_with_long_posts": "長い投稿のスレッド", + "personal_voice_i_am_happy_to_announce": "個人の声(「お知らせできて嬉しいです」)", + "company_voice_we_are_happy_to_announce": "会社の声(「お知らせできて嬉しいです」)", + "generate": "生成", + "generate_posts": "投稿を生成", + "purchase_a_life_time_pro_account_with_sol_199": "SOL($199)で生涯PROアカウントを購入できます。この購入には返金がないことをご承知おきください。", + "purchase_now": "今すぐ購入", + "pay_today": "今日支払う", + "we_are_sorry_to_see_you_go": "ご利用いただけなくなり残念です :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "どのように改善できたか、簡単に教えていただけますか?", + "cancel_subscription": "サブスクリプションを解約", + "plans": "プラン", + "monthly": "月額", + "yearly": "年額", + "reactivate_subscription": "サブスクリプションを再開", + "update_payment_method_invoices_history": "支払い方法の更新 / 請求履歴", + "cancel_subscription_1": "サブスクリプションを解約", + "your_subscription_will_be_canceled_at": "サブスクリプションは次の日付で解約されます:", + "you_will_never_be_charged_again": "今後、料金が請求されることはありません", + "current_package": "現在のパッケージ:", + "next_package": "次のパッケージ:", + "claim": "請求", + "frequently_asked_questions": "よくある質問", + "autopost": "自動投稿", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "自動投稿は、RSSの新しいアイテムを自動的にソーシャルメディアに投稿できます", + "title": "タイトル", + "add_an_autopost": "自動投稿を追加", + "post_content": "投稿内容", + "sign_up": "サインアップ", + "or": "または", + "by_registering_you_agree_to_our": "登録することで、あなたは当社の", + "and": "および", + "terms_of_service": "利用規約", + "privacy_policy": "プライバシーポリシー", + "create_account": "アカウントを作成", + "already_have_an_account": "すでにアカウントをお持ちですか?", + "sign_in": "サインイン", + "sign_in_1": "サインイン", + "don_t_have_an_account": "アカウントをお持ちでないですか?", + "forgot_password": "パスワードをお忘れですか", + "forgot_password_1": "パスワードをお忘れですか", + "send_password_reset_email": "パスワードリセットメールを送信", + "go_back_to_login": "ログイン画面に戻る", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "パスワードをリセットするためのリンクを記載したメールを送信しました。", + "change_password": "パスワードを変更", + "we_successfully_reset_your_password_you_can_now_login_with_your": "パスワードのリセットが完了しました。新しいパスワードでログインできます。", + "click_here_to_go_back_to_login": "ログイン画面に戻るにはこちらをクリックしてください", + "activate_your_account": "アカウントを有効化", + "thank_you_for_registering": "ご登録ありがとうございます!", + "please_check_your_email_to_activate_your_account": "アカウントを有効化するためにメールをご確認ください。", + "sign_in_with": "でサインイン", + "continue_with_google": "Googleで続行", + "sign_in_with_github": "GitHubでサインイン", + "continue_with_farcaster": "Farcasterで続行", + "continue_with_your_wallet": "ウォレットで続行", + "stars_per_day": "1日あたりのスター数", + "media": "メディア", + "check_launch": "ローンチを確認", + "load_your_github_repository_from_settings_to_see_analytics": "設定からGitHubリポジトリを読み込むと分析が表示されます", + "stars": "スター", + "processing_stars": "スターを処理中...", + "forks": "フォーク", + "registration_is_disabled": "登録は無効になっています", + "login_instead": "代わりにログイン", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "会話を選択してチャットを始めましょう。", + "adding_channel_redirecting_you": "チャンネルを追加中、リダイレクトしています", + "could_not_add_provider": "プロバイダーを追加できませんでした。", + "you_are_being_redirected_back": "元のページにリダイレクトしています", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "問題が発生しています。ページをリフレッシュしてみてください。", + "post_not_found": "投稿が見つかりません", + "publication_date": "公開日:", + "analytics": "アナリティクス", + "launches": "ローンチ", + "plugs": "プラグ", + "billing": "請求", + "affiliate": "アフィリエイト", + "monday": "月曜日", + "tuesday": "火曜日", + "wednesday": "水曜日", + "thursday": "木曜日", + "friday": "金曜日", + "saturday": "土曜日", + "sunday": "日曜日", + "can_t_change_date_remove_post_from_publication": "日付を変更できません。投稿を公開から削除してください。", + "predicted_github_trending_change": "GitHubトレンド予測の変化", + "duplicate_post": "重複した投稿", + "preview_post": "投稿をプレビュー", + "post_statistics": "投稿の統計", + "draft": "下書き", + "week_number": "第{{number}}週", + "top_title_edit_webhook": "Webhookを編集", + "top_title_add_webhook": "Webhookを追加", + "top_title_oh_no": "おっと", + "top_title_auto_plug": "自動プラグ:{{title}}", + "top_title_edit_autopost": "自動投稿を編集", + "top_title_add_autopost": "自動投稿を追加", + "top_title_send_a_new_offer": "新しいオファーを送信", + "top_title_media_library": "メディアライブラリ", + "top_title_add_signature": "署名を追加", + "top_title_send_a_message_to": "{{name}}にメッセージを送信", + "top_title_configure_provider": "プロバイダーを設定", + "top_title_add_member": "メンバーを追加", + "top_title_change_bot_picture": "ボットの画像を変更", + "top_title_create_a_new_tag": "新しいタグを作成", + "top_title_select_company": "会社を選択", + "top_title_additional_settings": "追加設定", + "top_title_time_table_slots": "タイムテーブル枠", + "top_title_design_media": "メディアをデザイン", + "top_title_edit_post": "投稿を編集", + "top_title_create_post": "投稿を作成", + "top_title_move__add_to_customer": "顧客に移動/追加", + "top_title_add_api_key_for": "{{name}}のAPIキーを追加", + "top_title_instance_url": "インスタンスURL", + "top_title_custom_url": "カスタムURL", + "top_title_add_channel": "チャンネルを追加", + "top_title_add_telegram": "Telegramを追加", + "top_title_add_wrapcast": "Wrapcastを追加", + "top_title_comments_for": "{{date}}のコメント", + "top_title_edit_signature": "署名を編集", + "label_name": "名前", + "label_url": "URL", + "label_title": "タイトル", + "label_subtitle": "サブタイトル", + "label_email": "メールアドレス", + "label_full_name": "氏名", + "label_password": "パスワード", + "label_confirm_password": "パスワードの確認", + "label_api_key": "APIキー", + "label_instance_url": "インスタンスURL", + "label_custom_url": "カスタムURL", + "label_feedback": "フィードバック", + "label_bio": "自己紹介", + "label_role": "役割", + "label_country": "国", + "label_audience_size": "全プラットフォームでのオーディエンス数", + "label_pick_time": "時間を選択", + "label_nickname": "ニックネーム", + "label_write_anything": "何でも書いてください", + "label_output_format": "出力形式", + "label_add_pictures": "画像を追加しますか?", + "label_hour": "時", + "label_minutes": "分", + "label_select_publication": "公開先を選択", + "label_canonical_link": "正規リンク", + "label_cover_picture": "カバー画像", + "label_tags": "タグ", + "label_topics": "トピック", + "label_tags_maximum_4": "タグ(最大4つ)", + "label_attachments": "添付ファイル", + "label_type": "タイプ", + "label_thumbnail": "サムネイル", + "label_who_can_see_this_video": "この動画を見られる人", + "label_content_posting_method": "コンテンツ投稿方法", + "label_auto_add_music": "自動で音楽を追加", + "label_duet": "デュエット", + "label_stitch": "ステッチ", + "label_comments": "コメント", + "label_disclose_video_content": "動画内容を公開する", + "label_your_brand": "あなたのブランド", + "label_branded_content": "ブランドコンテンツ", + "label_subreddit": "サブレディット", + "label_flair": "フレア", + "label_media": "メディア", + "label_search_subreddit": "サブレディットを検索", + "label_delay": "遅延", + "label_post_type": "投稿タイプ", + "label_collaborators": "共同作成者(最大3名)- アカウントは非公開にできません", + "label_community": "コミュニティ", + "label_search_community": "コミュニティを検索", + "label_channel": "チャンネル", + "label_search_channel": "チャンネルを検索", + "label_select_channel": "チャンネルを選択", + "label_new_password": "新しいパスワード", + "label_repeat_password": "パスワードを再入力", + "label_platform": "プラットフォーム", + "label_price_per_post": "投稿ごとの価格", + "label_integrations": "連携", + "label_code": "コード", + "label_should_sync_last_post": "現在の最新投稿を同期しますか?", + "label_when_post": "いつ投稿しますか?", + "label_autogenerate_content": "コンテンツを自動生成", + "label_generate_picture": "画像を生成しますか?", + "label_company": "会社", + "label_tag_color": "タグの色", + "label_select_board": "ボードを選択", + "label_select_organization": "組織を選択", + "label_auto_add_signature": "署名を自動追加しますか?", + "enable_color_picker": "カラーピッカーを有効にする", + "cancel_the_color_picker": "カラーピッカーをキャンセル", + "no_content_yet": "まだコンテンツがありません", + "write_your_reply": "投稿内容を入力してください...", + "add_a_tag": "タグを追加", + "add_to_calendar": "カレンダーに追加", + "select_channels_from_circles": "上のサークルからチャンネルを選択してください", + "not_matching_order": "順序が一致しません", + "submit_for_order": "注文を送信", + "schedule": "スケジュール", + "update": "更新", + "attachments": "添付ファイル", + "tags": "タグ", + "public_to_everyone": "全員に公開", + "mutual_follow_friends": "相互フォローの友達", + "follower_of_creator": "クリエイターのフォロワー", + "self_only": "自分のみ", + "post_content_directly_to_tiktok": "コンテンツを直接TikTokに投稿する", + "upload_content_to_tiktok_without_posting": "投稿せずにコンテンツをTikTokにアップロードする", + "choose_upload_without_posting_description": "公開前にTikTokアプリ内でコンテンツを確認・編集したい場合は「投稿せずにアップロード」を選択してください。これにより、TikTokの編集ツールを利用でき、投稿前に最終調整が可能です。", + "faq_am_i_going_to_be_charged_by_postiz": "Postizから料金が請求されますか?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "クレジットカード情報の確認のため、Postizは2ドルを一時的に保持し、すぐに解除します", + "faq_can_i_trust_postiz_gitroom": "Postizは信頼できますか?", + "faq_postiz_gitroom_is_proudly_open_source": "Postizは誇りを持ってオープンソースです!私たちは倫理的で透明性のある文化を信じており、Postizは永遠に存続します。全てのコードを確認したり、個人プロジェクトで利用したりできます。オープンソースリポジトリを見るには、<a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">こちらをクリック</a>してください。", + "faq_what_are_channels": "チャンネルとは何ですか?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postizでは、さまざまなチャンネル間で投稿のスケジューリングが可能です。\nチャンネルとは、投稿をスケジュールできる配信プラットフォームのことです。\n例えば、X、Facebook、Instagram、TikTok、YouTube、Reddit、Linkedin、Dribbble、Threads、Pinterestなどで投稿をスケジュールできます。", + "faq_what_are_team_members": "チームメンバーとは何ですか?", + "faq_if_you_have_a_team_with_multiple_members": "複数のメンバーがいるチームの場合、ワークスペースに招待して投稿の共同作業や個人チャンネルの追加ができます", + "faq_what_is_ai_auto_complete": "AI自動補完とは何ですか?", + "faq_we_automate_chatgpt_to_help_you_write": "ChatGPTを自動化して、SNS投稿や記事の作成をサポートします。", + "enter_email": "メールアドレスを入力", + "are_you_sure": "本当に宜しいですか?", + "yes_delete_it": "はい、削除します!", + "no_cancel": "いいえ、キャンセルします", + "are_you_sure_you_want_to_delete": "{{name}} を削除してもよろしいですか?", + "are_you_sure_you_want_to_delete_the_image": "この画像を削除してもよろしいですか?", + "are_you_sure_you_want_to_logout": "本当にログアウトしますか?", + "yes_logout": "はい、ログアウトします", + "are_you_sure_you_want_to_delete_this_slot": "このスロットを削除してもよろしいですか?", + "are_you_sure_you_want_to_delete_this_subreddit": "このSubredditを削除してもよろしいですか?", + "are_you_sure_you_want_to_close_the_window": "ウィンドウを閉じてもよろしいですか?", + "yes_close": "はい、閉じます", + "link_copied_to_clipboard": "リンクがクリップボードにコピーされました", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "このモーダルを閉じてもよろしいですか?(すべてのデータが失われます)", + "yes_close_it": "はい、閉じます!", + "uploading_pictures": "画像をアップロード中...", + "agent_starting": "エージェントを起動中", + "researching_your_content": "コンテンツを調査中...", + "understanding_the_category": "カテゴリを理解中...", + "finding_the_topic": "トピックを検索中...", + "finding_popular_posts_to_match_with": "マッチする人気投稿を検索中...", + "generating_hook": "フックを生成中...", + "generating_content": "コンテンツを生成中...", + "generating_pictures": "画像を生成中...", + "finding_time_to_post": "投稿する時間を検索中...", + "write_anything": "何でも書いてください", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "ご自由に何でも書くことができ、リンクも追加できます。リサーチは私たちが行います…", + "output_format": "出力形式", + "add_pictures": "画像を追加しますか?", + "7_days": "7日間", + "30_days": "30日間", + "90_days": "90日間", + "start_7_days_free_trial": "7日間の無料トライアルを開始", + "change_language": "言語を変更", + "that_a_wrap": "以上で終了です!\n\nこのスレッドを楽しんでいただけたなら:\n\n1. @{{username}} をフォローして、さらに多くの投稿をご覧ください\n2. 下のツイートをリツイートして、このスレッドをあなたのフォロワーと共有してください\n", + "post_as_images_carousel": "画像カルーセルとして投稿", + "save_set": "セットを保存", + "separate_post": "投稿を複数に分割", + "label_who_can_reply_to_this_post": "この投稿に返信できる人", + "delete_integration": "連携を削除", + "start_writing_your_post": "プレビュー用に投稿を書き始めてください", + "billing_join_over": "すでに参加している", + "billing_entrepreneurs_count": "18,000人以上の起業家", + "billing_who_use": "が利用している", + "billing_postiz_grow_social": "Postizでソーシャルプレゼンスを成長させる", + "billing_no_risk_trial": "100%リスクなしの無料トライアル", + "billing_pay_nothing_7_days": "最初の7日間は一切料金不要", + "billing_cancel_anytime": "いつでもキャンセル可能、手間なし", + "billing_choose_plan": "プランを選択", + "billing_monthly": "月額", + "billing_yearly": "年額", + "billing_20_percent_off": "20%オフ", + "billing_features": "機能", + "billing_channel": "チャンネル", + "billing_channels": "チャンネル", + "billing_unlimited": "無制限", + "billing_posts_per_month": "月あたりの投稿数", + "billing_unlimited_team_members": "チームメンバー無制限", + "billing_ai_auto_complete": "AI自動補完", + "billing_ai_copilots": "AIコパイロット", + "billing_ai_autocomplete": "AI自動補完", + "billing_advanced_picture_editor": "高度な画像編集ツール", + "billing_ai_images_per_month": "月あたりのAI画像数", + "billing_ai_videos_per_month": "月あたりのAI動画数", + "billing_billing_address": "請求先住所", + "billing_payment": "支払い", + "billing_powered_by_stripe": "Stripeによって提供されています", + "billing_your_7_day_trial_is": "7日間の無料トライアルは", + "billing_100_percent_free": "完全に無料", + "billing_ending": "終了します", + "billing_cancel_anytime_short": "いつでもキャンセル可能。", + "billing_pay_0_start_trial": "本日のお支払いは0円 - 無料トライアルを始めましょう!", + "billing_pay_now": "今すぐ支払う", + "billing_per_month": "/月" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index 7c4a9be8..cbc86399 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -1,505 +1,539 @@ { - "calendar": "캘린더", - "webhooks": "웹훅", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "웹훅은 Postiz에서 무언가 발생할 때 HTTP 요청을 통해 알림을 받는 방법입니다.", - "name": "이름", - "url": "URL", - "edit": "편집", - "delete": "삭제", - "add_a_webhook": "웹훅 추가", - "save": "저장", - "send_test": "테스트 전송", - "select_role": "역할 선택", - "video_made_with_ai": "AI로 제작된 영상", - "please_add_at_least": "최소 20자 이상 입력해 주세요", - "send_invitation_via_email": "이메일로 초대장을 보내시겠습니까?", - "global_settings": "글로벌 설정", - "copy_id": "채널 ID 복사", - "team_members": "팀원", - "invite_your_assistant_or_team_member_to_manage_your_account": "계정을 관리할 수 있도록 어시스턴트나 팀원을 초대하세요.", - "remove": "제거", - "add_another_member": "다른 멤버 추가", - "signatures": "서명", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "게시물에 사용할 수 있도록 계정에 서명을 추가할 수 있습니다.", - "content": "내용", - "auto_add": "자동 추가?", - "actions": "작업", - "use_signature": "서명 사용", - "add_a_signature": "서명 추가", - "no": "아니오", - "yes": "예", - "your_git_repository": "귀하의 Git 저장소", - "connect_your_github_repository_to_receive_updates_and_analytics": "업데이트와 분석을 받으려면 GitHub 저장소를 연결하세요", - "connected": "연결됨:", - "disconnect": "연결 해제", - "connect_your_repository": "저장소 연결", - "cancel": "취소", - "connect": "연결", - "public_api": "공개 API", - "check_n8n": "Postiz를 위한 저희의 N8N 커스텀 노드를 확인해보세요.", - "use_postiz_api_to_integrate_with_your_tools": "Postiz API를 사용하여 도구와 통합하세요.", - "read_how_to_use_it_over_the_documentation": "문서를 통해 사용 방법을 확인하세요.", - "reveal": "표시", - "copy_key": "키 복사", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "게시물을 더 빠르게 예약하려면 Postiz MCP 서버를 클라이언트에 연결하세요(Http 스트리밍).", - "share_with_a_client": "클라이언트와 공유", - "post": "게시물", - "comments": "댓글", - "user": "사용자", - "login_register_to_add_comments": "댓글을 추가하려면 로그인/회원가입하세요", - "status": "상태:", - "there_are_not_plugs_matching_your_channels": "채널에 맞는 플러그가 없습니다", - "you_have_to_add_x_or_linkedin_or_threads": "X 또는 LinkedIn 또는 Threads를 추가해야 합니다", - "go_to_the_calendar_to_add_channels": "채널을 추가하려면 캘린더로 이동하세요", - "channels": "채널", - "activate": "활성화", - "this_channel_needs_to_be_refreshed": "이 채널을 새로 고침해야 합니다.", - "click_here_to_refresh": "여기를 클릭하여 새로 고침", - "can_t_show_analytics_yet": "아직 분석 정보를 표시할 수 없습니다.", - "you_have_to_add_social_media_channels": "소셜 미디어 채널을 추가해야 합니다.", - "supported": "지원됨:", - "step": "단계", - "skip_onboarding": "온보딩 건너뛰기", - "onboarding": "온보딩", - "next": "다음", - "you_are_done_from_here_you_can": "완료되었습니다. 이제 다음을 할 수 있습니다:", - "view_analytics": "분석 보기", - "schedule_a_new_post": "새 게시물 예약", - "to_sell_posts_you_would_have_to": "게시물을 판매하려면 다음을 해야 합니다:", - "1_connect_at_least_one_channel": "1. 최소 한 개의 채널 연결", - "2_connect_you_bank_account": "2. 은행 계좌 연결", - "go_back_to_connect_channels": "채널 연결로 돌아가기", - "move_to_the_seller_page_to_connect_you_bank": "판매자 페이지로 이동하여 은행 계좌 연결", - "connect_channels": "채널 연결", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "소셜 미디어 및 퍼블리싱 웹사이트 채널을 연결하여\n나중에 게시물을 예약하세요.", - "social": "소셜", - "publishing_platforms": "퍼블리싱 플랫폼", - "no_channels": "아직 채널이 없습니다", - "connect_your_accounts": "소셜 계정을 연결하여 한 곳에서 예약, 게시, 분석을 시작하세요.", - "notifications": "알림", - "no_notifications": "알림 없음", - "send_message": "메시지 보내기", - "mar_28": "3월 28일", - "there_are_no_messages_yet": "아직 메시지가 없습니다.", - "checkout_the_marketplace": "마켓플레이스를 확인하세요", - "go_to_marketplace": "마켓플레이스로 이동", - "all_messages": "모든 메시지", - "previous": "이전", - "select_or_upload_pictures_maximum_5_at_a_time": "사진을 선택하거나 업로드하세요 (최대 5장까지 한 번에 가능)", - "you_can_also_drag_drop_pictures": "사진을 드래그 앤 드롭하여 추가할 수도 있습니다", - "you_don_t_have_any_assets_yet": "아직 자산이 없습니다.", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "선택한 미디어 추가", - "insert_media": "미디어 삽입", - "design_media": "디자인 미디어", - "select": "선택", - "editor": "에디터", - "clear": "지우기", - "order_completed": "주문 완료", - "the_order_has_been_completed": "주문이 완료되었습니다", - "post_has_been_published": "게시물이 게시되었습니다", - "url_1": "URL:", - "new_offer": "새로운 제안", - "platform": "플랫폼", - "posts": "게시물", - "pay_accept_offer": "결제 및 제안 수락", - "accepted": "수락됨", - "post_draft": "게시물 초안", - "revision_needed": "수정 필요", - "approve": "승인", - "preview": "미리보기", - "revision_requested": "수정 요청됨", - "accepted_1": "승인됨", - "cancelled_by_the_seller": "판매자가 취소함", - "please_select_your_country_where_your_business_is": "비즈니스가 위치한 국가를 선택하세요.", - "select_country": "--국가 선택--", - "connect_bank_account": "은행 계좌 연결", - "seller_mode": "판매자 모드", - "active": "활성화됨", - "details": "세부 정보", - "audience_size": "청중 규모", - "add_another_platform": "다른 플랫폼 추가", - "send_an_offer_for": "$에 대한 제안 보내기", - "complete_order_and_pay_early": "주문 완료 및 조기 결제", - "order_in_progress": "주문 진행 중", - "create_a_new_offer": "새 제안 만들기", - "orders": "주문", - "price": "가격", - "state": "상태", - "showing": "표시 중", - "to": "까지", - "from": "부터", - "results": "결과", - "content_writer": "콘텐츠 작가", - "influencer": "인플루언서", - "request_service": "서비스 요청", - "the_marketplace_is_not_opened_yet": "마켓플레이스가 아직 열리지 않았습니다", - "check_again_soon": "곧 다시 확인해 주세요!", - "filter": "필터", - "result": "결과", - "seller": "판매자", - "buyer": "구매자", - "discord_support": "디스코드 지원", - "teams": "팀", - "webhooks_1": "웹훅", - "auto_post": "자동 게시", - "logout_from": "로그아웃", - "join_10000_entrepreneurs_who_use_postiz": "Postiz를 사용하는 10,000명 이상의 창업가들과 함께하세요", - "to_manage_all_your_social_media_channels": "모든 소셜 미디어 채널을 관리하세요", - "100_no_risk_trial": "100% 무위험 체험", - "pay_nothing_for_the_first_7_days": "처음 7일 동안은 결제되지 않습니다", - "cancel_anytime_hassle_free": "언제든지 간편하게 취소 가능", - "add_free_subscription": "-- 무료 구독 추가 --", - "currently_impersonating": "현재 가장 중", - "user_1": "사용자:", - "drag_n_drop_some_files_here": "여기에 파일을 드래그 앤 드롭하세요", - "add_time_slot": "시간대 추가", - "add_slot": "슬롯 추가", - "cancel_publication": "게시 취소", - "statistics": "통계", - "loading": "로딩 중", - "short_link": "단축 링크", - "original_link": "원본 링크", - "clicks": "클릭수", - "selected_customer": "선택된 고객", - "customer": "고객:", - "repeat_post_every": "게시 반복 주기...", - "use_this_media": "이 미디어 사용", - "create_new_post": "게시물 만들기", - "update_post": "기존 게시물 업데이트", - "merge_comments_into_one_post": "댓글을 하나의 게시물로 합치기", - "accounts_that_will_engage": "참여할 계정:", - "day": "일", - "week": "주", - "month": "월", - "remove_from_customer": "고객에서 제거", - "show_more": "+ 더 보기", - "show_less": "- 간략히 보기", - "upload": "업로드", - "ai": "AI", - "add_channel": "채널 추가", - "add_platform": "플랫폼 추가", - "articles": "기사", - "add_comment": "댓글 추가", - "add_post": "스레드에 게시물 추가", - "add_comment_or_post": "댓글 / 게시물 추가", - "you_are_in_global_editing_mode": "현재 전체 편집 모드입니다", - "the_post_should_be_at_least_6_characters_long": "게시글은 최소 6자 이상이어야 합니다", - "are_you_sure_you_want_to_delete_post": "이 게시물을 삭제하시겠습니까?", - "post_deleted_successfully": "게시물이 성공적으로 삭제되었습니다", - "delete_post": "게시글 삭제", - "save_as_draft": "임시 저장", - "post_now": "지금 게시", - "please_add": "추가해 주세요", - "to_your_telegram_group_channel_and_click_here": "텔레그램 그룹/채널에 추가하고 여기를 클릭하세요:", - "connect_telegram": "텔레그램 연결", - "please_add_the_following_command_in_your_chat": "채팅에 다음 명령어를 추가해 주세요:", - "copy": "복사", - "settings": "설정", - "integrations": "통합", - "add_integration": "통합 추가", - "you_are_now_editing_only": "현재 이 항목만 편집 중입니다", - "tag_a_company": "회사 태그하기", - "video_length_is_invalid_must_be_up_to": "영상 길이가 잘못되었습니다. 최대", - "seconds": "초까지 가능합니다", - "this_feature_available_only_for_photos": "이 기능은 사진에만 사용할 수 있으며, 기본 음악이 추가됩니다. 나중에 변경할 수 있습니다.", - "allow_user_to": "사용자에게 허용:", - "your_video_will_be_labeled_promotional": "동영상에 \"프로모션 콘텐츠\" 라벨이 표시됩니다.", - "this_cannot_be_changed_once_posted": "게시 후에는 변경할 수 없습니다.", - "turn_on_to_disclose_video_promotes": "이 동영상이 대가를 받고 상품 또는 서비스를 홍보함을 공개하려면 켜세요. 이 동영상은 본인, 제3자 또는 둘 다를 홍보할 수 있습니다.", - "you_are_promoting_yourself": "본인 또는 본인 브랜드를 홍보하고 있습니다.", - "this_video_will_be_classified_brand_organic": "이 동영상은 브랜드 오가닉으로 분류됩니다.", - "you_are_promoting_another_brand": "다른 브랜드 또는 제3자를 홍보하고 있습니다.", - "this_video_will_be_classified_branded_content": "이 동영상은 브랜드드 콘텐츠로 분류됩니다.", - "by_posting_you_agree_to_tiktoks": "게시함으로써 TikTok의 약관에 동의하게 됩니다.", - "music_usage_confirmation": "음악 사용 확인", - "branded_content_policy": "브랜드 콘텐츠 정책", - "select_1": "--선택--", - "select_flair": "--플레어 선택--", - "link": "링크", - "add_subreddit": "서브레딧 추가", - "please_add_at_least_one_subreddit": "최소한 하나의 서브레딧을 추가해 주세요.", - "add_community": "커뮤니티 추가", - "select_post_type": "게시물 유형 선택...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "연결된 LinkedIn 페이지에 연결된 비즈니스를 찾을 수 없습니다.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "이 대화 상자를 닫고, 새 페이지를 만든 후 새 채널을 다시 추가해 주세요.", - "select_linkedin_page": "LinkedIn 페이지 선택:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "선택한 페이지에 연결된 비즈니스를 찾을 수 없습니다.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "모든 페이지와 모든 비즈니스를 연결하는 것을 권장합니다.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "이 대화 상자를 닫고, 통합을 삭제한 후 새 채널을 다시 추가해 주세요.", - "select_instagram_account": "Instagram 계정 선택:", - "select_page": "페이지 선택:", - "generate_image_with_ai": "AI로 이미지 생성", - "reconnect_channel": "채널 다시 연결", - "update_credentials": "자격 증명 업데이트", - "additional_settings": "추가 설정", - "change_bot": "봇 변경", - "move_add_to_customer": "고객으로 이동/추가", - "edit_time_slots": "시간대 편집", - "enable_channel": "채널 활성화", - "disable_channel": "채널 비활성화", - "add": "추가", - "short_post": "짧은 게시물", - "long_post": "긴 게시물", - "a_thread_with_short_posts": "짧은 게시물로 이루어진 스레드", - "a_thread_with_long_posts": "긴 게시물로 이루어진 스레드", - "personal_voice_i_am_happy_to_announce": "개인 목소리(\"기쁘게 소식을 전합니다\")", - "company_voice_we_are_happy_to_announce": "회사 목소리(\"기쁘게 소식을 전해드립니다\")", - "generate": "생성", - "generate_posts": "게시물 생성", - "purchase_a_life_time_pro_account_with_sol_199": "SOL($199)로 평생 PRO 계정을 구매하세요. 이 구매는 환불이 불가하니 유의해 주세요.", - "purchase_now": "지금 구매하기", - "pay_today": "오늘 결제하기", - "we_are_sorry_to_see_you_go": "떠나셔서 아쉽습니다 :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "저희가 더 잘할 수 있었던 점을 간단히 말씀해주실 수 있나요?", - "cancel_subscription": "구독 취소", - "plans": "요금제", - "monthly": "월간", - "yearly": "연간", - "reactivate_subscription": "구독 재활성화", - "update_payment_method_invoices_history": "결제 수단/청구 내역 업데이트", - "cancel_subscription_1": "구독 취소", - "your_subscription_will_be_canceled_at": "구독이 다음 날짜에 취소됩니다:", - "you_will_never_be_charged_again": "더 이상 결제되지 않습니다", - "current_package": "현재 패키지:", - "next_package": "다음 패키지:", - "claim": "청구", - "frequently_asked_questions": "자주 묻는 질문", - "autopost": "자동 게시", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "자동 게시 기능은 RSS의 새 항목을 소셜 미디어에 자동으로 게시할 수 있습니다.", - "title": "제목", - "add_an_autopost": "자동 게시 추가", - "post_content": "게시물 내용", - "sign_up": "회원가입", - "or": "또는", - "by_registering_you_agree_to_our": "회원가입을 통해 다음에 동의합니다:", - "and": "및", - "terms_of_service": "이용약관", - "privacy_policy": "개인정보 처리방침", - "create_account": "계정 만들기", - "already_have_an_account": "이미 계정이 있으신가요?", - "sign_in": "로그인", - "sign_in_1": "로그인", - "don_t_have_an_account": "계정이 없으신가요?", - "forgot_password": "비밀번호 찾기", - "forgot_password_1": "비밀번호 찾기", - "send_password_reset_email": "비밀번호 재설정 이메일 보내기", - "go_back_to_login": "로그인 화면으로 돌아가기", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "비밀번호를 재설정할 수 있는 링크가 포함된 이메일을 보냈습니다.", - "change_password": "비밀번호 변경", - "we_successfully_reset_your_password_you_can_now_login_with_your": "비밀번호가 성공적으로 재설정되었습니다. 이제 로그인하실 수 있습니다.", - "click_here_to_go_back_to_login": "로그인 화면으로 돌아가려면 여기를 클릭하세요", - "activate_your_account": "계정 활성화", - "thank_you_for_registering": "회원가입해 주셔서 감사합니다!", - "please_check_your_email_to_activate_your_account": "계정 활성화를 위해 이메일을 확인해 주세요.", - "sign_in_with": "다음으로 로그인", - "continue_with_google": "Google로 계속하기", - "sign_in_with_github": "GitHub로 로그인", - "continue_with_farcaster": "Farcaster로 계속하기", - "continue_with_your_wallet": "지갑으로 계속하기", - "stars_per_day": "일일 별점", - "media": "미디어", - "check_launch": "런치 확인", - "load_your_github_repository_from_settings_to_see_analytics": "설정에서 GitHub 저장소를 불러와 분석 정보를 확인하세요", - "stars": "별점", - "processing_stars": "별점 처리 중...", - "forks": "포크", - "registration_is_disabled": "회원가입이 비활성화되었습니다", - "login_instead": "대신 로그인하기", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "대화를 선택하고 채팅을 시작하세요.", - "adding_channel_redirecting_you": "채널 추가 중, 리디렉션합니다", - "could_not_add_provider": "공급자를 추가할 수 없습니다.", - "you_are_being_redirected_back": "이전 페이지로 리디렉션됩니다.", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "문제가 발생했습니다. 페이지를 새로고침해 주세요.", - "post_not_found": "게시물을 찾을 수 없습니다.", - "publication_date": "게시일:", - "analytics": "분석", - "launches": "출시", - "plugs": "홍보", - "billing": "결제", - "affiliate": "제휴", - "monday": "월요일", - "tuesday": "화요일", - "wednesday": "수요일", - "thursday": "목요일", - "friday": "금요일", - "saturday": "토요일", - "sunday": "일요일", - "can_t_change_date_remove_post_from_publication": "날짜를 변경할 수 없습니다. 게시물에서 제거해 주세요.", - "predicted_github_trending_change": "예상 GitHub 트렌드 변화", - "duplicate_post": "중복 게시물", - "preview_post": "게시물 미리보기", - "post_statistics": "게시물 통계", - "draft": "임시 저장", - "week_number": "{{number}}주차", - "top_title_edit_webhook": "웹훅 편집", - "top_title_add_webhook": "웹훅 추가", - "top_title_oh_no": "이런", - "top_title_auto_plug": "자동 플러그: {{title}}", - "top_title_edit_autopost": "자동 게시물 편집", - "top_title_add_autopost": "자동 게시물 추가", - "top_title_send_a_new_offer": "새로운 제안 보내기", - "top_title_media_library": "미디어 라이브러리", - "top_title_add_signature": "서명 추가", - "top_title_send_a_message_to": "{{name}}님에게 메시지 보내기", - "top_title_configure_provider": "공급자 구성", - "top_title_add_member": "멤버 추가", - "top_title_change_bot_picture": "봇 사진 변경", - "top_title_create_a_new_tag": "새 태그 만들기", - "top_title_select_company": "회사 선택", - "top_title_additional_settings": "추가 설정", - "top_title_time_table_slots": "시간표 슬롯", - "top_title_design_media": "미디어 디자인", - "top_title_edit_post": "게시물 편집", - "top_title_create_post": "게시물 만들기", - "top_title_move__add_to_customer": "고객에게 이동/추가", - "top_title_add_api_key_for": "{{name}}의 API 키 추가", - "top_title_instance_url": "인스턴스 URL", - "top_title_custom_url": "커스텀 URL", - "top_title_add_channel": "채널 추가", - "top_title_add_telegram": "텔레그램 추가", - "top_title_add_wrapcast": "랩캐스트 추가", - "top_title_comments_for": "{{date}}의 댓글", - "top_title_edit_signature": "서명 편집", - "label_name": "이름", - "label_url": "URL", - "label_title": "직함", - "label_subtitle": "부제목", - "label_email": "이메일", - "label_full_name": "전체 이름", - "label_password": "비밀번호", - "label_confirm_password": "비밀번호 확인", - "label_api_key": "API 키", - "label_instance_url": "인스턴스 URL", - "label_custom_url": "사용자 지정 URL", - "label_feedback": "피드백", - "label_bio": "소개", - "label_role": "역할", - "label_country": "국가", - "label_audience_size": "모든 플랫폼의 청중 규모", - "label_pick_time": "시간 선택", - "label_nickname": "별명", - "label_write_anything": "아무거나 작성하세요", - "label_output_format": "출력 형식", - "label_add_pictures": "사진 추가할까요?", - "label_hour": "시", - "label_minutes": "분", - "label_select_publication": "출판물 선택", - "label_canonical_link": "정식 링크", - "label_cover_picture": "커버 사진", - "label_tags": "태그", - "label_topics": "주제", - "label_tags_maximum_4": "태그 (최대 4개)", - "label_attachments": "첨부파일", - "label_type": "유형", - "label_thumbnail": "썸네일", - "label_who_can_see_this_video": "이 영상을 볼 수 있는 사람", - "label_content_posting_method": "콘텐츠 게시 방법", - "label_auto_add_music": "자동 음악 추가", - "label_duet": "듀엣", - "label_stitch": "스티치", - "label_comments": "댓글", - "label_disclose_video_content": "영상 내용 공개", - "label_your_brand": "당신의 브랜드", - "label_branded_content": "브랜드 콘텐츠", - "label_subreddit": "서브레딧", - "label_flair": "플레어", - "label_media": "미디어", - "label_search_subreddit": "서브레딧 검색", - "label_delay": "지연", - "label_post_type": "게시물 유형", - "label_collaborators": "협업자 (최대 3명) - 계정은 비공개일 수 없음", - "label_community": "커뮤니티", - "label_search_community": "커뮤니티 검색", - "label_channel": "채널", - "label_search_channel": "채널 검색", - "label_select_channel": "채널 선택", - "label_new_password": "새 비밀번호", - "label_repeat_password": "비밀번호 재입력", - "label_platform": "플랫폼", - "label_price_per_post": "게시글당 가격", - "label_integrations": "연동", - "label_code": "코드", - "label_should_sync_last_post": "현재 마지막 게시글을 동기화할까요?", - "label_when_post": "언제 게시할까요?", - "label_autogenerate_content": "콘텐츠 자동 생성", - "label_generate_picture": "이미지 생성할까요?", - "label_company": "회사", - "label_tag_color": "태그 색상", - "label_select_board": "게시판 선택", - "label_select_organization": "조직 선택", - "label_auto_add_signature": "서명 자동 추가할까요?", - "enable_color_picker": "색상 선택기 활성화", - "cancel_the_color_picker": "색상 선택기 취소", - "no_content_yet": "아직 콘텐츠가 없습니다", - "write_your_reply": "게시글을 작성하세요...", - "add_a_tag": "태그 추가", - "add_to_calendar": "캘린더에 추가", - "select_channels_from_circles": "위의 원에서 채널을 선택하세요", - "not_matching_order": "순서가 일치하지 않음", - "submit_for_order": "주문 제출", - "schedule": "일정", - "update": "업데이트", - "attachments": "첨부파일", - "tags": "태그", - "public_to_everyone": "모두에게 공개", - "mutual_follow_friends": "맞팔 친구", - "follower_of_creator": "크리에이터의 팔로워", - "self_only": "나만 보기", - "post_content_directly_to_tiktok": "콘텐츠를 TikTok에 바로 게시하기", - "upload_content_to_tiktok_without_posting": "게시하지 않고 TikTok에 콘텐츠 업로드하기", - "choose_upload_without_posting_description": "게시하지 않고 업로드를 선택하면 TikTok 앱 내에서 콘텐츠를 검토하고 편집한 후 게시할 수 있습니다. TikTok의 내장 편집 도구를 사용할 수 있으며, 게시 전에 최종 수정을 할 수 있습니다.", - "faq_am_i_going_to_be_charged_by_postiz": "Postiz에서 요금이 청구되나요?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "신용카드 정보를 확인하기 위해 Postiz는 $2를 보류했다가 즉시 해제합니다.", - "faq_can_i_trust_postiz_gitroom": "Postiz를 신뢰할 수 있나요?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz는 자랑스럽게 오픈소스입니다! 우리는 윤리적이고 투명한 문화를 믿으며, Postiz는 영원히 존재할 것입니다. 전체 코드를 확인하거나 개인 프로젝트에 사용할 수 있습니다. 오픈소스 저장소를 보려면 <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">여기를 클릭하세요</a>.", - "faq_what_are_channels": "채널이란 무엇인가요?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz를 통해 다양한 채널에 게시물을 예약할 수 있습니다.\n채널은 게시물을 예약할 수 있는 게시 플랫폼입니다.\n예를 들어, X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads, Pinterest에 게시물을 예약할 수 있습니다.", - "faq_what_are_team_members": "팀 멤버란 무엇인가요?", - "faq_if_you_have_a_team_with_multiple_members": "여러 명의 팀이 있다면, 워크스페이스에 초대하여 게시물 협업 및 개인 채널 추가가 가능합니다.", - "faq_what_is_ai_auto_complete": "AI 자동완성이란 무엇인가요?", - "faq_we_automate_chatgpt_to_help_you_write": "ChatGPT를 자동화하여 소셜 게시물과 글쓰기를 도와드립니다.", - "enter_email": "이메일 입력", - "are_you_sure": "정말로 하시겠습니까?", - "yes_delete_it": "네, 삭제하세요!", - "no_cancel": "아니요, 취소하세요!", - "are_you_sure_you_want_to_delete": "{{name}}을(를) 삭제하시겠습니까?", - "are_you_sure_you_want_to_delete_the_image": "이 이미지를 삭제하시겠습니까?", - "are_you_sure_you_want_to_logout": "로그아웃하시겠습니까?", - "yes_logout": "네, 로그아웃", - "are_you_sure_you_want_to_delete_this_slot": "이 슬롯을 삭제하시겠습니까?", - "are_you_sure_you_want_to_delete_this_subreddit": "이 서브레딧을 삭제하시겠습니까?", - "are_you_sure_you_want_to_close_the_window": "이 창을 닫으시겠습니까?", - "yes_close": "네, 닫기", - "link_copied_to_clipboard": "링크가 클립보드에 복사되었습니다", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "이 모달을 닫으시겠습니까? (모든 데이터가 사라집니다)", - "yes_close_it": "네, 닫으세요!", - "uploading_pictures": "사진 업로드 중...", - "agent_starting": "에이전트 시작 중", - "researching_your_content": "콘텐츠 조사 중...", - "understanding_the_category": "카테고리 이해 중...", - "finding_the_topic": "주제 찾는 중...", - "finding_popular_posts_to_match_with": "연관 인기 게시글 찾는 중...", - "generating_hook": "후킹 문구 생성 중...", - "generating_content": "콘텐츠 생성 중...", - "generating_pictures": "사진 생성 중...", - "finding_time_to_post": "게시할 시간 찾는 중...", - "write_anything": "아무거나 작성하세요", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "원하는 내용을 자유롭게 작성하고 링크도 추가할 수 있습니다. 저희가 대신 조사해드릴게요...", - "output_format": "출력 형식", - "add_pictures": "사진 추가하기?", - "7_days": "7일", - "30_days": "30일", - "90_days": "90일", - "start_7_days_free_trial": "7일 무료 체험 시작하기", - "change_language": "언어 변경", - "that_a_wrap": "여기까지입니다!\n\n이 스레드가 유익하셨다면:\n\n1. 더 많은 정보를 원하시면 @{{username}}를 팔로우하세요\n2. 아래 트윗을 리트윗해서 이 스레드를 여러분의 팔로워들과 공유하세요\n", - "post_as_images_carousel": "이미지 캐러셀로 게시", - "save_set": "세트 저장", - "separate_post": "게시물을 여러 개로 분리", - "label_who_can_reply_to_this_post": "이 게시물에 누가 답글을 달 수 있나요?", - "delete_integration": "통합 삭제", - "start_writing_your_post": "미리보기를 위해 게시글 작성을 시작하세요" + "calendar": "캘린더", + "webhooks": "웹훅", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "웹훅은 Postiz에서 무언가 발생할 때 HTTP 요청을 통해 알림을 받는 방법입니다.", + "name": "이름", + "url": "URL", + "edit": "편집", + "delete": "삭제", + "add_a_webhook": "웹훅 추가", + "save": "저장", + "send_test": "테스트 전송", + "select_role": "역할 선택", + "video_made_with_ai": "AI로 제작된 영상", + "please_add_at_least": "최소 20자 이상 입력해 주세요", + "send_invitation_via_email": "이메일로 초대장을 보내시겠습니까?", + "global_settings": "글로벌 설정", + "copy_id": "채널 ID 복사", + "team_members": "팀원", + "invite_your_assistant_or_team_member_to_manage_your_account": "계정을 관리할 수 있도록 어시스턴트나 팀원을 초대하세요.", + "remove": "제거", + "add_another_member": "다른 멤버 추가", + "signatures": "서명", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "게시물에 사용할 수 있도록 계정에 서명을 추가할 수 있습니다.", + "content": "내용", + "auto_add": "자동 추가?", + "actions": "작업", + "use_signature": "서명 사용", + "add_a_signature": "서명 추가", + "no": "아니오", + "yes": "예", + "your_git_repository": "귀하의 Git 저장소", + "connect_your_github_repository_to_receive_updates_and_analytics": "업데이트와 분석을 받으려면 GitHub 저장소를 연결하세요", + "connected": "연결됨:", + "disconnect": "연결 해제", + "connect_your_repository": "저장소 연결", + "cancel": "취소", + "connect": "연결", + "public_api": "공개 API", + "check_n8n": "Postiz를 위한 저희의 N8N 커스텀 노드를 확인해보세요.", + "use_postiz_api_to_integrate_with_your_tools": "Postiz API를 사용하여 도구와 통합하세요.", + "read_how_to_use_it_over_the_documentation": "문서를 통해 사용 방법을 확인하세요.", + "reveal": "표시", + "copy_key": "키 복사", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "게시물을 더 빠르게 예약하려면 Postiz MCP 서버를 클라이언트에 연결하세요(Http 스트리밍).", + "share_with_a_client": "클라이언트와 공유", + "post": "게시물", + "comments": "댓글", + "user": "사용자", + "login_register_to_add_comments": "댓글을 추가하려면 로그인/회원가입하세요", + "status": "상태:", + "there_are_not_plugs_matching_your_channels": "채널에 맞는 플러그가 없습니다", + "you_have_to_add_x_or_linkedin_or_threads": "X 또는 LinkedIn 또는 Threads를 추가해야 합니다", + "go_to_the_calendar_to_add_channels": "채널을 추가하려면 캘린더로 이동하세요", + "channels": "채널", + "activate": "활성화", + "this_channel_needs_to_be_refreshed": "이 채널을 새로 고침해야 합니다.", + "click_here_to_refresh": "여기를 클릭하여 새로 고침", + "can_t_show_analytics_yet": "아직 분석 정보를 표시할 수 없습니다.", + "you_have_to_add_social_media_channels": "소셜 미디어 채널을 추가해야 합니다.", + "supported": "지원됨:", + "step": "단계", + "skip_onboarding": "온보딩 건너뛰기", + "onboarding": "온보딩", + "next": "다음", + "you_are_done_from_here_you_can": "완료되었습니다. 이제 다음을 할 수 있습니다:", + "view_analytics": "분석 보기", + "schedule_a_new_post": "새 게시물 예약", + "to_sell_posts_you_would_have_to": "게시물을 판매하려면 다음을 해야 합니다:", + "1_connect_at_least_one_channel": "1. 최소 한 개의 채널 연결", + "2_connect_you_bank_account": "2. 은행 계좌 연결", + "go_back_to_connect_channels": "채널 연결로 돌아가기", + "move_to_the_seller_page_to_connect_you_bank": "판매자 페이지로 이동하여 은행 계좌 연결", + "connect_channels": "채널 연결", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "소셜 미디어 및 퍼블리싱 웹사이트 채널을 연결하여\n나중에 게시물을 예약하세요.", + "social": "소셜", + "publishing_platforms": "퍼블리싱 플랫폼", + "no_channels": "아직 채널이 없습니다", + "connect_your_accounts": "소셜 계정을 연결하여 한 곳에서 예약, 게시, 분석을 시작하세요.", + "notifications": "알림", + "no_notifications": "알림 없음", + "send_message": "메시지 보내기", + "mar_28": "3월 28일", + "there_are_no_messages_yet": "아직 메시지가 없습니다.", + "checkout_the_marketplace": "마켓플레이스를 확인하세요", + "go_to_marketplace": "마켓플레이스로 이동", + "all_messages": "모든 메시지", + "previous": "이전", + "select_or_upload_pictures_maximum_5_at_a_time": "사진을 선택하거나 업로드하세요 (최대 5장까지 한 번에 가능)", + "you_can_also_drag_drop_pictures": "사진을 드래그 앤 드롭하여 추가할 수도 있습니다", + "you_don_t_have_any_assets_yet": "아직 자산이 없습니다.", + "click_the_button_below_to_upload_one": "아래 버튼을 클릭하여 하나를 업로드하세요", + "click_the_button_below_to_upload_other": "아래 버튼을 클릭하여 여러 개를 업로드하세요", + "add_selected_media": "선택한 미디어 추가", + "insert_media": "미디어 삽입", + "design_media": "디자인 미디어", + "select": "선택", + "editor": "에디터", + "clear": "지우기", + "order_completed": "주문 완료", + "the_order_has_been_completed": "주문이 완료되었습니다", + "post_has_been_published": "게시물이 게시되었습니다", + "url_1": "URL:", + "new_offer": "새로운 제안", + "platform": "플랫폼", + "posts": "게시물", + "pay_accept_offer": "결제 및 제안 수락", + "accepted": "수락됨", + "post_draft": "게시물 초안", + "revision_needed": "수정 필요", + "approve": "승인", + "preview": "미리보기", + "revision_requested": "수정 요청됨", + "accepted_1": "승인됨", + "cancelled_by_the_seller": "판매자가 취소함", + "please_select_your_country_where_your_business_is": "비즈니스가 위치한 국가를 선택하세요.", + "select_country": "--국가 선택--", + "connect_bank_account": "은행 계좌 연결", + "seller_mode": "판매자 모드", + "active": "활성화됨", + "details": "세부 정보", + "audience_size": "청중 규모", + "add_another_platform": "다른 플랫폼 추가", + "send_an_offer_for": "$에 대한 제안 보내기", + "complete_order_and_pay_early": "주문 완료 및 조기 결제", + "order_in_progress": "주문 진행 중", + "create_a_new_offer": "새 제안 만들기", + "orders": "주문", + "price": "가격", + "state": "상태", + "showing": "표시 중", + "to": "까지", + "from": "부터", + "results": "결과", + "content_writer": "콘텐츠 작가", + "influencer": "인플루언서", + "request_service": "서비스 요청", + "the_marketplace_is_not_opened_yet": "마켓플레이스가 아직 열리지 않았습니다", + "check_again_soon": "곧 다시 확인해 주세요!", + "filter": "필터", + "result": "결과", + "seller": "판매자", + "buyer": "구매자", + "discord_support": "디스코드 지원", + "teams": "팀", + "webhooks_1": "웹훅", + "auto_post": "자동 게시", + "logout_from": "로그아웃", + "join_10000_entrepreneurs_who_use_postiz": "Postiz를 사용하는 10,000명 이상의 창업가들과 함께하세요", + "to_manage_all_your_social_media_channels": "모든 소셜 미디어 채널을 관리하세요", + "100_no_risk_trial": "100% 무위험 체험", + "pay_nothing_for_the_first_7_days": "처음 7일 동안은 결제되지 않습니다", + "cancel_anytime_hassle_free": "언제든지 간편하게 취소 가능", + "add_free_subscription": "-- 무료 구독 추가 --", + "currently_impersonating": "현재 가장 중", + "user_1": "사용자:", + "drag_n_drop_some_files_here": "여기에 파일을 드래그 앤 드롭하세요", + "add_time_slot": "시간대 추가", + "add_slot": "슬롯 추가", + "cancel_publication": "게시 취소", + "statistics": "통계", + "loading": "로딩 중", + "short_link": "단축 링크", + "original_link": "원본 링크", + "clicks": "클릭수", + "selected_customer": "선택된 고객", + "customer": "고객:", + "repeat_post_every": "게시 반복 주기...", + "use_this_media": "이 미디어 사용", + "create_new_post": "게시물 만들기", + "update_post": "기존 게시물 업데이트", + "merge_comments_into_one_post": "댓글을 하나의 게시물로 합치기", + "accounts_that_will_engage": "참여할 계정:", + "day": "일", + "week": "주", + "month": "월", + "remove_from_customer": "고객에서 제거", + "show_more": "+ 더 보기", + "show_less": "- 간략히 보기", + "upload": "업로드", + "ai": "AI", + "add_channel": "채널 추가", + "add_platform": "플랫폼 추가", + "articles": "기사", + "add_comment": "댓글 추가", + "add_post": "스레드에 게시물 추가", + "add_comment_or_post": "댓글 / 게시물 추가", + "you_are_in_global_editing_mode": "현재 전체 편집 모드입니다", + "the_post_should_be_at_least_6_characters_long": "게시글은 최소 6자 이상이어야 합니다", + "are_you_sure_you_want_to_delete_post": "이 게시물을 삭제하시겠습니까?", + "post_deleted_successfully": "게시물이 성공적으로 삭제되었습니다", + "delete_post": "게시글 삭제", + "save_as_draft": "임시 저장", + "post_now": "지금 게시", + "please_add": "추가해 주세요", + "to_your_telegram_group_channel_and_click_here": "텔레그램 그룹/채널에 추가하고 여기를 클릭하세요:", + "connect_telegram": "텔레그램 연결", + "please_add_the_following_command_in_your_chat": "채팅에 다음 명령어를 추가해 주세요:", + "copy": "복사", + "settings": "설정", + "integrations": "통합", + "add_integration": "통합 추가", + "you_are_now_editing_only": "현재 이 항목만 편집 중입니다", + "tag_a_company": "회사 태그하기", + "video_length_is_invalid_must_be_up_to": "영상 길이가 잘못되었습니다. 최대", + "seconds": "초까지 가능합니다", + "this_feature_available_only_for_photos": "이 기능은 사진에만 사용할 수 있으며, 기본 음악이 추가됩니다. 나중에 변경할 수 있습니다.", + "allow_user_to": "사용자에게 허용:", + "your_video_will_be_labeled_promotional": "동영상에 \"프로모션 콘텐츠\" 라벨이 표시됩니다.", + "this_cannot_be_changed_once_posted": "게시 후에는 변경할 수 없습니다.", + "turn_on_to_disclose_video_promotes": "이 동영상이 대가를 받고 상품 또는 서비스를 홍보함을 공개하려면 켜세요. 이 동영상은 본인, 제3자 또는 둘 다를 홍보할 수 있습니다.", + "you_are_promoting_yourself": "본인 또는 본인 브랜드를 홍보하고 있습니다.", + "this_video_will_be_classified_brand_organic": "이 동영상은 브랜드 오가닉으로 분류됩니다.", + "you_are_promoting_another_brand": "다른 브랜드 또는 제3자를 홍보하고 있습니다.", + "this_video_will_be_classified_branded_content": "이 동영상은 브랜드드 콘텐츠로 분류됩니다.", + "by_posting_you_agree_to_tiktoks": "게시함으로써 TikTok의 약관에 동의하게 됩니다.", + "music_usage_confirmation": "음악 사용 확인", + "branded_content_policy": "브랜드 콘텐츠 정책", + "select_1": "--선택--", + "select_flair": "--플레어 선택--", + "link": "링크", + "add_subreddit": "서브레딧 추가", + "please_add_at_least_one_subreddit": "최소한 하나의 서브레딧을 추가해 주세요.", + "add_community": "커뮤니티 추가", + "select_post_type": "게시물 유형 선택...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "연결된 LinkedIn 페이지에 연결된 비즈니스를 찾을 수 없습니다.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "이 대화 상자를 닫고, 새 페이지를 만든 후 새 채널을 다시 추가해 주세요.", + "select_linkedin_page": "LinkedIn 페이지 선택:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "선택한 페이지에 연결된 비즈니스를 찾을 수 없습니다.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "모든 페이지와 모든 비즈니스를 연결하는 것을 권장합니다.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "이 대화 상자를 닫고, 통합을 삭제한 후 새 채널을 다시 추가해 주세요.", + "select_instagram_account": "Instagram 계정 선택:", + "select_page": "페이지 선택:", + "generate_image_with_ai": "AI로 이미지 생성", + "reconnect_channel": "채널 다시 연결", + "update_credentials": "자격 증명 업데이트", + "additional_settings": "추가 설정", + "change_bot": "봇 변경", + "move_add_to_customer": "고객으로 이동/추가", + "edit_time_slots": "시간대 편집", + "enable_channel": "채널 활성화", + "disable_channel": "채널 비활성화", + "add": "추가", + "short_post": "짧은 게시물", + "long_post": "긴 게시물", + "a_thread_with_short_posts": "짧은 게시물로 이루어진 스레드", + "a_thread_with_long_posts": "긴 게시물로 이루어진 스레드", + "personal_voice_i_am_happy_to_announce": "개인 목소리(\"기쁘게 소식을 전합니다\")", + "company_voice_we_are_happy_to_announce": "회사 목소리(\"기쁘게 소식을 전해드립니다\")", + "generate": "생성", + "generate_posts": "게시물 생성", + "purchase_a_life_time_pro_account_with_sol_199": "SOL($199)로 평생 PRO 계정을 구매하세요. 이 구매는 환불이 불가하니 유의해 주세요.", + "purchase_now": "지금 구매하기", + "pay_today": "오늘 결제하기", + "we_are_sorry_to_see_you_go": "떠나셔서 아쉽습니다 :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "저희가 더 잘할 수 있었던 점을 간단히 말씀해주실 수 있나요?", + "cancel_subscription": "구독 취소", + "plans": "요금제", + "monthly": "월간", + "yearly": "연간", + "reactivate_subscription": "구독 재활성화", + "update_payment_method_invoices_history": "결제 수단/청구 내역 업데이트", + "cancel_subscription_1": "구독 취소", + "your_subscription_will_be_canceled_at": "구독이 다음 날짜에 취소됩니다:", + "you_will_never_be_charged_again": "더 이상 결제되지 않습니다", + "current_package": "현재 패키지:", + "next_package": "다음 패키지:", + "claim": "청구", + "frequently_asked_questions": "자주 묻는 질문", + "autopost": "자동 게시", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "자동 게시 기능은 RSS의 새 항목을 소셜 미디어에 자동으로 게시할 수 있습니다.", + "title": "제목", + "add_an_autopost": "자동 게시 추가", + "post_content": "게시물 내용", + "sign_up": "회원가입", + "or": "또는", + "by_registering_you_agree_to_our": "회원가입을 통해 다음에 동의합니다:", + "and": "및", + "terms_of_service": "이용약관", + "privacy_policy": "개인정보 처리방침", + "create_account": "계정 만들기", + "already_have_an_account": "이미 계정이 있으신가요?", + "sign_in": "로그인", + "sign_in_1": "로그인", + "don_t_have_an_account": "계정이 없으신가요?", + "forgot_password": "비밀번호 찾기", + "forgot_password_1": "비밀번호 찾기", + "send_password_reset_email": "비밀번호 재설정 이메일 보내기", + "go_back_to_login": "로그인 화면으로 돌아가기", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "비밀번호를 재설정할 수 있는 링크가 포함된 이메일을 보냈습니다.", + "change_password": "비밀번호 변경", + "we_successfully_reset_your_password_you_can_now_login_with_your": "비밀번호가 성공적으로 재설정되었습니다. 이제 로그인하실 수 있습니다.", + "click_here_to_go_back_to_login": "로그인 화면으로 돌아가려면 여기를 클릭하세요", + "activate_your_account": "계정 활성화", + "thank_you_for_registering": "회원가입해 주셔서 감사합니다!", + "please_check_your_email_to_activate_your_account": "계정 활성화를 위해 이메일을 확인해 주세요.", + "sign_in_with": "다음으로 로그인", + "continue_with_google": "Google로 계속하기", + "sign_in_with_github": "GitHub로 로그인", + "continue_with_farcaster": "Farcaster로 계속하기", + "continue_with_your_wallet": "지갑으로 계속하기", + "stars_per_day": "일일 별점", + "media": "미디어", + "check_launch": "런치 확인", + "load_your_github_repository_from_settings_to_see_analytics": "설정에서 GitHub 저장소를 불러와 분석 정보를 확인하세요", + "stars": "별점", + "processing_stars": "별점 처리 중...", + "forks": "포크", + "registration_is_disabled": "회원가입이 비활성화되었습니다", + "login_instead": "대신 로그인하기", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "대화를 선택하고 채팅을 시작하세요.", + "adding_channel_redirecting_you": "채널 추가 중, 리디렉션합니다", + "could_not_add_provider": "공급자를 추가할 수 없습니다.", + "you_are_being_redirected_back": "이전 페이지로 리디렉션됩니다.", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "문제가 발생했습니다. 페이지를 새로고침해 주세요.", + "post_not_found": "게시물을 찾을 수 없습니다.", + "publication_date": "게시일:", + "analytics": "분석", + "launches": "출시", + "plugs": "홍보", + "billing": "결제", + "affiliate": "제휴", + "monday": "월요일", + "tuesday": "화요일", + "wednesday": "수요일", + "thursday": "목요일", + "friday": "금요일", + "saturday": "토요일", + "sunday": "일요일", + "can_t_change_date_remove_post_from_publication": "날짜를 변경할 수 없습니다. 게시물에서 제거해 주세요.", + "predicted_github_trending_change": "예상 GitHub 트렌드 변화", + "duplicate_post": "중복 게시물", + "preview_post": "게시물 미리보기", + "post_statistics": "게시물 통계", + "draft": "임시 저장", + "week_number": "{{number}}주차", + "top_title_edit_webhook": "웹훅 편집", + "top_title_add_webhook": "웹훅 추가", + "top_title_oh_no": "이런", + "top_title_auto_plug": "자동 플러그: {{title}}", + "top_title_edit_autopost": "자동 게시물 편집", + "top_title_add_autopost": "자동 게시물 추가", + "top_title_send_a_new_offer": "새로운 제안 보내기", + "top_title_media_library": "미디어 라이브러리", + "top_title_add_signature": "서명 추가", + "top_title_send_a_message_to": "{{name}}님에게 메시지 보내기", + "top_title_configure_provider": "공급자 구성", + "top_title_add_member": "멤버 추가", + "top_title_change_bot_picture": "봇 사진 변경", + "top_title_create_a_new_tag": "새 태그 만들기", + "top_title_select_company": "회사 선택", + "top_title_additional_settings": "추가 설정", + "top_title_time_table_slots": "시간표 슬롯", + "top_title_design_media": "미디어 디자인", + "top_title_edit_post": "게시물 편집", + "top_title_create_post": "게시물 만들기", + "top_title_move__add_to_customer": "고객에게 이동/추가", + "top_title_add_api_key_for": "{{name}}의 API 키 추가", + "top_title_instance_url": "인스턴스 URL", + "top_title_custom_url": "커스텀 URL", + "top_title_add_channel": "채널 추가", + "top_title_add_telegram": "텔레그램 추가", + "top_title_add_wrapcast": "랩캐스트 추가", + "top_title_comments_for": "{{date}}의 댓글", + "top_title_edit_signature": "서명 편집", + "label_name": "이름", + "label_url": "URL", + "label_title": "직함", + "label_subtitle": "부제목", + "label_email": "이메일", + "label_full_name": "전체 이름", + "label_password": "비밀번호", + "label_confirm_password": "비밀번호 확인", + "label_api_key": "API 키", + "label_instance_url": "인스턴스 URL", + "label_custom_url": "사용자 지정 URL", + "label_feedback": "피드백", + "label_bio": "소개", + "label_role": "역할", + "label_country": "국가", + "label_audience_size": "모든 플랫폼의 청중 규모", + "label_pick_time": "시간 선택", + "label_nickname": "별명", + "label_write_anything": "아무거나 작성하세요", + "label_output_format": "출력 형식", + "label_add_pictures": "사진 추가할까요?", + "label_hour": "시", + "label_minutes": "분", + "label_select_publication": "출판물 선택", + "label_canonical_link": "정식 링크", + "label_cover_picture": "커버 사진", + "label_tags": "태그", + "label_topics": "주제", + "label_tags_maximum_4": "태그 (최대 4개)", + "label_attachments": "첨부파일", + "label_type": "유형", + "label_thumbnail": "썸네일", + "label_who_can_see_this_video": "이 영상을 볼 수 있는 사람", + "label_content_posting_method": "콘텐츠 게시 방법", + "label_auto_add_music": "자동 음악 추가", + "label_duet": "듀엣", + "label_stitch": "스티치", + "label_comments": "댓글", + "label_disclose_video_content": "영상 내용 공개", + "label_your_brand": "당신의 브랜드", + "label_branded_content": "브랜드 콘텐츠", + "label_subreddit": "서브레딧", + "label_flair": "플레어", + "label_media": "미디어", + "label_search_subreddit": "서브레딧 검색", + "label_delay": "지연", + "label_post_type": "게시물 유형", + "label_collaborators": "협업자 (최대 3명) - 계정은 비공개일 수 없음", + "label_community": "커뮤니티", + "label_search_community": "커뮤니티 검색", + "label_channel": "채널", + "label_search_channel": "채널 검색", + "label_select_channel": "채널 선택", + "label_new_password": "새 비밀번호", + "label_repeat_password": "비밀번호 재입력", + "label_platform": "플랫폼", + "label_price_per_post": "게시글당 가격", + "label_integrations": "연동", + "label_code": "코드", + "label_should_sync_last_post": "현재 마지막 게시글을 동기화할까요?", + "label_when_post": "언제 게시할까요?", + "label_autogenerate_content": "콘텐츠 자동 생성", + "label_generate_picture": "이미지 생성할까요?", + "label_company": "회사", + "label_tag_color": "태그 색상", + "label_select_board": "게시판 선택", + "label_select_organization": "조직 선택", + "label_auto_add_signature": "서명 자동 추가할까요?", + "enable_color_picker": "색상 선택기 활성화", + "cancel_the_color_picker": "색상 선택기 취소", + "no_content_yet": "아직 콘텐츠가 없습니다", + "write_your_reply": "게시글을 작성하세요...", + "add_a_tag": "태그 추가", + "add_to_calendar": "캘린더에 추가", + "select_channels_from_circles": "위의 원에서 채널을 선택하세요", + "not_matching_order": "순서가 일치하지 않음", + "submit_for_order": "주문 제출", + "schedule": "일정", + "update": "업데이트", + "attachments": "첨부파일", + "tags": "태그", + "public_to_everyone": "모두에게 공개", + "mutual_follow_friends": "맞팔 친구", + "follower_of_creator": "크리에이터의 팔로워", + "self_only": "나만 보기", + "post_content_directly_to_tiktok": "콘텐츠를 TikTok에 바로 게시하기", + "upload_content_to_tiktok_without_posting": "게시하지 않고 TikTok에 콘텐츠 업로드하기", + "choose_upload_without_posting_description": "게시하지 않고 업로드를 선택하면 TikTok 앱 내에서 콘텐츠를 검토하고 편집한 후 게시할 수 있습니다. TikTok의 내장 편집 도구를 사용할 수 있으며, 게시 전에 최종 수정을 할 수 있습니다.", + "faq_am_i_going_to_be_charged_by_postiz": "Postiz에서 요금이 청구되나요?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "신용카드 정보를 확인하기 위해 Postiz는 $2를 보류했다가 즉시 해제합니다.", + "faq_can_i_trust_postiz_gitroom": "Postiz를 신뢰할 수 있나요?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz는 자랑스럽게 오픈소스입니다! 우리는 윤리적이고 투명한 문화를 믿으며, Postiz는 영원히 존재할 것입니다. 전체 코드를 확인하거나 개인 프로젝트에 사용할 수 있습니다. 오픈소스 저장소를 보려면 <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">여기를 클릭하세요</a>.", + "faq_what_are_channels": "채널이란 무엇인가요?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz를 통해 다양한 채널에 게시물을 예약할 수 있습니다.\n채널은 게시물을 예약할 수 있는 게시 플랫폼입니다.\n예를 들어, X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads, Pinterest에 게시물을 예약할 수 있습니다.", + "faq_what_are_team_members": "팀 멤버란 무엇인가요?", + "faq_if_you_have_a_team_with_multiple_members": "여러 명의 팀이 있다면, 워크스페이스에 초대하여 게시물 협업 및 개인 채널 추가가 가능합니다.", + "faq_what_is_ai_auto_complete": "AI 자동완성이란 무엇인가요?", + "faq_we_automate_chatgpt_to_help_you_write": "ChatGPT를 자동화하여 소셜 게시물과 글쓰기를 도와드립니다.", + "enter_email": "이메일 입력", + "are_you_sure": "정말로 하시겠습니까?", + "yes_delete_it": "네, 삭제하세요!", + "no_cancel": "아니요, 취소하세요!", + "are_you_sure_you_want_to_delete": "{{name}}을(를) 삭제하시겠습니까?", + "are_you_sure_you_want_to_delete_the_image": "이 이미지를 삭제하시겠습니까?", + "are_you_sure_you_want_to_logout": "로그아웃하시겠습니까?", + "yes_logout": "네, 로그아웃", + "are_you_sure_you_want_to_delete_this_slot": "이 슬롯을 삭제하시겠습니까?", + "are_you_sure_you_want_to_delete_this_subreddit": "이 서브레딧을 삭제하시겠습니까?", + "are_you_sure_you_want_to_close_the_window": "이 창을 닫으시겠습니까?", + "yes_close": "네, 닫기", + "link_copied_to_clipboard": "링크가 클립보드에 복사되었습니다", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "이 모달을 닫으시겠습니까? (모든 데이터가 사라집니다)", + "yes_close_it": "네, 닫으세요!", + "uploading_pictures": "사진 업로드 중...", + "agent_starting": "에이전트 시작 중", + "researching_your_content": "콘텐츠 조사 중...", + "understanding_the_category": "카테고리 이해 중...", + "finding_the_topic": "주제 찾는 중...", + "finding_popular_posts_to_match_with": "연관 인기 게시글 찾는 중...", + "generating_hook": "후킹 문구 생성 중...", + "generating_content": "콘텐츠 생성 중...", + "generating_pictures": "사진 생성 중...", + "finding_time_to_post": "게시할 시간 찾는 중...", + "write_anything": "아무거나 작성하세요", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "원하는 내용을 자유롭게 작성하고 링크도 추가할 수 있습니다. 저희가 대신 조사해드릴게요...", + "output_format": "출력 형식", + "add_pictures": "사진 추가하기?", + "7_days": "7일", + "30_days": "30일", + "90_days": "90일", + "start_7_days_free_trial": "7일 무료 체험 시작하기", + "change_language": "언어 변경", + "that_a_wrap": "여기까지입니다!\n\n이 스레드가 유익하셨다면:\n\n1. 더 많은 정보를 원하시면 @{{username}}를 팔로우하세요\n2. 아래 트윗을 리트윗해서 이 스레드를 여러분의 팔로워들과 공유하세요\n", + "post_as_images_carousel": "이미지 캐러셀로 게시", + "save_set": "세트 저장", + "separate_post": "게시물을 여러 개로 분리", + "label_who_can_reply_to_this_post": "이 게시물에 누가 답글을 달 수 있나요?", + "delete_integration": "통합 삭제", + "start_writing_your_post": "미리보기를 위해 게시글 작성을 시작하세요", + "billing_join_over": "이미 가입한", + "billing_entrepreneurs_count": "18,000+ 명의 창업가", + "billing_who_use": "이(가) 사용하는", + "billing_postiz_grow_social": "Postiz로 소셜 존재감을 키우세요", + "billing_no_risk_trial": "100% 무위험 무료 체험", + "billing_pay_nothing_7_days": "처음 7일 동안은 결제 없이 이용하세요", + "billing_cancel_anytime": "언제든지 간편하게 해지 가능", + "billing_choose_plan": "요금제 선택", + "billing_monthly": "월간", + "billing_yearly": "연간", + "billing_20_percent_off": "20% 할인", + "billing_features": "기능", + "billing_channel": "채널", + "billing_channels": "채널", + "billing_unlimited": "무제한", + "billing_posts_per_month": "월별 게시물", + "billing_unlimited_team_members": "무제한 팀원", + "billing_ai_auto_complete": "AI 자동완성", + "billing_ai_copilots": "AI 코파일럿", + "billing_ai_autocomplete": "AI 자동완성", + "billing_advanced_picture_editor": "고급 사진 편집기", + "billing_ai_images_per_month": "월별 AI 이미지", + "billing_ai_videos_per_month": "월별 AI 비디오", + "billing_billing_address": "청구 주소", + "billing_payment": "결제", + "billing_powered_by_stripe": "Stripe로 제공됨", + "billing_your_7_day_trial_is": "7일 무료 체험이", + "billing_100_percent_free": "100% 무료", + "billing_ending": "종료됩니다", + "billing_cancel_anytime_short": "언제든지 취소 가능합니다.", + "billing_pay_0_start_trial": "오늘 $0 결제 - 무료 체험을 시작하세요!", + "billing_pay_now": "지금 결제", + "billing_per_month": "/월" } diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index 5f75aa7a..09ee5205 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -1,507 +1,539 @@ { - "calendar": "Calendário", - "webhooks": "Webhooks", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks são uma forma de ser notificado quando algo acontece no Postiz via uma solicitação HTTP.", - "name": "Nome", - "url": "URL", - "edit": "Editar", - "delete": "Excluir", - "add_a_webhook": "Adicionar um webhook", - "save": "Salvar", - "send_test": "Enviar teste", - "select_role": "Selecionar função", - "video_made_with_ai": "Vídeo feito com IA", - "please_add_at_least": "Por favor, adicione pelo menos 20 caracteres", - "send_invitation_via_email": "Enviar convite por e-mail?", - "global_settings": "Configurações Globais", - "copy_id": "Copiar ID do canal", - "team_members": "Membros da equipe", - "invite_your_assistant_or_team_member_to_manage_your_account": "Convide seu assistente ou membro da equipe para gerenciar sua conta", - "remove": "Remover", - "add_another_member": "Adicionar outro membro", - "signatures": "Assinaturas", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Você pode adicionar assinaturas à sua conta para serem usadas em suas postagens.", - "content": "Conteúdo", - "auto_add": "Adicionar automaticamente?", - "actions": "Ações", - "use_signature": "Usar assinatura", - "add_a_signature": "Adicionar uma assinatura", - "no": "Não", - "yes": "Sim", - "your_git_repository": "Seu repositório Git", - "connect_your_github_repository_to_receive_updates_and_analytics": "Conecte seu repositório GitHub para receber atualizações e análises", - "connected": "Conectado:", - "disconnect": "Desconectar", - "connect_your_repository": "Conectar seu repositório", - "cancel": "Cancelar", - "connect": "Conectar", - "public_api": "API Pública", - "check_n8n": "Confira nosso node personalizado do N8N para o Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Use a API do Postiz para integrar com suas ferramentas.", - "read_how_to_use_it_over_the_documentation": "Leia como usá-la na documentação.", - "reveal": "Revelar", - "copy_key": "Copiar chave", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Conecte o servidor MCP do Postiz ao seu cliente (transmissão HTTP) para agendar suas postagens mais rapidamente!", - "share_with_a_client": "Compartilhar com um cliente", - "post": "Postar", - "comments": "Comentários", - "user": "Usuário", - "login_register_to_add_comments": "Faça login / Cadastre-se para adicionar comentários", - "status": "Status:", - "there_are_not_plugs_matching_your_channels": "Não há plugs compatíveis com seus canais", - "you_have_to_add_x_or_linkedin_or_threads": "Você precisa adicionar: X ou LinkedIn ou Threads", - "go_to_the_calendar_to_add_channels": "Vá ao calendário para adicionar canais", - "channels": "Canais", - "activate": "Ativar", - "this_channel_needs_to_be_refreshed": "Este canal precisa ser atualizado,", - "click_here_to_refresh": "clique aqui para atualizar", - "can_t_show_analytics_yet": "Ainda não é possível mostrar análises", - "you_have_to_add_social_media_channels": "Você precisa adicionar canais de Mídias Sociais", - "supported": "Suportado:", - "step": "ETAPA", - "skip_onboarding": "Pular introdução", - "onboarding": "Introdução", - "next": "Próximo", - "you_are_done_from_here_you_can": "Pronto, a partir daqui você pode:", - "view_analytics": "Ver análises", - "schedule_a_new_post": "Agendar uma nova publicação", - "to_sell_posts_you_would_have_to": "Para vender publicações, você precisa:", - "1_connect_at_least_one_channel": "1. Conectar pelo menos um canal", - "2_connect_you_bank_account": "2. Conectar sua conta bancária", - "go_back_to_connect_channels": "Voltar para conectar canais", - "move_to_the_seller_page_to_connect_you_bank": "Ir para a página do vendedor para conectar sua conta bancária", - "connect_channels": "Conectar canais", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Conecte seus canais de mídias sociais e plataformas de publicação para\n agendar publicações depois", - "social": "Social", - "publishing_platforms": "Plataformas de Publicação", - "no_channels": "Ainda não há canais", - "connect_your_accounts": "Conecte suas contas sociais para começar a agendar, publicar e analisar — tudo em um só lugar.", - "notifications": "Notificações", - "no_notifications": "Nenhuma notificação", - "send_message": "Enviar mensagem", - "mar_28": "28 de mar", - "there_are_no_messages_yet": "Ainda não há mensagens.", - "checkout_the_marketplace": "Confira o Marketplace", - "go_to_marketplace": "Ir para o marketplace", - "all_messages": "Todas as mensagens", - "previous": "Anterior", - "select_or_upload_pictures_maximum_5_at_a_time": "Selecione ou envie fotos (máximo de 5 por vez)", - "you_can_also_drag_drop_pictures": "Você também pode arrastar e soltar fotos", - "you_don_t_have_any_assets_yet": "Você ainda não tem nenhum ativo.", - "click_the_button_below_to_upload_one": "Clique no botão abaixo para enviar um", - "click_the_button_below_to_upload_many": "", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "Adicionar mídia selecionada", - "insert_media": "Inserir mídia", - "design_media": "Mídia de design", - "select": "Selecionar", - "editor": "Editor", - "clear": "Limpar", - "order_completed": "Pedido concluído", - "the_order_has_been_completed": "O pedido foi concluído", - "post_has_been_published": "A publicação foi publicada", - "url_1": "URL:", - "new_offer": "Nova oferta", - "platform": "Plataforma", - "posts": "Publicações", - "pay_accept_offer": "Pagar e aceitar oferta", - "accepted": "Aceito", - "post_draft": "Rascunho de Postagem", - "revision_needed": "Revisão Necessária", - "approve": "Aprovar", - "preview": "Pré-visualizar", - "revision_requested": "Revisão Solicitada", - "accepted_1": "ACEITO", - "cancelled_by_the_seller": "Cancelado pelo vendedor", - "please_select_your_country_where_your_business_is": "Por favor, selecione o país onde está o seu negócio.", - "select_country": "--SELECIONE O PAÍS--", - "connect_bank_account": "Conectar Conta Bancária", - "seller_mode": "Modo Vendedor", - "active": "Ativo", - "details": "Detalhes", - "audience_size": "Tamanho do Público", - "add_another_platform": "Adicionar outra plataforma", - "send_an_offer_for": "Enviar uma oferta por $", - "complete_order_and_pay_early": "Concluir pedido e pagar antecipadamente", - "order_in_progress": "Pedido em andamento", - "create_a_new_offer": "Criar uma nova oferta", - "orders": "Pedidos", - "price": "Preço", - "state": "Estado", - "showing": "Mostrando", - "to": "até", - "from": "de", - "results": "Resultados", - "content_writer": "Redator de Conteúdo", - "influencer": "Influenciador", - "request_service": "Solicitar Serviço", - "the_marketplace_is_not_opened_yet": "O marketplace ainda não foi aberto", - "check_again_soon": "Verifique novamente em breve!", - "filter": "Filtro", - "result": "Resultado", - "seller": "Vendedor", - "buyer": "Comprador", - "discord_support": "Suporte no Discord", - "teams": "Equipes", - "webhooks_1": "Webhooks", - "auto_post": "Postagem Automática", - "logout_from": "Sair de", - "join_10000_entrepreneurs_who_use_postiz": "Junte-se a mais de 10.000 empreendedores que usam o Postiz", - "to_manage_all_your_social_media_channels": "Para gerenciar todos os seus canais de mídia social", - "100_no_risk_trial": "Teste 100% sem risco", - "pay_nothing_for_the_first_7_days": "Não pague nada nos primeiros 7 dias", - "cancel_anytime_hassle_free": "Cancele a qualquer momento, sem complicações", - "add_free_subscription": "-- ADICIONAR ASSINATURA GRATUITA --", - "currently_impersonating": "Atualmente personificando", - "user_1": "usuário:", - "drag_n_drop_some_files_here": "Arraste e solte alguns arquivos aqui", - "add_time_slot": "Adicionar Horário", - "add_slot": "Adicionar horário", - "cancel_publication": "Cancelar publicação", - "statistics": "Estatísticas", - "loading": "Carregando", - "short_link": "Link curto", - "original_link": "Link original", - "clicks": "Cliques", - "selected_customer": "Cliente selecionado", - "customer": "Cliente:", - "repeat_post_every": "Repetir postagem a cada...", - "use_this_media": "Usar esta mídia", - "create_new_post": "Criar publicação", - "update_post": "Atualizar postagem existente", - "merge_comments_into_one_post": "Mesclar comentários em uma postagem", - "accounts_that_will_engage": "Contas que irão interagir:", - "day": "Dia", - "week": "Semana", - "month": "Mês", - "remove_from_customer": "Remover do cliente", - "show_more": "+ Mostrar mais", - "show_less": "- Mostrar menos", - "upload": "Enviar", - "ai": "IA", - "add_channel": "Adicionar canal", - "add_platform": "Adicionar plataforma", - "articles": "Artigos", - "add_comment": "Adicionar comentário", - "add_post": "Adicionar postagem em um tópico", - "add_comment_or_post": "Adicionar comentário / postagem", - "you_are_in_global_editing_mode": "Você está no modo de edição global", - "the_post_should_be_at_least_6_characters_long": "A publicação deve ter pelo menos 6 caracteres", - "are_you_sure_you_want_to_delete_post": "Tem certeza de que deseja excluir esta postagem?", - "post_deleted_successfully": "Postagem excluída com sucesso", - "delete_post": "Excluir publicação", - "save_as_draft": "Salvar como rascunho", - "post_now": "Publicar agora", - "please_add": "Por favor, adicione", - "to_your_telegram_group_channel_and_click_here": "ao seu grupo/canal do Telegram e clique aqui:", - "connect_telegram": "Conectar Telegram", - "please_add_the_following_command_in_your_chat": "Por favor, adicione o seguinte comando no seu chat:", - "copy": "Copiar", - "settings": "Configurações", - "integrations": "Integrações", - "add_integration": "Adicionar integração", - "you_are_now_editing_only": "Agora você está editando apenas", - "tag_a_company": "Marcar uma empresa", - "video_length_is_invalid_must_be_up_to": "A duração do vídeo é inválida, deve ser de até", - "seconds": "segundos", - "this_feature_available_only_for_photos": "Este recurso está disponível apenas para fotos, será adicionada uma música padrão que você poderá alterar depois.", - "allow_user_to": "Permitir que o usuário:", - "your_video_will_be_labeled_promotional": "Seu vídeo será rotulado como \"Conteúdo Promocional\".", - "this_cannot_be_changed_once_posted": "Isso não poderá ser alterado depois que seu vídeo for publicado.", - "turn_on_to_disclose_video_promotes": "Ative para informar que este vídeo promove bens ou serviços em troca de algo de valor. Seu vídeo pode promover você mesmo, um terceiro ou ambos.", - "you_are_promoting_yourself": "Você está promovendo a si mesmo ou à sua própria marca.", - "this_video_will_be_classified_brand_organic": "Este vídeo será classificado como Marca Orgânica.", - "you_are_promoting_another_brand": "Você está promovendo outra marca ou um terceiro.", - "this_video_will_be_classified_branded_content": "Este vídeo será classificado como Conteúdo de Marca.", - "by_posting_you_agree_to_tiktoks": "Ao publicar, você concorda com os Termos do TikTok", - "music_usage_confirmation": "Confirmação de Uso de Música", - "branded_content_policy": "Política de Conteúdo de Marca", - "select_1": "--Selecionar--", - "select_flair": "--Selecionar Flair--", - "link": "Link", - "add_subreddit": "Adicionar Subreddit", - "please_add_at_least_one_subreddit": "Por favor, adicione pelo menos um Subreddit", - "add_community": "Adicionar Comunidade", - "select_post_type": "Selecionar Tipo de Postagem...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Não conseguimos encontrar nenhum negócio conectado à sua página do LinkedIn.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Por favor, feche este diálogo, crie uma nova página e adicione um novo canal novamente.", - "select_linkedin_page": "Selecione a Página do Linkedin:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Não conseguimos encontrar nenhum negócio conectado às páginas selecionadas.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Recomendamos que você conecte todas as páginas e todos os negócios.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Por favor, feche este diálogo, exclua sua integração e adicione um novo canal novamente.", - "select_instagram_account": "Selecione a Conta do Instagram:", - "select_page": "Selecione a Página:", - "generate_image_with_ai": "Gerar imagem com IA", - "reconnect_channel": "Reconectar canal", - "update_credentials": "Atualizar Credenciais", - "additional_settings": "Configurações Adicionais", - "change_bot": "Trocar Bot", - "move_add_to_customer": "Mover / adicionar para cliente", - "edit_time_slots": "Editar Faixas de Horário", - "enable_channel": "Ativar canal", - "disable_channel": "Desativar canal", - "add": "Adicionar", - "short_post": "Postagem curta", - "long_post": "Postagem longa", - "a_thread_with_short_posts": "Um tópico com postagens curtas", - "a_thread_with_long_posts": "Um tópico com postagens longas", - "personal_voice_i_am_happy_to_announce": "Voz pessoal (\"Estou feliz em anunciar\")", - "company_voice_we_are_happy_to_announce": "Voz da empresa (\"Estamos felizes em anunciar\")", - "generate": "Gerar", - "generate_posts": "Gerar postagens", - "purchase_a_life_time_pro_account_with_sol_199": "Adquira uma conta PRO vitalícia com SOL (US$199). Por favor, esteja ciente de que não há reembolso para esta compra.", - "purchase_now": "Comprar agora", - "pay_today": "Pagar hoje", - "we_are_sorry_to_see_you_go": "Lamentamos ver você partir :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Você poderia nos dizer brevemente o que poderíamos ter feito melhor?", - "cancel_subscription": "Cancelar assinatura", - "plans": "Planos", - "monthly": "MENSAL", - "yearly": "ANUAL", - "reactivate_subscription": "Reativar assinatura", - "update_payment_method_invoices_history": "Atualizar método de pagamento / Histórico de faturas", - "cancel_subscription_1": "Cancelar assinatura", - "your_subscription_will_be_canceled_at": "Sua assinatura será cancelada em", - "you_will_never_be_charged_again": "Você nunca mais será cobrado", - "current_package": "Pacote Atual:", - "next_package": "Próximo Pacote:", - "claim": "Resgatar", - "frequently_asked_questions": "Perguntas Frequentes", - "autopost": "Autopost", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "O Autopost pode publicar automaticamente seus novos itens RSS nas redes sociais", - "title": "Título", - "add_an_autopost": "Adicionar um autopost", - "post_content": "Conteúdo da postagem", - "sign_up": "Cadastrar-se", - "or": "OU", - "by_registering_you_agree_to_our": "Ao se registrar, você concorda com nossos", - "and": "e", - "terms_of_service": "Termos de Serviço", - "privacy_policy": "Política de Privacidade", - "create_account": "Criar Conta", - "already_have_an_account": "Já tem uma conta?", - "sign_in": "Entrar", - "sign_in_1": "Entrar", - "don_t_have_an_account": "Não tem uma conta?", - "forgot_password": "Esqueceu a senha", - "forgot_password_1": "Esqueceu a Senha", - "send_password_reset_email": "Enviar e-mail de redefinição de senha", - "go_back_to_login": "Voltar para o login", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Enviamos um e-mail para você com um link para redefinir sua senha.", - "change_password": "Alterar senha", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Redefinimos sua senha com sucesso. Agora você pode fazer login com sua", - "click_here_to_go_back_to_login": "Clique aqui para voltar ao login", - "activate_your_account": "Ative sua conta", - "thank_you_for_registering": "Obrigado por se registrar!", - "please_check_your_email_to_activate_your_account": "Por favor, verifique seu e-mail para ativar sua conta.", - "sign_in_with": "Entrar com", - "continue_with_google": "Continuar com o Google", - "sign_in_with_github": "Entrar com o GitHub", - "continue_with_farcaster": "Continuar com o Farcaster", - "continue_with_your_wallet": "Continuar com sua carteira", - "stars_per_day": "Estrelas por dia", - "media": "Mídia", - "check_launch": "Verificar lançamento", - "load_your_github_repository_from_settings_to_see_analytics": "Carregue seu repositório do GitHub nas configurações para ver as análises", - "stars": "Estrelas", - "processing_stars": "Processando estrelas...", - "forks": "Forks", - "registration_is_disabled": "O registro está desativado", - "login_instead": "Faça login em vez disso", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Selecione uma conversa e comece a conversar.", - "adding_channel_redirecting_you": "Adicionando canal, redirecionando você", - "could_not_add_provider": "Não foi possível adicionar o provedor.", - "you_are_being_redirected_back": "Você está sendo redirecionado de volta", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Estamos enfrentando alguma dificuldade, tente atualizar a página", - "post_not_found": "Post não encontrado", - "publication_date": "Data de publicação:", - "analytics": "Análises", - "launches": "Lançamentos", - "plugs": "Divulgações", - "billing": "Cobrança", - "affiliate": "Afiliado", - "monday": "Segunda-feira", - "tuesday": "Terça-feira", - "wednesday": "Quarta-feira", - "thursday": "Quinta-feira", - "friday": "Sexta-feira", - "saturday": "Sábado", - "sunday": "Domingo", - "can_t_change_date_remove_post_from_publication": "Não é possível alterar a data, remova o post da publicação", - "predicted_github_trending_change": "Mudança prevista no Trending do GitHub", - "duplicate_post": "Post duplicado", - "preview_post": "Visualizar post", - "post_statistics": "Estatísticas do post", - "draft": "Rascunho", - "week_number": "Semana {{number}}", - "top_title_edit_webhook": "Editar webhook", - "top_title_add_webhook": "Adicionar webhook", - "top_title_oh_no": "Oh não", - "top_title_auto_plug": "Auto Plug: {{title}}", - "top_title_edit_autopost": "Editar autopost", - "top_title_add_autopost": "Adicionar autopost", - "top_title_send_a_new_offer": "Enviar uma nova oferta", - "top_title_media_library": "Biblioteca de Mídia", - "top_title_add_signature": "Adicionar assinatura", - "top_title_send_a_message_to": "Enviar uma mensagem para {{name}}", - "top_title_configure_provider": "Configurar Provedor", - "top_title_add_member": "Adicionar Membro", - "top_title_change_bot_picture": "Alterar Foto do Bot", - "top_title_create_a_new_tag": "Criar uma nova tag", - "top_title_select_company": "Selecionar Empresa", - "top_title_additional_settings": "Configurações Adicionais", - "top_title_time_table_slots": "Horários da Tabela", - "top_title_design_media": "Design de Mídia", - "top_title_edit_post": "Editar Publicação", - "top_title_create_post": "Criar Publicação", - "top_title_move__add_to_customer": "Mover / Adicionar ao cliente", - "top_title_add_api_key_for": "Adicionar chave de API para {{name}}", - "top_title_instance_url": "URL da Instância", - "top_title_custom_url": "URL Personalizada", - "top_title_add_channel": "Adicionar Canal", - "top_title_add_telegram": "Adicionar Telegram", - "top_title_add_wrapcast": "Adicionar Wrapcast", - "top_title_comments_for": "Comentários para {{date}}", - "top_title_edit_signature": "Editar assinatura", - "label_name": "Nome", - "label_url": "URL", - "label_title": "Título", - "label_subtitle": "Subtítulo", - "label_email": "E-mail", - "label_full_name": "Nome completo", - "label_password": "Senha", - "label_confirm_password": "Confirmar senha", - "label_api_key": "Chave de API", - "label_instance_url": "URL da instância", - "label_custom_url": "URL personalizada", - "label_feedback": "Feedback", - "label_bio": "Biografia", - "label_role": "Função", - "label_country": "País", - "label_audience_size": "Tamanho do público em todas as plataformas", - "label_pick_time": "Escolher horário", - "label_nickname": "Apelido", - "label_write_anything": "Escreva qualquer coisa", - "label_output_format": "Formato de saída", - "label_add_pictures": "Adicionar imagens?", - "label_hour": "Hora", - "label_minutes": "Minutos", - "label_select_publication": "Selecionar publicação", - "label_canonical_link": "Link canônico", - "label_cover_picture": "Imagem de capa", - "label_tags": "Tags", - "label_topics": "Tópicos", - "label_tags_maximum_4": "Tags (Máximo 4)", - "label_attachments": "Anexos", - "label_type": "Tipo", - "label_thumbnail": "Miniatura", - "label_who_can_see_this_video": "Quem pode ver este vídeo?", - "label_content_posting_method": "Método de postagem de conteúdo", - "label_auto_add_music": "Adicionar música automaticamente", - "label_duet": "Dueto", - "label_stitch": "Stitch", - "label_comments": "Comentários", - "label_disclose_video_content": "Divulgar conteúdo do vídeo", - "label_your_brand": "Sua marca", - "label_branded_content": "Conteúdo de marca", - "label_subreddit": "Subreddit", - "label_flair": "Flair", - "label_media": "Mídia", - "label_search_subreddit": "Buscar Subreddit", - "label_delay": "Atraso", - "label_post_type": "Tipo de postagem", - "label_collaborators": "Colaboradores (máx. 3) - contas não podem ser privadas", - "label_community": "Comunidade", - "label_search_community": "Pesquisar comunidade", - "label_channel": "Canal", - "label_search_channel": "Pesquisar canal", - "label_select_channel": "Selecionar canal", - "label_new_password": "Nova senha", - "label_repeat_password": "Repetir senha", - "label_platform": "Plataforma", - "label_price_per_post": "Preço por postagem", - "label_integrations": "Integrações", - "label_code": "Código", - "label_should_sync_last_post": "Devemos sincronizar a última postagem atual?", - "label_when_post": "Quando devemos postar?", - "label_autogenerate_content": "Gerar conteúdo automaticamente", - "label_generate_picture": "Gerar imagem?", - "label_company": "Empresa", - "label_tag_color": "Cor da tag", - "label_select_board": "Selecionar quadro", - "label_select_organization": "Selecionar organização", - "label_auto_add_signature": "Adicionar assinatura automaticamente?", - "enable_color_picker": "Ativar seletor de cores", - "cancel_the_color_picker": "Cancelar o seletor de cores", - "no_content_yet": "Ainda não há conteúdo", - "write_your_reply": "Escreva sua postagem...", - "add_a_tag": "Adicionar uma tag", - "add_to_calendar": "Adicionar ao calendário", - "select_channels_from_circles": "Selecione os canais dos círculos acima", - "not_matching_order": "Ordem não correspondente", - "submit_for_order": "Enviar para pedido", - "schedule": "Agendar", - "update": "Atualizar", - "attachments": "Anexos", - "tags": "Tags", - "public_to_everyone": "Público para todos", - "mutual_follow_friends": "Amigos com seguimento mútuo", - "follower_of_creator": "Seguidor do criador", - "self_only": "Apenas eu", - "post_content_directly_to_tiktok": "Publicar conteúdo diretamente no TikTok", - "upload_content_to_tiktok_without_posting": "Enviar conteúdo para o TikTok sem publicar", - "choose_upload_without_posting_description": "Escolha enviar sem publicar se quiser revisar e editar seu conteúdo no aplicativo do TikTok antes de publicar. Isso lhe dá acesso às ferramentas de edição do próprio TikTok e permite fazer ajustes finais antes de postar.", - "faq_am_i_going_to_be_charged_by_postiz": "Vou ser cobrado pelo Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar as informações do cartão de crédito, o Postiz irá reter R$2 e liberá-lo imediatamente", - "faq_can_i_trust_postiz_gitroom": "Posso confiar no Postiz?", - "faq_postiz_gitroom_is_proudly_open_source": "O Postiz é orgulhosamente open-source! Acreditamos em uma cultura ética e transparente, o que significa que o Postiz viverá para sempre. Você pode conferir todo o código ou usá-lo em projetos pessoais. Para ver o repositório open-source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">clique aqui</a>.", - "faq_what_are_channels": "O que são canais?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "O Postiz permite que você agende suas publicações entre diferentes canais.\nUm canal é uma plataforma de publicação onde você pode agendar seus posts.\nPor exemplo, você pode agendar suas publicações no X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads e Pinterest.", - "faq_what_are_team_members": "O que são membros da equipe?", - "faq_if_you_have_a_team_with_multiple_members": "Se você tem uma equipe com vários membros, pode convidá-los para o seu workspace para colaborar nas postagens e adicionar seus canais pessoais", - "faq_what_is_ai_auto_complete": "O que é autocompletar por IA?", - "faq_we_automate_chatgpt_to_help_you_write": "Automatizamos o ChatGPT para ajudar você a escrever postagens e artigos para redes sociais.", - "enter_email": "Digite o e-mail", - "are_you_sure": "Tem certeza?", - "yes_delete_it": "Sim, exclua!", - "no_cancel": "Não, cancelar!", - "are_you_sure_you_want_to_delete": "Tem certeza de que deseja excluir {{name}}?", - "are_you_sure_you_want_to_delete_the_image": "Tem certeza de que deseja excluir a imagem?", - "are_you_sure_you_want_to_logout": "Tem certeza de que deseja sair?", - "yes_logout": "Sim, sair", - "are_you_sure_you_want_to_delete_this_slot": "Tem certeza de que deseja excluir este slot?", - "are_you_sure_you_want_to_delete_this_subreddit": "Tem certeza de que deseja excluir este Subreddit?", - "are_you_sure_you_want_to_close_the_window": "Tem certeza de que deseja fechar a janela?", - "yes_close": "Sim, fechar", - "link_copied_to_clipboard": "Link copiado para a área de transferência", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Tem certeza de que deseja fechar este modal? (todos os dados serão perdidos)", - "yes_close_it": "Sim, feche!", - "uploading_pictures": "Enviando imagens...", - "agent_starting": "Agente iniciando", - "researching_your_content": "Pesquisando seu conteúdo...", - "understanding_the_category": "Entendendo a categoria...", - "finding_the_topic": "Encontrando o tópico...", - "finding_popular_posts_to_match_with": "Encontrando posts populares para combinar...", - "generating_hook": "Gerando gancho...", - "generating_content": "Gerando conteúdo...", - "generating_pictures": "Gerando imagens...", - "finding_time_to_post": "Encontrando o melhor horário para postar...", - "write_anything": "Escreva qualquer coisa", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Você pode escrever o que quiser e também adicionar links, nós faremos a pesquisa para você...", - "output_format": "Formato de saída", - "add_pictures": "Adicionar imagens?", - "7_days": "7 Dias", - "30_days": "30 Dias", - "90_days": "90 Dias", - "start_7_days_free_trial": "Comece o teste gratuito de 7 dias", - "change_language": "Mudar idioma", - "that_a_wrap": "É isso aí!\n\nSe você gostou deste fio:\n\n1. Siga-me @{{username}} para ver mais conteúdos como este\n2. Dê RT no tweet abaixo para compartilhar este fio com seu público\n", - "post_as_images_carousel": "Publicar como carrossel de imagens", - "save_set": "Salvar conjunto", - "separate_post": "Separar postagem em várias postagens", - "label_who_can_reply_to_this_post": "Quem pode responder a esta postagem?", - "delete_integration": "Excluir integração", - "start_writing_your_post": "Comece a escrever sua postagem para visualizar" + "calendar": "Calendário", + "webhooks": "Webhooks", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks são uma forma de ser notificado quando algo acontece no Postiz via uma solicitação HTTP.", + "name": "Nome", + "url": "URL", + "edit": "Editar", + "delete": "Excluir", + "add_a_webhook": "Adicionar um webhook", + "save": "Salvar", + "send_test": "Enviar teste", + "select_role": "Selecionar função", + "video_made_with_ai": "Vídeo feito com IA", + "please_add_at_least": "Por favor, adicione pelo menos 20 caracteres", + "send_invitation_via_email": "Enviar convite por e-mail?", + "global_settings": "Configurações Globais", + "copy_id": "Copiar ID do canal", + "team_members": "Membros da equipe", + "invite_your_assistant_or_team_member_to_manage_your_account": "Convide seu assistente ou membro da equipe para gerenciar sua conta", + "remove": "Remover", + "add_another_member": "Adicionar outro membro", + "signatures": "Assinaturas", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Você pode adicionar assinaturas à sua conta para serem usadas em suas postagens.", + "content": "Conteúdo", + "auto_add": "Adicionar automaticamente?", + "actions": "Ações", + "use_signature": "Usar assinatura", + "add_a_signature": "Adicionar uma assinatura", + "no": "Não", + "yes": "Sim", + "your_git_repository": "Seu repositório Git", + "connect_your_github_repository_to_receive_updates_and_analytics": "Conecte seu repositório GitHub para receber atualizações e análises", + "connected": "Conectado:", + "disconnect": "Desconectar", + "connect_your_repository": "Conectar seu repositório", + "cancel": "Cancelar", + "connect": "Conectar", + "public_api": "API Pública", + "check_n8n": "Confira nosso node personalizado do N8N para o Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Use a API do Postiz para integrar com suas ferramentas.", + "read_how_to_use_it_over_the_documentation": "Leia como usá-la na documentação.", + "reveal": "Revelar", + "copy_key": "Copiar chave", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Conecte o servidor MCP do Postiz ao seu cliente (transmissão HTTP) para agendar suas postagens mais rapidamente!", + "share_with_a_client": "Compartilhar com um cliente", + "post": "Postar", + "comments": "Comentários", + "user": "Usuário", + "login_register_to_add_comments": "Faça login / Cadastre-se para adicionar comentários", + "status": "Status:", + "there_are_not_plugs_matching_your_channels": "Não há plugs compatíveis com seus canais", + "you_have_to_add_x_or_linkedin_or_threads": "Você precisa adicionar: X ou LinkedIn ou Threads", + "go_to_the_calendar_to_add_channels": "Vá ao calendário para adicionar canais", + "channels": "Canais", + "activate": "Ativar", + "this_channel_needs_to_be_refreshed": "Este canal precisa ser atualizado,", + "click_here_to_refresh": "clique aqui para atualizar", + "can_t_show_analytics_yet": "Ainda não é possível mostrar análises", + "you_have_to_add_social_media_channels": "Você precisa adicionar canais de Mídias Sociais", + "supported": "Suportado:", + "step": "ETAPA", + "skip_onboarding": "Pular introdução", + "onboarding": "Introdução", + "next": "Próximo", + "you_are_done_from_here_you_can": "Pronto, a partir daqui você pode:", + "view_analytics": "Ver análises", + "schedule_a_new_post": "Agendar uma nova publicação", + "to_sell_posts_you_would_have_to": "Para vender publicações, você precisa:", + "1_connect_at_least_one_channel": "1. Conectar pelo menos um canal", + "2_connect_you_bank_account": "2. Conectar sua conta bancária", + "go_back_to_connect_channels": "Voltar para conectar canais", + "move_to_the_seller_page_to_connect_you_bank": "Ir para a página do vendedor para conectar sua conta bancária", + "connect_channels": "Conectar canais", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Conecte seus canais de mídias sociais e plataformas de publicação para\n agendar publicações depois", + "social": "Social", + "publishing_platforms": "Plataformas de Publicação", + "no_channels": "Ainda não há canais", + "connect_your_accounts": "Conecte suas contas sociais para começar a agendar, publicar e analisar — tudo em um só lugar.", + "notifications": "Notificações", + "no_notifications": "Nenhuma notificação", + "send_message": "Enviar mensagem", + "mar_28": "28 de mar", + "there_are_no_messages_yet": "Ainda não há mensagens.", + "checkout_the_marketplace": "Confira o Marketplace", + "go_to_marketplace": "Ir para o marketplace", + "all_messages": "Todas as mensagens", + "previous": "Anterior", + "select_or_upload_pictures_maximum_5_at_a_time": "Selecione ou envie fotos (máximo de 5 por vez)", + "you_can_also_drag_drop_pictures": "Você também pode arrastar e soltar fotos", + "you_don_t_have_any_assets_yet": "Você ainda não tem nenhum ativo.", + "click_the_button_below_to_upload_one": "Clique no botão abaixo para enviar um", + "click_the_button_below_to_upload_other": "Clique no botão abaixo para enviar vários", + "add_selected_media": "Adicionar mídia selecionada", + "insert_media": "Inserir mídia", + "design_media": "Mídia de design", + "select": "Selecionar", + "editor": "Editor", + "clear": "Limpar", + "order_completed": "Pedido concluído", + "the_order_has_been_completed": "O pedido foi concluído", + "post_has_been_published": "A publicação foi publicada", + "url_1": "URL:", + "new_offer": "Nova oferta", + "platform": "Plataforma", + "posts": "Publicações", + "pay_accept_offer": "Pagar e aceitar oferta", + "accepted": "Aceito", + "post_draft": "Rascunho de Postagem", + "revision_needed": "Revisão Necessária", + "approve": "Aprovar", + "preview": "Pré-visualizar", + "revision_requested": "Revisão Solicitada", + "accepted_1": "ACEITO", + "cancelled_by_the_seller": "Cancelado pelo vendedor", + "please_select_your_country_where_your_business_is": "Por favor, selecione o país onde está o seu negócio.", + "select_country": "--SELECIONE O PAÍS--", + "connect_bank_account": "Conectar Conta Bancária", + "seller_mode": "Modo Vendedor", + "active": "Ativo", + "details": "Detalhes", + "audience_size": "Tamanho do Público", + "add_another_platform": "Adicionar outra plataforma", + "send_an_offer_for": "Enviar uma oferta por $", + "complete_order_and_pay_early": "Concluir pedido e pagar antecipadamente", + "order_in_progress": "Pedido em andamento", + "create_a_new_offer": "Criar uma nova oferta", + "orders": "Pedidos", + "price": "Preço", + "state": "Estado", + "showing": "Mostrando", + "to": "até", + "from": "de", + "results": "Resultados", + "content_writer": "Redator de Conteúdo", + "influencer": "Influenciador", + "request_service": "Solicitar Serviço", + "the_marketplace_is_not_opened_yet": "O marketplace ainda não foi aberto", + "check_again_soon": "Verifique novamente em breve!", + "filter": "Filtro", + "result": "Resultado", + "seller": "Vendedor", + "buyer": "Comprador", + "discord_support": "Suporte no Discord", + "teams": "Equipes", + "webhooks_1": "Webhooks", + "auto_post": "Postagem Automática", + "logout_from": "Sair de", + "join_10000_entrepreneurs_who_use_postiz": "Junte-se a mais de 10.000 empreendedores que usam o Postiz", + "to_manage_all_your_social_media_channels": "Para gerenciar todos os seus canais de mídia social", + "100_no_risk_trial": "Teste 100% sem risco", + "pay_nothing_for_the_first_7_days": "Não pague nada nos primeiros 7 dias", + "cancel_anytime_hassle_free": "Cancele a qualquer momento, sem complicações", + "add_free_subscription": "-- ADICIONAR ASSINATURA GRATUITA --", + "currently_impersonating": "Atualmente personificando", + "user_1": "usuário:", + "drag_n_drop_some_files_here": "Arraste e solte alguns arquivos aqui", + "add_time_slot": "Adicionar Horário", + "add_slot": "Adicionar horário", + "cancel_publication": "Cancelar publicação", + "statistics": "Estatísticas", + "loading": "Carregando", + "short_link": "Link curto", + "original_link": "Link original", + "clicks": "Cliques", + "selected_customer": "Cliente selecionado", + "customer": "Cliente:", + "repeat_post_every": "Repetir postagem a cada...", + "use_this_media": "Usar esta mídia", + "create_new_post": "Criar publicação", + "update_post": "Atualizar postagem existente", + "merge_comments_into_one_post": "Mesclar comentários em uma postagem", + "accounts_that_will_engage": "Contas que irão interagir:", + "day": "Dia", + "week": "Semana", + "month": "Mês", + "remove_from_customer": "Remover do cliente", + "show_more": "+ Mostrar mais", + "show_less": "- Mostrar menos", + "upload": "Enviar", + "ai": "IA", + "add_channel": "Adicionar canal", + "add_platform": "Adicionar plataforma", + "articles": "Artigos", + "add_comment": "Adicionar comentário", + "add_post": "Adicionar postagem em um tópico", + "add_comment_or_post": "Adicionar comentário / postagem", + "you_are_in_global_editing_mode": "Você está no modo de edição global", + "the_post_should_be_at_least_6_characters_long": "A publicação deve ter pelo menos 6 caracteres", + "are_you_sure_you_want_to_delete_post": "Tem certeza de que deseja excluir esta postagem?", + "post_deleted_successfully": "Postagem excluída com sucesso", + "delete_post": "Excluir publicação", + "save_as_draft": "Salvar como rascunho", + "post_now": "Publicar agora", + "please_add": "Por favor, adicione", + "to_your_telegram_group_channel_and_click_here": "ao seu grupo/canal do Telegram e clique aqui:", + "connect_telegram": "Conectar Telegram", + "please_add_the_following_command_in_your_chat": "Por favor, adicione o seguinte comando no seu chat:", + "copy": "Copiar", + "settings": "Configurações", + "integrations": "Integrações", + "add_integration": "Adicionar integração", + "you_are_now_editing_only": "Agora você está editando apenas", + "tag_a_company": "Marcar uma empresa", + "video_length_is_invalid_must_be_up_to": "A duração do vídeo é inválida, deve ser de até", + "seconds": "segundos", + "this_feature_available_only_for_photos": "Este recurso está disponível apenas para fotos, será adicionada uma música padrão que você poderá alterar depois.", + "allow_user_to": "Permitir que o usuário:", + "your_video_will_be_labeled_promotional": "Seu vídeo será rotulado como \"Conteúdo Promocional\".", + "this_cannot_be_changed_once_posted": "Isso não poderá ser alterado depois que seu vídeo for publicado.", + "turn_on_to_disclose_video_promotes": "Ative para informar que este vídeo promove bens ou serviços em troca de algo de valor. Seu vídeo pode promover você mesmo, um terceiro ou ambos.", + "you_are_promoting_yourself": "Você está promovendo a si mesmo ou à sua própria marca.", + "this_video_will_be_classified_brand_organic": "Este vídeo será classificado como Marca Orgânica.", + "you_are_promoting_another_brand": "Você está promovendo outra marca ou um terceiro.", + "this_video_will_be_classified_branded_content": "Este vídeo será classificado como Conteúdo de Marca.", + "by_posting_you_agree_to_tiktoks": "Ao publicar, você concorda com os Termos do TikTok", + "music_usage_confirmation": "Confirmação de Uso de Música", + "branded_content_policy": "Política de Conteúdo de Marca", + "select_1": "--Selecionar--", + "select_flair": "--Selecionar Flair--", + "link": "Link", + "add_subreddit": "Adicionar Subreddit", + "please_add_at_least_one_subreddit": "Por favor, adicione pelo menos um Subreddit", + "add_community": "Adicionar Comunidade", + "select_post_type": "Selecionar Tipo de Postagem...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Não conseguimos encontrar nenhum negócio conectado à sua página do LinkedIn.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Por favor, feche este diálogo, crie uma nova página e adicione um novo canal novamente.", + "select_linkedin_page": "Selecione a Página do Linkedin:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Não conseguimos encontrar nenhum negócio conectado às páginas selecionadas.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Recomendamos que você conecte todas as páginas e todos os negócios.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Por favor, feche este diálogo, exclua sua integração e adicione um novo canal novamente.", + "select_instagram_account": "Selecione a Conta do Instagram:", + "select_page": "Selecione a Página:", + "generate_image_with_ai": "Gerar imagem com IA", + "reconnect_channel": "Reconectar canal", + "update_credentials": "Atualizar Credenciais", + "additional_settings": "Configurações Adicionais", + "change_bot": "Trocar Bot", + "move_add_to_customer": "Mover / adicionar para cliente", + "edit_time_slots": "Editar Faixas de Horário", + "enable_channel": "Ativar canal", + "disable_channel": "Desativar canal", + "add": "Adicionar", + "short_post": "Postagem curta", + "long_post": "Postagem longa", + "a_thread_with_short_posts": "Um tópico com postagens curtas", + "a_thread_with_long_posts": "Um tópico com postagens longas", + "personal_voice_i_am_happy_to_announce": "Voz pessoal (\"Estou feliz em anunciar\")", + "company_voice_we_are_happy_to_announce": "Voz da empresa (\"Estamos felizes em anunciar\")", + "generate": "Gerar", + "generate_posts": "Gerar postagens", + "purchase_a_life_time_pro_account_with_sol_199": "Adquira uma conta PRO vitalícia com SOL (US$199). Por favor, esteja ciente de que não há reembolso para esta compra.", + "purchase_now": "Comprar agora", + "pay_today": "Pagar hoje", + "we_are_sorry_to_see_you_go": "Lamentamos ver você partir :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Você poderia nos dizer brevemente o que poderíamos ter feito melhor?", + "cancel_subscription": "Cancelar assinatura", + "plans": "Planos", + "monthly": "MENSAL", + "yearly": "ANUAL", + "reactivate_subscription": "Reativar assinatura", + "update_payment_method_invoices_history": "Atualizar método de pagamento / Histórico de faturas", + "cancel_subscription_1": "Cancelar assinatura", + "your_subscription_will_be_canceled_at": "Sua assinatura será cancelada em", + "you_will_never_be_charged_again": "Você nunca mais será cobrado", + "current_package": "Pacote Atual:", + "next_package": "Próximo Pacote:", + "claim": "Resgatar", + "frequently_asked_questions": "Perguntas Frequentes", + "autopost": "Autopost", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "O Autopost pode publicar automaticamente seus novos itens RSS nas redes sociais", + "title": "Título", + "add_an_autopost": "Adicionar um autopost", + "post_content": "Conteúdo da postagem", + "sign_up": "Cadastrar-se", + "or": "OU", + "by_registering_you_agree_to_our": "Ao se registrar, você concorda com nossos", + "and": "e", + "terms_of_service": "Termos de Serviço", + "privacy_policy": "Política de Privacidade", + "create_account": "Criar Conta", + "already_have_an_account": "Já tem uma conta?", + "sign_in": "Entrar", + "sign_in_1": "Entrar", + "don_t_have_an_account": "Não tem uma conta?", + "forgot_password": "Esqueceu a senha", + "forgot_password_1": "Esqueceu a Senha", + "send_password_reset_email": "Enviar e-mail de redefinição de senha", + "go_back_to_login": "Voltar para o login", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Enviamos um e-mail para você com um link para redefinir sua senha.", + "change_password": "Alterar senha", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Redefinimos sua senha com sucesso. Agora você pode fazer login com sua", + "click_here_to_go_back_to_login": "Clique aqui para voltar ao login", + "activate_your_account": "Ative sua conta", + "thank_you_for_registering": "Obrigado por se registrar!", + "please_check_your_email_to_activate_your_account": "Por favor, verifique seu e-mail para ativar sua conta.", + "sign_in_with": "Entrar com", + "continue_with_google": "Continuar com o Google", + "sign_in_with_github": "Entrar com o GitHub", + "continue_with_farcaster": "Continuar com o Farcaster", + "continue_with_your_wallet": "Continuar com sua carteira", + "stars_per_day": "Estrelas por dia", + "media": "Mídia", + "check_launch": "Verificar lançamento", + "load_your_github_repository_from_settings_to_see_analytics": "Carregue seu repositório do GitHub nas configurações para ver as análises", + "stars": "Estrelas", + "processing_stars": "Processando estrelas...", + "forks": "Forks", + "registration_is_disabled": "O registro está desativado", + "login_instead": "Faça login em vez disso", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Selecione uma conversa e comece a conversar.", + "adding_channel_redirecting_you": "Adicionando canal, redirecionando você", + "could_not_add_provider": "Não foi possível adicionar o provedor.", + "you_are_being_redirected_back": "Você está sendo redirecionado de volta", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Estamos enfrentando alguma dificuldade, tente atualizar a página", + "post_not_found": "Post não encontrado", + "publication_date": "Data de publicação:", + "analytics": "Análises", + "launches": "Lançamentos", + "plugs": "Divulgações", + "billing": "Cobrança", + "affiliate": "Afiliado", + "monday": "Segunda-feira", + "tuesday": "Terça-feira", + "wednesday": "Quarta-feira", + "thursday": "Quinta-feira", + "friday": "Sexta-feira", + "saturday": "Sábado", + "sunday": "Domingo", + "can_t_change_date_remove_post_from_publication": "Não é possível alterar a data, remova o post da publicação", + "predicted_github_trending_change": "Mudança prevista no Trending do GitHub", + "duplicate_post": "Post duplicado", + "preview_post": "Visualizar post", + "post_statistics": "Estatísticas do post", + "draft": "Rascunho", + "week_number": "Semana {{number}}", + "top_title_edit_webhook": "Editar webhook", + "top_title_add_webhook": "Adicionar webhook", + "top_title_oh_no": "Oh não", + "top_title_auto_plug": "Auto Plug: {{title}}", + "top_title_edit_autopost": "Editar autopost", + "top_title_add_autopost": "Adicionar autopost", + "top_title_send_a_new_offer": "Enviar uma nova oferta", + "top_title_media_library": "Biblioteca de Mídia", + "top_title_add_signature": "Adicionar assinatura", + "top_title_send_a_message_to": "Enviar uma mensagem para {{name}}", + "top_title_configure_provider": "Configurar Provedor", + "top_title_add_member": "Adicionar Membro", + "top_title_change_bot_picture": "Alterar Foto do Bot", + "top_title_create_a_new_tag": "Criar uma nova tag", + "top_title_select_company": "Selecionar Empresa", + "top_title_additional_settings": "Configurações Adicionais", + "top_title_time_table_slots": "Horários da Tabela", + "top_title_design_media": "Design de Mídia", + "top_title_edit_post": "Editar Publicação", + "top_title_create_post": "Criar Publicação", + "top_title_move__add_to_customer": "Mover / Adicionar ao cliente", + "top_title_add_api_key_for": "Adicionar chave de API para {{name}}", + "top_title_instance_url": "URL da Instância", + "top_title_custom_url": "URL Personalizada", + "top_title_add_channel": "Adicionar Canal", + "top_title_add_telegram": "Adicionar Telegram", + "top_title_add_wrapcast": "Adicionar Wrapcast", + "top_title_comments_for": "Comentários para {{date}}", + "top_title_edit_signature": "Editar assinatura", + "label_name": "Nome", + "label_url": "URL", + "label_title": "Título", + "label_subtitle": "Subtítulo", + "label_email": "E-mail", + "label_full_name": "Nome completo", + "label_password": "Senha", + "label_confirm_password": "Confirmar senha", + "label_api_key": "Chave de API", + "label_instance_url": "URL da instância", + "label_custom_url": "URL personalizada", + "label_feedback": "Feedback", + "label_bio": "Biografia", + "label_role": "Função", + "label_country": "País", + "label_audience_size": "Tamanho do público em todas as plataformas", + "label_pick_time": "Escolher horário", + "label_nickname": "Apelido", + "label_write_anything": "Escreva qualquer coisa", + "label_output_format": "Formato de saída", + "label_add_pictures": "Adicionar imagens?", + "label_hour": "Hora", + "label_minutes": "Minutos", + "label_select_publication": "Selecionar publicação", + "label_canonical_link": "Link canônico", + "label_cover_picture": "Imagem de capa", + "label_tags": "Tags", + "label_topics": "Tópicos", + "label_tags_maximum_4": "Tags (Máximo 4)", + "label_attachments": "Anexos", + "label_type": "Tipo", + "label_thumbnail": "Miniatura", + "label_who_can_see_this_video": "Quem pode ver este vídeo?", + "label_content_posting_method": "Método de postagem de conteúdo", + "label_auto_add_music": "Adicionar música automaticamente", + "label_duet": "Dueto", + "label_stitch": "Stitch", + "label_comments": "Comentários", + "label_disclose_video_content": "Divulgar conteúdo do vídeo", + "label_your_brand": "Sua marca", + "label_branded_content": "Conteúdo de marca", + "label_subreddit": "Subreddit", + "label_flair": "Flair", + "label_media": "Mídia", + "label_search_subreddit": "Buscar Subreddit", + "label_delay": "Atraso", + "label_post_type": "Tipo de postagem", + "label_collaborators": "Colaboradores (máx. 3) - contas não podem ser privadas", + "label_community": "Comunidade", + "label_search_community": "Pesquisar comunidade", + "label_channel": "Canal", + "label_search_channel": "Pesquisar canal", + "label_select_channel": "Selecionar canal", + "label_new_password": "Nova senha", + "label_repeat_password": "Repetir senha", + "label_platform": "Plataforma", + "label_price_per_post": "Preço por postagem", + "label_integrations": "Integrações", + "label_code": "Código", + "label_should_sync_last_post": "Devemos sincronizar a última postagem atual?", + "label_when_post": "Quando devemos postar?", + "label_autogenerate_content": "Gerar conteúdo automaticamente", + "label_generate_picture": "Gerar imagem?", + "label_company": "Empresa", + "label_tag_color": "Cor da tag", + "label_select_board": "Selecionar quadro", + "label_select_organization": "Selecionar organização", + "label_auto_add_signature": "Adicionar assinatura automaticamente?", + "enable_color_picker": "Ativar seletor de cores", + "cancel_the_color_picker": "Cancelar o seletor de cores", + "no_content_yet": "Ainda não há conteúdo", + "write_your_reply": "Escreva sua postagem...", + "add_a_tag": "Adicionar uma tag", + "add_to_calendar": "Adicionar ao calendário", + "select_channels_from_circles": "Selecione os canais dos círculos acima", + "not_matching_order": "Ordem não correspondente", + "submit_for_order": "Enviar para pedido", + "schedule": "Agendar", + "update": "Atualizar", + "attachments": "Anexos", + "tags": "Tags", + "public_to_everyone": "Público para todos", + "mutual_follow_friends": "Amigos com seguimento mútuo", + "follower_of_creator": "Seguidor do criador", + "self_only": "Apenas eu", + "post_content_directly_to_tiktok": "Publicar conteúdo diretamente no TikTok", + "upload_content_to_tiktok_without_posting": "Enviar conteúdo para o TikTok sem publicar", + "choose_upload_without_posting_description": "Escolha enviar sem publicar se quiser revisar e editar seu conteúdo no aplicativo do TikTok antes de publicar. Isso lhe dá acesso às ferramentas de edição do próprio TikTok e permite fazer ajustes finais antes de postar.", + "faq_am_i_going_to_be_charged_by_postiz": "Vou ser cobrado pelo Postiz?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar as informações do cartão de crédito, o Postiz irá reter R$2 e liberá-lo imediatamente", + "faq_can_i_trust_postiz_gitroom": "Posso confiar no Postiz?", + "faq_postiz_gitroom_is_proudly_open_source": "O Postiz é orgulhosamente open-source! Acreditamos em uma cultura ética e transparente, o que significa que o Postiz viverá para sempre. Você pode conferir todo o código ou usá-lo em projetos pessoais. Para ver o repositório open-source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">clique aqui</a>.", + "faq_what_are_channels": "O que são canais?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "O Postiz permite que você agende suas publicações entre diferentes canais.\nUm canal é uma plataforma de publicação onde você pode agendar seus posts.\nPor exemplo, você pode agendar suas publicações no X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads e Pinterest.", + "faq_what_are_team_members": "O que são membros da equipe?", + "faq_if_you_have_a_team_with_multiple_members": "Se você tem uma equipe com vários membros, pode convidá-los para o seu workspace para colaborar nas postagens e adicionar seus canais pessoais", + "faq_what_is_ai_auto_complete": "O que é autocompletar por IA?", + "faq_we_automate_chatgpt_to_help_you_write": "Automatizamos o ChatGPT para ajudar você a escrever postagens e artigos para redes sociais.", + "enter_email": "Digite o e-mail", + "are_you_sure": "Tem certeza?", + "yes_delete_it": "Sim, exclua!", + "no_cancel": "Não, cancelar!", + "are_you_sure_you_want_to_delete": "Tem certeza de que deseja excluir {{name}}?", + "are_you_sure_you_want_to_delete_the_image": "Tem certeza de que deseja excluir a imagem?", + "are_you_sure_you_want_to_logout": "Tem certeza de que deseja sair?", + "yes_logout": "Sim, sair", + "are_you_sure_you_want_to_delete_this_slot": "Tem certeza de que deseja excluir este slot?", + "are_you_sure_you_want_to_delete_this_subreddit": "Tem certeza de que deseja excluir este Subreddit?", + "are_you_sure_you_want_to_close_the_window": "Tem certeza de que deseja fechar a janela?", + "yes_close": "Sim, fechar", + "link_copied_to_clipboard": "Link copiado para a área de transferência", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Tem certeza de que deseja fechar este modal? (todos os dados serão perdidos)", + "yes_close_it": "Sim, feche!", + "uploading_pictures": "Enviando imagens...", + "agent_starting": "Agente iniciando", + "researching_your_content": "Pesquisando seu conteúdo...", + "understanding_the_category": "Entendendo a categoria...", + "finding_the_topic": "Encontrando o tópico...", + "finding_popular_posts_to_match_with": "Encontrando posts populares para combinar...", + "generating_hook": "Gerando gancho...", + "generating_content": "Gerando conteúdo...", + "generating_pictures": "Gerando imagens...", + "finding_time_to_post": "Encontrando o melhor horário para postar...", + "write_anything": "Escreva qualquer coisa", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Você pode escrever o que quiser e também adicionar links, nós faremos a pesquisa para você...", + "output_format": "Formato de saída", + "add_pictures": "Adicionar imagens?", + "7_days": "7 Dias", + "30_days": "30 Dias", + "90_days": "90 Dias", + "start_7_days_free_trial": "Comece o teste gratuito de 7 dias", + "change_language": "Mudar idioma", + "that_a_wrap": "É isso aí!\n\nSe você gostou deste fio:\n\n1. Siga-me @{{username}} para ver mais conteúdos como este\n2. Dê RT no tweet abaixo para compartilhar este fio com seu público\n", + "post_as_images_carousel": "Publicar como carrossel de imagens", + "save_set": "Salvar conjunto", + "separate_post": "Separar postagem em várias postagens", + "label_who_can_reply_to_this_post": "Quem pode responder a esta postagem?", + "delete_integration": "Excluir integração", + "start_writing_your_post": "Comece a escrever sua postagem para visualizar", + "billing_join_over": "Junte-se a mais de", + "billing_entrepreneurs_count": "18.000+ empreendedores", + "billing_who_use": "que usam", + "billing_postiz_grow_social": "Postiz para aumentar sua presença social", + "billing_no_risk_trial": "Teste gratuito 100% sem risco", + "billing_pay_nothing_7_days": "Não pague NADA nos primeiros 7 dias", + "billing_cancel_anytime": "Cancele a qualquer momento, sem complicações", + "billing_choose_plan": "Escolha um plano", + "billing_monthly": "Mensal", + "billing_yearly": "Anual", + "billing_20_percent_off": "20% de desconto", + "billing_features": "Recursos", + "billing_channel": "canal", + "billing_channels": "canais", + "billing_unlimited": "Ilimitado", + "billing_posts_per_month": "publicações por mês", + "billing_unlimited_team_members": "Membros de equipe ilimitados", + "billing_ai_auto_complete": "Autocompletar com IA", + "billing_ai_copilots": "Copilotos de IA", + "billing_ai_autocomplete": "Autocompletar com IA", + "billing_advanced_picture_editor": "Editor de imagens avançado", + "billing_ai_images_per_month": "Imagens de IA por mês", + "billing_ai_videos_per_month": "Vídeos de IA por mês", + "billing_billing_address": "Endereço de cobrança", + "billing_payment": "Pagamento", + "billing_powered_by_stripe": "Desenvolvido por Stripe", + "billing_your_7_day_trial_is": "Seu teste de 7 dias é", + "billing_100_percent_free": "100% gratuito", + "billing_ending": "terminando", + "billing_cancel_anytime_short": "Cancele a qualquer momento.", + "billing_pay_0_start_trial": "Pague R$0 hoje - Comece seu teste gratuito!", + "billing_pay_now": "Pagar agora", + "billing_per_month": "/ mês" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index 2d4d76df..cb59cf5d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -1,507 +1,539 @@ { - "calendar": "Календарь", - "webhooks": "Вебхуки", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Вебхуки — это способ получать уведомления о событиях в Postiz через HTTP-запрос.", - "name": "Имя", - "url": "URL", - "edit": "Редактировать", - "delete": "Удалить", - "add_a_webhook": "Добавить вебхук", - "save": "Сохранить", - "send_test": "Отправить тест", - "select_role": "Выбрать роль", - "video_made_with_ai": "Видео создано с помощью ИИ", - "please_add_at_least": "Пожалуйста, добавьте не менее 20 символов", - "send_invitation_via_email": "Отправить приглашение по электронной почте?", - "global_settings": "Глобальные настройки", - "copy_id": "Скопировать ID канала", - "team_members": "Члены команды", - "invite_your_assistant_or_team_member_to_manage_your_account": "Пригласите помощника или члена команды для управления вашим аккаунтом", - "remove": "Удалить", - "add_another_member": "Добавить еще одного участника", - "signatures": "Подписи", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Вы можете добавить подписи к своему аккаунту для использования в ваших публикациях.", - "content": "Содержание", - "auto_add": "Добавлять автоматически?", - "actions": "Действия", - "use_signature": "Использовать подпись", - "add_a_signature": "Добавить подпись", - "no": "Нет", - "yes": "Да", - "your_git_repository": "Ваш Git-репозиторий", - "connect_your_github_repository_to_receive_updates_and_analytics": "Подключите ваш репозиторий GitHub, чтобы получать обновления и аналитику", - "connected": "Подключено:", - "disconnect": "Отключить", - "connect_your_repository": "Подключить репозиторий", - "cancel": "Отмена", - "connect": "Подключить", - "public_api": "Публичный API", - "check_n8n": "Ознакомьтесь с нашим пользовательским узлом N8N для Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Используйте API Postiz для интеграции с вашими инструментами.", - "read_how_to_use_it_over_the_documentation": "Прочитайте, как использовать его, в документации.", - "reveal": "Показать", - "copy_key": "Скопировать ключ", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Подключите сервер Postiz MCP к вашему клиенту (HTTP-поток), чтобы быстрее планировать ваши публикации!", - "share_with_a_client": "Поделиться с клиентом", - "post": "Пост", - "comments": "Комментарии", - "user": "Пользователь", - "login_register_to_add_comments": "Войдите или зарегистрируйтесь, чтобы добавить комментарии", - "status": "Статус:", - "there_are_not_plugs_matching_your_channels": "Нет плагинов, соответствующих вашим каналам", - "you_have_to_add_x_or_linkedin_or_threads": "Вам нужно добавить: X, LinkedIn или Threads", - "go_to_the_calendar_to_add_channels": "Перейдите в календарь, чтобы добавить каналы", - "channels": "Каналы", - "activate": "Активировать", - "this_channel_needs_to_be_refreshed": "Этот канал нужно обновить,", - "click_here_to_refresh": "нажмите здесь, чтобы обновить", - "can_t_show_analytics_yet": "Пока нельзя показать аналитику", - "you_have_to_add_social_media_channels": "Вам нужно добавить каналы социальных сетей", - "supported": "Поддерживается:", - "step": "ШАГ", - "skip_onboarding": "Пропустить вводный курс", - "onboarding": "Вводный курс", - "next": "Далее", - "you_are_done_from_here_you_can": "Готово, отсюда вы можете:", - "view_analytics": "Просмотреть аналитику", - "schedule_a_new_post": "Запланировать новую публикацию", - "to_sell_posts_you_would_have_to": "Чтобы продавать публикации, вам нужно:", - "1_connect_at_least_one_channel": "1. Подключить хотя бы один канал", - "2_connect_you_bank_account": "2. Подключить свой банковский счет", - "go_back_to_connect_channels": "Вернуться к подключению каналов", - "move_to_the_seller_page_to_connect_you_bank": "Перейти на страницу продавца для подключения банковского счета", - "connect_channels": "Подключить каналы", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Подключите свои каналы социальных сетей и платформ публикаций,\n чтобы позже планировать публикации", - "social": "Социальные сети", - "publishing_platforms": "Платформы публикаций", - "no_channels": "Каналов пока нет", - "connect_your_accounts": "Подключите свои социальные аккаунты, чтобы начать планировать, публиковать и анализировать — всё в одном месте.", - "notifications": "Уведомления", - "no_notifications": "Нет уведомлений", - "send_message": "Отправить сообщение", - "mar_28": "28 мар", - "there_are_no_messages_yet": "Пока нет сообщений.", - "checkout_the_marketplace": "Посетите маркетплейс", - "go_to_marketplace": "Перейти в маркетплейс", - "all_messages": "Все сообщения", - "previous": "Назад", - "select_or_upload_pictures_maximum_5_at_a_time": "Выберите или загрузите изображения (максимум 5 за раз)", - "you_can_also_drag_drop_pictures": "Вы также можете перетащить изображения мышью", - "you_don_t_have_any_assets_yet": "У вас пока нет ресурсов.", - "click_the_button_below_to_upload_one": "Нажмите кнопку ниже, чтобы загрузить", - "click_the_button_below_to_upload_few": "", - "click_the_button_below_to_upload_many": "", - "add_selected_media": "Добавить выбранные медиа", - "insert_media": "Вставить медиа", - "design_media": "Оформить медиа", - "select": "Выбрать", - "editor": "Редактор", - "clear": "Очистить", - "order_completed": "Заказ выполнен", - "the_order_has_been_completed": "Заказ был выполнен", - "post_has_been_published": "Пост опубликован", - "url_1": "URL:", - "new_offer": "Новое предложение", - "platform": "Платформа", - "posts": "Посты", - "pay_accept_offer": "Оплатить и принять предложение", - "accepted": "Принято", - "post_draft": "Черновик поста", - "revision_needed": "Требуется доработка", - "approve": "Одобрить", - "preview": "Предпросмотр", - "revision_requested": "Запрошена доработка", - "accepted_1": "ПРИНЯТО", - "cancelled_by_the_seller": "Отменено продавцом", - "please_select_your_country_where_your_business_is": "Пожалуйста, выберите страну, в которой находится ваш бизнес.", - "select_country": "--ВЫБЕРИТЕ СТРАНУ--", - "connect_bank_account": "Подключить банковский счет", - "seller_mode": "Режим продавца", - "active": "Активен", - "details": "Детали", - "audience_size": "Размер аудитории", - "add_another_platform": "Добавить другую платформу", - "send_an_offer_for": "Отправить предложение на $", - "complete_order_and_pay_early": "Завершить заказ и оплатить заранее", - "order_in_progress": "Заказ в процессе", - "create_a_new_offer": "Создать новое предложение", - "orders": "Заказы", - "price": "Цена", - "state": "Статус", - "showing": "Показано", - "to": "до", - "from": "от", - "results": "Результаты", - "content_writer": "Копирайтер", - "influencer": "Инфлюенсер", - "request_service": "Запросить услугу", - "the_marketplace_is_not_opened_yet": "Маркетплейс еще не открыт", - "check_again_soon": "Проверьте снова скоро!", - "filter": "Фильтр", - "result": "Результат", - "seller": "Продавец", - "buyer": "Покупатель", - "discord_support": "Поддержка Discord", - "teams": "Команды", - "webhooks_1": "Вебхуки", - "auto_post": "Автопостинг", - "logout_from": "Выйти из", - "join_10000_entrepreneurs_who_use_postiz": "Присоединяйтесь к 10 000+ предпринимателей, которые используют Postiz", - "to_manage_all_your_social_media_channels": "Для управления всеми вашими социальными сетями", - "100_no_risk_trial": "100% безрисковый пробный период", - "pay_nothing_for_the_first_7_days": "Не платите ничего первые 7 дней", - "cancel_anytime_hassle_free": "Отменяйте в любое время, без проблем", - "add_free_subscription": "-- ДОБАВИТЬ БЕСПЛАТНУЮ ПОДПИСКУ --", - "currently_impersonating": "В данный момент имитируется", - "user_1": "пользователь:", - "drag_n_drop_some_files_here": "Перетащите сюда файлы", - "add_time_slot": "Добавить временной слот", - "add_slot": "Добавить слот", - "cancel_publication": "Отменить публикацию", - "statistics": "Статистика", - "loading": "Загрузка", - "short_link": "Короткая ссылка", - "original_link": "Оригинальная ссылка", - "clicks": "Клики", - "selected_customer": "Выбранный клиент", - "customer": "Клиент:", - "repeat_post_every": "Повторять публикацию каждые...", - "use_this_media": "Использовать этот медиафайл", - "create_new_post": "Создать пост", - "update_post": "Обновить существующий пост", - "merge_comments_into_one_post": "Объединить комментарии в один пост", - "accounts_that_will_engage": "Аккаунты, которые будут взаимодействовать:", - "day": "День", - "week": "Неделя", - "month": "Месяц", - "remove_from_customer": "Удалить от клиента", - "show_more": "+ Показать больше", - "show_less": "- Показать меньше", - "upload": "Загрузить", - "ai": "ИИ", - "add_channel": "Добавить канал", - "add_platform": "Добавить платформу", - "articles": "Статьи", - "add_comment": "Добавить комментарий", - "add_post": "Добавить сообщение в теме", - "add_comment_or_post": "Добавить комментарий / сообщение", - "you_are_in_global_editing_mode": "Вы находитесь в режиме глобального редактирования", - "the_post_should_be_at_least_6_characters_long": "Пост должен содержать не менее 6 символов", - "are_you_sure_you_want_to_delete_post": "Вы уверены, что хотите удалить этот пост?", - "post_deleted_successfully": "Пост успешно удалён", - "delete_post": "Удалить пост", - "save_as_draft": "Сохранить как черновик", - "post_now": "Опубликовать сейчас", - "please_add": "Пожалуйста, добавьте", - "to_your_telegram_group_channel_and_click_here": "в вашу группу/канал Telegram и нажмите здесь:", - "connect_telegram": "Подключить Telegram", - "please_add_the_following_command_in_your_chat": "Пожалуйста, добавьте следующую команду в ваш чат:", - "copy": "Копировать", - "settings": "Настройки", - "integrations": "Интеграции", - "add_integration": "Добавить интеграцию", - "you_are_now_editing_only": "Сейчас вы редактируете только", - "tag_a_company": "Отметить компанию", - "video_length_is_invalid_must_be_up_to": "Недопустимая длина видео, должно быть не более", - "seconds": "секунд", - "this_feature_available_only_for_photos": "Эта функция доступна только для фотографий, будет добавлена стандартная музыка, которую вы сможете изменить позже.", - "allow_user_to": "Разрешить пользователю:", - "your_video_will_be_labeled_promotional": "Ваше видео будет помечено как «Рекламный контент».", - "this_cannot_be_changed_once_posted": "Это нельзя будет изменить после публикации видео.", - "turn_on_to_disclose_video_promotes": "Включите, чтобы указать, что это видео продвигает товары или услуги в обмен на что-либо ценное. Ваше видео может продвигать вас, третью сторону или обоих.", - "you_are_promoting_yourself": "Вы продвигаете себя или свой бренд.", - "this_video_will_be_classified_brand_organic": "Это видео будет классифицировано как органический брендовый контент.", - "you_are_promoting_another_brand": "Вы продвигаете другой бренд или третью сторону.", - "this_video_will_be_classified_branded_content": "Это видео будет классифицировано как брендированный контент.", - "by_posting_you_agree_to_tiktoks": "Публикуя, вы соглашаетесь с условиями TikTok", - "music_usage_confirmation": "Подтверждение использования музыки", - "branded_content_policy": "Политика брендированного контента", - "select_1": "--Выбрать--", - "select_flair": "--Выбрать метку--", - "link": "Ссылка", - "add_subreddit": "Добавить сабреддит", - "please_add_at_least_one_subreddit": "Пожалуйста, добавьте хотя бы один сабреддит", - "add_community": "Добавить сообщество", - "select_post_type": "Выберите тип поста...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Мы не смогли найти ни одного бизнеса, связанного с вашей страницей LinkedIn.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Пожалуйста, закройте это окно, создайте новую страницу и снова добавьте новый канал.", - "select_linkedin_page": "Выберите страницу LinkedIn:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Мы не смогли найти ни одного бизнеса, связанного с выбранными страницами.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Рекомендуем подключить все страницы и все бизнесы.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Пожалуйста, закройте это окно, удалите интеграцию и снова добавьте новый канал.", - "select_instagram_account": "Выберите аккаунт Instagram:", - "select_page": "Выберите страницу:", - "generate_image_with_ai": "Сгенерировать изображение с помощью ИИ", - "reconnect_channel": "Переподключить канал", - "update_credentials": "Обновить учетные данные", - "additional_settings": "Дополнительные настройки", - "change_bot": "Сменить бота", - "move_add_to_customer": "Переместить / добавить к клиенту", - "edit_time_slots": "Редактировать временные интервалы", - "enable_channel": "Включить канал", - "disable_channel": "Отключить канал", - "add": "Добавить", - "short_post": "Короткий пост", - "long_post": "Длинный пост", - "a_thread_with_short_posts": "Тема с короткими постами", - "a_thread_with_long_posts": "Тема с длинными постами", - "personal_voice_i_am_happy_to_announce": "Личный стиль (\"Рад сообщить\")", - "company_voice_we_are_happy_to_announce": "От лица компании (\"Рады сообщить\")", - "generate": "Сгенерировать", - "generate_posts": "Сгенерировать посты", - "purchase_a_life_time_pro_account_with_sol_199": "Приобретите пожизненный PRO-аккаунт за SOL ($199). Обратите внимание, что возврат средств за эту покупку не предусмотрен.", - "purchase_now": "Купить сейчас", - "pay_today": "Оплатить сегодня", - "we_are_sorry_to_see_you_go": "Жаль, что вы уходите :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Не могли бы вы коротко рассказать, что мы могли бы улучшить?", - "cancel_subscription": "Отменить подписку", - "plans": "Тарифы", - "monthly": "МЕСЯЧНО", - "yearly": "ГОДОВОЙ", - "reactivate_subscription": "Возобновить подписку", - "update_payment_method_invoices_history": "Обновить способ оплаты / История счетов", - "cancel_subscription_1": "Отменить подписку", - "your_subscription_will_be_canceled_at": "Ваша подписка будет отменена", - "you_will_never_be_charged_again": "С вас больше не будет взиматься плата", - "current_package": "Текущий пакет:", - "next_package": "Следующий пакет:", - "claim": "Получить", - "frequently_asked_questions": "Часто задаваемые вопросы", - "autopost": "Автопостинг", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Автопостинг может автоматически публиковать новые элементы вашего RSS в социальных сетях", - "title": "Заголовок", - "add_an_autopost": "Добавить автопостинг", - "post_content": "Содержимое поста", - "sign_up": "Зарегистрироваться", - "or": "ИЛИ", - "by_registering_you_agree_to_our": "Регистрируясь, вы соглашаетесь с нашими", - "and": "и", - "terms_of_service": "Условиями обслуживания", - "privacy_policy": "Политикой конфиденциальности", - "create_account": "Создать аккаунт", - "already_have_an_account": "Уже есть аккаунт?", - "sign_in": "Войти", - "sign_in_1": "Войти", - "don_t_have_an_account": "Нет аккаунта?", - "forgot_password": "Забыли пароль", - "forgot_password_1": "Забыли пароль", - "send_password_reset_email": "Отправить письмо для сброса пароля", - "go_back_to_login": "Вернуться к входу", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Мы отправили вам письмо со ссылкой для сброса пароля.", - "change_password": "Сменить пароль", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Ваш пароль успешно сброшен. Теперь вы можете войти, используя новый пароль.", - "click_here_to_go_back_to_login": "Нажмите здесь, чтобы вернуться к входу", - "activate_your_account": "Активируйте свой аккаунт", - "thank_you_for_registering": "Спасибо за регистрацию!", - "please_check_your_email_to_activate_your_account": "Пожалуйста, проверьте свою электронную почту для активации аккаунта.", - "sign_in_with": "Войти с помощью", - "continue_with_google": "Продолжить через Google", - "sign_in_with_github": "Войти через GitHub", - "continue_with_farcaster": "Продолжить через Farcaster", - "continue_with_your_wallet": "Продолжить через кошелёк", - "stars_per_day": "Звёзд в день", - "media": "Медиа", - "check_launch": "Проверить запуск", - "load_your_github_repository_from_settings_to_see_analytics": "Загрузите свой репозиторий GitHub в настройках, чтобы увидеть аналитику", - "stars": "Звёзды", - "processing_stars": "Обработка звёзд...", - "forks": "Форки", - "registration_is_disabled": "Регистрация отключена", - "login_instead": "Войти вместо этого", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Выберите беседу и начните общение.", - "adding_channel_redirecting_you": "Добавление канала, перенаправляем вас", - "could_not_add_provider": "Не удалось добавить провайдера.", - "you_are_being_redirected_back": "Вы перенаправляетесь обратно", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Возникли некоторые трудности, попробуйте обновить страницу", - "post_not_found": "Пост не найден", - "publication_date": "Дата публикации:", - "analytics": "Аналитика", - "launches": "Запуски", - "plugs": "Плагины", - "billing": "Оплата", - "affiliate": "Партнёрская программа", - "monday": "Понедельник", - "tuesday": "Вторник", - "wednesday": "Среда", - "thursday": "Четверг", - "friday": "Пятница", - "saturday": "Суббота", - "sunday": "Воскресенье", - "can_t_change_date_remove_post_from_publication": "Нельзя изменить дату, удалите пост из публикации", - "predicted_github_trending_change": "Прогнозируемое изменение трендов GitHub", - "duplicate_post": "Дублировать пост", - "preview_post": "Предпросмотр поста", - "post_statistics": "Статистика поста", - "draft": "Черновик", - "week_number": "Неделя {{number}}", - "top_title_edit_webhook": "Редактировать вебхук", - "top_title_add_webhook": "Добавить вебхук", - "top_title_oh_no": "О нет", - "top_title_auto_plug": "Авто-плагин: {{title}}", - "top_title_edit_autopost": "Редактировать автопост", - "top_title_add_autopost": "Добавить автопост", - "top_title_send_a_new_offer": "Отправить новое предложение", - "top_title_media_library": "Медиатека", - "top_title_add_signature": "Добавить подпись", - "top_title_send_a_message_to": "Отправить сообщение для {{name}}", - "top_title_configure_provider": "Настроить провайдера", - "top_title_add_member": "Добавить участника", - "top_title_change_bot_picture": "Изменить изображение бота", - "top_title_create_a_new_tag": "Создать новый тег", - "top_title_select_company": "Выбрать компанию", - "top_title_additional_settings": "Дополнительные настройки", - "top_title_time_table_slots": "Слоты расписания", - "top_title_design_media": "Дизайн медиа", - "top_title_edit_post": "Редактировать пост", - "top_title_create_post": "Создать пост", - "top_title_move__add_to_customer": "Переместить / Добавить к клиенту", - "top_title_add_api_key_for": "Добавить API-ключ для {{name}}", - "top_title_instance_url": "URL экземпляра", - "top_title_custom_url": "Пользовательский URL", - "top_title_add_channel": "Добавить канал", - "top_title_add_telegram": "Добавить Telegram", - "top_title_add_wrapcast": "Добавить Wrapcast", - "top_title_comments_for": "Комментарии за {{date}}", - "top_title_edit_signature": "Редактировать подпись", - "label_name": "Имя", - "label_url": "URL", - "label_title": "Заголовок", - "label_subtitle": "Подзаголовок", - "label_email": "Электронная почта", - "label_full_name": "Полное имя", - "label_password": "Пароль", - "label_confirm_password": "Подтвердите пароль", - "label_api_key": "API-ключ", - "label_instance_url": "URL экземпляра", - "label_custom_url": "Пользовательский URL", - "label_feedback": "Обратная связь", - "label_bio": "Биография", - "label_role": "Роль", - "label_country": "Страна", - "label_audience_size": "Размер аудитории на всех платформах", - "label_pick_time": "Выберите время", - "label_nickname": "Никнейм", - "label_write_anything": "Напишите что угодно", - "label_output_format": "Формат вывода", - "label_add_pictures": "Добавить изображения?", - "label_hour": "Час", - "label_minutes": "Минуты", - "label_select_publication": "Выберите публикацию", - "label_canonical_link": "Каноническая ссылка", - "label_cover_picture": "Обложка", - "label_tags": "Теги", - "label_topics": "Темы", - "label_tags_maximum_4": "Теги (максимум 4)", - "label_attachments": "Вложения", - "label_type": "Тип", - "label_thumbnail": "Миниатюра", - "label_who_can_see_this_video": "Кто может видеть это видео?", - "label_content_posting_method": "Способ публикации контента", - "label_auto_add_music": "Автоматически добавить музыку", - "label_duet": "Дуэт", - "label_stitch": "Сшивание", - "label_comments": "Комментарии", - "label_disclose_video_content": "Раскрыть содержание видео", - "label_your_brand": "Ваш бренд", - "label_branded_content": "Брендированный контент", - "label_subreddit": "Сабреддит", - "label_flair": "Флэр", - "label_media": "Медиа", - "label_search_subreddit": "Поиск сабреддита", - "label_delay": "Задержка", - "label_post_type": "Тип поста", - "label_collaborators": "Соавторы (макс. 3) — аккаунты не могут быть приватными", - "label_community": "Сообщество", - "label_search_community": "Поиск сообщества", - "label_channel": "Канал", - "label_search_channel": "Поиск канала", - "label_select_channel": "Выбрать канал", - "label_new_password": "Новый пароль", - "label_repeat_password": "Повторите пароль", - "label_platform": "Платформа", - "label_price_per_post": "Цена за пост", - "label_integrations": "Интеграции", - "label_code": "Код", - "label_should_sync_last_post": "Синхронизировать последний пост?", - "label_when_post": "Когда опубликовать?", - "label_autogenerate_content": "Автоматически сгенерировать контент", - "label_generate_picture": "Сгенерировать изображение?", - "label_company": "Компания", - "label_tag_color": "Цвет тега", - "label_select_board": "Выбрать доску", - "label_select_organization": "Выбрать организацию", - "label_auto_add_signature": "Автоматически добавлять подпись?", - "enable_color_picker": "Включить выбор цвета", - "cancel_the_color_picker": "Отменить выбор цвета", - "no_content_yet": "Пока нет контента", - "write_your_reply": "Напишите свой пост...", - "add_a_tag": "Добавить тег", - "add_to_calendar": "Добавить в календарь", - "select_channels_from_circles": "Выберите каналы из кругов выше", - "not_matching_order": "Порядок не совпадает", - "submit_for_order": "Отправить для заказа", - "schedule": "Расписание", - "update": "Обновить", - "attachments": "Вложения", - "tags": "Теги", - "public_to_everyone": "Доступно всем", - "mutual_follow_friends": "Взаимные друзья", - "follower_of_creator": "Подписчик автора", - "self_only": "Только для себя", - "post_content_directly_to_tiktok": "Публиковать контент напрямую в TikTok", - "upload_content_to_tiktok_without_posting": "Загрузить контент в TikTok без публикации", - "choose_upload_without_posting_description": "Выберите загрузку без публикации, если хотите просмотреть и отредактировать свой контент в приложении TikTok перед публикацией. Это даст вам доступ к встроенным инструментам редактирования TikTok и позволит внести финальные изменения перед публикацией.", - "faq_am_i_going_to_be_charged_by_postiz": "Будет ли с меня взиматься плата Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Для подтверждения информации о кредитной карте Postiz удержит $2 и сразу же их вернет", - "faq_can_i_trust_postiz_gitroom": "Можно ли доверять Postiz?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz — это полностью open-source! Мы верим в этичную и прозрачную культуру, а значит, Postiz будет существовать всегда. Вы можете ознакомиться с полным кодом или использовать его для личных проектов. Чтобы просмотреть open-source репозиторий, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">нажмите здесь</a>.", - "faq_what_are_channels": "Что такое каналы?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz позволяет планировать публикации между разными каналами.\nКанал — это платформа для публикаций, где вы можете планировать свои посты.\nНапример, вы можете планировать публикации в X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads и Pinterest.", - "faq_what_are_team_members": "Кто такие участники команды?", - "faq_if_you_have_a_team_with_multiple_members": "Если у вас есть команда с несколькими участниками, вы можете пригласить их в рабочее пространство для совместной работы над публикациями и добавления их личных каналов", - "faq_what_is_ai_auto_complete": "Что такое автозаполнение на базе ИИ?", - "faq_we_automate_chatgpt_to_help_you_write": "Мы автоматизируем ChatGPT, чтобы помочь вам писать посты и статьи для соцсетей.", - "enter_email": "Введите email", - "are_you_sure": "Вы уверены?", - "yes_delete_it": "Да, удалить!", - "no_cancel": "Нет, отмена!", - "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить {{name}}?", - "are_you_sure_you_want_to_delete_the_image": "Вы уверены, что хотите удалить изображение?", - "are_you_sure_you_want_to_logout": "Вы уверены, что хотите выйти?", - "yes_logout": "Да, выйти", - "are_you_sure_you_want_to_delete_this_slot": "Вы уверены, что хотите удалить этот слот?", - "are_you_sure_you_want_to_delete_this_subreddit": "Вы уверены, что хотите удалить этот сабреддит?", - "are_you_sure_you_want_to_close_the_window": "Вы уверены, что хотите закрыть окно?", - "yes_close": "Да, закрыть", - "link_copied_to_clipboard": "Ссылка скопирована в буфер обмена", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Вы уверены, что хотите закрыть это окно? (все данные будут потеряны)", - "yes_close_it": "Да, закрыть!", - "uploading_pictures": "Загрузка изображений...", - "agent_starting": "Запуск агента", - "researching_your_content": "Анализируем ваш контент...", - "understanding_the_category": "Определяем категорию...", - "finding_the_topic": "Определяем тему...", - "finding_popular_posts_to_match_with": "Ищем популярные посты для сопоставления...", - "generating_hook": "Генерируем зацепку...", - "generating_content": "Генерируем контент...", - "generating_pictures": "Генерируем изображения...", - "finding_time_to_post": "Определяем время для публикации...", - "write_anything": "Напишите что угодно", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Вы можете написать всё, что хотите, и также добавить ссылки — мы проведём исследование за вас...", - "output_format": "Формат вывода", - "add_pictures": "Добавить изображения?", - "7_days": "7 дней", - "30_days": "30 дней", - "90_days": "90 дней", - "start_7_days_free_trial": "Начать 7-дневную бесплатную пробную версию", - "change_language": "Сменить язык", - "that_a_wrap": "На этом всё!\n\nЕсли вам понравилась эта серия:\n\n1. Подпишитесь на меня @{{username}}, чтобы не пропустить новые посты\n2. Ретвитните твит ниже, чтобы поделиться этой серией со своей аудиторией\n", - "post_as_images_carousel": "Опубликовать как карусель изображений", - "save_set": "Сохранить набор", - "separate_post": "Разделить пост на несколько постов", - "label_who_can_reply_to_this_post": "Кто может отвечать на этот пост?", - "delete_integration": "Удалить интеграцию", - "start_writing_your_post": "Начните писать свой пост для предварительного просмотра" + "calendar": "Календарь", + "webhooks": "Вебхуки", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Вебхуки — это способ получать уведомления о событиях в Postiz через HTTP-запрос.", + "name": "Имя", + "url": "URL", + "edit": "Редактировать", + "delete": "Удалить", + "add_a_webhook": "Добавить вебхук", + "save": "Сохранить", + "send_test": "Отправить тест", + "select_role": "Выбрать роль", + "video_made_with_ai": "Видео создано с помощью ИИ", + "please_add_at_least": "Пожалуйста, добавьте не менее 20 символов", + "send_invitation_via_email": "Отправить приглашение по электронной почте?", + "global_settings": "Глобальные настройки", + "copy_id": "Скопировать ID канала", + "team_members": "Члены команды", + "invite_your_assistant_or_team_member_to_manage_your_account": "Пригласите помощника или члена команды для управления вашим аккаунтом", + "remove": "Удалить", + "add_another_member": "Добавить еще одного участника", + "signatures": "Подписи", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Вы можете добавить подписи к своему аккаунту для использования в ваших публикациях.", + "content": "Содержание", + "auto_add": "Добавлять автоматически?", + "actions": "Действия", + "use_signature": "Использовать подпись", + "add_a_signature": "Добавить подпись", + "no": "Нет", + "yes": "Да", + "your_git_repository": "Ваш Git-репозиторий", + "connect_your_github_repository_to_receive_updates_and_analytics": "Подключите ваш репозиторий GitHub, чтобы получать обновления и аналитику", + "connected": "Подключено:", + "disconnect": "Отключить", + "connect_your_repository": "Подключить репозиторий", + "cancel": "Отмена", + "connect": "Подключить", + "public_api": "Публичный API", + "check_n8n": "Ознакомьтесь с нашим пользовательским узлом N8N для Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Используйте API Postiz для интеграции с вашими инструментами.", + "read_how_to_use_it_over_the_documentation": "Прочитайте, как использовать его, в документации.", + "reveal": "Показать", + "copy_key": "Скопировать ключ", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Подключите сервер Postiz MCP к вашему клиенту (HTTP-поток), чтобы быстрее планировать ваши публикации!", + "share_with_a_client": "Поделиться с клиентом", + "post": "Пост", + "comments": "Комментарии", + "user": "Пользователь", + "login_register_to_add_comments": "Войдите или зарегистрируйтесь, чтобы добавить комментарии", + "status": "Статус:", + "there_are_not_plugs_matching_your_channels": "Нет плагинов, соответствующих вашим каналам", + "you_have_to_add_x_or_linkedin_or_threads": "Вам нужно добавить: X, LinkedIn или Threads", + "go_to_the_calendar_to_add_channels": "Перейдите в календарь, чтобы добавить каналы", + "channels": "Каналы", + "activate": "Активировать", + "this_channel_needs_to_be_refreshed": "Этот канал нужно обновить,", + "click_here_to_refresh": "нажмите здесь, чтобы обновить", + "can_t_show_analytics_yet": "Пока нельзя показать аналитику", + "you_have_to_add_social_media_channels": "Вам нужно добавить каналы социальных сетей", + "supported": "Поддерживается:", + "step": "ШАГ", + "skip_onboarding": "Пропустить вводный курс", + "onboarding": "Вводный курс", + "next": "Далее", + "you_are_done_from_here_you_can": "Готово, отсюда вы можете:", + "view_analytics": "Просмотреть аналитику", + "schedule_a_new_post": "Запланировать новую публикацию", + "to_sell_posts_you_would_have_to": "Чтобы продавать публикации, вам нужно:", + "1_connect_at_least_one_channel": "1. Подключить хотя бы один канал", + "2_connect_you_bank_account": "2. Подключить свой банковский счет", + "go_back_to_connect_channels": "Вернуться к подключению каналов", + "move_to_the_seller_page_to_connect_you_bank": "Перейти на страницу продавца для подключения банковского счета", + "connect_channels": "Подключить каналы", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Подключите свои каналы социальных сетей и платформ публикаций,\n чтобы позже планировать публикации", + "social": "Социальные сети", + "publishing_platforms": "Платформы публикаций", + "no_channels": "Каналов пока нет", + "connect_your_accounts": "Подключите свои социальные аккаунты, чтобы начать планировать, публиковать и анализировать — всё в одном месте.", + "notifications": "Уведомления", + "no_notifications": "Нет уведомлений", + "send_message": "Отправить сообщение", + "mar_28": "28 мар", + "there_are_no_messages_yet": "Пока нет сообщений.", + "checkout_the_marketplace": "Посетите маркетплейс", + "go_to_marketplace": "Перейти в маркетплейс", + "all_messages": "Все сообщения", + "previous": "Назад", + "select_or_upload_pictures_maximum_5_at_a_time": "Выберите или загрузите изображения (максимум 5 за раз)", + "you_can_also_drag_drop_pictures": "Вы также можете перетащить изображения мышью", + "you_don_t_have_any_assets_yet": "У вас пока нет ресурсов.", + "click_the_button_below_to_upload_one": "Нажмите кнопку ниже, чтобы загрузить", + "click_the_button_below_to_upload_other": "Нажмите кнопку ниже, чтобы загрузить несколько файлов", + "add_selected_media": "Добавить выбранные медиа", + "insert_media": "Вставить медиа", + "design_media": "Оформить медиа", + "select": "Выбрать", + "editor": "Редактор", + "clear": "Очистить", + "order_completed": "Заказ выполнен", + "the_order_has_been_completed": "Заказ был выполнен", + "post_has_been_published": "Пост опубликован", + "url_1": "URL:", + "new_offer": "Новое предложение", + "platform": "Платформа", + "posts": "Посты", + "pay_accept_offer": "Оплатить и принять предложение", + "accepted": "Принято", + "post_draft": "Черновик поста", + "revision_needed": "Требуется доработка", + "approve": "Одобрить", + "preview": "Предпросмотр", + "revision_requested": "Запрошена доработка", + "accepted_1": "ПРИНЯТО", + "cancelled_by_the_seller": "Отменено продавцом", + "please_select_your_country_where_your_business_is": "Пожалуйста, выберите страну, в которой находится ваш бизнес.", + "select_country": "--ВЫБЕРИТЕ СТРАНУ--", + "connect_bank_account": "Подключить банковский счет", + "seller_mode": "Режим продавца", + "active": "Активен", + "details": "Детали", + "audience_size": "Размер аудитории", + "add_another_platform": "Добавить другую платформу", + "send_an_offer_for": "Отправить предложение на $", + "complete_order_and_pay_early": "Завершить заказ и оплатить заранее", + "order_in_progress": "Заказ в процессе", + "create_a_new_offer": "Создать новое предложение", + "orders": "Заказы", + "price": "Цена", + "state": "Статус", + "showing": "Показано", + "to": "до", + "from": "от", + "results": "Результаты", + "content_writer": "Копирайтер", + "influencer": "Инфлюенсер", + "request_service": "Запросить услугу", + "the_marketplace_is_not_opened_yet": "Маркетплейс еще не открыт", + "check_again_soon": "Проверьте снова скоро!", + "filter": "Фильтр", + "result": "Результат", + "seller": "Продавец", + "buyer": "Покупатель", + "discord_support": "Поддержка Discord", + "teams": "Команды", + "webhooks_1": "Вебхуки", + "auto_post": "Автопостинг", + "logout_from": "Выйти из", + "join_10000_entrepreneurs_who_use_postiz": "Присоединяйтесь к 10 000+ предпринимателей, которые используют Postiz", + "to_manage_all_your_social_media_channels": "Для управления всеми вашими социальными сетями", + "100_no_risk_trial": "100% безрисковый пробный период", + "pay_nothing_for_the_first_7_days": "Не платите ничего первые 7 дней", + "cancel_anytime_hassle_free": "Отменяйте в любое время, без проблем", + "add_free_subscription": "-- ДОБАВИТЬ БЕСПЛАТНУЮ ПОДПИСКУ --", + "currently_impersonating": "В данный момент имитируется", + "user_1": "пользователь:", + "drag_n_drop_some_files_here": "Перетащите сюда файлы", + "add_time_slot": "Добавить временной слот", + "add_slot": "Добавить слот", + "cancel_publication": "Отменить публикацию", + "statistics": "Статистика", + "loading": "Загрузка", + "short_link": "Короткая ссылка", + "original_link": "Оригинальная ссылка", + "clicks": "Клики", + "selected_customer": "Выбранный клиент", + "customer": "Клиент:", + "repeat_post_every": "Повторять публикацию каждые...", + "use_this_media": "Использовать этот медиафайл", + "create_new_post": "Создать пост", + "update_post": "Обновить существующий пост", + "merge_comments_into_one_post": "Объединить комментарии в один пост", + "accounts_that_will_engage": "Аккаунты, которые будут взаимодействовать:", + "day": "День", + "week": "Неделя", + "month": "Месяц", + "remove_from_customer": "Удалить от клиента", + "show_more": "+ Показать больше", + "show_less": "- Показать меньше", + "upload": "Загрузить", + "ai": "ИИ", + "add_channel": "Добавить канал", + "add_platform": "Добавить платформу", + "articles": "Статьи", + "add_comment": "Добавить комментарий", + "add_post": "Добавить сообщение в теме", + "add_comment_or_post": "Добавить комментарий / сообщение", + "you_are_in_global_editing_mode": "Вы находитесь в режиме глобального редактирования", + "the_post_should_be_at_least_6_characters_long": "Пост должен содержать не менее 6 символов", + "are_you_sure_you_want_to_delete_post": "Вы уверены, что хотите удалить этот пост?", + "post_deleted_successfully": "Пост успешно удалён", + "delete_post": "Удалить пост", + "save_as_draft": "Сохранить как черновик", + "post_now": "Опубликовать сейчас", + "please_add": "Пожалуйста, добавьте", + "to_your_telegram_group_channel_and_click_here": "в вашу группу/канал Telegram и нажмите здесь:", + "connect_telegram": "Подключить Telegram", + "please_add_the_following_command_in_your_chat": "Пожалуйста, добавьте следующую команду в ваш чат:", + "copy": "Копировать", + "settings": "Настройки", + "integrations": "Интеграции", + "add_integration": "Добавить интеграцию", + "you_are_now_editing_only": "Сейчас вы редактируете только", + "tag_a_company": "Отметить компанию", + "video_length_is_invalid_must_be_up_to": "Недопустимая длина видео, должно быть не более", + "seconds": "секунд", + "this_feature_available_only_for_photos": "Эта функция доступна только для фотографий, будет добавлена стандартная музыка, которую вы сможете изменить позже.", + "allow_user_to": "Разрешить пользователю:", + "your_video_will_be_labeled_promotional": "Ваше видео будет помечено как «Рекламный контент».", + "this_cannot_be_changed_once_posted": "Это нельзя будет изменить после публикации видео.", + "turn_on_to_disclose_video_promotes": "Включите, чтобы указать, что это видео продвигает товары или услуги в обмен на что-либо ценное. Ваше видео может продвигать вас, третью сторону или обоих.", + "you_are_promoting_yourself": "Вы продвигаете себя или свой бренд.", + "this_video_will_be_classified_brand_organic": "Это видео будет классифицировано как органический брендовый контент.", + "you_are_promoting_another_brand": "Вы продвигаете другой бренд или третью сторону.", + "this_video_will_be_classified_branded_content": "Это видео будет классифицировано как брендированный контент.", + "by_posting_you_agree_to_tiktoks": "Публикуя, вы соглашаетесь с условиями TikTok", + "music_usage_confirmation": "Подтверждение использования музыки", + "branded_content_policy": "Политика брендированного контента", + "select_1": "--Выбрать--", + "select_flair": "--Выбрать метку--", + "link": "Ссылка", + "add_subreddit": "Добавить сабреддит", + "please_add_at_least_one_subreddit": "Пожалуйста, добавьте хотя бы один сабреддит", + "add_community": "Добавить сообщество", + "select_post_type": "Выберите тип поста...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Мы не смогли найти ни одного бизнеса, связанного с вашей страницей LinkedIn.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Пожалуйста, закройте это окно, создайте новую страницу и снова добавьте новый канал.", + "select_linkedin_page": "Выберите страницу LinkedIn:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Мы не смогли найти ни одного бизнеса, связанного с выбранными страницами.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Рекомендуем подключить все страницы и все бизнесы.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Пожалуйста, закройте это окно, удалите интеграцию и снова добавьте новый канал.", + "select_instagram_account": "Выберите аккаунт Instagram:", + "select_page": "Выберите страницу:", + "generate_image_with_ai": "Сгенерировать изображение с помощью ИИ", + "reconnect_channel": "Переподключить канал", + "update_credentials": "Обновить учетные данные", + "additional_settings": "Дополнительные настройки", + "change_bot": "Сменить бота", + "move_add_to_customer": "Переместить / добавить к клиенту", + "edit_time_slots": "Редактировать временные интервалы", + "enable_channel": "Включить канал", + "disable_channel": "Отключить канал", + "add": "Добавить", + "short_post": "Короткий пост", + "long_post": "Длинный пост", + "a_thread_with_short_posts": "Тема с короткими постами", + "a_thread_with_long_posts": "Тема с длинными постами", + "personal_voice_i_am_happy_to_announce": "Личный стиль (\"Рад сообщить\")", + "company_voice_we_are_happy_to_announce": "От лица компании (\"Рады сообщить\")", + "generate": "Сгенерировать", + "generate_posts": "Сгенерировать посты", + "purchase_a_life_time_pro_account_with_sol_199": "Приобретите пожизненный PRO-аккаунт за SOL ($199). Обратите внимание, что возврат средств за эту покупку не предусмотрен.", + "purchase_now": "Купить сейчас", + "pay_today": "Оплатить сегодня", + "we_are_sorry_to_see_you_go": "Жаль, что вы уходите :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Не могли бы вы коротко рассказать, что мы могли бы улучшить?", + "cancel_subscription": "Отменить подписку", + "plans": "Тарифы", + "monthly": "МЕСЯЧНО", + "yearly": "ГОДОВОЙ", + "reactivate_subscription": "Возобновить подписку", + "update_payment_method_invoices_history": "Обновить способ оплаты / История счетов", + "cancel_subscription_1": "Отменить подписку", + "your_subscription_will_be_canceled_at": "Ваша подписка будет отменена", + "you_will_never_be_charged_again": "С вас больше не будет взиматься плата", + "current_package": "Текущий пакет:", + "next_package": "Следующий пакет:", + "claim": "Получить", + "frequently_asked_questions": "Часто задаваемые вопросы", + "autopost": "Автопостинг", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Автопостинг может автоматически публиковать новые элементы вашего RSS в социальных сетях", + "title": "Заголовок", + "add_an_autopost": "Добавить автопостинг", + "post_content": "Содержимое поста", + "sign_up": "Зарегистрироваться", + "or": "ИЛИ", + "by_registering_you_agree_to_our": "Регистрируясь, вы соглашаетесь с нашими", + "and": "и", + "terms_of_service": "Условиями обслуживания", + "privacy_policy": "Политикой конфиденциальности", + "create_account": "Создать аккаунт", + "already_have_an_account": "Уже есть аккаунт?", + "sign_in": "Войти", + "sign_in_1": "Войти", + "don_t_have_an_account": "Нет аккаунта?", + "forgot_password": "Забыли пароль", + "forgot_password_1": "Забыли пароль", + "send_password_reset_email": "Отправить письмо для сброса пароля", + "go_back_to_login": "Вернуться к входу", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Мы отправили вам письмо со ссылкой для сброса пароля.", + "change_password": "Сменить пароль", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Ваш пароль успешно сброшен. Теперь вы можете войти, используя новый пароль.", + "click_here_to_go_back_to_login": "Нажмите здесь, чтобы вернуться к входу", + "activate_your_account": "Активируйте свой аккаунт", + "thank_you_for_registering": "Спасибо за регистрацию!", + "please_check_your_email_to_activate_your_account": "Пожалуйста, проверьте свою электронную почту для активации аккаунта.", + "sign_in_with": "Войти с помощью", + "continue_with_google": "Продолжить через Google", + "sign_in_with_github": "Войти через GitHub", + "continue_with_farcaster": "Продолжить через Farcaster", + "continue_with_your_wallet": "Продолжить через кошелёк", + "stars_per_day": "Звёзд в день", + "media": "Медиа", + "check_launch": "Проверить запуск", + "load_your_github_repository_from_settings_to_see_analytics": "Загрузите свой репозиторий GitHub в настройках, чтобы увидеть аналитику", + "stars": "Звёзды", + "processing_stars": "Обработка звёзд...", + "forks": "Форки", + "registration_is_disabled": "Регистрация отключена", + "login_instead": "Войти вместо этого", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Выберите беседу и начните общение.", + "adding_channel_redirecting_you": "Добавление канала, перенаправляем вас", + "could_not_add_provider": "Не удалось добавить провайдера.", + "you_are_being_redirected_back": "Вы перенаправляетесь обратно", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Возникли некоторые трудности, попробуйте обновить страницу", + "post_not_found": "Пост не найден", + "publication_date": "Дата публикации:", + "analytics": "Аналитика", + "launches": "Запуски", + "plugs": "Плагины", + "billing": "Оплата", + "affiliate": "Партнёрская программа", + "monday": "Понедельник", + "tuesday": "Вторник", + "wednesday": "Среда", + "thursday": "Четверг", + "friday": "Пятница", + "saturday": "Суббота", + "sunday": "Воскресенье", + "can_t_change_date_remove_post_from_publication": "Нельзя изменить дату, удалите пост из публикации", + "predicted_github_trending_change": "Прогнозируемое изменение трендов GitHub", + "duplicate_post": "Дублировать пост", + "preview_post": "Предпросмотр поста", + "post_statistics": "Статистика поста", + "draft": "Черновик", + "week_number": "Неделя {{number}}", + "top_title_edit_webhook": "Редактировать вебхук", + "top_title_add_webhook": "Добавить вебхук", + "top_title_oh_no": "О нет", + "top_title_auto_plug": "Авто-плагин: {{title}}", + "top_title_edit_autopost": "Редактировать автопост", + "top_title_add_autopost": "Добавить автопост", + "top_title_send_a_new_offer": "Отправить новое предложение", + "top_title_media_library": "Медиатека", + "top_title_add_signature": "Добавить подпись", + "top_title_send_a_message_to": "Отправить сообщение для {{name}}", + "top_title_configure_provider": "Настроить провайдера", + "top_title_add_member": "Добавить участника", + "top_title_change_bot_picture": "Изменить изображение бота", + "top_title_create_a_new_tag": "Создать новый тег", + "top_title_select_company": "Выбрать компанию", + "top_title_additional_settings": "Дополнительные настройки", + "top_title_time_table_slots": "Слоты расписания", + "top_title_design_media": "Дизайн медиа", + "top_title_edit_post": "Редактировать пост", + "top_title_create_post": "Создать пост", + "top_title_move__add_to_customer": "Переместить / Добавить к клиенту", + "top_title_add_api_key_for": "Добавить API-ключ для {{name}}", + "top_title_instance_url": "URL экземпляра", + "top_title_custom_url": "Пользовательский URL", + "top_title_add_channel": "Добавить канал", + "top_title_add_telegram": "Добавить Telegram", + "top_title_add_wrapcast": "Добавить Wrapcast", + "top_title_comments_for": "Комментарии за {{date}}", + "top_title_edit_signature": "Редактировать подпись", + "label_name": "Имя", + "label_url": "URL", + "label_title": "Заголовок", + "label_subtitle": "Подзаголовок", + "label_email": "Электронная почта", + "label_full_name": "Полное имя", + "label_password": "Пароль", + "label_confirm_password": "Подтвердите пароль", + "label_api_key": "API-ключ", + "label_instance_url": "URL экземпляра", + "label_custom_url": "Пользовательский URL", + "label_feedback": "Обратная связь", + "label_bio": "Биография", + "label_role": "Роль", + "label_country": "Страна", + "label_audience_size": "Размер аудитории на всех платформах", + "label_pick_time": "Выберите время", + "label_nickname": "Никнейм", + "label_write_anything": "Напишите что угодно", + "label_output_format": "Формат вывода", + "label_add_pictures": "Добавить изображения?", + "label_hour": "Час", + "label_minutes": "Минуты", + "label_select_publication": "Выберите публикацию", + "label_canonical_link": "Каноническая ссылка", + "label_cover_picture": "Обложка", + "label_tags": "Теги", + "label_topics": "Темы", + "label_tags_maximum_4": "Теги (максимум 4)", + "label_attachments": "Вложения", + "label_type": "Тип", + "label_thumbnail": "Миниатюра", + "label_who_can_see_this_video": "Кто может видеть это видео?", + "label_content_posting_method": "Способ публикации контента", + "label_auto_add_music": "Автоматически добавить музыку", + "label_duet": "Дуэт", + "label_stitch": "Сшивание", + "label_comments": "Комментарии", + "label_disclose_video_content": "Раскрыть содержание видео", + "label_your_brand": "Ваш бренд", + "label_branded_content": "Брендированный контент", + "label_subreddit": "Сабреддит", + "label_flair": "Флэр", + "label_media": "Медиа", + "label_search_subreddit": "Поиск сабреддита", + "label_delay": "Задержка", + "label_post_type": "Тип поста", + "label_collaborators": "Соавторы (макс. 3) — аккаунты не могут быть приватными", + "label_community": "Сообщество", + "label_search_community": "Поиск сообщества", + "label_channel": "Канал", + "label_search_channel": "Поиск канала", + "label_select_channel": "Выбрать канал", + "label_new_password": "Новый пароль", + "label_repeat_password": "Повторите пароль", + "label_platform": "Платформа", + "label_price_per_post": "Цена за пост", + "label_integrations": "Интеграции", + "label_code": "Код", + "label_should_sync_last_post": "Синхронизировать последний пост?", + "label_when_post": "Когда опубликовать?", + "label_autogenerate_content": "Автоматически сгенерировать контент", + "label_generate_picture": "Сгенерировать изображение?", + "label_company": "Компания", + "label_tag_color": "Цвет тега", + "label_select_board": "Выбрать доску", + "label_select_organization": "Выбрать организацию", + "label_auto_add_signature": "Автоматически добавлять подпись?", + "enable_color_picker": "Включить выбор цвета", + "cancel_the_color_picker": "Отменить выбор цвета", + "no_content_yet": "Пока нет контента", + "write_your_reply": "Напишите свой пост...", + "add_a_tag": "Добавить тег", + "add_to_calendar": "Добавить в календарь", + "select_channels_from_circles": "Выберите каналы из кругов выше", + "not_matching_order": "Порядок не совпадает", + "submit_for_order": "Отправить для заказа", + "schedule": "Расписание", + "update": "Обновить", + "attachments": "Вложения", + "tags": "Теги", + "public_to_everyone": "Доступно всем", + "mutual_follow_friends": "Взаимные друзья", + "follower_of_creator": "Подписчик автора", + "self_only": "Только для себя", + "post_content_directly_to_tiktok": "Публиковать контент напрямую в TikTok", + "upload_content_to_tiktok_without_posting": "Загрузить контент в TikTok без публикации", + "choose_upload_without_posting_description": "Выберите загрузку без публикации, если хотите просмотреть и отредактировать свой контент в приложении TikTok перед публикацией. Это даст вам доступ к встроенным инструментам редактирования TikTok и позволит внести финальные изменения перед публикацией.", + "faq_am_i_going_to_be_charged_by_postiz": "Будет ли с меня взиматься плата Postiz?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Для подтверждения информации о кредитной карте Postiz удержит $2 и сразу же их вернет", + "faq_can_i_trust_postiz_gitroom": "Можно ли доверять Postiz?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz — это полностью open-source! Мы верим в этичную и прозрачную культуру, а значит, Postiz будет существовать всегда. Вы можете ознакомиться с полным кодом или использовать его для личных проектов. Чтобы просмотреть open-source репозиторий, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">нажмите здесь</a>.", + "faq_what_are_channels": "Что такое каналы?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz позволяет планировать публикации между разными каналами.\nКанал — это платформа для публикаций, где вы можете планировать свои посты.\nНапример, вы можете планировать публикации в X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads и Pinterest.", + "faq_what_are_team_members": "Кто такие участники команды?", + "faq_if_you_have_a_team_with_multiple_members": "Если у вас есть команда с несколькими участниками, вы можете пригласить их в рабочее пространство для совместной работы над публикациями и добавления их личных каналов", + "faq_what_is_ai_auto_complete": "Что такое автозаполнение на базе ИИ?", + "faq_we_automate_chatgpt_to_help_you_write": "Мы автоматизируем ChatGPT, чтобы помочь вам писать посты и статьи для соцсетей.", + "enter_email": "Введите email", + "are_you_sure": "Вы уверены?", + "yes_delete_it": "Да, удалить!", + "no_cancel": "Нет, отмена!", + "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить {{name}}?", + "are_you_sure_you_want_to_delete_the_image": "Вы уверены, что хотите удалить изображение?", + "are_you_sure_you_want_to_logout": "Вы уверены, что хотите выйти?", + "yes_logout": "Да, выйти", + "are_you_sure_you_want_to_delete_this_slot": "Вы уверены, что хотите удалить этот слот?", + "are_you_sure_you_want_to_delete_this_subreddit": "Вы уверены, что хотите удалить этот сабреддит?", + "are_you_sure_you_want_to_close_the_window": "Вы уверены, что хотите закрыть окно?", + "yes_close": "Да, закрыть", + "link_copied_to_clipboard": "Ссылка скопирована в буфер обмена", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Вы уверены, что хотите закрыть это окно? (все данные будут потеряны)", + "yes_close_it": "Да, закрыть!", + "uploading_pictures": "Загрузка изображений...", + "agent_starting": "Запуск агента", + "researching_your_content": "Анализируем ваш контент...", + "understanding_the_category": "Определяем категорию...", + "finding_the_topic": "Определяем тему...", + "finding_popular_posts_to_match_with": "Ищем популярные посты для сопоставления...", + "generating_hook": "Генерируем зацепку...", + "generating_content": "Генерируем контент...", + "generating_pictures": "Генерируем изображения...", + "finding_time_to_post": "Определяем время для публикации...", + "write_anything": "Напишите что угодно", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Вы можете написать всё, что хотите, и также добавить ссылки — мы проведём исследование за вас...", + "output_format": "Формат вывода", + "add_pictures": "Добавить изображения?", + "7_days": "7 дней", + "30_days": "30 дней", + "90_days": "90 дней", + "start_7_days_free_trial": "Начать 7-дневную бесплатную пробную версию", + "change_language": "Сменить язык", + "that_a_wrap": "На этом всё!\n\nЕсли вам понравилась эта серия:\n\n1. Подпишитесь на меня @{{username}}, чтобы не пропустить новые посты\n2. Ретвитните твит ниже, чтобы поделиться этой серией со своей аудиторией\n", + "post_as_images_carousel": "Опубликовать как карусель изображений", + "save_set": "Сохранить набор", + "separate_post": "Разделить пост на несколько постов", + "label_who_can_reply_to_this_post": "Кто может отвечать на этот пост?", + "delete_integration": "Удалить интеграцию", + "start_writing_your_post": "Начните писать свой пост для предварительного просмотра", + "billing_join_over": "Присоединяйтесь к", + "billing_entrepreneurs_count": "18 000+ предпринимателей", + "billing_who_use": "которые используют", + "billing_postiz_grow_social": "Postiz для роста своей социальной активности", + "billing_no_risk_trial": "100% бесплатный пробный период без риска", + "billing_pay_nothing_7_days": "Платите НИЧЕГО первые 7 дней", + "billing_cancel_anytime": "Отменить можно в любое время, без хлопот", + "billing_choose_plan": "Выберите тариф", + "billing_monthly": "Ежемесячно", + "billing_yearly": "Ежегодно", + "billing_20_percent_off": "Скидка 20%", + "billing_features": "Функции", + "billing_channel": "канал", + "billing_channels": "каналы", + "billing_unlimited": "Безлимитно", + "billing_posts_per_month": "постов в месяц", + "billing_unlimited_team_members": "Неограниченное количество участников команды", + "billing_ai_auto_complete": "Автозаполнение на базе ИИ", + "billing_ai_copilots": "ИИ-ассистенты", + "billing_ai_autocomplete": "Автозаполнение на базе ИИ", + "billing_advanced_picture_editor": "Продвинутый редактор изображений", + "billing_ai_images_per_month": "ИИ-изображений в месяц", + "billing_ai_videos_per_month": "ИИ-видео в месяц", + "billing_billing_address": "Платёжный адрес", + "billing_payment": "Платёж", + "billing_powered_by_stripe": "Работает на Stripe", + "billing_your_7_day_trial_is": "Ваш 7-дневный пробный период", + "billing_100_percent_free": "100% бесплатно", + "billing_ending": "заканчивается", + "billing_cancel_anytime_short": "Отменить можно в любое время.", + "billing_pay_0_start_trial": "Заплатите $0 сегодня — начните бесплатный пробный период!", + "billing_pay_now": "Оплатить сейчас", + "billing_per_month": "/ месяц" } diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index a8b4d719..a695c60a 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -1,506 +1,539 @@ { - "calendar": "Takvim", - "webhooks": "Web kancaları", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Web kancaları, Postiz'de bir şey olduğunda bir HTTP isteği aracılığıyla bildirim almanın bir yoludur.", - "name": "İsim", - "url": "URL", - "edit": "Düzenle", - "delete": "Sil", - "add_a_webhook": "Bir web kancası ekle", - "save": "Kaydet", - "send_test": "Test Gönder", - "select_role": "Rol Seç", - "video_made_with_ai": "Video yapay zeka ile oluşturuldu", - "please_add_at_least": "Lütfen en az 20 karakter ekleyin", - "send_invitation_via_email": "Davetiyeyi e-posta ile gönderilsin mi?", - "global_settings": "Genel Ayarlar", - "copy_id": "Kanal Kimliğini Kopyala", - "team_members": "Takım Üyeleri", - "invite_your_assistant_or_team_member_to_manage_your_account": "Hesabınızı yönetmesi için asistanınızı veya takım üyenizi davet edin", - "remove": "Kaldır", - "add_another_member": "Başka bir üye ekle", - "signatures": "İmzalar", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Gönderilerinizde kullanılmak üzere hesabınıza imzalar ekleyebilirsiniz.", - "content": "İçerik", - "auto_add": "Otomatik Ekle?", - "actions": "Eylemler", - "use_signature": "İmzayı Kullan", - "add_a_signature": "Bir imza ekle", - "no": "Hayır", - "yes": "Evet", - "your_git_repository": "Git Depo'nuz", - "connect_your_github_repository_to_receive_updates_and_analytics": "Güncellemeler ve analizler almak için GitHub deponuzu bağlayın", - "connected": "Bağlandı:", - "disconnect": "Bağlantıyı Kes", - "connect_your_repository": "Depoyu Bağla", - "cancel": "İptal", - "connect": "Bağla", - "public_api": "Genel API", - "check_n8n": "Postiz için özel N8N düğümümüzü inceleyin.", - "use_postiz_api_to_integrate_with_your_tools": "Araçlarınızla entegre olmak için Postiz API'sini kullanın.", - "read_how_to_use_it_over_the_documentation": "Nasıl kullanılacağını dokümantasyondan okuyun.", - "reveal": "Göster", - "copy_key": "Anahtarı Kopyala", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Gönderilerinizi daha hızlı planlamak için Postiz MCP sunucusunu istemcinize (Http akışı) bağlayın!", - "share_with_a_client": "Bir müşteriyle paylaş", - "post": "Gönderi", - "comments": "Yorumlar", - "user": "Kullanıcı", - "login_register_to_add_comments": "Yorum eklemek için Giriş Yap / Kayıt Ol", - "status": "Durum:", - "there_are_not_plugs_matching_your_channels": "Kanallarınızla eşleşen eklenti yok", - "you_have_to_add_x_or_linkedin_or_threads": "Şunlardan birini eklemelisiniz: X veya LinkedIn veya Threads", - "go_to_the_calendar_to_add_channels": "Kanalları eklemek için takvime gidin", - "channels": "Kanallar", - "activate": "Aktifleştir", - "this_channel_needs_to_be_refreshed": "Bu kanalın yenilenmesi gerekiyor,", - "click_here_to_refresh": "yenilemek için buraya tıklayın", - "can_t_show_analytics_yet": "Analitikler henüz gösterilemiyor", - "you_have_to_add_social_media_channels": "Sosyal medya kanalları eklemelisiniz", - "supported": "Desteklenen:", - "step": "ADIM", - "skip_onboarding": "Başlangıç rehberini atla", - "onboarding": "Başlangıç rehberi", - "next": "Sonraki", - "you_are_done_from_here_you_can": "Hazırsınız, buradan şunları yapabilirsiniz:", - "view_analytics": "Analitikleri Görüntüle", - "schedule_a_new_post": "Yeni bir gönderi planla", - "to_sell_posts_you_would_have_to": "Gönderi satmak için şunları yapmalısınız:", - "1_connect_at_least_one_channel": "1. En az bir kanal bağlayın", - "2_connect_you_bank_account": "2. Banka hesabınızı bağlayın", - "go_back_to_connect_channels": "Kanalları bağlamaya geri dön", - "move_to_the_seller_page_to_connect_you_bank": "Banka hesabınızı bağlamak için satıcı sayfasına gidin", - "connect_channels": "Kanalları Bağla", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Sosyal medya ve yayın platformu kanallarınızı bağlayarak\n gönderileri daha sonra planlayabilirsiniz", - "social": "Sosyal", - "publishing_platforms": "Yayın Platformları", - "no_channels": "Henüz kanal yok", - "connect_your_accounts": "Zamanlama, paylaşım ve analiz işlemlerine başlamak için sosyal hesaplarınızı bağlayın — hepsi tek bir yerde.", - "notifications": "Bildirimler", - "no_notifications": "Bildirim yok", - "send_message": "Mesaj Gönder", - "mar_28": "28 Mar", - "there_are_no_messages_yet": "Henüz mesaj yok.", - "checkout_the_marketplace": "Pazaryerini incele", - "go_to_marketplace": "Pazaryerine git", - "all_messages": "Tüm Mesajlar", - "previous": "Önceki", - "select_or_upload_pictures_maximum_5_at_a_time": "Resim seçin veya yükleyin (en fazla 5 adet aynı anda)", - "you_can_also_drag_drop_pictures": "Ayrıca resimleri sürükleyip bırakabilirsiniz", - "you_don_t_have_any_assets_yet": "Henüz herhangi bir varlığınız yok.", - "click_the_button_below_to_upload_one": "Bir tane yüklemek için aşağıdaki butona tıklayın", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "Seçili medyayı ekle", - "insert_media": "Medya Ekle", - "design_media": "Medya Tasarla", - "select": "Seç", - "editor": "Editör", - "clear": "Temizle", - "order_completed": "Sipariş tamamlandı", - "the_order_has_been_completed": "Sipariş tamamlandı", - "post_has_been_published": "Gönderi yayınlandı", - "url_1": "URL:", - "new_offer": "Yeni Teklif", - "platform": "Platform", - "posts": "Gönderiler", - "pay_accept_offer": "Öde ve Teklifi Kabul Et", - "accepted": "Kabul Edildi", - "post_draft": "Taslak Gönderi", - "revision_needed": "Düzeltme Gerekli", - "approve": "Onayla", - "preview": "Önizleme", - "revision_requested": "Düzeltme Talep Edildi", - "accepted_1": "KABUL EDİLDİ", - "cancelled_by_the_seller": "Satıcı tarafından iptal edildi", - "please_select_your_country_where_your_business_is": "Lütfen işletmenizin bulunduğu ülkeyi seçin.", - "select_country": "--ÜLKE SEÇİN--", - "connect_bank_account": "Banka Hesabını Bağla", - "seller_mode": "Satıcı Modu", - "active": "Aktif", - "details": "Detaylar", - "audience_size": "Hedef Kitle Büyüklüğü", - "add_another_platform": "Başka bir platform ekle", - "send_an_offer_for": "Şu fiyatla teklif gönder: $", - "complete_order_and_pay_early": "Siparişi tamamla ve erken öde", - "order_in_progress": "Sipariş devam ediyor", - "create_a_new_offer": "Yeni bir teklif oluştur", - "orders": "Siparişler", - "price": "Fiyat", - "state": "Durum", - "showing": "Gösteriliyor", - "to": "-e", - "from": "-den", - "results": "Sonuçlar", - "content_writer": "İçerik Yazarı", - "influencer": "Influencer", - "request_service": "Hizmet Talep Et", - "the_marketplace_is_not_opened_yet": "Pazar yeri henüz açılmadı", - "check_again_soon": "Yakında tekrar kontrol et!", - "filter": "Filtrele", - "result": "Sonuç", - "seller": "Satıcı", - "buyer": "Alıcı", - "discord_support": "Discord Desteği", - "teams": "Takımlar", - "webhooks_1": "Webhooks", - "auto_post": "Otomatik Paylaşım", - "logout_from": "Çıkış yap", - "join_10000_entrepreneurs_who_use_postiz": "Postiz kullanan 10.000+ girişimciye katılın", - "to_manage_all_your_social_media_channels": "Tüm sosyal medya kanallarınızı yönetmek için", - "100_no_risk_trial": "%100 risksiz deneme", - "pay_nothing_for_the_first_7_days": "İlk 7 gün hiçbir ücret ödeme", - "cancel_anytime_hassle_free": "İstediğin zaman zahmetsizce iptal et", - "add_free_subscription": "-- ÜCRETSİZ ABONELİK EKLE --", - "currently_impersonating": "Şu anda taklit ediliyor", - "user_1": "kullanıcı:", - "drag_n_drop_some_files_here": "Dosyaları buraya sürükleyip bırakın", - "add_time_slot": "Zaman Dilimi Ekle", - "add_slot": "Zaman Dilimi Ekle", - "cancel_publication": "Yayını İptal Et", - "statistics": "İstatistikler", - "loading": "Yükleniyor", - "short_link": "Kısa Link", - "original_link": "Orijinal Link", - "clicks": "Tıklamalar", - "selected_customer": "Seçili Müşteri", - "customer": "Müşteri:", - "repeat_post_every": "Gönderiyi Her ... Tekrarla", - "use_this_media": "Bu medyayı kullan", - "create_new_post": "Gönderi Oluştur", - "update_post": "Mevcut Gönderiyi Güncelle", - "merge_comments_into_one_post": "Yorumları tek gönderide birleştir", - "accounts_that_will_engage": "Etkileşime Geçecek Hesaplar:", - "day": "Gün", - "week": "Hafta", - "month": "Ay", - "remove_from_customer": "Müşteriden kaldır", - "show_more": "+ Daha fazla göster", - "show_less": "- Daha az göster", - "upload": "Yükle", - "ai": "Yapay Zeka", - "add_channel": "Kanal Ekle", - "add_platform": "Platform ekle", - "articles": "Makaleler", - "add_comment": "Yorum ekle", - "add_post": "Bir başlıkta gönderi ekle", - "add_comment_or_post": "Yorum / gönderi ekle", - "you_are_in_global_editing_mode": "Genel düzenleme modundasınız", - "the_post_should_be_at_least_6_characters_long": "Gönderi en az 6 karakter uzunluğunda olmalıdır", - "are_you_sure_you_want_to_delete_post": "Bu gönderiyi silmek istediğinizden emin misiniz?", - "post_deleted_successfully": "Gönderi başarıyla silindi", - "delete_post": "Gönderiyi sil", - "save_as_draft": "Taslak olarak kaydet", - "post_now": "Şimdi paylaş", - "please_add": "Lütfen ekleyin", - "to_your_telegram_group_channel_and_click_here": "Telegram grup/kanalınıza ekleyin ve buraya tıklayın:", - "connect_telegram": "Telegram'ı bağla", - "please_add_the_following_command_in_your_chat": "Lütfen sohbetinize aşağıdaki komutu ekleyin:", - "copy": "Kopyala", - "settings": "Ayarlar", - "integrations": "Entegrasyonlar", - "add_integration": "Entegrasyon Ekle", - "you_are_now_editing_only": "Şu anda yalnızca düzenliyorsunuz", - "tag_a_company": "Bir şirket etiketle", - "video_length_is_invalid_must_be_up_to": "Video uzunluğu geçersiz, en fazla", - "seconds": "saniye olmalıdır", - "this_feature_available_only_for_photos": "Bu özellik yalnızca fotoğraflar için geçerlidir, daha sonra değiştirebileceğiniz varsayılan bir müzik ekleyecektir.", - "allow_user_to": "Kullanıcıya izin ver:", - "your_video_will_be_labeled_promotional": "Videonuz \"Tanıtım İçeriği\" olarak etiketlenecek.", - "this_cannot_be_changed_once_posted": "Videonuz paylaşıldıktan sonra bu değiştirilemez.", - "turn_on_to_disclose_video_promotes": "Bu videonun bir değer karşılığında mal veya hizmet tanıttığını belirtmek için açın. Videonuz kendinizi, üçüncü bir tarafı veya her ikisini tanıtıyor olabilir.", - "you_are_promoting_yourself": "Kendinizi veya kendi markanızı tanıtıyorsunuz.", - "this_video_will_be_classified_brand_organic": "Bu video Marka Organik olarak sınıflandırılacak.", - "you_are_promoting_another_brand": "Başka bir markayı veya üçüncü bir tarafı tanıtıyorsunuz.", - "this_video_will_be_classified_branded_content": "Bu video Markalı İçerik olarak sınıflandırılacak.", - "by_posting_you_agree_to_tiktoks": "Paylaşım yaparak TikTok'un", - "music_usage_confirmation": "Müzik Kullanım Onayı", - "branded_content_policy": "Markalı İçerik Politikası", - "select_1": "--Seç--", - "select_flair": "--Flair Seç--", - "link": "Bağlantı", - "add_subreddit": "Subreddit Ekle", - "please_add_at_least_one_subreddit": "Lütfen en az bir Subreddit ekleyin", - "add_community": "Topluluk Ekle", - "select_post_type": "Gönderi Türü Seç...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "LinkedIn Sayfanıza bağlı herhangi bir işletme bulamadık.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Lütfen bu pencereyi kapatın, yeni bir sayfa oluşturun ve tekrar yeni bir kanal ekleyin.", - "select_linkedin_page": "LinkedIn Sayfası Seç:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Seçilen sayfalara bağlı herhangi bir işletme bulamadık.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Tüm sayfaları ve tüm işletmeleri bağlamanızı öneririz.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Lütfen bu pencereyi kapatın, entegrasyonunuzu silin ve tekrar yeni bir kanal ekleyin.", - "select_instagram_account": "Instagram Hesabı Seç:", - "select_page": "Sayfa Seç:", - "generate_image_with_ai": "Yapay Zeka ile görsel oluştur", - "reconnect_channel": "Kanalı yeniden bağla", - "update_credentials": "Kimlik Bilgilerini Güncelle", - "additional_settings": "Ek Ayarlar", - "change_bot": "Botu Değiştir", - "move_add_to_customer": "Müşteriye taşı / ekle", - "edit_time_slots": "Zaman Dilimlerini Düzenle", - "enable_channel": "Kanalı Etkinleştir", - "disable_channel": "Kanalı Devre Dışı Bırak", - "add": "Ekle", - "short_post": "Kısa gönderi", - "long_post": "Uzun gönderi", - "a_thread_with_short_posts": "Kısa gönderilerden oluşan bir konu", - "a_thread_with_long_posts": "Uzun gönderilerden oluşan bir konu", - "personal_voice_i_am_happy_to_announce": "Kişisel ses (\"Duyurmaktan mutluyum\")", - "company_voice_we_are_happy_to_announce": "Şirket sesi (\"Duyurmaktan mutluyuz\")", - "generate": "Oluştur", - "generate_posts": "Gönderi Oluştur", - "purchase_a_life_time_pro_account_with_sol_199": "SOL ($199) ile ömür boyu PRO hesap satın alın. Lütfen bu satın alma için iade yapılmadığını unutmayın.", - "purchase_now": "Şimdi satın al", - "pay_today": "Bugün öde", - "we_are_sorry_to_see_you_go": "Gitmene üzüldük :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Kısaca neyi daha iyi yapabileceğimizi söyler misin?", - "cancel_subscription": "Aboneliği İptal Et", - "plans": "Planlar", - "monthly": "AYLIK", - "yearly": "YILLIK", - "reactivate_subscription": "Aboneliği Yeniden Aktifleştir", - "update_payment_method_invoices_history": "Ödeme Yöntemini Güncelle / Fatura Geçmişi", - "cancel_subscription_1": "Aboneliği iptal et", - "your_subscription_will_be_canceled_at": "Aboneliğiniz şu tarihte iptal edilecek:", - "you_will_never_be_charged_again": "Bir daha asla ücretlendirilmeyeceksiniz", - "current_package": "Mevcut Paket:", - "next_package": "Sonraki Paket:", - "claim": "Talep Et", - "frequently_asked_questions": "Sıkça Sorulan Sorular", - "autopost": "Otomatik Paylaşım", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Otomatik paylaşım, RSS'inizdeki yeni öğeleri sosyal medyaya otomatik olarak gönderebilir", - "title": "Başlık", - "add_an_autopost": "Otomatik paylaşım ekle", - "post_content": "Paylaşım içeriği", - "sign_up": "Kayıt Ol", - "or": "VEYA", - "by_registering_you_agree_to_our": "Kayıt olarak şunları kabul etmiş olursunuz:", - "and": "ve", - "terms_of_service": "Hizmet Şartları", - "privacy_policy": "Gizlilik Politikası", - "create_account": "Hesap Oluştur", - "already_have_an_account": "Zaten bir hesabınız var mı?", - "sign_in": "Giriş Yap", - "sign_in_1": "Giriş yap", - "don_t_have_an_account": "Hesabınız yok mu?", - "forgot_password": "Şifremi unuttum", - "forgot_password_1": "Şifremi Unuttum", - "send_password_reset_email": "Şifre Sıfırlama E-postası Gönder", - "go_back_to_login": "Girişe geri dön", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Şifrenizi sıfırlamanız için size bir e-posta gönderdik.", - "change_password": "Şifreyi Değiştir", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Şifrenizi başarıyla sıfırladık. Artık giriş yapabilirsiniz.", - "click_here_to_go_back_to_login": "Giriş ekranına dönmek için buraya tıklayın", - "activate_your_account": "Hesabınızı Aktifleştirin", - "thank_you_for_registering": "Kayıt olduğunuz için teşekkürler!", - "please_check_your_email_to_activate_your_account": "Hesabınızı aktifleştirmek için lütfen e-postanızı kontrol edin.", - "sign_in_with": "İle giriş yap", - "continue_with_google": "Google ile devam et", - "sign_in_with_github": "GitHub ile giriş yap", - "continue_with_farcaster": "Farcaster ile devam et", - "continue_with_your_wallet": "Cüzdanınız ile devam edin", - "stars_per_day": "Günlük yıldızlar", - "media": "Medya", - "check_launch": "Lansmanı Kontrol Et", - "load_your_github_repository_from_settings_to_see_analytics": "Analitikleri görmek için GitHub deposunu ayarlardan yükleyin", - "stars": "Yıldızlar", - "processing_stars": "Yıldızlar işleniyor...", - "forks": "Çatallar", - "registration_is_disabled": "Kayıt olma devre dışı bırakıldı", - "login_instead": "Bunun yerine giriş yapın", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Bir sohbet seçin ve konuşmaya başlayın.", - "adding_channel_redirecting_you": "Kanal ekleniyor, yönlendiriliyorsunuz", - "could_not_add_provider": "Sağlayıcı eklenemedi.", - "you_are_being_redirected_back": "Geri yönlendiriliyorsunuz", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Bazı zorluklar yaşıyoruz, sayfayı yenilemeyi deneyin", - "post_not_found": "Gönderi bulunamadı", - "publication_date": "Yayınlanma Tarihi:", - "analytics": "Analitik", - "launches": "Lansmanlar", - "plugs": "Duyurular", - "billing": "Faturalandırma", - "affiliate": "Ortaklık", - "monday": "Pazartesi", - "tuesday": "Salı", - "wednesday": "Çarşamba", - "thursday": "Perşembe", - "friday": "Cuma", - "saturday": "Cumartesi", - "sunday": "Pazar", - "can_t_change_date_remove_post_from_publication": "Tarihi değiştiremiyorsunuz, gönderiyi yayından kaldırın", - "predicted_github_trending_change": "Tahmini GitHub Trend Değişimi", - "duplicate_post": "Yinelenen Gönderi", - "preview_post": "Gönderiyi Önizle", - "post_statistics": "Gönderi İstatistikleri", - "draft": "Taslak", - "week_number": "{{number}}. Hafta", - "top_title_edit_webhook": "Webhook'u düzenle", - "top_title_add_webhook": "Webhook ekle", - "top_title_oh_no": "Ah hayır", - "top_title_auto_plug": "Otomatik Eklenti: {{title}}", - "top_title_edit_autopost": "Otomatik gönderiyi düzenle", - "top_title_add_autopost": "Otomatik gönderi ekle", - "top_title_send_a_new_offer": "Yeni bir teklif gönder", - "top_title_media_library": "Medya Kütüphanesi", - "top_title_add_signature": "İmza ekle", - "top_title_send_a_message_to": "{{name}} kişisine mesaj gönder", - "top_title_configure_provider": "Sağlayıcıyı Yapılandır", - "top_title_add_member": "Üye Ekle", - "top_title_change_bot_picture": "Bot Resmini Değiştir", - "top_title_create_a_new_tag": "Yeni bir etiket oluştur", - "top_title_select_company": "Şirket Seç", - "top_title_additional_settings": "Ek Ayarlar", - "top_title_time_table_slots": "Zaman Tablosu Dilimleri", - "top_title_design_media": "Medya Tasarla", - "top_title_edit_post": "Gönderiyi Düzenle", - "top_title_create_post": "Gönderi Oluştur", - "top_title_move__add_to_customer": "Taşı / Müşteriye Ekle", - "top_title_add_api_key_for": "{{name}} için API anahtarı ekle", - "top_title_instance_url": "Instance URL", - "top_title_custom_url": "Özel URL", - "top_title_add_channel": "Kanal Ekle", - "top_title_add_telegram": "Telegram Ekle", - "top_title_add_wrapcast": "Wrapcast Ekle", - "top_title_comments_for": "{{date}} için yorumlar", - "top_title_edit_signature": "İmza Düzenle", - "label_name": "Ad", - "label_url": "URL", - "label_title": "Başlık", - "label_subtitle": "Alt Başlık", - "label_email": "E-posta", - "label_full_name": "Tam Adı", - "label_password": "Şifre", - "label_confirm_password": "Şifreyi Onayla", - "label_api_key": "API Anahtarı", - "label_instance_url": "Instance URL", - "label_custom_url": "Özel URL", - "label_feedback": "Geri Bildirim", - "label_bio": "Biyografi", - "label_role": "Rol", - "label_country": "Ülke", - "label_audience_size": "Tüm platformlardaki takipçi sayısı", - "label_pick_time": "Zaman Seç", - "label_nickname": "Takma Ad", - "label_write_anything": "Herhangi bir şey yazın", - "label_output_format": "Çıktı formatı", - "label_add_pictures": "Resim ekle?", - "label_hour": "Saat", - "label_minutes": "Dakika", - "label_select_publication": "Yayın Seç", - "label_canonical_link": "Kanonik Bağlantı", - "label_cover_picture": "Kapak resmi", - "label_tags": "Etiketler", - "label_topics": "Konular", - "label_tags_maximum_4": "Etiketler (En fazla 4)", - "label_attachments": "Ekler", - "label_type": "Tür", - "label_thumbnail": "Küçük resim", - "label_who_can_see_this_video": "Bu videoyu kimler görebilir?", - "label_content_posting_method": "İçerik paylaşım yöntemi", - "label_auto_add_music": "Otomatik müzik ekle", - "label_duet": "Düet", - "label_stitch": "Birleştir", - "label_comments": "Yorumlar", - "label_disclose_video_content": "Video İçeriğini Açıkla", - "label_your_brand": "Markanız", - "label_branded_content": "Markalı içerik", - "label_subreddit": "Subreddit", - "label_flair": "Rozet", - "label_media": "Medya", - "label_search_subreddit": "Subreddit Ara", - "label_delay": "Gecikme", - "label_post_type": "Gönderi Türü", - "label_collaborators": "İşbirlikçiler (en fazla 3) - hesaplar gizli olamaz", - "label_community": "Topluluk", - "label_search_community": "Toplulukta Ara", - "label_channel": "Kanal", - "label_search_channel": "Kanal Ara", - "label_select_channel": "Kanal Seç", - "label_new_password": "Yeni Şifre", - "label_repeat_password": "Şifreyi Tekrarla", - "label_platform": "Platform", - "label_price_per_post": "Gönderi başına fiyat", - "label_integrations": "Entegrasyonlar", - "label_code": "Kod", - "label_should_sync_last_post": "Mevcut son gönderiyi senkronize edelim mi?", - "label_when_post": "Ne zaman paylaşalım?", - "label_autogenerate_content": "İçeriği otomatik oluştur", - "label_generate_picture": "Resim oluşturulsun mu?", - "label_company": "Şirket", - "label_tag_color": "Etiket Rengi", - "label_select_board": "Pano Seç", - "label_select_organization": "Organizasyon Seç", - "label_auto_add_signature": "Otomatik imza eklensin mi?", - "enable_color_picker": "Renk seçiciyi etkinleştir", - "cancel_the_color_picker": "Renk seçiciyi iptal et", - "no_content_yet": "Henüz İçerik Yok", - "write_your_reply": "Gönderinizi yazın...", - "add_a_tag": "Etiket ekle", - "add_to_calendar": "Takvime Ekle", - "select_channels_from_circles": "Yukarıdaki dairelerden kanalları seçin", - "not_matching_order": "Sıra eşleşmiyor", - "submit_for_order": "Sipariş için gönder", - "schedule": "Zamanla", - "update": "Güncelle", - "attachments": "Ekler", - "tags": "Etiketler", - "public_to_everyone": "Herkese açık", - "mutual_follow_friends": "Karşılıklı takip edilen arkadaşlar", - "follower_of_creator": "Oluşturucunun takipçisi", - "self_only": "Sadece kendim", - "post_content_directly_to_tiktok": "İçeriği doğrudan TikTok'a gönder", - "upload_content_to_tiktok_without_posting": "İçeriği TikTok'a paylaşmadan yükle", - "choose_upload_without_posting_description": "İçeriğinizi yayınlamadan önce TikTok uygulamasında gözden geçirmek ve düzenlemek istiyorsanız, paylaşmadan yüklemeyi seçin. Bu, TikTok'un yerleşik düzenleme araçlarına erişmenizi sağlar ve paylaşmadan önce son düzenlemeleri yapmanıza olanak tanır.", - "faq_am_i_going_to_be_charged_by_postiz": "Postiz tarafından ücretlendirilecek miyim?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Kredi kartı bilgilerinizi doğrulamak için Postiz 2$ tutarında bir provizyon alacak ve hemen serbest bırakacaktır", - "faq_can_i_trust_postiz_gitroom": "Postiz'e güvenebilir miyim?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz gururla açık kaynaklıdır! Etik ve şeffaf bir kültüre inanıyoruz, bu da Postiz'in sonsuza dek yaşayacağı anlamına gelir. Tüm kodu inceleyebilir veya kişisel projelerinizde kullanabilirsiniz. Açık kaynak deposunu görüntülemek için <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">buraya tıklayın</a>.", - "faq_what_are_channels": "Kanallar nedir?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz, gönderilerinizi farklı kanallar arasında zamanlamanızı sağlar.\nKanal, gönderilerinizi zamanlayabileceğiniz bir paylaşım platformudur.\nÖrneğin, gönderilerinizi X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads ve Pinterest'te zamanlayabilirsiniz.", - "faq_what_are_team_members": "Takım üyeleri nedir?", - "faq_if_you_have_a_team_with_multiple_members": "Birden fazla üyesi olan bir ekibiniz varsa, onları çalışma alanınıza davet ederek gönderileriniz üzerinde iş birliği yapabilir ve kendi kişisel kanallarını ekleyebilirler.", - "faq_what_is_ai_auto_complete": "Yapay zeka otomatik tamamlama nedir?", - "faq_we_automate_chatgpt_to_help_you_write": "Sosyal medya gönderileri ve makaleler yazmanıza yardımcı olmak için ChatGPT'yi otomatikleştiriyoruz.", - "enter_email": "E-posta girin", - "are_you_sure": "Emin misiniz?", - "yes_delete_it": "Evet, sil!", - "no_cancel": "Hayır, iptal et!", - "are_you_sure_you_want_to_delete": "{{name}}'i silmek istediğinizden emin misiniz?", - "are_you_sure_you_want_to_delete_the_image": "Görseli silmek istediğinizden emin misiniz?", - "are_you_sure_you_want_to_logout": "Çıkış yapmak istediğinizden emin misiniz?", - "yes_logout": "Evet, çıkış yap", - "are_you_sure_you_want_to_delete_this_slot": "Bu slotu silmek istediğinizden emin misiniz?", - "are_you_sure_you_want_to_delete_this_subreddit": "Bu Subreddit'i silmek istediğinizden emin misiniz?", - "are_you_sure_you_want_to_close_the_window": "Pencereyi kapatmak istediğinizden emin misiniz?", - "yes_close": "Evet, kapat", - "link_copied_to_clipboard": "Bağlantı panoya kopyalandı", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Bu modali kapatmak istediğinizden emin misiniz? (tüm veriler kaybolacak)", - "yes_close_it": "Evet, kapat!", - "uploading_pictures": "Resimler yükleniyor...", - "agent_starting": "Aracı başlatılıyor", - "researching_your_content": "İçeriğiniz araştırılıyor...", - "understanding_the_category": "Kategori anlaşılıyor...", - "finding_the_topic": "Konu bulunuyor...", - "finding_popular_posts_to_match_with": "Eşleşecek popüler gönderiler bulunuyor...", - "generating_hook": "Kanca oluşturuluyor...", - "generating_content": "İçerik oluşturuluyor...", - "generating_pictures": "Resimler oluşturuluyor...", - "finding_time_to_post": "Paylaşım için zaman bulunuyor...", - "write_anything": "Herhangi bir şey yazın", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "İstediğiniz her şeyi yazabilir, ayrıca bağlantılar ekleyebilirsiniz, araştırmayı sizin için biz yapacağız...", - "output_format": "Çıktı formatı", - "add_pictures": "Resim ekle?", - "7_days": "7 Gün", - "30_days": "30 Gün", - "90_days": "90 Gün", - "start_7_days_free_trial": "7 gün ücretsiz denemeyi başlat", - "change_language": "Dili Değiştir", - "that_a_wrap": "Bu iş burada bitti!\n\nEğer bu diziyi beğendiyseniz:\n\n1. Daha fazlası için beni @{{username}} hesabından takip edin\n2. Aşağıdaki tweet'i RT'leyerek bu diziyi kendi kitlenizle paylaşın\n", - "post_as_images_carousel": "Görselleri kaydırmalı gönder olarak paylaş", - "save_set": "Seti Kaydet", - "separate_post": "Gönderiyi birden fazla gönderiye ayır", - "label_who_can_reply_to_this_post": "Bu gönderiye kim yanıt verebilir?", - "delete_integration": "Entegrasyonu Sil", - "start_writing_your_post": "Önizleme için gönderinizi yazmaya başlayın" + "calendar": "Takvim", + "webhooks": "Web kancaları", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Web kancaları, Postiz'de bir şey olduğunda bir HTTP isteği aracılığıyla bildirim almanın bir yoludur.", + "name": "İsim", + "url": "URL", + "edit": "Düzenle", + "delete": "Sil", + "add_a_webhook": "Bir web kancası ekle", + "save": "Kaydet", + "send_test": "Test Gönder", + "select_role": "Rol Seç", + "video_made_with_ai": "Video yapay zeka ile oluşturuldu", + "please_add_at_least": "Lütfen en az 20 karakter ekleyin", + "send_invitation_via_email": "Davetiyeyi e-posta ile gönderilsin mi?", + "global_settings": "Genel Ayarlar", + "copy_id": "Kanal Kimliğini Kopyala", + "team_members": "Takım Üyeleri", + "invite_your_assistant_or_team_member_to_manage_your_account": "Hesabınızı yönetmesi için asistanınızı veya takım üyenizi davet edin", + "remove": "Kaldır", + "add_another_member": "Başka bir üye ekle", + "signatures": "İmzalar", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Gönderilerinizde kullanılmak üzere hesabınıza imzalar ekleyebilirsiniz.", + "content": "İçerik", + "auto_add": "Otomatik Ekle?", + "actions": "Eylemler", + "use_signature": "İmzayı Kullan", + "add_a_signature": "Bir imza ekle", + "no": "Hayır", + "yes": "Evet", + "your_git_repository": "Git Depo'nuz", + "connect_your_github_repository_to_receive_updates_and_analytics": "Güncellemeler ve analizler almak için GitHub deponuzu bağlayın", + "connected": "Bağlandı:", + "disconnect": "Bağlantıyı Kes", + "connect_your_repository": "Depoyu Bağla", + "cancel": "İptal", + "connect": "Bağla", + "public_api": "Genel API", + "check_n8n": "Postiz için özel N8N düğümümüzü inceleyin.", + "use_postiz_api_to_integrate_with_your_tools": "Araçlarınızla entegre olmak için Postiz API'sini kullanın.", + "read_how_to_use_it_over_the_documentation": "Nasıl kullanılacağını dokümantasyondan okuyun.", + "reveal": "Göster", + "copy_key": "Anahtarı Kopyala", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Gönderilerinizi daha hızlı planlamak için Postiz MCP sunucusunu istemcinize (Http akışı) bağlayın!", + "share_with_a_client": "Bir müşteriyle paylaş", + "post": "Gönderi", + "comments": "Yorumlar", + "user": "Kullanıcı", + "login_register_to_add_comments": "Yorum eklemek için Giriş Yap / Kayıt Ol", + "status": "Durum:", + "there_are_not_plugs_matching_your_channels": "Kanallarınızla eşleşen eklenti yok", + "you_have_to_add_x_or_linkedin_or_threads": "Şunlardan birini eklemelisiniz: X veya LinkedIn veya Threads", + "go_to_the_calendar_to_add_channels": "Kanalları eklemek için takvime gidin", + "channels": "Kanallar", + "activate": "Aktifleştir", + "this_channel_needs_to_be_refreshed": "Bu kanalın yenilenmesi gerekiyor,", + "click_here_to_refresh": "yenilemek için buraya tıklayın", + "can_t_show_analytics_yet": "Analitikler henüz gösterilemiyor", + "you_have_to_add_social_media_channels": "Sosyal medya kanalları eklemelisiniz", + "supported": "Desteklenen:", + "step": "ADIM", + "skip_onboarding": "Başlangıç rehberini atla", + "onboarding": "Başlangıç rehberi", + "next": "Sonraki", + "you_are_done_from_here_you_can": "Hazırsınız, buradan şunları yapabilirsiniz:", + "view_analytics": "Analitikleri Görüntüle", + "schedule_a_new_post": "Yeni bir gönderi planla", + "to_sell_posts_you_would_have_to": "Gönderi satmak için şunları yapmalısınız:", + "1_connect_at_least_one_channel": "1. En az bir kanal bağlayın", + "2_connect_you_bank_account": "2. Banka hesabınızı bağlayın", + "go_back_to_connect_channels": "Kanalları bağlamaya geri dön", + "move_to_the_seller_page_to_connect_you_bank": "Banka hesabınızı bağlamak için satıcı sayfasına gidin", + "connect_channels": "Kanalları Bağla", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Sosyal medya ve yayın platformu kanallarınızı bağlayarak\n gönderileri daha sonra planlayabilirsiniz", + "social": "Sosyal", + "publishing_platforms": "Yayın Platformları", + "no_channels": "Henüz kanal yok", + "connect_your_accounts": "Zamanlama, paylaşım ve analiz işlemlerine başlamak için sosyal hesaplarınızı bağlayın — hepsi tek bir yerde.", + "notifications": "Bildirimler", + "no_notifications": "Bildirim yok", + "send_message": "Mesaj Gönder", + "mar_28": "28 Mar", + "there_are_no_messages_yet": "Henüz mesaj yok.", + "checkout_the_marketplace": "Pazaryerini incele", + "go_to_marketplace": "Pazaryerine git", + "all_messages": "Tüm Mesajlar", + "previous": "Önceki", + "select_or_upload_pictures_maximum_5_at_a_time": "Resim seçin veya yükleyin (en fazla 5 adet aynı anda)", + "you_can_also_drag_drop_pictures": "Ayrıca resimleri sürükleyip bırakabilirsiniz", + "you_don_t_have_any_assets_yet": "Henüz herhangi bir varlığınız yok.", + "click_the_button_below_to_upload_one": "Bir tane yüklemek için aşağıdaki butona tıklayın", + "click_the_button_below_to_upload_other": "Birden fazla yüklemek için aşağıdaki butona tıklayın", + "add_selected_media": "Seçili medyayı ekle", + "insert_media": "Medya Ekle", + "design_media": "Medya Tasarla", + "select": "Seç", + "editor": "Editör", + "clear": "Temizle", + "order_completed": "Sipariş tamamlandı", + "the_order_has_been_completed": "Sipariş tamamlandı", + "post_has_been_published": "Gönderi yayınlandı", + "url_1": "URL:", + "new_offer": "Yeni Teklif", + "platform": "Platform", + "posts": "Gönderiler", + "pay_accept_offer": "Öde ve Teklifi Kabul Et", + "accepted": "Kabul Edildi", + "post_draft": "Taslak Gönderi", + "revision_needed": "Düzeltme Gerekli", + "approve": "Onayla", + "preview": "Önizleme", + "revision_requested": "Düzeltme Talep Edildi", + "accepted_1": "KABUL EDİLDİ", + "cancelled_by_the_seller": "Satıcı tarafından iptal edildi", + "please_select_your_country_where_your_business_is": "Lütfen işletmenizin bulunduğu ülkeyi seçin.", + "select_country": "--ÜLKE SEÇİN--", + "connect_bank_account": "Banka Hesabını Bağla", + "seller_mode": "Satıcı Modu", + "active": "Aktif", + "details": "Detaylar", + "audience_size": "Hedef Kitle Büyüklüğü", + "add_another_platform": "Başka bir platform ekle", + "send_an_offer_for": "Şu fiyatla teklif gönder: $", + "complete_order_and_pay_early": "Siparişi tamamla ve erken öde", + "order_in_progress": "Sipariş devam ediyor", + "create_a_new_offer": "Yeni bir teklif oluştur", + "orders": "Siparişler", + "price": "Fiyat", + "state": "Durum", + "showing": "Gösteriliyor", + "to": "-e", + "from": "-den", + "results": "Sonuçlar", + "content_writer": "İçerik Yazarı", + "influencer": "Influencer", + "request_service": "Hizmet Talep Et", + "the_marketplace_is_not_opened_yet": "Pazar yeri henüz açılmadı", + "check_again_soon": "Yakında tekrar kontrol et!", + "filter": "Filtrele", + "result": "Sonuç", + "seller": "Satıcı", + "buyer": "Alıcı", + "discord_support": "Discord Desteği", + "teams": "Takımlar", + "webhooks_1": "Webhooks", + "auto_post": "Otomatik Paylaşım", + "logout_from": "Çıkış yap", + "join_10000_entrepreneurs_who_use_postiz": "Postiz kullanan 10.000+ girişimciye katılın", + "to_manage_all_your_social_media_channels": "Tüm sosyal medya kanallarınızı yönetmek için", + "100_no_risk_trial": "%100 risksiz deneme", + "pay_nothing_for_the_first_7_days": "İlk 7 gün hiçbir ücret ödeme", + "cancel_anytime_hassle_free": "İstediğin zaman zahmetsizce iptal et", + "add_free_subscription": "-- ÜCRETSİZ ABONELİK EKLE --", + "currently_impersonating": "Şu anda taklit ediliyor", + "user_1": "kullanıcı:", + "drag_n_drop_some_files_here": "Dosyaları buraya sürükleyip bırakın", + "add_time_slot": "Zaman Dilimi Ekle", + "add_slot": "Zaman Dilimi Ekle", + "cancel_publication": "Yayını İptal Et", + "statistics": "İstatistikler", + "loading": "Yükleniyor", + "short_link": "Kısa Link", + "original_link": "Orijinal Link", + "clicks": "Tıklamalar", + "selected_customer": "Seçili Müşteri", + "customer": "Müşteri:", + "repeat_post_every": "Gönderiyi Her ... Tekrarla", + "use_this_media": "Bu medyayı kullan", + "create_new_post": "Gönderi Oluştur", + "update_post": "Mevcut Gönderiyi Güncelle", + "merge_comments_into_one_post": "Yorumları tek gönderide birleştir", + "accounts_that_will_engage": "Etkileşime Geçecek Hesaplar:", + "day": "Gün", + "week": "Hafta", + "month": "Ay", + "remove_from_customer": "Müşteriden kaldır", + "show_more": "+ Daha fazla göster", + "show_less": "- Daha az göster", + "upload": "Yükle", + "ai": "Yapay Zeka", + "add_channel": "Kanal Ekle", + "add_platform": "Platform ekle", + "articles": "Makaleler", + "add_comment": "Yorum ekle", + "add_post": "Bir başlıkta gönderi ekle", + "add_comment_or_post": "Yorum / gönderi ekle", + "you_are_in_global_editing_mode": "Genel düzenleme modundasınız", + "the_post_should_be_at_least_6_characters_long": "Gönderi en az 6 karakter uzunluğunda olmalıdır", + "are_you_sure_you_want_to_delete_post": "Bu gönderiyi silmek istediğinizden emin misiniz?", + "post_deleted_successfully": "Gönderi başarıyla silindi", + "delete_post": "Gönderiyi sil", + "save_as_draft": "Taslak olarak kaydet", + "post_now": "Şimdi paylaş", + "please_add": "Lütfen ekleyin", + "to_your_telegram_group_channel_and_click_here": "Telegram grup/kanalınıza ekleyin ve buraya tıklayın:", + "connect_telegram": "Telegram'ı bağla", + "please_add_the_following_command_in_your_chat": "Lütfen sohbetinize aşağıdaki komutu ekleyin:", + "copy": "Kopyala", + "settings": "Ayarlar", + "integrations": "Entegrasyonlar", + "add_integration": "Entegrasyon Ekle", + "you_are_now_editing_only": "Şu anda yalnızca düzenliyorsunuz", + "tag_a_company": "Bir şirket etiketle", + "video_length_is_invalid_must_be_up_to": "Video uzunluğu geçersiz, en fazla", + "seconds": "saniye olmalıdır", + "this_feature_available_only_for_photos": "Bu özellik yalnızca fotoğraflar için geçerlidir, daha sonra değiştirebileceğiniz varsayılan bir müzik ekleyecektir.", + "allow_user_to": "Kullanıcıya izin ver:", + "your_video_will_be_labeled_promotional": "Videonuz \"Tanıtım İçeriği\" olarak etiketlenecek.", + "this_cannot_be_changed_once_posted": "Videonuz paylaşıldıktan sonra bu değiştirilemez.", + "turn_on_to_disclose_video_promotes": "Bu videonun bir değer karşılığında mal veya hizmet tanıttığını belirtmek için açın. Videonuz kendinizi, üçüncü bir tarafı veya her ikisini tanıtıyor olabilir.", + "you_are_promoting_yourself": "Kendinizi veya kendi markanızı tanıtıyorsunuz.", + "this_video_will_be_classified_brand_organic": "Bu video Marka Organik olarak sınıflandırılacak.", + "you_are_promoting_another_brand": "Başka bir markayı veya üçüncü bir tarafı tanıtıyorsunuz.", + "this_video_will_be_classified_branded_content": "Bu video Markalı İçerik olarak sınıflandırılacak.", + "by_posting_you_agree_to_tiktoks": "Paylaşım yaparak TikTok'un", + "music_usage_confirmation": "Müzik Kullanım Onayı", + "branded_content_policy": "Markalı İçerik Politikası", + "select_1": "--Seç--", + "select_flair": "--Flair Seç--", + "link": "Bağlantı", + "add_subreddit": "Subreddit Ekle", + "please_add_at_least_one_subreddit": "Lütfen en az bir Subreddit ekleyin", + "add_community": "Topluluk Ekle", + "select_post_type": "Gönderi Türü Seç...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "LinkedIn Sayfanıza bağlı herhangi bir işletme bulamadık.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Lütfen bu pencereyi kapatın, yeni bir sayfa oluşturun ve tekrar yeni bir kanal ekleyin.", + "select_linkedin_page": "LinkedIn Sayfası Seç:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Seçilen sayfalara bağlı herhangi bir işletme bulamadık.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Tüm sayfaları ve tüm işletmeleri bağlamanızı öneririz.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Lütfen bu pencereyi kapatın, entegrasyonunuzu silin ve tekrar yeni bir kanal ekleyin.", + "select_instagram_account": "Instagram Hesabı Seç:", + "select_page": "Sayfa Seç:", + "generate_image_with_ai": "Yapay Zeka ile görsel oluştur", + "reconnect_channel": "Kanalı yeniden bağla", + "update_credentials": "Kimlik Bilgilerini Güncelle", + "additional_settings": "Ek Ayarlar", + "change_bot": "Botu Değiştir", + "move_add_to_customer": "Müşteriye taşı / ekle", + "edit_time_slots": "Zaman Dilimlerini Düzenle", + "enable_channel": "Kanalı Etkinleştir", + "disable_channel": "Kanalı Devre Dışı Bırak", + "add": "Ekle", + "short_post": "Kısa gönderi", + "long_post": "Uzun gönderi", + "a_thread_with_short_posts": "Kısa gönderilerden oluşan bir konu", + "a_thread_with_long_posts": "Uzun gönderilerden oluşan bir konu", + "personal_voice_i_am_happy_to_announce": "Kişisel ses (\"Duyurmaktan mutluyum\")", + "company_voice_we_are_happy_to_announce": "Şirket sesi (\"Duyurmaktan mutluyuz\")", + "generate": "Oluştur", + "generate_posts": "Gönderi Oluştur", + "purchase_a_life_time_pro_account_with_sol_199": "SOL ($199) ile ömür boyu PRO hesap satın alın. Lütfen bu satın alma için iade yapılmadığını unutmayın.", + "purchase_now": "Şimdi satın al", + "pay_today": "Bugün öde", + "we_are_sorry_to_see_you_go": "Gitmene üzüldük :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Kısaca neyi daha iyi yapabileceğimizi söyler misin?", + "cancel_subscription": "Aboneliği İptal Et", + "plans": "Planlar", + "monthly": "AYLIK", + "yearly": "YILLIK", + "reactivate_subscription": "Aboneliği Yeniden Aktifleştir", + "update_payment_method_invoices_history": "Ödeme Yöntemini Güncelle / Fatura Geçmişi", + "cancel_subscription_1": "Aboneliği iptal et", + "your_subscription_will_be_canceled_at": "Aboneliğiniz şu tarihte iptal edilecek:", + "you_will_never_be_charged_again": "Bir daha asla ücretlendirilmeyeceksiniz", + "current_package": "Mevcut Paket:", + "next_package": "Sonraki Paket:", + "claim": "Talep Et", + "frequently_asked_questions": "Sıkça Sorulan Sorular", + "autopost": "Otomatik Paylaşım", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Otomatik paylaşım, RSS'inizdeki yeni öğeleri sosyal medyaya otomatik olarak gönderebilir", + "title": "Başlık", + "add_an_autopost": "Otomatik paylaşım ekle", + "post_content": "Paylaşım içeriği", + "sign_up": "Kayıt Ol", + "or": "VEYA", + "by_registering_you_agree_to_our": "Kayıt olarak şunları kabul etmiş olursunuz:", + "and": "ve", + "terms_of_service": "Hizmet Şartları", + "privacy_policy": "Gizlilik Politikası", + "create_account": "Hesap Oluştur", + "already_have_an_account": "Zaten bir hesabınız var mı?", + "sign_in": "Giriş Yap", + "sign_in_1": "Giriş yap", + "don_t_have_an_account": "Hesabınız yok mu?", + "forgot_password": "Şifremi unuttum", + "forgot_password_1": "Şifremi Unuttum", + "send_password_reset_email": "Şifre Sıfırlama E-postası Gönder", + "go_back_to_login": "Girişe geri dön", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Şifrenizi sıfırlamanız için size bir e-posta gönderdik.", + "change_password": "Şifreyi Değiştir", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Şifrenizi başarıyla sıfırladık. Artık giriş yapabilirsiniz.", + "click_here_to_go_back_to_login": "Giriş ekranına dönmek için buraya tıklayın", + "activate_your_account": "Hesabınızı Aktifleştirin", + "thank_you_for_registering": "Kayıt olduğunuz için teşekkürler!", + "please_check_your_email_to_activate_your_account": "Hesabınızı aktifleştirmek için lütfen e-postanızı kontrol edin.", + "sign_in_with": "İle giriş yap", + "continue_with_google": "Google ile devam et", + "sign_in_with_github": "GitHub ile giriş yap", + "continue_with_farcaster": "Farcaster ile devam et", + "continue_with_your_wallet": "Cüzdanınız ile devam edin", + "stars_per_day": "Günlük yıldızlar", + "media": "Medya", + "check_launch": "Lansmanı Kontrol Et", + "load_your_github_repository_from_settings_to_see_analytics": "Analitikleri görmek için GitHub deposunu ayarlardan yükleyin", + "stars": "Yıldızlar", + "processing_stars": "Yıldızlar işleniyor...", + "forks": "Çatallar", + "registration_is_disabled": "Kayıt olma devre dışı bırakıldı", + "login_instead": "Bunun yerine giriş yapın", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Bir sohbet seçin ve konuşmaya başlayın.", + "adding_channel_redirecting_you": "Kanal ekleniyor, yönlendiriliyorsunuz", + "could_not_add_provider": "Sağlayıcı eklenemedi.", + "you_are_being_redirected_back": "Geri yönlendiriliyorsunuz", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Bazı zorluklar yaşıyoruz, sayfayı yenilemeyi deneyin", + "post_not_found": "Gönderi bulunamadı", + "publication_date": "Yayınlanma Tarihi:", + "analytics": "Analitik", + "launches": "Lansmanlar", + "plugs": "Duyurular", + "billing": "Faturalandırma", + "affiliate": "Ortaklık", + "monday": "Pazartesi", + "tuesday": "Salı", + "wednesday": "Çarşamba", + "thursday": "Perşembe", + "friday": "Cuma", + "saturday": "Cumartesi", + "sunday": "Pazar", + "can_t_change_date_remove_post_from_publication": "Tarihi değiştiremiyorsunuz, gönderiyi yayından kaldırın", + "predicted_github_trending_change": "Tahmini GitHub Trend Değişimi", + "duplicate_post": "Yinelenen Gönderi", + "preview_post": "Gönderiyi Önizle", + "post_statistics": "Gönderi İstatistikleri", + "draft": "Taslak", + "week_number": "{{number}}. Hafta", + "top_title_edit_webhook": "Webhook'u düzenle", + "top_title_add_webhook": "Webhook ekle", + "top_title_oh_no": "Ah hayır", + "top_title_auto_plug": "Otomatik Eklenti: {{title}}", + "top_title_edit_autopost": "Otomatik gönderiyi düzenle", + "top_title_add_autopost": "Otomatik gönderi ekle", + "top_title_send_a_new_offer": "Yeni bir teklif gönder", + "top_title_media_library": "Medya Kütüphanesi", + "top_title_add_signature": "İmza ekle", + "top_title_send_a_message_to": "{{name}} kişisine mesaj gönder", + "top_title_configure_provider": "Sağlayıcıyı Yapılandır", + "top_title_add_member": "Üye Ekle", + "top_title_change_bot_picture": "Bot Resmini Değiştir", + "top_title_create_a_new_tag": "Yeni bir etiket oluştur", + "top_title_select_company": "Şirket Seç", + "top_title_additional_settings": "Ek Ayarlar", + "top_title_time_table_slots": "Zaman Tablosu Dilimleri", + "top_title_design_media": "Medya Tasarla", + "top_title_edit_post": "Gönderiyi Düzenle", + "top_title_create_post": "Gönderi Oluştur", + "top_title_move__add_to_customer": "Taşı / Müşteriye Ekle", + "top_title_add_api_key_for": "{{name}} için API anahtarı ekle", + "top_title_instance_url": "Instance URL", + "top_title_custom_url": "Özel URL", + "top_title_add_channel": "Kanal Ekle", + "top_title_add_telegram": "Telegram Ekle", + "top_title_add_wrapcast": "Wrapcast Ekle", + "top_title_comments_for": "{{date}} için yorumlar", + "top_title_edit_signature": "İmza Düzenle", + "label_name": "Ad", + "label_url": "URL", + "label_title": "Başlık", + "label_subtitle": "Alt Başlık", + "label_email": "E-posta", + "label_full_name": "Tam Adı", + "label_password": "Şifre", + "label_confirm_password": "Şifreyi Onayla", + "label_api_key": "API Anahtarı", + "label_instance_url": "Instance URL", + "label_custom_url": "Özel URL", + "label_feedback": "Geri Bildirim", + "label_bio": "Biyografi", + "label_role": "Rol", + "label_country": "Ülke", + "label_audience_size": "Tüm platformlardaki takipçi sayısı", + "label_pick_time": "Zaman Seç", + "label_nickname": "Takma Ad", + "label_write_anything": "Herhangi bir şey yazın", + "label_output_format": "Çıktı formatı", + "label_add_pictures": "Resim ekle?", + "label_hour": "Saat", + "label_minutes": "Dakika", + "label_select_publication": "Yayın Seç", + "label_canonical_link": "Kanonik Bağlantı", + "label_cover_picture": "Kapak resmi", + "label_tags": "Etiketler", + "label_topics": "Konular", + "label_tags_maximum_4": "Etiketler (En fazla 4)", + "label_attachments": "Ekler", + "label_type": "Tür", + "label_thumbnail": "Küçük resim", + "label_who_can_see_this_video": "Bu videoyu kimler görebilir?", + "label_content_posting_method": "İçerik paylaşım yöntemi", + "label_auto_add_music": "Otomatik müzik ekle", + "label_duet": "Düet", + "label_stitch": "Birleştir", + "label_comments": "Yorumlar", + "label_disclose_video_content": "Video İçeriğini Açıkla", + "label_your_brand": "Markanız", + "label_branded_content": "Markalı içerik", + "label_subreddit": "Subreddit", + "label_flair": "Rozet", + "label_media": "Medya", + "label_search_subreddit": "Subreddit Ara", + "label_delay": "Gecikme", + "label_post_type": "Gönderi Türü", + "label_collaborators": "İşbirlikçiler (en fazla 3) - hesaplar gizli olamaz", + "label_community": "Topluluk", + "label_search_community": "Toplulukta Ara", + "label_channel": "Kanal", + "label_search_channel": "Kanal Ara", + "label_select_channel": "Kanal Seç", + "label_new_password": "Yeni Şifre", + "label_repeat_password": "Şifreyi Tekrarla", + "label_platform": "Platform", + "label_price_per_post": "Gönderi başına fiyat", + "label_integrations": "Entegrasyonlar", + "label_code": "Kod", + "label_should_sync_last_post": "Mevcut son gönderiyi senkronize edelim mi?", + "label_when_post": "Ne zaman paylaşalım?", + "label_autogenerate_content": "İçeriği otomatik oluştur", + "label_generate_picture": "Resim oluşturulsun mu?", + "label_company": "Şirket", + "label_tag_color": "Etiket Rengi", + "label_select_board": "Pano Seç", + "label_select_organization": "Organizasyon Seç", + "label_auto_add_signature": "Otomatik imza eklensin mi?", + "enable_color_picker": "Renk seçiciyi etkinleştir", + "cancel_the_color_picker": "Renk seçiciyi iptal et", + "no_content_yet": "Henüz İçerik Yok", + "write_your_reply": "Gönderinizi yazın...", + "add_a_tag": "Etiket ekle", + "add_to_calendar": "Takvime Ekle", + "select_channels_from_circles": "Yukarıdaki dairelerden kanalları seçin", + "not_matching_order": "Sıra eşleşmiyor", + "submit_for_order": "Sipariş için gönder", + "schedule": "Zamanla", + "update": "Güncelle", + "attachments": "Ekler", + "tags": "Etiketler", + "public_to_everyone": "Herkese açık", + "mutual_follow_friends": "Karşılıklı takip edilen arkadaşlar", + "follower_of_creator": "Oluşturucunun takipçisi", + "self_only": "Sadece kendim", + "post_content_directly_to_tiktok": "İçeriği doğrudan TikTok'a gönder", + "upload_content_to_tiktok_without_posting": "İçeriği TikTok'a paylaşmadan yükle", + "choose_upload_without_posting_description": "İçeriğinizi yayınlamadan önce TikTok uygulamasında gözden geçirmek ve düzenlemek istiyorsanız, paylaşmadan yüklemeyi seçin. Bu, TikTok'un yerleşik düzenleme araçlarına erişmenizi sağlar ve paylaşmadan önce son düzenlemeleri yapmanıza olanak tanır.", + "faq_am_i_going_to_be_charged_by_postiz": "Postiz tarafından ücretlendirilecek miyim?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Kredi kartı bilgilerinizi doğrulamak için Postiz 2$ tutarında bir provizyon alacak ve hemen serbest bırakacaktır", + "faq_can_i_trust_postiz_gitroom": "Postiz'e güvenebilir miyim?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz gururla açık kaynaklıdır! Etik ve şeffaf bir kültüre inanıyoruz, bu da Postiz'in sonsuza dek yaşayacağı anlamına gelir. Tüm kodu inceleyebilir veya kişisel projelerinizde kullanabilirsiniz. Açık kaynak deposunu görüntülemek için <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">buraya tıklayın</a>.", + "faq_what_are_channels": "Kanallar nedir?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz, gönderilerinizi farklı kanallar arasında zamanlamanızı sağlar.\nKanal, gönderilerinizi zamanlayabileceğiniz bir paylaşım platformudur.\nÖrneğin, gönderilerinizi X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads ve Pinterest'te zamanlayabilirsiniz.", + "faq_what_are_team_members": "Takım üyeleri nedir?", + "faq_if_you_have_a_team_with_multiple_members": "Birden fazla üyesi olan bir ekibiniz varsa, onları çalışma alanınıza davet ederek gönderileriniz üzerinde iş birliği yapabilir ve kendi kişisel kanallarını ekleyebilirler.", + "faq_what_is_ai_auto_complete": "Yapay zeka otomatik tamamlama nedir?", + "faq_we_automate_chatgpt_to_help_you_write": "Sosyal medya gönderileri ve makaleler yazmanıza yardımcı olmak için ChatGPT'yi otomatikleştiriyoruz.", + "enter_email": "E-posta girin", + "are_you_sure": "Emin misiniz?", + "yes_delete_it": "Evet, sil!", + "no_cancel": "Hayır, iptal et!", + "are_you_sure_you_want_to_delete": "{{name}}'i silmek istediğinizden emin misiniz?", + "are_you_sure_you_want_to_delete_the_image": "Görseli silmek istediğinizden emin misiniz?", + "are_you_sure_you_want_to_logout": "Çıkış yapmak istediğinizden emin misiniz?", + "yes_logout": "Evet, çıkış yap", + "are_you_sure_you_want_to_delete_this_slot": "Bu slotu silmek istediğinizden emin misiniz?", + "are_you_sure_you_want_to_delete_this_subreddit": "Bu Subreddit'i silmek istediğinizden emin misiniz?", + "are_you_sure_you_want_to_close_the_window": "Pencereyi kapatmak istediğinizden emin misiniz?", + "yes_close": "Evet, kapat", + "link_copied_to_clipboard": "Bağlantı panoya kopyalandı", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Bu modali kapatmak istediğinizden emin misiniz? (tüm veriler kaybolacak)", + "yes_close_it": "Evet, kapat!", + "uploading_pictures": "Resimler yükleniyor...", + "agent_starting": "Aracı başlatılıyor", + "researching_your_content": "İçeriğiniz araştırılıyor...", + "understanding_the_category": "Kategori anlaşılıyor...", + "finding_the_topic": "Konu bulunuyor...", + "finding_popular_posts_to_match_with": "Eşleşecek popüler gönderiler bulunuyor...", + "generating_hook": "Kanca oluşturuluyor...", + "generating_content": "İçerik oluşturuluyor...", + "generating_pictures": "Resimler oluşturuluyor...", + "finding_time_to_post": "Paylaşım için zaman bulunuyor...", + "write_anything": "Herhangi bir şey yazın", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "İstediğiniz her şeyi yazabilir, ayrıca bağlantılar ekleyebilirsiniz, araştırmayı sizin için biz yapacağız...", + "output_format": "Çıktı formatı", + "add_pictures": "Resim ekle?", + "7_days": "7 Gün", + "30_days": "30 Gün", + "90_days": "90 Gün", + "start_7_days_free_trial": "7 gün ücretsiz denemeyi başlat", + "change_language": "Dili Değiştir", + "that_a_wrap": "Bu iş burada bitti!\n\nEğer bu diziyi beğendiyseniz:\n\n1. Daha fazlası için beni @{{username}} hesabından takip edin\n2. Aşağıdaki tweet'i RT'leyerek bu diziyi kendi kitlenizle paylaşın\n", + "post_as_images_carousel": "Görselleri kaydırmalı gönder olarak paylaş", + "save_set": "Seti Kaydet", + "separate_post": "Gönderiyi birden fazla gönderiye ayır", + "label_who_can_reply_to_this_post": "Bu gönderiye kim yanıt verebilir?", + "delete_integration": "Entegrasyonu Sil", + "start_writing_your_post": "Önizleme için gönderinizi yazmaya başlayın", + "billing_join_over": "Katılan", + "billing_entrepreneurs_count": "18.000+ Girişimci", + "billing_who_use": "kullanan", + "billing_postiz_grow_social": "Sosyal Varlıklarını Büyütmek İçin Postiz", + "billing_no_risk_trial": "%100 Risksiz Ücretsiz Deneme", + "billing_pay_nothing_7_days": "İlk 7 gün boyunca HİÇBİR ŞEY ödemeyin", + "billing_cancel_anytime": "İstediğiniz zaman, zahmetsizce iptal edin", + "billing_choose_plan": "Bir Plan Seçin", + "billing_monthly": "Aylık", + "billing_yearly": "Yıllık", + "billing_20_percent_off": "%20 İndirim", + "billing_features": "Özellikler", + "billing_channel": "kanal", + "billing_channels": "kanallar", + "billing_unlimited": "Sınırsız", + "billing_posts_per_month": "aylık gönderi", + "billing_unlimited_team_members": "Sınırsız ekip üyesi", + "billing_ai_auto_complete": "Yapay Zeka otomatik tamamlama", + "billing_ai_copilots": "Yapay Zeka yardımcıları", + "billing_ai_autocomplete": "Yapay Zeka Otomatik Tamamlama", + "billing_advanced_picture_editor": "Gelişmiş Resim Editörü", + "billing_ai_images_per_month": "Aylık Yapay Zeka Görselleri", + "billing_ai_videos_per_month": "Aylık Yapay Zeka Videoları", + "billing_billing_address": "Fatura Adresi", + "billing_payment": "Ödeme", + "billing_powered_by_stripe": "Stripe ile desteklenmektedir", + "billing_your_7_day_trial_is": "7 günlük deneme süreniz", + "billing_100_percent_free": "%100 ücretsiz", + "billing_ending": "sona eriyor", + "billing_cancel_anytime_short": "İstediğiniz zaman iptal edin.", + "billing_pay_0_start_trial": "Bugün 0$ ödeyin - Ücretsiz denemenizi başlatın!", + "billing_pay_now": "Şimdi Öde", + "billing_per_month": "/ ay" } diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 263a4e82..24b95ceb 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -1,505 +1,539 @@ { - "calendar": "Lịch", - "webhooks": "Webhook", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhook là một cách để nhận thông báo khi có sự kiện xảy ra trong Postiz thông qua một yêu cầu HTTP.", - "name": "Tên", - "url": "URL", - "edit": "Chỉnh sửa", - "delete": "Xóa", - "add_a_webhook": "Thêm webhook", - "save": "Lưu", - "send_test": "Gửi thử", - "select_role": "Chọn vai trò", - "video_made_with_ai": "Video được tạo bằng AI", - "please_add_at_least": "Vui lòng thêm ít nhất 20 ký tự", - "send_invitation_via_email": "Gửi lời mời qua email?", - "global_settings": "Cài đặt toàn cục", - "copy_id": "Sao chép ID kênh", - "team_members": "Thành viên nhóm", - "invite_your_assistant_or_team_member_to_manage_your_account": "Mời trợ lý hoặc thành viên nhóm của bạn để quản lý tài khoản của bạn", - "remove": "Xóa", - "add_another_member": "Thêm thành viên khác", - "signatures": "Chữ ký", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Bạn có thể thêm chữ ký vào tài khoản của mình để sử dụng trong các bài đăng.", - "content": "Nội dung", - "auto_add": "Tự động thêm?", - "actions": "Hành động", - "use_signature": "Sử dụng chữ ký", - "add_a_signature": "Thêm chữ ký", - "no": "Không", - "yes": "Có", - "your_git_repository": "Kho lưu trữ Git của bạn", - "connect_your_github_repository_to_receive_updates_and_analytics": "Kết nối kho lưu trữ GitHub của bạn để nhận cập nhật và phân tích", - "connected": "Đã kết nối:", - "disconnect": "Ngắt kết nối", - "connect_your_repository": "Kết nối kho lưu trữ của bạn", - "cancel": "Hủy", - "connect": "Kết nối", - "public_api": "API công khai", - "check_n8n": "Hãy xem node tùy chỉnh N8N của chúng tôi cho Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Sử dụng API của Postiz để tích hợp với các công cụ của bạn.", - "read_how_to_use_it_over_the_documentation": "Đọc cách sử dụng trong tài liệu hướng dẫn.", - "reveal": "Hiển thị", - "copy_key": "Sao chép khóa", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Kết nối máy chủ MCP của Postiz với máy khách của bạn (Http streaming) để lên lịch bài đăng nhanh hơn!", - "share_with_a_client": "Chia sẻ với khách hàng", - "post": "Bài đăng", - "comments": "Bình luận", - "user": "Người dùng", - "login_register_to_add_comments": "Đăng nhập / Đăng ký để thêm bình luận", - "status": "Trạng thái:", - "there_are_not_plugs_matching_your_channels": "Không có plug nào phù hợp với kênh của bạn", - "you_have_to_add_x_or_linkedin_or_threads": "Bạn cần thêm: X hoặc LinkedIn hoặc Threads", - "go_to_the_calendar_to_add_channels": "Đi tới lịch để thêm kênh", - "channels": "Kênh", - "activate": "Kích hoạt", - "this_channel_needs_to_be_refreshed": "Kênh này cần được làm mới,", - "click_here_to_refresh": "bấm vào đây để làm mới", - "can_t_show_analytics_yet": "Chưa thể hiển thị phân tích", - "you_have_to_add_social_media_channels": "Bạn cần thêm các kênh mạng xã hội", - "supported": "Hỗ trợ:", - "step": "BƯỚC", - "skip_onboarding": "Bỏ qua hướng dẫn", - "onboarding": "Hướng dẫn bắt đầu", - "next": "Tiếp theo", - "you_are_done_from_here_you_can": "Bạn đã hoàn thành, từ đây bạn có thể:", - "view_analytics": "Xem phân tích", - "schedule_a_new_post": "Lên lịch bài đăng mới", - "to_sell_posts_you_would_have_to": "Để bán bài đăng, bạn cần:", - "1_connect_at_least_one_channel": "1. Kết nối ít nhất một kênh", - "2_connect_you_bank_account": "2. Kết nối tài khoản ngân hàng của bạn", - "go_back_to_connect_channels": "Quay lại để kết nối các kênh", - "move_to_the_seller_page_to_connect_you_bank": "Chuyển đến trang người bán để kết nối tài khoản ngân hàng của bạn", - "connect_channels": "Kết nối kênh", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Kết nối các kênh mạng xã hội và trang xuất bản của bạn để\n lên lịch bài đăng sau", - "social": "Mạng xã hội", - "publishing_platforms": "Nền tảng xuất bản", - "no_channels": "Chưa có kênh nào", - "connect_your_accounts": "Kết nối các tài khoản mạng xã hội của bạn để bắt đầu lên lịch, đăng bài và phân tích — tất cả trong một nơi.", - "notifications": "Thông báo", - "no_notifications": "Không có thông báo nào", - "send_message": "Gửi tin nhắn", - "mar_28": "28 Thg 3", - "there_are_no_messages_yet": "Chưa có tin nhắn nào.", - "checkout_the_marketplace": "Khám phá Marketplace", - "go_to_marketplace": "Đi tới Marketplace", - "all_messages": "Tất cả tin nhắn", - "previous": "Trước", - "select_or_upload_pictures_maximum_5_at_a_time": "Chọn hoặc tải lên hình ảnh (tối đa 5 hình mỗi lần)", - "you_can_also_drag_drop_pictures": "Bạn cũng có thể kéo & thả hình ảnh", - "you_don_t_have_any_assets_yet": "Bạn chưa có tài sản nào.", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "Thêm phương tiện đã chọn", - "insert_media": "Chèn phương tiện", - "design_media": "Thiết kế phương tiện", - "select": "Chọn", - "editor": "Trình chỉnh sửa", - "clear": "Xóa", - "order_completed": "Đơn hàng đã hoàn thành", - "the_order_has_been_completed": "Đơn hàng đã được hoàn thành", - "post_has_been_published": "Bài đăng đã được xuất bản", - "url_1": "URL:", - "new_offer": "Ưu đãi mới", - "platform": "Nền tảng", - "posts": "Bài đăng", - "pay_accept_offer": "Thanh toán & Chấp nhận ưu đãi", - "accepted": "Đã chấp nhận", - "post_draft": "Bản nháp bài đăng", - "revision_needed": "Cần chỉnh sửa", - "approve": "Phê duyệt", - "preview": "Xem trước", - "revision_requested": "Yêu cầu chỉnh sửa", - "accepted_1": "ĐÃ CHẤP NHẬN", - "cancelled_by_the_seller": "Đã bị hủy bởi người bán", - "please_select_your_country_where_your_business_is": "Vui lòng chọn quốc gia nơi doanh nghiệp của bạn đặt trụ sở.", - "select_country": "--CHỌN QUỐC GIA--", - "connect_bank_account": "Kết nối tài khoản ngân hàng", - "seller_mode": "Chế độ người bán", - "active": "Đang hoạt động", - "details": "Chi tiết", - "audience_size": "Quy mô khán giả", - "add_another_platform": "Thêm nền tảng khác", - "send_an_offer_for": "Gửi một đề nghị với giá $", - "complete_order_and_pay_early": "Hoàn thành đơn hàng và thanh toán sớm", - "order_in_progress": "Đơn hàng đang được xử lý", - "create_a_new_offer": "Tạo đề nghị mới", - "orders": "Đơn hàng", - "price": "Giá", - "state": "Trạng thái", - "showing": "Hiển thị", - "to": "đến", - "from": "từ", - "results": "Kết quả", - "content_writer": "Người viết nội dung", - "influencer": "Người ảnh hưởng", - "request_service": "Yêu cầu dịch vụ", - "the_marketplace_is_not_opened_yet": "Chợ chưa được mở", - "check_again_soon": "Hãy kiểm tra lại sau!", - "filter": "Bộ lọc", - "result": "Kết quả", - "seller": "Người bán", - "buyer": "Người mua", - "discord_support": "Hỗ trợ Discord", - "teams": "Nhóm", - "webhooks_1": "Webhooks", - "auto_post": "Tự động đăng", - "logout_from": "Đăng xuất khỏi", - "join_10000_entrepreneurs_who_use_postiz": "Tham gia cùng hơn 10.000 doanh nhân đang sử dụng Postiz", - "to_manage_all_your_social_media_channels": "Để quản lý tất cả các kênh mạng xã hội của bạn", - "100_no_risk_trial": "Dùng thử 100% không rủi ro", - "pay_nothing_for_the_first_7_days": "Không mất phí trong 7 ngày đầu tiên", - "cancel_anytime_hassle_free": "Hủy bất cứ lúc nào, không rắc rối", - "add_free_subscription": "-- THÊM GÓI MIỄN PHÍ --", - "currently_impersonating": "Đang giả lập tài khoản", - "user_1": "người dùng:", - "drag_n_drop_some_files_here": "Kéo và thả một số tệp vào đây", - "add_time_slot": "Thêm khung giờ", - "add_slot": "Thêm khung giờ", - "cancel_publication": "Hủy xuất bản", - "statistics": "Thống kê", - "loading": "Đang tải", - "short_link": "Liên kết rút gọn", - "original_link": "Liên kết gốc", - "clicks": "Lượt nhấp", - "selected_customer": "Khách hàng đã chọn", - "customer": "Khách hàng:", - "repeat_post_every": "Lặp lại bài đăng mỗi...", - "use_this_media": "Sử dụng phương tiện này", - "create_new_post": "Tạo bài viết", - "update_post": "Cập nhật bài viết hiện có", - "merge_comments_into_one_post": "Gộp bình luận vào một bài đăng", - "accounts_that_will_engage": "Các tài khoản sẽ tương tác:", - "day": "Ngày", - "week": "Tuần", - "month": "Tháng", - "remove_from_customer": "Xóa khỏi khách hàng", - "show_more": "+ Xem thêm", - "show_less": "- Thu gọn", - "upload": "Tải lên", - "ai": "AI", - "add_channel": "Thêm kênh", - "add_platform": "Thêm nền tảng", - "articles": "Bài viết", - "add_comment": "Thêm bình luận", - "add_post": "Thêm bài viết vào một chuỗi", - "add_comment_or_post": "Thêm bình luận / bài viết", - "you_are_in_global_editing_mode": "Bạn đang ở chế độ chỉnh sửa toàn cục", - "the_post_should_be_at_least_6_characters_long": "Bài đăng phải có ít nhất 6 ký tự", - "are_you_sure_you_want_to_delete_post": "Bạn có chắc chắn muốn xóa bài viết này không?", - "post_deleted_successfully": "Xóa bài viết thành công", - "delete_post": "Xóa bài đăng", - "save_as_draft": "Lưu dưới dạng bản nháp", - "post_now": "Đăng ngay", - "please_add": "Vui lòng thêm", - "to_your_telegram_group_channel_and_click_here": "vào nhóm/kênh Telegram của bạn và nhấn vào đây:", - "connect_telegram": "Kết nối Telegram", - "please_add_the_following_command_in_your_chat": "Vui lòng thêm lệnh sau vào cuộc trò chuyện của bạn:", - "copy": "Sao chép", - "settings": "Cài đặt", - "integrations": "Tích hợp", - "add_integration": "Thêm tích hợp", - "you_are_now_editing_only": "Bạn hiện chỉ đang chỉnh sửa", - "tag_a_company": "Gắn thẻ công ty", - "video_length_is_invalid_must_be_up_to": "Độ dài video không hợp lệ, phải tối đa", - "seconds": "giây", - "this_feature_available_only_for_photos": "Tính năng này chỉ áp dụng cho ảnh, sẽ thêm nhạc mặc định mà bạn có thể thay đổi sau.", - "allow_user_to": "Cho phép người dùng:", - "your_video_will_be_labeled_promotional": "Video của bạn sẽ được dán nhãn \"Nội dung quảng cáo\".", - "this_cannot_be_changed_once_posted": "Điều này không thể thay đổi sau khi video đã được đăng.", - "turn_on_to_disclose_video_promotes": "Bật để tiết lộ rằng video này quảng bá hàng hóa hoặc dịch vụ để đổi lấy một giá trị nào đó. Video của bạn có thể quảng bá cho chính bạn, bên thứ ba hoặc cả hai.", - "you_are_promoting_yourself": "Bạn đang quảng bá cho chính mình hoặc thương hiệu của bạn.", - "this_video_will_be_classified_brand_organic": "Video này sẽ được phân loại là Thương hiệu hữu cơ.", - "you_are_promoting_another_brand": "Bạn đang quảng bá cho thương hiệu khác hoặc bên thứ ba.", - "this_video_will_be_classified_branded_content": "Video này sẽ được phân loại là Nội dung thương hiệu.", - "by_posting_you_agree_to_tiktoks": "Bằng cách đăng bài, bạn đồng ý với", - "music_usage_confirmation": "Xác nhận sử dụng nhạc", - "branded_content_policy": "Chính sách nội dung thương hiệu", - "select_1": "--Chọn--", - "select_flair": "--Chọn nhãn--", - "link": "Liên kết", - "add_subreddit": "Thêm Subreddit", - "please_add_at_least_one_subreddit": "Vui lòng thêm ít nhất một Subreddit", - "add_community": "Thêm cộng đồng", - "select_post_type": "Chọn loại bài đăng...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Chúng tôi không tìm thấy doanh nghiệp nào liên kết với Trang LinkedIn của bạn.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Vui lòng đóng hộp thoại này, tạo một trang mới và thêm kênh mới lại.", - "select_linkedin_page": "Chọn Trang Linkedin:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Chúng tôi không tìm thấy doanh nghiệp nào liên kết với các trang đã chọn.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Chúng tôi khuyên bạn nên kết nối tất cả các trang và tất cả các doanh nghiệp.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Vui lòng đóng hộp thoại này, xóa tích hợp của bạn và thêm kênh mới lại.", - "select_instagram_account": "Chọn tài khoản Instagram:", - "select_page": "Chọn Trang:", - "generate_image_with_ai": "Tạo hình ảnh bằng AI", - "reconnect_channel": "Kết nối lại kênh", - "update_credentials": "Cập nhật thông tin đăng nhập", - "additional_settings": "Cài đặt bổ sung", - "change_bot": "Thay đổi bot", - "move_add_to_customer": "Chuyển / thêm vào khách hàng", - "edit_time_slots": "Chỉnh sửa khung giờ", - "enable_channel": "Bật kênh", - "disable_channel": "Tắt kênh", - "add": "Thêm", - "short_post": "Bài đăng ngắn", - "long_post": "Bài đăng dài", - "a_thread_with_short_posts": "Chuỗi bài đăng ngắn", - "a_thread_with_long_posts": "Chuỗi bài đăng dài", - "personal_voice_i_am_happy_to_announce": "Giọng cá nhân (\"Tôi vui mừng thông báo\")", - "company_voice_we_are_happy_to_announce": "Giọng công ty (\"Chúng tôi vui mừng thông báo\")", - "generate": "Tạo", - "generate_posts": "Tạo bài đăng", - "purchase_a_life_time_pro_account_with_sol_199": "Mua tài khoản PRO trọn đời với SOL ($199). Lưu ý: giao dịch này không được hoàn tiền.", - "purchase_now": "Mua ngay", - "pay_today": "Thanh toán hôm nay", - "we_are_sorry_to_see_you_go": "Chúng tôi rất tiếc khi thấy bạn rời đi :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Bạn có thể cho chúng tôi biết ngắn gọn điều gì chúng tôi có thể làm tốt hơn không?", - "cancel_subscription": "Hủy đăng ký", - "plans": "Gói", - "monthly": "HÀNG THÁNG", - "yearly": "HÀNG NĂM", - "reactivate_subscription": "Kích hoạt lại đăng ký", - "update_payment_method_invoices_history": "Cập nhật phương thức thanh toán / Lịch sử hóa đơn", - "cancel_subscription_1": "Hủy đăng ký", - "your_subscription_will_be_canceled_at": "Đăng ký của bạn sẽ bị hủy vào", - "you_will_never_be_charged_again": "Bạn sẽ không bao giờ bị tính phí nữa", - "current_package": "Gói hiện tại:", - "next_package": "Gói tiếp theo:", - "claim": "Yêu cầu", - "frequently_asked_questions": "Câu hỏi thường gặp", - "autopost": "Tự động đăng", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Tự động đăng có thể tự động đăng các mục mới từ RSS của bạn lên mạng xã hội", - "title": "Tiêu đề", - "add_an_autopost": "Thêm tự động đăng", - "post_content": "Nội dung bài đăng", - "sign_up": "Đăng ký", - "or": "HOẶC", - "by_registering_you_agree_to_our": "Bằng việc đăng ký, bạn đồng ý với", - "and": "và", - "terms_of_service": "Điều khoản dịch vụ", - "privacy_policy": "Chính sách bảo mật", - "create_account": "Tạo tài khoản", - "already_have_an_account": "Đã có tài khoản?", - "sign_in": "Đăng nhập", - "sign_in_1": "Đăng nhập", - "don_t_have_an_account": "Chưa có tài khoản?", - "forgot_password": "Quên mật khẩu", - "forgot_password_1": "Quên mật khẩu", - "send_password_reset_email": "Gửi email đặt lại mật khẩu", - "go_back_to_login": "Quay lại đăng nhập", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Chúng tôi đã gửi cho bạn một email với liên kết để đặt lại mật khẩu.", - "change_password": "Đổi mật khẩu", - "we_successfully_reset_your_password_you_can_now_login_with_your": "Chúng tôi đã đặt lại mật khẩu của bạn thành công. Bây giờ bạn có thể đăng nhập bằng", - "click_here_to_go_back_to_login": "Bấm vào đây để quay lại đăng nhập", - "activate_your_account": "Kích hoạt tài khoản của bạn", - "thank_you_for_registering": "Cảm ơn bạn đã đăng ký!", - "please_check_your_email_to_activate_your_account": "Vui lòng kiểm tra email của bạn để kích hoạt tài khoản.", - "sign_in_with": "Đăng nhập với", - "continue_with_google": "Tiếp tục với Google", - "sign_in_with_github": "Đăng nhập với GitHub", - "continue_with_farcaster": "Tiếp tục với Farcaster", - "continue_with_your_wallet": "Tiếp tục với Ví của bạn", - "stars_per_day": "Số sao mỗi ngày", - "media": "Phương tiện", - "check_launch": "Kiểm tra khởi chạy", - "load_your_github_repository_from_settings_to_see_analytics": "Tải kho GitHub của bạn từ cài đặt để xem phân tích", - "stars": "Sao", - "processing_stars": "Đang xử lý số sao...", - "forks": "Nhánh", - "registration_is_disabled": "Đăng ký đã bị vô hiệu hóa", - "login_instead": "Đăng nhập thay thế", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Chọn một cuộc trò chuyện và bắt đầu chat.", - "adding_channel_redirecting_you": "Đang thêm kênh, đang chuyển hướng bạn", - "could_not_add_provider": "Không thể thêm nhà cung cấp.", - "you_are_being_redirected_back": "Bạn đang được chuyển hướng trở lại", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Chúng tôi đang gặp một số sự cố, hãy thử làm mới trang", - "post_not_found": "Không tìm thấy bài viết", - "publication_date": "Ngày xuất bản:", - "analytics": "Phân tích", - "launches": "Ra mắt", - "plugs": "Quảng bá", - "billing": "Thanh toán", - "affiliate": "Liên kết", - "monday": "Thứ Hai", - "tuesday": "Thứ Ba", - "wednesday": "Thứ Tư", - "thursday": "Thứ Năm", - "friday": "Thứ Sáu", - "saturday": "Thứ Bảy", - "sunday": "Chủ Nhật", - "can_t_change_date_remove_post_from_publication": "Không thể thay đổi ngày, hãy xóa bài viết khỏi xuất bản", - "predicted_github_trending_change": "Dự đoán thay đổi xu hướng GitHub", - "duplicate_post": "Bài viết trùng lặp", - "preview_post": "Xem trước bài viết", - "post_statistics": "Thống kê bài viết", - "draft": "Bản nháp", - "week_number": "Tuần {{number}}", - "top_title_edit_webhook": "Chỉnh sửa webhook", - "top_title_add_webhook": "Thêm webhook", - "top_title_oh_no": "Ôi không", - "top_title_auto_plug": "Tự động cắm: {{title}}", - "top_title_edit_autopost": "Chỉnh sửa tự động đăng", - "top_title_add_autopost": "Thêm tự động đăng", - "top_title_send_a_new_offer": "Gửi ưu đãi mới", - "top_title_media_library": "Thư viện phương tiện", - "top_title_add_signature": "Thêm chữ ký", - "top_title_send_a_message_to": "Gửi tin nhắn cho {{name}}", - "top_title_configure_provider": "Cấu hình nhà cung cấp", - "top_title_add_member": "Thêm thành viên", - "top_title_change_bot_picture": "Thay đổi ảnh bot", - "top_title_create_a_new_tag": "Tạo thẻ mới", - "top_title_select_company": "Chọn công ty", - "top_title_additional_settings": "Cài đặt bổ sung", - "top_title_time_table_slots": "Khung giờ thời gian biểu", - "top_title_design_media": "Thiết kế phương tiện", - "top_title_edit_post": "Chỉnh sửa bài đăng", - "top_title_create_post": "Tạo bài đăng", - "top_title_move__add_to_customer": "Chuyển / Thêm vào khách hàng", - "top_title_add_api_key_for": "Thêm API key cho {{name}}", - "top_title_instance_url": "URL phiên bản", - "top_title_custom_url": "URL tùy chỉnh", - "top_title_add_channel": "Thêm kênh", - "top_title_add_telegram": "Thêm Telegram", - "top_title_add_wrapcast": "Thêm Wrapcast", - "top_title_comments_for": "Bình luận cho {{date}}", - "top_title_edit_signature": "Chỉnh sửa chữ ký", - "label_name": "Tên", - "label_url": "URL", - "label_title": "Tiêu đề", - "label_subtitle": "Phụ đề", - "label_email": "Email", - "label_full_name": "Họ và tên", - "label_password": "Mật khẩu", - "label_confirm_password": "Xác nhận mật khẩu", - "label_api_key": "API Key", - "label_instance_url": "URL phiên bản", - "label_custom_url": "URL tùy chỉnh", - "label_feedback": "Phản hồi", - "label_bio": "Tiểu sử", - "label_role": "Vai trò", - "label_country": "Quốc gia", - "label_audience_size": "Quy mô khán giả trên tất cả các nền tảng", - "label_pick_time": "Chọn thời gian", - "label_nickname": "Biệt danh", - "label_write_anything": "Viết bất cứ điều gì", - "label_output_format": "Định dạng đầu ra", - "label_add_pictures": "Thêm hình ảnh?", - "label_hour": "Giờ", - "label_minutes": "Phút", - "label_select_publication": "Chọn ấn phẩm", - "label_canonical_link": "Liên kết chuẩn", - "label_cover_picture": "Ảnh bìa", - "label_tags": "Thẻ", - "label_topics": "Chủ đề", - "label_tags_maximum_4": "Thẻ (Tối đa 4)", - "label_attachments": "Tệp đính kèm", - "label_type": "Loại", - "label_thumbnail": "Ảnh thu nhỏ", - "label_who_can_see_this_video": "Ai có thể xem video này?", - "label_content_posting_method": "Phương thức đăng nội dung", - "label_auto_add_music": "Tự động thêm nhạc", - "label_duet": "Song ca", - "label_stitch": "Ghép", - "label_comments": "Bình luận", - "label_disclose_video_content": "Công khai nội dung video", - "label_your_brand": "Thương hiệu của bạn", - "label_branded_content": "Nội dung thương hiệu", - "label_subreddit": "Subreddit", - "label_flair": "Flair", - "label_media": "Phương tiện", - "label_search_subreddit": "Tìm kiếm Subreddit", - "label_delay": "Trì hoãn", - "label_post_type": "Loại bài đăng", - "label_collaborators": "Cộng tác viên (tối đa 3) - tài khoản không được để riêng tư", - "label_community": "Cộng đồng", - "label_search_community": "Tìm kiếm cộng đồng", - "label_channel": "Kênh", - "label_search_channel": "Tìm kiếm kênh", - "label_select_channel": "Chọn kênh", - "label_new_password": "Mật khẩu mới", - "label_repeat_password": "Nhập lại mật khẩu", - "label_platform": "Nền tảng", - "label_price_per_post": "Giá mỗi bài đăng", - "label_integrations": "Tích hợp", - "label_code": "Mã", - "label_should_sync_last_post": "Chúng ta có nên đồng bộ bài đăng cuối hiện tại không?", - "label_when_post": "Khi nào chúng ta nên đăng?", - "label_autogenerate_content": "Tự động tạo nội dung", - "label_generate_picture": "Tạo hình ảnh?", - "label_company": "Công ty", - "label_tag_color": "Màu thẻ", - "label_select_board": "Chọn bảng", - "label_select_organization": "Chọn tổ chức", - "label_auto_add_signature": "Tự động thêm chữ ký?", - "enable_color_picker": "Bật bộ chọn màu", - "cancel_the_color_picker": "Hủy bộ chọn màu", - "no_content_yet": "Chưa có nội dung", - "write_your_reply": "Viết bài của bạn...", - "add_a_tag": "Thêm thẻ", - "add_to_calendar": "Thêm vào lịch", - "select_channels_from_circles": "Chọn kênh từ các vòng tròn phía trên", - "not_matching_order": "Không đúng thứ tự", - "submit_for_order": "Gửi để đặt hàng", - "schedule": "Lên lịch", - "update": "Cập nhật", - "attachments": "Tệp đính kèm", - "tags": "Thẻ", - "public_to_everyone": "Công khai với mọi người", - "mutual_follow_friends": "Bạn bè theo dõi lẫn nhau", - "follower_of_creator": "Người theo dõi của người tạo", - "self_only": "Chỉ mình tôi", - "post_content_directly_to_tiktok": "Đăng nội dung trực tiếp lên TikTok", - "upload_content_to_tiktok_without_posting": "Tải nội dung lên TikTok mà không đăng", - "choose_upload_without_posting_description": "Chọn tải lên mà không đăng nếu bạn muốn xem lại và chỉnh sửa nội dung trong ứng dụng TikTok trước khi xuất bản. Điều này giúp bạn sử dụng các công cụ chỉnh sửa tích hợp của TikTok và thực hiện các điều chỉnh cuối cùng trước khi đăng.", - "faq_am_i_going_to_be_charged_by_postiz": "Tôi có bị Postiz tính phí không?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Để xác nhận thông tin thẻ tín dụng, Postiz sẽ giữ $2 và hoàn trả ngay lập tức", - "faq_can_i_trust_postiz_gitroom": "Tôi có thể tin tưởng Postiz không?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz tự hào là mã nguồn mở! Chúng tôi tin vào một văn hóa đạo đức và minh bạch, nghĩa là Postiz sẽ tồn tại mãi mãi. Bạn có thể xem toàn bộ mã nguồn hoặc sử dụng cho các dự án cá nhân. Để xem kho mã nguồn mở, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">bấm vào đây</a>.", - "faq_what_are_channels": "Kênh là gì?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz cho phép bạn lên lịch đăng bài trên các kênh khác nhau.\nMột kênh là một nền tảng xuất bản nơi bạn có thể lên lịch đăng bài.\nVí dụ, bạn có thể lên lịch đăng bài trên X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads và Pinterest.", - "faq_what_are_team_members": "Thành viên nhóm là gì?", - "faq_if_you_have_a_team_with_multiple_members": "Nếu bạn có một nhóm với nhiều thành viên, bạn có thể mời họ vào không gian làm việc để cùng cộng tác đăng bài và thêm các kênh cá nhân của họ", - "faq_what_is_ai_auto_complete": "Tự động hoàn thành bằng AI là gì?", - "faq_we_automate_chatgpt_to_help_you_write": "Chúng tôi tự động hóa ChatGPT để giúp bạn viết bài đăng mạng xã hội và bài viết.", - "enter_email": "Nhập email", - "are_you_sure": "Bạn có chắc không?", - "yes_delete_it": "Vâng, xóa nó đi!", - "no_cancel": "Không, hủy bỏ!", - "are_you_sure_you_want_to_delete": "Bạn có chắc muốn xóa {{name}} không?", - "are_you_sure_you_want_to_delete_the_image": "Bạn có chắc muốn xóa hình ảnh này không?", - "are_you_sure_you_want_to_logout": "Bạn có chắc muốn đăng xuất không?", - "yes_logout": "Vâng, đăng xuất", - "are_you_sure_you_want_to_delete_this_slot": "Bạn có chắc muốn xóa khung này không?", - "are_you_sure_you_want_to_delete_this_subreddit": "Bạn có chắc muốn xóa Subreddit này không?", - "are_you_sure_you_want_to_close_the_window": "Bạn có chắc muốn đóng cửa sổ này không?", - "yes_close": "Vâng, đóng lại", - "link_copied_to_clipboard": "Đã sao chép liên kết vào bộ nhớ tạm", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Bạn có chắc muốn đóng cửa sổ này không? (tất cả dữ liệu sẽ bị mất)", - "yes_close_it": "Vâng, đóng lại!", - "uploading_pictures": "Đang tải ảnh lên...", - "agent_starting": "Đang khởi động tác vụ", - "researching_your_content": "Đang nghiên cứu nội dung của bạn...", - "understanding_the_category": "Đang tìm hiểu danh mục...", - "finding_the_topic": "Đang tìm chủ đề...", - "finding_popular_posts_to_match_with": "Đang tìm các bài viết phổ biến để ghép nối...", - "generating_hook": "Đang tạo tiêu đề hấp dẫn...", - "generating_content": "Đang tạo nội dung...", - "generating_pictures": "Đang tạo hình ảnh...", - "finding_time_to_post": "Đang tìm thời gian đăng bài...", - "write_anything": "Viết bất cứ điều gì", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Bạn có thể viết bất cứ điều gì bạn muốn, và cũng có thể thêm liên kết, chúng tôi sẽ nghiên cứu cho bạn...", - "output_format": "Định dạng đầu ra", - "add_pictures": "Thêm hình ảnh?", - "7_days": "7 ngày", - "30_days": "30 ngày", - "90_days": "90 ngày", - "start_7_days_free_trial": "Bắt đầu dùng thử miễn phí 7 ngày", - "change_language": "Thay đổi ngôn ngữ", - "that_a_wrap": "Kết thúc rồi!\n\nNếu bạn thích chuỗi bài này:\n\n1. Hãy theo dõi tôi @{{username}} để xem thêm nhiều nội dung như vậy\n2. Retweet bài bên dưới để chia sẻ chuỗi này với mọi người\n", - "post_as_images_carousel": "Đăng dưới dạng băng chuyền hình ảnh", - "save_set": "Lưu bộ", - "separate_post": "Tách bài viết thành nhiều bài", - "label_who_can_reply_to_this_post": "Ai có thể trả lời bài viết này?", - "delete_integration": "Xóa tích hợp", - "start_writing_your_post": "Bắt đầu viết bài của bạn để xem trước" + "calendar": "Lịch", + "webhooks": "Webhook", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhook là một cách để nhận thông báo khi có sự kiện xảy ra trong Postiz thông qua một yêu cầu HTTP.", + "name": "Tên", + "url": "URL", + "edit": "Chỉnh sửa", + "delete": "Xóa", + "add_a_webhook": "Thêm webhook", + "save": "Lưu", + "send_test": "Gửi thử", + "select_role": "Chọn vai trò", + "video_made_with_ai": "Video được tạo bằng AI", + "please_add_at_least": "Vui lòng thêm ít nhất 20 ký tự", + "send_invitation_via_email": "Gửi lời mời qua email?", + "global_settings": "Cài đặt toàn cục", + "copy_id": "Sao chép ID kênh", + "team_members": "Thành viên nhóm", + "invite_your_assistant_or_team_member_to_manage_your_account": "Mời trợ lý hoặc thành viên nhóm của bạn để quản lý tài khoản của bạn", + "remove": "Xóa", + "add_another_member": "Thêm thành viên khác", + "signatures": "Chữ ký", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Bạn có thể thêm chữ ký vào tài khoản của mình để sử dụng trong các bài đăng.", + "content": "Nội dung", + "auto_add": "Tự động thêm?", + "actions": "Hành động", + "use_signature": "Sử dụng chữ ký", + "add_a_signature": "Thêm chữ ký", + "no": "Không", + "yes": "Có", + "your_git_repository": "Kho lưu trữ Git của bạn", + "connect_your_github_repository_to_receive_updates_and_analytics": "Kết nối kho lưu trữ GitHub của bạn để nhận cập nhật và phân tích", + "connected": "Đã kết nối:", + "disconnect": "Ngắt kết nối", + "connect_your_repository": "Kết nối kho lưu trữ của bạn", + "cancel": "Hủy", + "connect": "Kết nối", + "public_api": "API công khai", + "check_n8n": "Hãy xem node tùy chỉnh N8N của chúng tôi cho Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Sử dụng API của Postiz để tích hợp với các công cụ của bạn.", + "read_how_to_use_it_over_the_documentation": "Đọc cách sử dụng trong tài liệu hướng dẫn.", + "reveal": "Hiển thị", + "copy_key": "Sao chép khóa", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Kết nối máy chủ MCP của Postiz với máy khách của bạn (Http streaming) để lên lịch bài đăng nhanh hơn!", + "share_with_a_client": "Chia sẻ với khách hàng", + "post": "Bài đăng", + "comments": "Bình luận", + "user": "Người dùng", + "login_register_to_add_comments": "Đăng nhập / Đăng ký để thêm bình luận", + "status": "Trạng thái:", + "there_are_not_plugs_matching_your_channels": "Không có plug nào phù hợp với kênh của bạn", + "you_have_to_add_x_or_linkedin_or_threads": "Bạn cần thêm: X hoặc LinkedIn hoặc Threads", + "go_to_the_calendar_to_add_channels": "Đi tới lịch để thêm kênh", + "channels": "Kênh", + "activate": "Kích hoạt", + "this_channel_needs_to_be_refreshed": "Kênh này cần được làm mới,", + "click_here_to_refresh": "bấm vào đây để làm mới", + "can_t_show_analytics_yet": "Chưa thể hiển thị phân tích", + "you_have_to_add_social_media_channels": "Bạn cần thêm các kênh mạng xã hội", + "supported": "Hỗ trợ:", + "step": "BƯỚC", + "skip_onboarding": "Bỏ qua hướng dẫn", + "onboarding": "Hướng dẫn bắt đầu", + "next": "Tiếp theo", + "you_are_done_from_here_you_can": "Bạn đã hoàn thành, từ đây bạn có thể:", + "view_analytics": "Xem phân tích", + "schedule_a_new_post": "Lên lịch bài đăng mới", + "to_sell_posts_you_would_have_to": "Để bán bài đăng, bạn cần:", + "1_connect_at_least_one_channel": "1. Kết nối ít nhất một kênh", + "2_connect_you_bank_account": "2. Kết nối tài khoản ngân hàng của bạn", + "go_back_to_connect_channels": "Quay lại để kết nối các kênh", + "move_to_the_seller_page_to_connect_you_bank": "Chuyển đến trang người bán để kết nối tài khoản ngân hàng của bạn", + "connect_channels": "Kết nối kênh", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Kết nối các kênh mạng xã hội và trang xuất bản của bạn để\n lên lịch bài đăng sau", + "social": "Mạng xã hội", + "publishing_platforms": "Nền tảng xuất bản", + "no_channels": "Chưa có kênh nào", + "connect_your_accounts": "Kết nối các tài khoản mạng xã hội của bạn để bắt đầu lên lịch, đăng bài và phân tích — tất cả trong một nơi.", + "notifications": "Thông báo", + "no_notifications": "Không có thông báo nào", + "send_message": "Gửi tin nhắn", + "mar_28": "28 Thg 3", + "there_are_no_messages_yet": "Chưa có tin nhắn nào.", + "checkout_the_marketplace": "Khám phá Marketplace", + "go_to_marketplace": "Đi tới Marketplace", + "all_messages": "Tất cả tin nhắn", + "previous": "Trước", + "select_or_upload_pictures_maximum_5_at_a_time": "Chọn hoặc tải lên hình ảnh (tối đa 5 hình mỗi lần)", + "you_can_also_drag_drop_pictures": "Bạn cũng có thể kéo & thả hình ảnh", + "you_don_t_have_any_assets_yet": "Bạn chưa có tài sản nào.", + "click_the_button_below_to_upload_one": "Nhấn nút bên dưới để tải lên một tệp", + "click_the_button_below_to_upload_other": "Nhấn nút bên dưới để tải lên nhiều tệp", + "add_selected_media": "Thêm phương tiện đã chọn", + "insert_media": "Chèn phương tiện", + "design_media": "Thiết kế phương tiện", + "select": "Chọn", + "editor": "Trình chỉnh sửa", + "clear": "Xóa", + "order_completed": "Đơn hàng đã hoàn thành", + "the_order_has_been_completed": "Đơn hàng đã được hoàn thành", + "post_has_been_published": "Bài đăng đã được xuất bản", + "url_1": "URL:", + "new_offer": "Ưu đãi mới", + "platform": "Nền tảng", + "posts": "Bài đăng", + "pay_accept_offer": "Thanh toán & Chấp nhận ưu đãi", + "accepted": "Đã chấp nhận", + "post_draft": "Bản nháp bài đăng", + "revision_needed": "Cần chỉnh sửa", + "approve": "Phê duyệt", + "preview": "Xem trước", + "revision_requested": "Yêu cầu chỉnh sửa", + "accepted_1": "ĐÃ CHẤP NHẬN", + "cancelled_by_the_seller": "Đã bị hủy bởi người bán", + "please_select_your_country_where_your_business_is": "Vui lòng chọn quốc gia nơi doanh nghiệp của bạn đặt trụ sở.", + "select_country": "--CHỌN QUỐC GIA--", + "connect_bank_account": "Kết nối tài khoản ngân hàng", + "seller_mode": "Chế độ người bán", + "active": "Đang hoạt động", + "details": "Chi tiết", + "audience_size": "Quy mô khán giả", + "add_another_platform": "Thêm nền tảng khác", + "send_an_offer_for": "Gửi một đề nghị với giá $", + "complete_order_and_pay_early": "Hoàn thành đơn hàng và thanh toán sớm", + "order_in_progress": "Đơn hàng đang được xử lý", + "create_a_new_offer": "Tạo đề nghị mới", + "orders": "Đơn hàng", + "price": "Giá", + "state": "Trạng thái", + "showing": "Hiển thị", + "to": "đến", + "from": "từ", + "results": "Kết quả", + "content_writer": "Người viết nội dung", + "influencer": "Người ảnh hưởng", + "request_service": "Yêu cầu dịch vụ", + "the_marketplace_is_not_opened_yet": "Chợ chưa được mở", + "check_again_soon": "Hãy kiểm tra lại sau!", + "filter": "Bộ lọc", + "result": "Kết quả", + "seller": "Người bán", + "buyer": "Người mua", + "discord_support": "Hỗ trợ Discord", + "teams": "Nhóm", + "webhooks_1": "Webhooks", + "auto_post": "Tự động đăng", + "logout_from": "Đăng xuất khỏi", + "join_10000_entrepreneurs_who_use_postiz": "Tham gia cùng hơn 10.000 doanh nhân đang sử dụng Postiz", + "to_manage_all_your_social_media_channels": "Để quản lý tất cả các kênh mạng xã hội của bạn", + "100_no_risk_trial": "Dùng thử 100% không rủi ro", + "pay_nothing_for_the_first_7_days": "Không mất phí trong 7 ngày đầu tiên", + "cancel_anytime_hassle_free": "Hủy bất cứ lúc nào, không rắc rối", + "add_free_subscription": "-- THÊM GÓI MIỄN PHÍ --", + "currently_impersonating": "Đang giả lập tài khoản", + "user_1": "người dùng:", + "drag_n_drop_some_files_here": "Kéo và thả một số tệp vào đây", + "add_time_slot": "Thêm khung giờ", + "add_slot": "Thêm khung giờ", + "cancel_publication": "Hủy xuất bản", + "statistics": "Thống kê", + "loading": "Đang tải", + "short_link": "Liên kết rút gọn", + "original_link": "Liên kết gốc", + "clicks": "Lượt nhấp", + "selected_customer": "Khách hàng đã chọn", + "customer": "Khách hàng:", + "repeat_post_every": "Lặp lại bài đăng mỗi...", + "use_this_media": "Sử dụng phương tiện này", + "create_new_post": "Tạo bài viết", + "update_post": "Cập nhật bài viết hiện có", + "merge_comments_into_one_post": "Gộp bình luận vào một bài đăng", + "accounts_that_will_engage": "Các tài khoản sẽ tương tác:", + "day": "Ngày", + "week": "Tuần", + "month": "Tháng", + "remove_from_customer": "Xóa khỏi khách hàng", + "show_more": "+ Xem thêm", + "show_less": "- Thu gọn", + "upload": "Tải lên", + "ai": "AI", + "add_channel": "Thêm kênh", + "add_platform": "Thêm nền tảng", + "articles": "Bài viết", + "add_comment": "Thêm bình luận", + "add_post": "Thêm bài viết vào một chuỗi", + "add_comment_or_post": "Thêm bình luận / bài viết", + "you_are_in_global_editing_mode": "Bạn đang ở chế độ chỉnh sửa toàn cục", + "the_post_should_be_at_least_6_characters_long": "Bài đăng phải có ít nhất 6 ký tự", + "are_you_sure_you_want_to_delete_post": "Bạn có chắc chắn muốn xóa bài viết này không?", + "post_deleted_successfully": "Xóa bài viết thành công", + "delete_post": "Xóa bài đăng", + "save_as_draft": "Lưu dưới dạng bản nháp", + "post_now": "Đăng ngay", + "please_add": "Vui lòng thêm", + "to_your_telegram_group_channel_and_click_here": "vào nhóm/kênh Telegram của bạn và nhấn vào đây:", + "connect_telegram": "Kết nối Telegram", + "please_add_the_following_command_in_your_chat": "Vui lòng thêm lệnh sau vào cuộc trò chuyện của bạn:", + "copy": "Sao chép", + "settings": "Cài đặt", + "integrations": "Tích hợp", + "add_integration": "Thêm tích hợp", + "you_are_now_editing_only": "Bạn hiện chỉ đang chỉnh sửa", + "tag_a_company": "Gắn thẻ công ty", + "video_length_is_invalid_must_be_up_to": "Độ dài video không hợp lệ, phải tối đa", + "seconds": "giây", + "this_feature_available_only_for_photos": "Tính năng này chỉ áp dụng cho ảnh, sẽ thêm nhạc mặc định mà bạn có thể thay đổi sau.", + "allow_user_to": "Cho phép người dùng:", + "your_video_will_be_labeled_promotional": "Video của bạn sẽ được dán nhãn \"Nội dung quảng cáo\".", + "this_cannot_be_changed_once_posted": "Điều này không thể thay đổi sau khi video đã được đăng.", + "turn_on_to_disclose_video_promotes": "Bật để tiết lộ rằng video này quảng bá hàng hóa hoặc dịch vụ để đổi lấy một giá trị nào đó. Video của bạn có thể quảng bá cho chính bạn, bên thứ ba hoặc cả hai.", + "you_are_promoting_yourself": "Bạn đang quảng bá cho chính mình hoặc thương hiệu của bạn.", + "this_video_will_be_classified_brand_organic": "Video này sẽ được phân loại là Thương hiệu hữu cơ.", + "you_are_promoting_another_brand": "Bạn đang quảng bá cho thương hiệu khác hoặc bên thứ ba.", + "this_video_will_be_classified_branded_content": "Video này sẽ được phân loại là Nội dung thương hiệu.", + "by_posting_you_agree_to_tiktoks": "Bằng cách đăng bài, bạn đồng ý với", + "music_usage_confirmation": "Xác nhận sử dụng nhạc", + "branded_content_policy": "Chính sách nội dung thương hiệu", + "select_1": "--Chọn--", + "select_flair": "--Chọn nhãn--", + "link": "Liên kết", + "add_subreddit": "Thêm Subreddit", + "please_add_at_least_one_subreddit": "Vui lòng thêm ít nhất một Subreddit", + "add_community": "Thêm cộng đồng", + "select_post_type": "Chọn loại bài đăng...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "Chúng tôi không tìm thấy doanh nghiệp nào liên kết với Trang LinkedIn của bạn.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Vui lòng đóng hộp thoại này, tạo một trang mới và thêm kênh mới lại.", + "select_linkedin_page": "Chọn Trang Linkedin:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "Chúng tôi không tìm thấy doanh nghiệp nào liên kết với các trang đã chọn.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "Chúng tôi khuyên bạn nên kết nối tất cả các trang và tất cả các doanh nghiệp.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Vui lòng đóng hộp thoại này, xóa tích hợp của bạn và thêm kênh mới lại.", + "select_instagram_account": "Chọn tài khoản Instagram:", + "select_page": "Chọn Trang:", + "generate_image_with_ai": "Tạo hình ảnh bằng AI", + "reconnect_channel": "Kết nối lại kênh", + "update_credentials": "Cập nhật thông tin đăng nhập", + "additional_settings": "Cài đặt bổ sung", + "change_bot": "Thay đổi bot", + "move_add_to_customer": "Chuyển / thêm vào khách hàng", + "edit_time_slots": "Chỉnh sửa khung giờ", + "enable_channel": "Bật kênh", + "disable_channel": "Tắt kênh", + "add": "Thêm", + "short_post": "Bài đăng ngắn", + "long_post": "Bài đăng dài", + "a_thread_with_short_posts": "Chuỗi bài đăng ngắn", + "a_thread_with_long_posts": "Chuỗi bài đăng dài", + "personal_voice_i_am_happy_to_announce": "Giọng cá nhân (\"Tôi vui mừng thông báo\")", + "company_voice_we_are_happy_to_announce": "Giọng công ty (\"Chúng tôi vui mừng thông báo\")", + "generate": "Tạo", + "generate_posts": "Tạo bài đăng", + "purchase_a_life_time_pro_account_with_sol_199": "Mua tài khoản PRO trọn đời với SOL ($199). Lưu ý: giao dịch này không được hoàn tiền.", + "purchase_now": "Mua ngay", + "pay_today": "Thanh toán hôm nay", + "we_are_sorry_to_see_you_go": "Chúng tôi rất tiếc khi thấy bạn rời đi :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Bạn có thể cho chúng tôi biết ngắn gọn điều gì chúng tôi có thể làm tốt hơn không?", + "cancel_subscription": "Hủy đăng ký", + "plans": "Gói", + "monthly": "HÀNG THÁNG", + "yearly": "HÀNG NĂM", + "reactivate_subscription": "Kích hoạt lại đăng ký", + "update_payment_method_invoices_history": "Cập nhật phương thức thanh toán / Lịch sử hóa đơn", + "cancel_subscription_1": "Hủy đăng ký", + "your_subscription_will_be_canceled_at": "Đăng ký của bạn sẽ bị hủy vào", + "you_will_never_be_charged_again": "Bạn sẽ không bao giờ bị tính phí nữa", + "current_package": "Gói hiện tại:", + "next_package": "Gói tiếp theo:", + "claim": "Yêu cầu", + "frequently_asked_questions": "Câu hỏi thường gặp", + "autopost": "Tự động đăng", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Tự động đăng có thể tự động đăng các mục mới từ RSS của bạn lên mạng xã hội", + "title": "Tiêu đề", + "add_an_autopost": "Thêm tự động đăng", + "post_content": "Nội dung bài đăng", + "sign_up": "Đăng ký", + "or": "HOẶC", + "by_registering_you_agree_to_our": "Bằng việc đăng ký, bạn đồng ý với", + "and": "và", + "terms_of_service": "Điều khoản dịch vụ", + "privacy_policy": "Chính sách bảo mật", + "create_account": "Tạo tài khoản", + "already_have_an_account": "Đã có tài khoản?", + "sign_in": "Đăng nhập", + "sign_in_1": "Đăng nhập", + "don_t_have_an_account": "Chưa có tài khoản?", + "forgot_password": "Quên mật khẩu", + "forgot_password_1": "Quên mật khẩu", + "send_password_reset_email": "Gửi email đặt lại mật khẩu", + "go_back_to_login": "Quay lại đăng nhập", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "Chúng tôi đã gửi cho bạn một email với liên kết để đặt lại mật khẩu.", + "change_password": "Đổi mật khẩu", + "we_successfully_reset_your_password_you_can_now_login_with_your": "Chúng tôi đã đặt lại mật khẩu của bạn thành công. Bây giờ bạn có thể đăng nhập bằng", + "click_here_to_go_back_to_login": "Bấm vào đây để quay lại đăng nhập", + "activate_your_account": "Kích hoạt tài khoản của bạn", + "thank_you_for_registering": "Cảm ơn bạn đã đăng ký!", + "please_check_your_email_to_activate_your_account": "Vui lòng kiểm tra email của bạn để kích hoạt tài khoản.", + "sign_in_with": "Đăng nhập với", + "continue_with_google": "Tiếp tục với Google", + "sign_in_with_github": "Đăng nhập với GitHub", + "continue_with_farcaster": "Tiếp tục với Farcaster", + "continue_with_your_wallet": "Tiếp tục với Ví của bạn", + "stars_per_day": "Số sao mỗi ngày", + "media": "Phương tiện", + "check_launch": "Kiểm tra khởi chạy", + "load_your_github_repository_from_settings_to_see_analytics": "Tải kho GitHub của bạn từ cài đặt để xem phân tích", + "stars": "Sao", + "processing_stars": "Đang xử lý số sao...", + "forks": "Nhánh", + "registration_is_disabled": "Đăng ký đã bị vô hiệu hóa", + "login_instead": "Đăng nhập thay thế", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Chọn một cuộc trò chuyện và bắt đầu chat.", + "adding_channel_redirecting_you": "Đang thêm kênh, đang chuyển hướng bạn", + "could_not_add_provider": "Không thể thêm nhà cung cấp.", + "you_are_being_redirected_back": "Bạn đang được chuyển hướng trở lại", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "Chúng tôi đang gặp một số sự cố, hãy thử làm mới trang", + "post_not_found": "Không tìm thấy bài viết", + "publication_date": "Ngày xuất bản:", + "analytics": "Phân tích", + "launches": "Ra mắt", + "plugs": "Quảng bá", + "billing": "Thanh toán", + "affiliate": "Liên kết", + "monday": "Thứ Hai", + "tuesday": "Thứ Ba", + "wednesday": "Thứ Tư", + "thursday": "Thứ Năm", + "friday": "Thứ Sáu", + "saturday": "Thứ Bảy", + "sunday": "Chủ Nhật", + "can_t_change_date_remove_post_from_publication": "Không thể thay đổi ngày, hãy xóa bài viết khỏi xuất bản", + "predicted_github_trending_change": "Dự đoán thay đổi xu hướng GitHub", + "duplicate_post": "Bài viết trùng lặp", + "preview_post": "Xem trước bài viết", + "post_statistics": "Thống kê bài viết", + "draft": "Bản nháp", + "week_number": "Tuần {{number}}", + "top_title_edit_webhook": "Chỉnh sửa webhook", + "top_title_add_webhook": "Thêm webhook", + "top_title_oh_no": "Ôi không", + "top_title_auto_plug": "Tự động cắm: {{title}}", + "top_title_edit_autopost": "Chỉnh sửa tự động đăng", + "top_title_add_autopost": "Thêm tự động đăng", + "top_title_send_a_new_offer": "Gửi ưu đãi mới", + "top_title_media_library": "Thư viện phương tiện", + "top_title_add_signature": "Thêm chữ ký", + "top_title_send_a_message_to": "Gửi tin nhắn cho {{name}}", + "top_title_configure_provider": "Cấu hình nhà cung cấp", + "top_title_add_member": "Thêm thành viên", + "top_title_change_bot_picture": "Thay đổi ảnh bot", + "top_title_create_a_new_tag": "Tạo thẻ mới", + "top_title_select_company": "Chọn công ty", + "top_title_additional_settings": "Cài đặt bổ sung", + "top_title_time_table_slots": "Khung giờ thời gian biểu", + "top_title_design_media": "Thiết kế phương tiện", + "top_title_edit_post": "Chỉnh sửa bài đăng", + "top_title_create_post": "Tạo bài đăng", + "top_title_move__add_to_customer": "Chuyển / Thêm vào khách hàng", + "top_title_add_api_key_for": "Thêm API key cho {{name}}", + "top_title_instance_url": "URL phiên bản", + "top_title_custom_url": "URL tùy chỉnh", + "top_title_add_channel": "Thêm kênh", + "top_title_add_telegram": "Thêm Telegram", + "top_title_add_wrapcast": "Thêm Wrapcast", + "top_title_comments_for": "Bình luận cho {{date}}", + "top_title_edit_signature": "Chỉnh sửa chữ ký", + "label_name": "Tên", + "label_url": "URL", + "label_title": "Tiêu đề", + "label_subtitle": "Phụ đề", + "label_email": "Email", + "label_full_name": "Họ và tên", + "label_password": "Mật khẩu", + "label_confirm_password": "Xác nhận mật khẩu", + "label_api_key": "API Key", + "label_instance_url": "URL phiên bản", + "label_custom_url": "URL tùy chỉnh", + "label_feedback": "Phản hồi", + "label_bio": "Tiểu sử", + "label_role": "Vai trò", + "label_country": "Quốc gia", + "label_audience_size": "Quy mô khán giả trên tất cả các nền tảng", + "label_pick_time": "Chọn thời gian", + "label_nickname": "Biệt danh", + "label_write_anything": "Viết bất cứ điều gì", + "label_output_format": "Định dạng đầu ra", + "label_add_pictures": "Thêm hình ảnh?", + "label_hour": "Giờ", + "label_minutes": "Phút", + "label_select_publication": "Chọn ấn phẩm", + "label_canonical_link": "Liên kết chuẩn", + "label_cover_picture": "Ảnh bìa", + "label_tags": "Thẻ", + "label_topics": "Chủ đề", + "label_tags_maximum_4": "Thẻ (Tối đa 4)", + "label_attachments": "Tệp đính kèm", + "label_type": "Loại", + "label_thumbnail": "Ảnh thu nhỏ", + "label_who_can_see_this_video": "Ai có thể xem video này?", + "label_content_posting_method": "Phương thức đăng nội dung", + "label_auto_add_music": "Tự động thêm nhạc", + "label_duet": "Song ca", + "label_stitch": "Ghép", + "label_comments": "Bình luận", + "label_disclose_video_content": "Công khai nội dung video", + "label_your_brand": "Thương hiệu của bạn", + "label_branded_content": "Nội dung thương hiệu", + "label_subreddit": "Subreddit", + "label_flair": "Flair", + "label_media": "Phương tiện", + "label_search_subreddit": "Tìm kiếm Subreddit", + "label_delay": "Trì hoãn", + "label_post_type": "Loại bài đăng", + "label_collaborators": "Cộng tác viên (tối đa 3) - tài khoản không được để riêng tư", + "label_community": "Cộng đồng", + "label_search_community": "Tìm kiếm cộng đồng", + "label_channel": "Kênh", + "label_search_channel": "Tìm kiếm kênh", + "label_select_channel": "Chọn kênh", + "label_new_password": "Mật khẩu mới", + "label_repeat_password": "Nhập lại mật khẩu", + "label_platform": "Nền tảng", + "label_price_per_post": "Giá mỗi bài đăng", + "label_integrations": "Tích hợp", + "label_code": "Mã", + "label_should_sync_last_post": "Chúng ta có nên đồng bộ bài đăng cuối hiện tại không?", + "label_when_post": "Khi nào chúng ta nên đăng?", + "label_autogenerate_content": "Tự động tạo nội dung", + "label_generate_picture": "Tạo hình ảnh?", + "label_company": "Công ty", + "label_tag_color": "Màu thẻ", + "label_select_board": "Chọn bảng", + "label_select_organization": "Chọn tổ chức", + "label_auto_add_signature": "Tự động thêm chữ ký?", + "enable_color_picker": "Bật bộ chọn màu", + "cancel_the_color_picker": "Hủy bộ chọn màu", + "no_content_yet": "Chưa có nội dung", + "write_your_reply": "Viết bài của bạn...", + "add_a_tag": "Thêm thẻ", + "add_to_calendar": "Thêm vào lịch", + "select_channels_from_circles": "Chọn kênh từ các vòng tròn phía trên", + "not_matching_order": "Không đúng thứ tự", + "submit_for_order": "Gửi để đặt hàng", + "schedule": "Lên lịch", + "update": "Cập nhật", + "attachments": "Tệp đính kèm", + "tags": "Thẻ", + "public_to_everyone": "Công khai với mọi người", + "mutual_follow_friends": "Bạn bè theo dõi lẫn nhau", + "follower_of_creator": "Người theo dõi của người tạo", + "self_only": "Chỉ mình tôi", + "post_content_directly_to_tiktok": "Đăng nội dung trực tiếp lên TikTok", + "upload_content_to_tiktok_without_posting": "Tải nội dung lên TikTok mà không đăng", + "choose_upload_without_posting_description": "Chọn tải lên mà không đăng nếu bạn muốn xem lại và chỉnh sửa nội dung trong ứng dụng TikTok trước khi xuất bản. Điều này giúp bạn sử dụng các công cụ chỉnh sửa tích hợp của TikTok và thực hiện các điều chỉnh cuối cùng trước khi đăng.", + "faq_am_i_going_to_be_charged_by_postiz": "Tôi có bị Postiz tính phí không?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Để xác nhận thông tin thẻ tín dụng, Postiz sẽ giữ $2 và hoàn trả ngay lập tức", + "faq_can_i_trust_postiz_gitroom": "Tôi có thể tin tưởng Postiz không?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz tự hào là mã nguồn mở! Chúng tôi tin vào một văn hóa đạo đức và minh bạch, nghĩa là Postiz sẽ tồn tại mãi mãi. Bạn có thể xem toàn bộ mã nguồn hoặc sử dụng cho các dự án cá nhân. Để xem kho mã nguồn mở, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">bấm vào đây</a>.", + "faq_what_are_channels": "Kênh là gì?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz cho phép bạn lên lịch đăng bài trên các kênh khác nhau.\nMột kênh là một nền tảng xuất bản nơi bạn có thể lên lịch đăng bài.\nVí dụ, bạn có thể lên lịch đăng bài trên X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads và Pinterest.", + "faq_what_are_team_members": "Thành viên nhóm là gì?", + "faq_if_you_have_a_team_with_multiple_members": "Nếu bạn có một nhóm với nhiều thành viên, bạn có thể mời họ vào không gian làm việc để cùng cộng tác đăng bài và thêm các kênh cá nhân của họ", + "faq_what_is_ai_auto_complete": "Tự động hoàn thành bằng AI là gì?", + "faq_we_automate_chatgpt_to_help_you_write": "Chúng tôi tự động hóa ChatGPT để giúp bạn viết bài đăng mạng xã hội và bài viết.", + "enter_email": "Nhập email", + "are_you_sure": "Bạn có chắc không?", + "yes_delete_it": "Vâng, xóa nó đi!", + "no_cancel": "Không, hủy bỏ!", + "are_you_sure_you_want_to_delete": "Bạn có chắc muốn xóa {{name}} không?", + "are_you_sure_you_want_to_delete_the_image": "Bạn có chắc muốn xóa hình ảnh này không?", + "are_you_sure_you_want_to_logout": "Bạn có chắc muốn đăng xuất không?", + "yes_logout": "Vâng, đăng xuất", + "are_you_sure_you_want_to_delete_this_slot": "Bạn có chắc muốn xóa khung này không?", + "are_you_sure_you_want_to_delete_this_subreddit": "Bạn có chắc muốn xóa Subreddit này không?", + "are_you_sure_you_want_to_close_the_window": "Bạn có chắc muốn đóng cửa sổ này không?", + "yes_close": "Vâng, đóng lại", + "link_copied_to_clipboard": "Đã sao chép liên kết vào bộ nhớ tạm", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Bạn có chắc muốn đóng cửa sổ này không? (tất cả dữ liệu sẽ bị mất)", + "yes_close_it": "Vâng, đóng lại!", + "uploading_pictures": "Đang tải ảnh lên...", + "agent_starting": "Đang khởi động tác vụ", + "researching_your_content": "Đang nghiên cứu nội dung của bạn...", + "understanding_the_category": "Đang tìm hiểu danh mục...", + "finding_the_topic": "Đang tìm chủ đề...", + "finding_popular_posts_to_match_with": "Đang tìm các bài viết phổ biến để ghép nối...", + "generating_hook": "Đang tạo tiêu đề hấp dẫn...", + "generating_content": "Đang tạo nội dung...", + "generating_pictures": "Đang tạo hình ảnh...", + "finding_time_to_post": "Đang tìm thời gian đăng bài...", + "write_anything": "Viết bất cứ điều gì", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "Bạn có thể viết bất cứ điều gì bạn muốn, và cũng có thể thêm liên kết, chúng tôi sẽ nghiên cứu cho bạn...", + "output_format": "Định dạng đầu ra", + "add_pictures": "Thêm hình ảnh?", + "7_days": "7 ngày", + "30_days": "30 ngày", + "90_days": "90 ngày", + "start_7_days_free_trial": "Bắt đầu dùng thử miễn phí 7 ngày", + "change_language": "Thay đổi ngôn ngữ", + "that_a_wrap": "Kết thúc rồi!\n\nNếu bạn thích chuỗi bài này:\n\n1. Hãy theo dõi tôi @{{username}} để xem thêm nhiều nội dung như vậy\n2. Retweet bài bên dưới để chia sẻ chuỗi này với mọi người\n", + "post_as_images_carousel": "Đăng dưới dạng băng chuyền hình ảnh", + "save_set": "Lưu bộ", + "separate_post": "Tách bài viết thành nhiều bài", + "label_who_can_reply_to_this_post": "Ai có thể trả lời bài viết này?", + "delete_integration": "Xóa tích hợp", + "start_writing_your_post": "Bắt đầu viết bài của bạn để xem trước", + "billing_join_over": "Tham gia cùng hơn", + "billing_entrepreneurs_count": "18.000+ doanh nhân", + "billing_who_use": "đang sử dụng", + "billing_postiz_grow_social": "Postiz để phát triển sự hiện diện trên mạng xã hội của họ", + "billing_no_risk_trial": "Dùng thử miễn phí 100% không rủi ro", + "billing_pay_nothing_7_days": "Không phải trả gì trong 7 ngày đầu tiên", + "billing_cancel_anytime": "Hủy bất cứ lúc nào, không ràng buộc", + "billing_choose_plan": "Chọn gói", + "billing_monthly": "Hàng tháng", + "billing_yearly": "Hàng năm", + "billing_20_percent_off": "Giảm 20%", + "billing_features": "Tính năng", + "billing_channel": "kênh", + "billing_channels": "các kênh", + "billing_unlimited": "Không giới hạn", + "billing_posts_per_month": "bài đăng mỗi tháng", + "billing_unlimited_team_members": "Không giới hạn thành viên nhóm", + "billing_ai_auto_complete": "AI tự động hoàn thành", + "billing_ai_copilots": "AI hỗ trợ", + "billing_ai_autocomplete": "AI Tự động hoàn thành", + "billing_advanced_picture_editor": "Trình chỉnh sửa ảnh nâng cao", + "billing_ai_images_per_month": "Hình ảnh AI mỗi tháng", + "billing_ai_videos_per_month": "Video AI mỗi tháng", + "billing_billing_address": "Địa chỉ thanh toán", + "billing_payment": "Thanh toán", + "billing_powered_by_stripe": "Được hỗ trợ bởi Stripe", + "billing_your_7_day_trial_is": "Dùng thử 7 ngày của bạn là", + "billing_100_percent_free": "Miễn phí 100%", + "billing_ending": "kết thúc", + "billing_cancel_anytime_short": "Hủy bất cứ lúc nào.", + "billing_pay_0_start_trial": "Thanh toán $0 hôm nay - Bắt đầu dùng thử miễn phí!", + "billing_pay_now": "Thanh toán ngay", + "billing_per_month": "/ tháng" } diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index 5f0e88c6..8b8e636f 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -1,505 +1,539 @@ { - "calendar": "日历", - "webhooks": "Webhook(网络钩子)", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhook 是一种通过 HTTP 请求在 Postiz 中有事件发生时获得通知的方式。", - "name": "名称", - "url": "URL", - "edit": "编辑", - "delete": "删除", - "add_a_webhook": "添加 Webhook", - "save": "保存", - "send_test": "发送测试", - "select_role": "选择角色", - "video_made_with_ai": "视频由AI制作", - "please_add_at_least": "请至少添加20个字符", - "send_invitation_via_email": "通过电子邮件发送邀请?", - "global_settings": "全局设置", - "copy_id": "复制频道ID", - "team_members": "团队成员", - "invite_your_assistant_or_team_member_to_manage_your_account": "邀请你的助理或团队成员来管理你的账户", - "remove": "移除", - "add_another_member": "添加其他成员", - "signatures": "签名", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "你可以为你的账户添加签名,以便在你的帖子中使用。", - "content": "内容", - "auto_add": "自动添加?", - "actions": "操作", - "use_signature": "使用签名", - "add_a_signature": "添加签名", - "no": "否", - "yes": "是", - "your_git_repository": "你的Git仓库", - "connect_your_github_repository_to_receive_updates_and_analytics": "连接你的GitHub仓库以接收更新和分析", - "connected": "已连接:", - "disconnect": "断开连接", - "connect_your_repository": "连接你的仓库", - "cancel": "取消", - "connect": "连接", - "public_api": "公共API", - "check_n8n": "查看我们为Postiz定制的N8N节点。", - "use_postiz_api_to_integrate_with_your_tools": "使用Postiz API与您的工具集成。", - "read_how_to_use_it_over_the_documentation": "请阅读文档了解如何使用。", - "reveal": "显示", - "copy_key": "复制密钥", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "将 Postiz MCP 服务器连接到您的客户端(Http 流式传输),以更快地安排您的帖子!", - "share_with_a_client": "与客户分享", - "post": "帖子", - "comments": "评论", - "user": "用户", - "login_register_to_add_comments": "登录/注册以添加评论", - "status": "状态:", - "there_are_not_plugs_matching_your_channels": "没有与您的频道匹配的插件", - "you_have_to_add_x_or_linkedin_or_threads": "你需要添加:X或LinkedIn或Threads", - "go_to_the_calendar_to_add_channels": "前往日历添加频道", - "channels": "频道", - "activate": "激活", - "this_channel_needs_to_be_refreshed": "该频道需要刷新,", - "click_here_to_refresh": "点击这里刷新", - "can_t_show_analytics_yet": "暂时无法显示分析数据", - "you_have_to_add_social_media_channels": "你需要添加社交媒体频道", - "supported": "支持:", - "step": "步骤", - "skip_onboarding": "跳过引导", - "onboarding": "新手引导", - "next": "下一步", - "you_are_done_from_here_you_can": "你已完成,从这里你可以:", - "view_analytics": "查看分析数据", - "schedule_a_new_post": "安排新帖子", - "to_sell_posts_you_would_have_to": "要出售帖子,你需要:", - "1_connect_at_least_one_channel": "1. 至少连接一个频道", - "2_connect_you_bank_account": "2. 连接你的银行账户", - "go_back_to_connect_channels": "返回连接频道", - "move_to_the_seller_page_to_connect_you_bank": "前往卖家页面连接你的银行账户", - "connect_channels": "连接频道", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "连接你的社交媒体和发布网站频道,以便稍后安排帖子", - "social": "社交", - "publishing_platforms": "发布平台", - "no_channels": "还没有频道", - "connect_your_accounts": "连接您的社交账号,即可在一个平台上开始安排、发布和分析内容。", - "notifications": "通知", - "no_notifications": "暂无通知", - "send_message": "发送消息", - "mar_28": "3月28日", - "there_are_no_messages_yet": "还没有消息。", - "checkout_the_marketplace": "查看市场", - "go_to_marketplace": "前往市场", - "all_messages": "所有消息", - "previous": "上一页", - "select_or_upload_pictures_maximum_5_at_a_time": "选择或上传图片(每次最多5张)", - "you_can_also_drag_drop_pictures": "你也可以拖放图片", - "you_don_t_have_any_assets_yet": "你还没有任何资产。", - "click_the_button_below_to_upload_other": "", - "add_selected_media": "添加所选媒体", - "insert_media": "插入媒体", - "design_media": "设计媒体", - "select": "选择", - "editor": "编辑器", - "clear": "清除", - "order_completed": "订单已完成", - "the_order_has_been_completed": "订单已完成", - "post_has_been_published": "帖子已发布", - "url_1": "网址:", - "new_offer": "新报价", - "platform": "平台", - "posts": "帖子", - "pay_accept_offer": "支付并接受报价", - "accepted": "已接受", - "post_draft": "草稿", - "revision_needed": "需要修改", - "approve": "批准", - "preview": "预览", - "revision_requested": "已请求修改", - "accepted_1": "已接受", - "cancelled_by_the_seller": "卖家已取消", - "please_select_your_country_where_your_business_is": "请选择您的企业所在国家。", - "select_country": "--选择国家--", - "connect_bank_account": "连接银行账户", - "seller_mode": "卖家模式", - "active": "活跃", - "details": "详情", - "audience_size": "受众规模", - "add_another_platform": "添加另一个平台", - "send_an_offer_for": "发送报价 $", - "complete_order_and_pay_early": "完成订单并提前付款", - "order_in_progress": "订单进行中", - "create_a_new_offer": "创建新报价", - "orders": "订单", - "price": "价格", - "state": "状态", - "showing": "显示", - "to": "到", - "from": "从", - "results": "结果", - "content_writer": "内容创作者", - "influencer": "影响者", - "request_service": "请求服务", - "the_marketplace_is_not_opened_yet": "市场尚未开放", - "check_again_soon": "请稍后再查!", - "filter": "筛选", - "result": "结果", - "seller": "卖家", - "buyer": "买家", - "discord_support": "Discord 支持", - "teams": "团队", - "webhooks_1": "Webhooks(网络钩子)", - "auto_post": "自动发布", - "logout_from": "登出", - "join_10000_entrepreneurs_who_use_postiz": "加入超过10,000名使用Postiz的创业者", - "to_manage_all_your_social_media_channels": "管理你所有的社交媒体渠道", - "100_no_risk_trial": "100% 无风险试用", - "pay_nothing_for_the_first_7_days": "前 7 天免费", - "cancel_anytime_hassle_free": "随时取消,无需担忧", - "add_free_subscription": "-- 添加免费订阅 --", - "currently_impersonating": "当前模拟身份", - "user_1": "用户:", - "drag_n_drop_some_files_here": "将文件拖放到此处", - "add_time_slot": "添加时间段", - "add_slot": "添加时段", - "cancel_publication": "取消发布", - "statistics": "统计", - "loading": "加载中", - "short_link": "短链接", - "original_link": "原始链接", - "clicks": "点击量", - "selected_customer": "已选客户", - "customer": "客户:", - "repeat_post_every": "每隔...重复发布", - "use_this_media": "使用此媒体", - "create_new_post": "创建帖子", - "update_post": "更新已有帖子", - "merge_comments_into_one_post": "将评论合并为一条帖子", - "accounts_that_will_engage": "将参与的账号:", - "day": "天", - "week": "周", - "month": "月", - "remove_from_customer": "从客户中移除", - "show_more": "+ 显示更多", - "show_less": "- 显示更少", - "upload": "上传", - "ai": "AI", - "add_channel": "添加频道", - "add_platform": "添加平台", - "articles": "文章", - "add_comment": "添加评论", - "add_post": "在主题中添加帖子", - "add_comment_or_post": "添加评论/帖子", - "you_are_in_global_editing_mode": "您当前处于全局编辑模式", - "the_post_should_be_at_least_6_characters_long": "帖子内容至少需要6个字符", - "are_you_sure_you_want_to_delete_post": "您确定要删除这条帖子吗?", - "post_deleted_successfully": "帖子删除成功", - "delete_post": "删除帖子", - "save_as_draft": "保存为草稿", - "post_now": "立即发布", - "please_add": "请添加", - "to_your_telegram_group_channel_and_click_here": "到您的Telegram群组/频道并点击这里:", - "connect_telegram": "连接Telegram", - "please_add_the_following_command_in_your_chat": "请在您的聊天中添加以下命令:", - "copy": "复制", - "settings": "设置", - "integrations": "集成", - "add_integration": "添加集成", - "you_are_now_editing_only": "您现在仅在编辑", - "tag_a_company": "标记公司", - "video_length_is_invalid_must_be_up_to": "视频时长无效,最长不得超过", - "seconds": "秒", - "this_feature_available_only_for_photos": "此功能仅适用于照片,将添加默认音乐,您之后可以更改。", - "allow_user_to": "允许用户:", - "your_video_will_be_labeled_promotional": "您的视频将被标记为“推广内容”。", - "this_cannot_be_changed_once_posted": "视频发布后此项不可更改。", - "turn_on_to_disclose_video_promotes": "开启后将披露该视频为推广商品或服务以换取有价物。您的视频可以推广自己、第三方或两者。", - "you_are_promoting_yourself": "您正在推广自己或自己的品牌。", - "this_video_will_be_classified_brand_organic": "该视频将被归类为品牌原生内容。", - "you_are_promoting_another_brand": "您正在推广其他品牌或第三方。", - "this_video_will_be_classified_branded_content": "该视频将被归类为品牌内容。", - "by_posting_you_agree_to_tiktoks": "发布即表示您同意 TikTok 的", - "music_usage_confirmation": "音乐使用确认", - "branded_content_policy": "品牌内容政策", - "select_1": "--请选择--", - "select_flair": "--选择标识--", - "link": "链接", - "add_subreddit": "添加子版块", - "please_add_at_least_one_subreddit": "请至少添加一个子版块", - "add_community": "添加社区", - "select_post_type": "选择帖子类型...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "我们未能找到与您的 LinkedIn 页面关联的任何企业。", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "请关闭此对话框,创建新页面并再次添加新频道。", - "select_linkedin_page": "选择 Linkedin 页面:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "我们未能找到与所选页面关联的任何企业。", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "我们建议您连接所有页面和所有企业。", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "请关闭此对话框,删除您的集成并再次添加新频道。", - "select_instagram_account": "选择 Instagram 账号:", - "select_page": "选择页面:", - "generate_image_with_ai": "用 AI 生成图片", - "reconnect_channel": "重新连接频道", - "update_credentials": "更新凭证", - "additional_settings": "附加设置", - "change_bot": "更换机器人", - "move_add_to_customer": "移动/添加到客户", - "edit_time_slots": "编辑时间段", - "enable_channel": "启用频道", - "disable_channel": "禁用频道", - "add": "添加", - "short_post": "短帖子", - "long_post": "长帖子", - "a_thread_with_short_posts": "包含短帖的主题", - "a_thread_with_long_posts": "包含长帖的主题", - "personal_voice_i_am_happy_to_announce": "个人语气(“我很高兴地宣布”)", - "company_voice_we_are_happy_to_announce": "公司语气(“我们很高兴地宣布”)", - "generate": "生成", - "generate_posts": "生成帖子", - "purchase_a_life_time_pro_account_with_sol_199": "购买终身PRO账户,价格为SOL($199)。请注意,此购买不支持退款。", - "purchase_now": "立即购买", - "pay_today": "今日付款", - "we_are_sorry_to_see_you_go": "很遗憾看到你离开 :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "请简要告诉我们可以改进的地方好吗?", - "cancel_subscription": "取消订阅", - "plans": "套餐", - "monthly": "月付", - "yearly": "年付", - "reactivate_subscription": "重新激活订阅", - "update_payment_method_invoices_history": "更新支付方式 / 发票历史", - "cancel_subscription_1": "取消订阅", - "your_subscription_will_be_canceled_at": "你的订阅将在以下时间被取消", - "you_will_never_be_charged_again": "你将不会再被扣费", - "current_package": "当前套餐:", - "next_package": "下一个套餐:", - "claim": "领取", - "frequently_asked_questions": "常见问题", - "autopost": "自动发布", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "自动发布可以将您的RSS新内容自动发布到社交媒体", - "title": "标题", - "add_an_autopost": "添加自动发布", - "post_content": "发布内容", - "sign_up": "注册", - "or": "或", - "by_registering_you_agree_to_our": "注册即表示您同意我们的", - "and": "和", - "terms_of_service": "服务条款", - "privacy_policy": "隐私政策", - "create_account": "创建账户", - "already_have_an_account": "已有账户?", - "sign_in": "登录", - "sign_in_1": "登录", - "don_t_have_an_account": "还没有账户?", - "forgot_password": "忘记密码", - "forgot_password_1": "忘记密码", - "send_password_reset_email": "发送密码重置邮件", - "go_back_to_login": "返回登录", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "我们已向您的邮箱发送了一封包含重置密码链接的邮件。", - "change_password": "更改密码", - "we_successfully_reset_your_password_you_can_now_login_with_your": "我们已成功重置您的密码。您现在可以使用新密码登录。", - "click_here_to_go_back_to_login": "点击这里返回登录", - "activate_your_account": "激活您的账户", - "thank_you_for_registering": "感谢您的注册!", - "please_check_your_email_to_activate_your_account": "请检查您的邮箱以激活账户。", - "sign_in_with": "使用以下方式登录", - "continue_with_google": "使用 Google 继续", - "sign_in_with_github": "使用 GitHub 登录", - "continue_with_farcaster": "使用 Farcaster 继续", - "continue_with_your_wallet": "使用钱包继续", - "stars_per_day": "每日星标", - "media": "媒体", - "check_launch": "检查启动", - "load_your_github_repository_from_settings_to_see_analytics": "请在设置中加载您的 GitHub 仓库以查看分析数据", - "stars": "星标", - "processing_stars": "正在处理星标...", - "forks": "分支", - "registration_is_disabled": "注册已被禁用", - "login_instead": "请登录", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "请选择一个会话并开始聊天。", - "adding_channel_redirecting_you": "正在添加频道,正在为您跳转", - "could_not_add_provider": "无法添加提供者。", - "you_are_being_redirected_back": "您正在被重定向返回", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "我们遇到了一些问题,请尝试刷新页面", - "post_not_found": "未找到帖子", - "publication_date": "发布日期:", - "analytics": "分析", - "launches": "发布", - "plugs": "推荐", - "billing": "账单", - "affiliate": "联盟", - "monday": "星期一", - "tuesday": "星期二", - "wednesday": "星期三", - "thursday": "星期四", - "friday": "星期五", - "saturday": "星期六", - "sunday": "星期日", - "can_t_change_date_remove_post_from_publication": "无法更改日期,请先将帖子从发布中移除", - "predicted_github_trending_change": "预测 GitHub 趋势变化", - "duplicate_post": "重复帖子", - "preview_post": "预览帖子", - "post_statistics": "帖子统计", - "draft": "草稿", - "week_number": "第 {{number}} 周", - "top_title_edit_webhook": "编辑 webhook", - "top_title_add_webhook": "添加 webhook", - "top_title_oh_no": "哦不", - "top_title_auto_plug": "自动插件:{{title}}", - "top_title_edit_autopost": "编辑自动发布", - "top_title_add_autopost": "添加自动发布", - "top_title_send_a_new_offer": "发送新报价", - "top_title_media_library": "媒体库", - "top_title_add_signature": "添加签名", - "top_title_send_a_message_to": "发送消息给{{name}}", - "top_title_configure_provider": "配置服务商", - "top_title_add_member": "添加成员", - "top_title_change_bot_picture": "更改机器人头像", - "top_title_create_a_new_tag": "创建新标签", - "top_title_select_company": "选择公司", - "top_title_additional_settings": "附加设置", - "top_title_time_table_slots": "时间表时段", - "top_title_design_media": "设计媒体", - "top_title_edit_post": "编辑帖子", - "top_title_create_post": "创建帖子", - "top_title_move__add_to_customer": "移动/添加到客户", - "top_title_add_api_key_for": "为{{name}}添加API密钥", - "top_title_instance_url": "实例URL", - "top_title_custom_url": "自定义URL", - "top_title_add_channel": "添加频道", - "top_title_add_telegram": "添加Telegram", - "top_title_add_wrapcast": "添加Wrapcast", - "top_title_comments_for": "{{date}}的评论", - "top_title_edit_signature": "编辑签名", - "label_name": "姓名", - "label_url": "网址", - "label_title": "标题", - "label_subtitle": "副标题", - "label_email": "电子邮件", - "label_full_name": "全名", - "label_password": "密码", - "label_confirm_password": "确认密码", - "label_api_key": "API 密钥", - "label_instance_url": "实例网址", - "label_custom_url": "自定义网址", - "label_feedback": "反馈", - "label_bio": "个人简介", - "label_role": "角色", - "label_country": "国家", - "label_audience_size": "所有平台的受众规模", - "label_pick_time": "选择时间", - "label_nickname": "昵称", - "label_write_anything": "随便写点什么", - "label_output_format": "输出格式", - "label_add_pictures": "添加图片?", - "label_hour": "小时", - "label_minutes": "分钟", - "label_select_publication": "选择出版物", - "label_canonical_link": "规范链接", - "label_cover_picture": "封面图片", - "label_tags": "标签", - "label_topics": "话题", - "label_tags_maximum_4": "标签(最多4个)", - "label_attachments": "附件", - "label_type": "类型", - "label_thumbnail": "缩略图", - "label_who_can_see_this_video": "谁可以观看此视频?", - "label_content_posting_method": "内容发布方式", - "label_auto_add_music": "自动添加音乐", - "label_duet": "合拍", - "label_stitch": "拼接", - "label_comments": "评论", - "label_disclose_video_content": "公开视频内容", - "label_your_brand": "你的品牌", - "label_branded_content": "品牌内容", - "label_subreddit": "子版块", - "label_flair": "标记", - "label_media": "媒体", - "label_search_subreddit": "搜索子版块", - "label_delay": "延迟", - "label_post_type": "帖子类型", - "label_collaborators": "协作者(最多3人)- 账号不能为私密", - "label_community": "社区", - "label_search_community": "搜索社区", - "label_channel": "频道", - "label_search_channel": "搜索频道", - "label_select_channel": "选择频道", - "label_new_password": "新密码", - "label_repeat_password": "重复密码", - "label_platform": "平台", - "label_price_per_post": "每帖价格", - "label_integrations": "集成", - "label_code": "代码", - "label_should_sync_last_post": "是否同步当前最新帖子?", - "label_when_post": "我们应该何时发布?", - "label_autogenerate_content": "自动生成内容", - "label_generate_picture": "生成图片?", - "label_company": "公司", - "label_tag_color": "标签颜色", - "label_select_board": "选择看板", - "label_select_organization": "选择组织", - "label_auto_add_signature": "自动添加签名?", - "enable_color_picker": "启用取色器", - "cancel_the_color_picker": "取消取色器", - "no_content_yet": "暂无内容", - "write_your_reply": "写下你的帖子...", - "add_a_tag": "添加标签", - "add_to_calendar": "添加到日历", - "select_channels_from_circles": "从上方的圈子中选择频道", - "not_matching_order": "顺序不匹配", - "submit_for_order": "提交订单", - "schedule": "日程安排", - "update": "更新", - "attachments": "附件", - "tags": "标签", - "public_to_everyone": "对所有人公开", - "mutual_follow_friends": "互相关注的好友", - "follower_of_creator": "创作者的粉丝", - "self_only": "仅自己可见", - "post_content_directly_to_tiktok": "直接将内容发布到 TikTok", - "upload_content_to_tiktok_without_posting": "上传内容到 TikTok 但不发布", - "choose_upload_without_posting_description": "如果你希望在 TikTok 应用内审核和编辑内容后再发布,请选择“上传但不发布”。这样你可以使用 TikTok 内置的编辑工具,并在发布前进行最终调整。", - "faq_am_i_going_to_be_charged_by_postiz": "我会被 Postiz 收费吗?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "为确认信用卡信息,Postiz 会暂时预授权 2 美元并立即释放", - "faq_can_i_trust_postiz_gitroom": "我可以信任Postiz吗?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz自豪地开源!我们相信道德和透明的文化,这意味着Postiz将永远存在。您可以查看全部代码或将其用于个人项目。要查看开源仓库,<a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">请点击这里</a>。", - "faq_what_are_channels": "什么是频道?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz允许您在不同频道之间安排您的帖子。\n频道是一个发布平台,您可以在上面安排您的帖子。\n例如,您可以在X、Facebook、Instagram、TikTok、YouTube、Reddit、Linkedin、Dribbble、Threads和Pinterest上安排您的帖子。", - "faq_what_are_team_members": "什么是团队成员?", - "faq_if_you_have_a_team_with_multiple_members": "如果你有一个包含多名成员的团队,你可以邀请他们加入你的工作区,共同协作发布内容,并添加他们的个人频道。", - "faq_what_is_ai_auto_complete": "什么是 AI 自动补全?", - "faq_we_automate_chatgpt_to_help_you_write": "我们自动化集成了 ChatGPT,帮助你撰写社交帖子和文章。", - "enter_email": "请输入邮箱", - "are_you_sure": "你确定吗?", - "yes_delete_it": "是的,删除它!", - "no_cancel": "不,取消!", - "are_you_sure_you_want_to_delete": "你确定要删除{{name}}吗?", - "are_you_sure_you_want_to_delete_the_image": "你确定要删除这张图片吗?", - "are_you_sure_you_want_to_logout": "你确定要登出吗?", - "yes_logout": "是的,登出", - "are_you_sure_you_want_to_delete_this_slot": "你确定要删除这个槽位吗?", - "are_you_sure_you_want_to_delete_this_subreddit": "你确定要删除这个Subreddit吗?", - "are_you_sure_you_want_to_close_the_window": "你确定要关闭窗口吗?", - "yes_close": "是的,关闭", - "link_copied_to_clipboard": "链接已复制到剪贴板", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "你确定要关闭此弹窗吗?(所有数据将丢失)", - "yes_close_it": "是的,关闭它!", - "uploading_pictures": "正在上传图片...", - "agent_starting": "代理启动中", - "researching_your_content": "正在研究你的内容...", - "understanding_the_category": "正在理解类别...", - "finding_the_topic": "正在查找主题...", - "finding_popular_posts_to_match_with": "正在查找匹配的热门帖子...", - "generating_hook": "正在生成引子...", - "generating_content": "正在生成内容...", - "generating_pictures": "正在生成图片...", - "finding_time_to_post": "正在查找发布时间...", - "write_anything": "随便写点什么", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "你可以写任何你想写的内容,也可以添加链接,我们会为你做研究……", - "output_format": "输出格式", - "add_pictures": "添加图片?", - "7_days": "7天", - "30_days": "30天", - "90_days": "90天", - "start_7_days_free_trial": "开始7天免费试用", - "change_language": "切换语言", - "that_a_wrap": "本帖到此结束!\n\n如果你喜欢这个话题:\n\n1. 关注我 @{{username}},获取更多类似内容\n2. 转发下方推文,与更多人分享本帖\n", - "post_as_images_carousel": "以图片轮播的形式发布", - "save_set": "保存设置", - "separate_post": "将帖子拆分为多个帖子", - "label_who_can_reply_to_this_post": "谁可以回复此帖子?", - "delete_integration": "删除集成", - "start_writing_your_post": "开始写你的帖子以预览" + "calendar": "日历", + "webhooks": "Webhook(网络钩子)", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhook 是一种通过 HTTP 请求在 Postiz 中有事件发生时获得通知的方式。", + "name": "名称", + "url": "URL", + "edit": "编辑", + "delete": "删除", + "add_a_webhook": "添加 Webhook", + "save": "保存", + "send_test": "发送测试", + "select_role": "选择角色", + "video_made_with_ai": "视频由AI制作", + "please_add_at_least": "请至少添加20个字符", + "send_invitation_via_email": "通过电子邮件发送邀请?", + "global_settings": "全局设置", + "copy_id": "复制频道ID", + "team_members": "团队成员", + "invite_your_assistant_or_team_member_to_manage_your_account": "邀请你的助理或团队成员来管理你的账户", + "remove": "移除", + "add_another_member": "添加其他成员", + "signatures": "签名", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "你可以为你的账户添加签名,以便在你的帖子中使用。", + "content": "内容", + "auto_add": "自动添加?", + "actions": "操作", + "use_signature": "使用签名", + "add_a_signature": "添加签名", + "no": "否", + "yes": "是", + "your_git_repository": "你的Git仓库", + "connect_your_github_repository_to_receive_updates_and_analytics": "连接你的GitHub仓库以接收更新和分析", + "connected": "已连接:", + "disconnect": "断开连接", + "connect_your_repository": "连接你的仓库", + "cancel": "取消", + "connect": "连接", + "public_api": "公共API", + "check_n8n": "查看我们为Postiz定制的N8N节点。", + "use_postiz_api_to_integrate_with_your_tools": "使用Postiz API与您的工具集成。", + "read_how_to_use_it_over_the_documentation": "请阅读文档了解如何使用。", + "reveal": "显示", + "copy_key": "复制密钥", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "将 Postiz MCP 服务器连接到您的客户端(Http 流式传输),以更快地安排您的帖子!", + "share_with_a_client": "与客户分享", + "post": "帖子", + "comments": "评论", + "user": "用户", + "login_register_to_add_comments": "登录/注册以添加评论", + "status": "状态:", + "there_are_not_plugs_matching_your_channels": "没有与您的频道匹配的插件", + "you_have_to_add_x_or_linkedin_or_threads": "你需要添加:X或LinkedIn或Threads", + "go_to_the_calendar_to_add_channels": "前往日历添加频道", + "channels": "频道", + "activate": "激活", + "this_channel_needs_to_be_refreshed": "该频道需要刷新,", + "click_here_to_refresh": "点击这里刷新", + "can_t_show_analytics_yet": "暂时无法显示分析数据", + "you_have_to_add_social_media_channels": "你需要添加社交媒体频道", + "supported": "支持:", + "step": "步骤", + "skip_onboarding": "跳过引导", + "onboarding": "新手引导", + "next": "下一步", + "you_are_done_from_here_you_can": "你已完成,从这里你可以:", + "view_analytics": "查看分析数据", + "schedule_a_new_post": "安排新帖子", + "to_sell_posts_you_would_have_to": "要出售帖子,你需要:", + "1_connect_at_least_one_channel": "1. 至少连接一个频道", + "2_connect_you_bank_account": "2. 连接你的银行账户", + "go_back_to_connect_channels": "返回连接频道", + "move_to_the_seller_page_to_connect_you_bank": "前往卖家页面连接你的银行账户", + "connect_channels": "连接频道", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "连接你的社交媒体和发布网站频道,以便稍后安排帖子", + "social": "社交", + "publishing_platforms": "发布平台", + "no_channels": "还没有频道", + "connect_your_accounts": "连接您的社交账号,即可在一个平台上开始安排、发布和分析内容。", + "notifications": "通知", + "no_notifications": "暂无通知", + "send_message": "发送消息", + "mar_28": "3月28日", + "there_are_no_messages_yet": "还没有消息。", + "checkout_the_marketplace": "查看市场", + "go_to_marketplace": "前往市场", + "all_messages": "所有消息", + "previous": "上一页", + "select_or_upload_pictures_maximum_5_at_a_time": "选择或上传图片(每次最多5张)", + "you_can_also_drag_drop_pictures": "你也可以拖放图片", + "you_don_t_have_any_assets_yet": "你还没有任何资产。", + "click_the_button_below_to_upload_one": "点击下方按钮上传一个", + "click_the_button_below_to_upload_other": "点击下方按钮上传多个", + "add_selected_media": "添加所选媒体", + "insert_media": "插入媒体", + "design_media": "设计媒体", + "select": "选择", + "editor": "编辑器", + "clear": "清除", + "order_completed": "订单已完成", + "the_order_has_been_completed": "订单已完成", + "post_has_been_published": "帖子已发布", + "url_1": "网址:", + "new_offer": "新报价", + "platform": "平台", + "posts": "帖子", + "pay_accept_offer": "支付并接受报价", + "accepted": "已接受", + "post_draft": "草稿", + "revision_needed": "需要修改", + "approve": "批准", + "preview": "预览", + "revision_requested": "已请求修改", + "accepted_1": "已接受", + "cancelled_by_the_seller": "卖家已取消", + "please_select_your_country_where_your_business_is": "请选择您的企业所在国家。", + "select_country": "--选择国家--", + "connect_bank_account": "连接银行账户", + "seller_mode": "卖家模式", + "active": "活跃", + "details": "详情", + "audience_size": "受众规模", + "add_another_platform": "添加另一个平台", + "send_an_offer_for": "发送报价 $", + "complete_order_and_pay_early": "完成订单并提前付款", + "order_in_progress": "订单进行中", + "create_a_new_offer": "创建新报价", + "orders": "订单", + "price": "价格", + "state": "状态", + "showing": "显示", + "to": "到", + "from": "从", + "results": "结果", + "content_writer": "内容创作者", + "influencer": "影响者", + "request_service": "请求服务", + "the_marketplace_is_not_opened_yet": "市场尚未开放", + "check_again_soon": "请稍后再查!", + "filter": "筛选", + "result": "结果", + "seller": "卖家", + "buyer": "买家", + "discord_support": "Discord 支持", + "teams": "团队", + "webhooks_1": "Webhooks(网络钩子)", + "auto_post": "自动发布", + "logout_from": "登出", + "join_10000_entrepreneurs_who_use_postiz": "加入超过10,000名使用Postiz的创业者", + "to_manage_all_your_social_media_channels": "管理你所有的社交媒体渠道", + "100_no_risk_trial": "100% 无风险试用", + "pay_nothing_for_the_first_7_days": "前 7 天免费", + "cancel_anytime_hassle_free": "随时取消,无需担忧", + "add_free_subscription": "-- 添加免费订阅 --", + "currently_impersonating": "当前模拟身份", + "user_1": "用户:", + "drag_n_drop_some_files_here": "将文件拖放到此处", + "add_time_slot": "添加时间段", + "add_slot": "添加时段", + "cancel_publication": "取消发布", + "statistics": "统计", + "loading": "加载中", + "short_link": "短链接", + "original_link": "原始链接", + "clicks": "点击量", + "selected_customer": "已选客户", + "customer": "客户:", + "repeat_post_every": "每隔...重复发布", + "use_this_media": "使用此媒体", + "create_new_post": "创建帖子", + "update_post": "更新已有帖子", + "merge_comments_into_one_post": "将评论合并为一条帖子", + "accounts_that_will_engage": "将参与的账号:", + "day": "天", + "week": "周", + "month": "月", + "remove_from_customer": "从客户中移除", + "show_more": "+ 显示更多", + "show_less": "- 显示更少", + "upload": "上传", + "ai": "AI", + "add_channel": "添加频道", + "add_platform": "添加平台", + "articles": "文章", + "add_comment": "添加评论", + "add_post": "在主题中添加帖子", + "add_comment_or_post": "添加评论/帖子", + "you_are_in_global_editing_mode": "您当前处于全局编辑模式", + "the_post_should_be_at_least_6_characters_long": "帖子内容至少需要6个字符", + "are_you_sure_you_want_to_delete_post": "您确定要删除这条帖子吗?", + "post_deleted_successfully": "帖子删除成功", + "delete_post": "删除帖子", + "save_as_draft": "保存为草稿", + "post_now": "立即发布", + "please_add": "请添加", + "to_your_telegram_group_channel_and_click_here": "到您的Telegram群组/频道并点击这里:", + "connect_telegram": "连接Telegram", + "please_add_the_following_command_in_your_chat": "请在您的聊天中添加以下命令:", + "copy": "复制", + "settings": "设置", + "integrations": "集成", + "add_integration": "添加集成", + "you_are_now_editing_only": "您现在仅在编辑", + "tag_a_company": "标记公司", + "video_length_is_invalid_must_be_up_to": "视频时长无效,最长不得超过", + "seconds": "秒", + "this_feature_available_only_for_photos": "此功能仅适用于照片,将添加默认音乐,您之后可以更改。", + "allow_user_to": "允许用户:", + "your_video_will_be_labeled_promotional": "您的视频将被标记为“推广内容”。", + "this_cannot_be_changed_once_posted": "视频发布后此项不可更改。", + "turn_on_to_disclose_video_promotes": "开启后将披露该视频为推广商品或服务以换取有价物。您的视频可以推广自己、第三方或两者。", + "you_are_promoting_yourself": "您正在推广自己或自己的品牌。", + "this_video_will_be_classified_brand_organic": "该视频将被归类为品牌原生内容。", + "you_are_promoting_another_brand": "您正在推广其他品牌或第三方。", + "this_video_will_be_classified_branded_content": "该视频将被归类为品牌内容。", + "by_posting_you_agree_to_tiktoks": "发布即表示您同意 TikTok 的", + "music_usage_confirmation": "音乐使用确认", + "branded_content_policy": "品牌内容政策", + "select_1": "--请选择--", + "select_flair": "--选择标识--", + "link": "链接", + "add_subreddit": "添加子版块", + "please_add_at_least_one_subreddit": "请至少添加一个子版块", + "add_community": "添加社区", + "select_post_type": "选择帖子类型...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "我们未能找到与您的 LinkedIn 页面关联的任何企业。", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "请关闭此对话框,创建新页面并再次添加新频道。", + "select_linkedin_page": "选择 Linkedin 页面:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "我们未能找到与所选页面关联的任何企业。", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "我们建议您连接所有页面和所有企业。", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "请关闭此对话框,删除您的集成并再次添加新频道。", + "select_instagram_account": "选择 Instagram 账号:", + "select_page": "选择页面:", + "generate_image_with_ai": "用 AI 生成图片", + "reconnect_channel": "重新连接频道", + "update_credentials": "更新凭证", + "additional_settings": "附加设置", + "change_bot": "更换机器人", + "move_add_to_customer": "移动/添加到客户", + "edit_time_slots": "编辑时间段", + "enable_channel": "启用频道", + "disable_channel": "禁用频道", + "add": "添加", + "short_post": "短帖子", + "long_post": "长帖子", + "a_thread_with_short_posts": "包含短帖的主题", + "a_thread_with_long_posts": "包含长帖的主题", + "personal_voice_i_am_happy_to_announce": "个人语气(“我很高兴地宣布”)", + "company_voice_we_are_happy_to_announce": "公司语气(“我们很高兴地宣布”)", + "generate": "生成", + "generate_posts": "生成帖子", + "purchase_a_life_time_pro_account_with_sol_199": "购买终身PRO账户,价格为SOL($199)。请注意,此购买不支持退款。", + "purchase_now": "立即购买", + "pay_today": "今日付款", + "we_are_sorry_to_see_you_go": "很遗憾看到你离开 :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "请简要告诉我们可以改进的地方好吗?", + "cancel_subscription": "取消订阅", + "plans": "套餐", + "monthly": "月付", + "yearly": "年付", + "reactivate_subscription": "重新激活订阅", + "update_payment_method_invoices_history": "更新支付方式 / 发票历史", + "cancel_subscription_1": "取消订阅", + "your_subscription_will_be_canceled_at": "你的订阅将在以下时间被取消", + "you_will_never_be_charged_again": "你将不会再被扣费", + "current_package": "当前套餐:", + "next_package": "下一个套餐:", + "claim": "领取", + "frequently_asked_questions": "常见问题", + "autopost": "自动发布", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "自动发布可以将您的RSS新内容自动发布到社交媒体", + "title": "标题", + "add_an_autopost": "添加自动发布", + "post_content": "发布内容", + "sign_up": "注册", + "or": "或", + "by_registering_you_agree_to_our": "注册即表示您同意我们的", + "and": "和", + "terms_of_service": "服务条款", + "privacy_policy": "隐私政策", + "create_account": "创建账户", + "already_have_an_account": "已有账户?", + "sign_in": "登录", + "sign_in_1": "登录", + "don_t_have_an_account": "还没有账户?", + "forgot_password": "忘记密码", + "forgot_password_1": "忘记密码", + "send_password_reset_email": "发送密码重置邮件", + "go_back_to_login": "返回登录", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "我们已向您的邮箱发送了一封包含重置密码链接的邮件。", + "change_password": "更改密码", + "we_successfully_reset_your_password_you_can_now_login_with_your": "我们已成功重置您的密码。您现在可以使用新密码登录。", + "click_here_to_go_back_to_login": "点击这里返回登录", + "activate_your_account": "激活您的账户", + "thank_you_for_registering": "感谢您的注册!", + "please_check_your_email_to_activate_your_account": "请检查您的邮箱以激活账户。", + "sign_in_with": "使用以下方式登录", + "continue_with_google": "使用 Google 继续", + "sign_in_with_github": "使用 GitHub 登录", + "continue_with_farcaster": "使用 Farcaster 继续", + "continue_with_your_wallet": "使用钱包继续", + "stars_per_day": "每日星标", + "media": "媒体", + "check_launch": "检查启动", + "load_your_github_repository_from_settings_to_see_analytics": "请在设置中加载您的 GitHub 仓库以查看分析数据", + "stars": "星标", + "processing_stars": "正在处理星标...", + "forks": "分支", + "registration_is_disabled": "注册已被禁用", + "login_instead": "请登录", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "请选择一个会话并开始聊天。", + "adding_channel_redirecting_you": "正在添加频道,正在为您跳转", + "could_not_add_provider": "无法添加提供者。", + "you_are_being_redirected_back": "您正在被重定向返回", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "我们遇到了一些问题,请尝试刷新页面", + "post_not_found": "未找到帖子", + "publication_date": "发布日期:", + "analytics": "分析", + "launches": "发布", + "plugs": "推荐", + "billing": "账单", + "affiliate": "联盟", + "monday": "星期一", + "tuesday": "星期二", + "wednesday": "星期三", + "thursday": "星期四", + "friday": "星期五", + "saturday": "星期六", + "sunday": "星期日", + "can_t_change_date_remove_post_from_publication": "无法更改日期,请先将帖子从发布中移除", + "predicted_github_trending_change": "预测 GitHub 趋势变化", + "duplicate_post": "重复帖子", + "preview_post": "预览帖子", + "post_statistics": "帖子统计", + "draft": "草稿", + "week_number": "第 {{number}} 周", + "top_title_edit_webhook": "编辑 webhook", + "top_title_add_webhook": "添加 webhook", + "top_title_oh_no": "哦不", + "top_title_auto_plug": "自动插件:{{title}}", + "top_title_edit_autopost": "编辑自动发布", + "top_title_add_autopost": "添加自动发布", + "top_title_send_a_new_offer": "发送新报价", + "top_title_media_library": "媒体库", + "top_title_add_signature": "添加签名", + "top_title_send_a_message_to": "发送消息给{{name}}", + "top_title_configure_provider": "配置服务商", + "top_title_add_member": "添加成员", + "top_title_change_bot_picture": "更改机器人头像", + "top_title_create_a_new_tag": "创建新标签", + "top_title_select_company": "选择公司", + "top_title_additional_settings": "附加设置", + "top_title_time_table_slots": "时间表时段", + "top_title_design_media": "设计媒体", + "top_title_edit_post": "编辑帖子", + "top_title_create_post": "创建帖子", + "top_title_move__add_to_customer": "移动/添加到客户", + "top_title_add_api_key_for": "为{{name}}添加API密钥", + "top_title_instance_url": "实例URL", + "top_title_custom_url": "自定义URL", + "top_title_add_channel": "添加频道", + "top_title_add_telegram": "添加Telegram", + "top_title_add_wrapcast": "添加Wrapcast", + "top_title_comments_for": "{{date}}的评论", + "top_title_edit_signature": "编辑签名", + "label_name": "姓名", + "label_url": "网址", + "label_title": "标题", + "label_subtitle": "副标题", + "label_email": "电子邮件", + "label_full_name": "全名", + "label_password": "密码", + "label_confirm_password": "确认密码", + "label_api_key": "API 密钥", + "label_instance_url": "实例网址", + "label_custom_url": "自定义网址", + "label_feedback": "反馈", + "label_bio": "个人简介", + "label_role": "角色", + "label_country": "国家", + "label_audience_size": "所有平台的受众规模", + "label_pick_time": "选择时间", + "label_nickname": "昵称", + "label_write_anything": "随便写点什么", + "label_output_format": "输出格式", + "label_add_pictures": "添加图片?", + "label_hour": "小时", + "label_minutes": "分钟", + "label_select_publication": "选择出版物", + "label_canonical_link": "规范链接", + "label_cover_picture": "封面图片", + "label_tags": "标签", + "label_topics": "话题", + "label_tags_maximum_4": "标签(最多4个)", + "label_attachments": "附件", + "label_type": "类型", + "label_thumbnail": "缩略图", + "label_who_can_see_this_video": "谁可以观看此视频?", + "label_content_posting_method": "内容发布方式", + "label_auto_add_music": "自动添加音乐", + "label_duet": "合拍", + "label_stitch": "拼接", + "label_comments": "评论", + "label_disclose_video_content": "公开视频内容", + "label_your_brand": "你的品牌", + "label_branded_content": "品牌内容", + "label_subreddit": "子版块", + "label_flair": "标记", + "label_media": "媒体", + "label_search_subreddit": "搜索子版块", + "label_delay": "延迟", + "label_post_type": "帖子类型", + "label_collaborators": "协作者(最多3人)- 账号不能为私密", + "label_community": "社区", + "label_search_community": "搜索社区", + "label_channel": "频道", + "label_search_channel": "搜索频道", + "label_select_channel": "选择频道", + "label_new_password": "新密码", + "label_repeat_password": "重复密码", + "label_platform": "平台", + "label_price_per_post": "每帖价格", + "label_integrations": "集成", + "label_code": "代码", + "label_should_sync_last_post": "是否同步当前最新帖子?", + "label_when_post": "我们应该何时发布?", + "label_autogenerate_content": "自动生成内容", + "label_generate_picture": "生成图片?", + "label_company": "公司", + "label_tag_color": "标签颜色", + "label_select_board": "选择看板", + "label_select_organization": "选择组织", + "label_auto_add_signature": "自动添加签名?", + "enable_color_picker": "启用取色器", + "cancel_the_color_picker": "取消取色器", + "no_content_yet": "暂无内容", + "write_your_reply": "写下你的帖子...", + "add_a_tag": "添加标签", + "add_to_calendar": "添加到日历", + "select_channels_from_circles": "从上方的圈子中选择频道", + "not_matching_order": "顺序不匹配", + "submit_for_order": "提交订单", + "schedule": "日程安排", + "update": "更新", + "attachments": "附件", + "tags": "标签", + "public_to_everyone": "对所有人公开", + "mutual_follow_friends": "互相关注的好友", + "follower_of_creator": "创作者的粉丝", + "self_only": "仅自己可见", + "post_content_directly_to_tiktok": "直接将内容发布到 TikTok", + "upload_content_to_tiktok_without_posting": "上传内容到 TikTok 但不发布", + "choose_upload_without_posting_description": "如果你希望在 TikTok 应用内审核和编辑内容后再发布,请选择“上传但不发布”。这样你可以使用 TikTok 内置的编辑工具,并在发布前进行最终调整。", + "faq_am_i_going_to_be_charged_by_postiz": "我会被 Postiz 收费吗?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "为确认信用卡信息,Postiz 会暂时预授权 2 美元并立即释放", + "faq_can_i_trust_postiz_gitroom": "我可以信任Postiz吗?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz自豪地开源!我们相信道德和透明的文化,这意味着Postiz将永远存在。您可以查看全部代码或将其用于个人项目。要查看开源仓库,<a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">请点击这里</a>。", + "faq_what_are_channels": "什么是频道?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz允许您在不同频道之间安排您的帖子。\n频道是一个发布平台,您可以在上面安排您的帖子。\n例如,您可以在X、Facebook、Instagram、TikTok、YouTube、Reddit、Linkedin、Dribbble、Threads和Pinterest上安排您的帖子。", + "faq_what_are_team_members": "什么是团队成员?", + "faq_if_you_have_a_team_with_multiple_members": "如果你有一个包含多名成员的团队,你可以邀请他们加入你的工作区,共同协作发布内容,并添加他们的个人频道。", + "faq_what_is_ai_auto_complete": "什么是 AI 自动补全?", + "faq_we_automate_chatgpt_to_help_you_write": "我们自动化集成了 ChatGPT,帮助你撰写社交帖子和文章。", + "enter_email": "请输入邮箱", + "are_you_sure": "你确定吗?", + "yes_delete_it": "是的,删除它!", + "no_cancel": "不,取消!", + "are_you_sure_you_want_to_delete": "你确定要删除{{name}}吗?", + "are_you_sure_you_want_to_delete_the_image": "你确定要删除这张图片吗?", + "are_you_sure_you_want_to_logout": "你确定要登出吗?", + "yes_logout": "是的,登出", + "are_you_sure_you_want_to_delete_this_slot": "你确定要删除这个槽位吗?", + "are_you_sure_you_want_to_delete_this_subreddit": "你确定要删除这个Subreddit吗?", + "are_you_sure_you_want_to_close_the_window": "你确定要关闭窗口吗?", + "yes_close": "是的,关闭", + "link_copied_to_clipboard": "链接已复制到剪贴板", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "你确定要关闭此弹窗吗?(所有数据将丢失)", + "yes_close_it": "是的,关闭它!", + "uploading_pictures": "正在上传图片...", + "agent_starting": "代理启动中", + "researching_your_content": "正在研究你的内容...", + "understanding_the_category": "正在理解类别...", + "finding_the_topic": "正在查找主题...", + "finding_popular_posts_to_match_with": "正在查找匹配的热门帖子...", + "generating_hook": "正在生成引子...", + "generating_content": "正在生成内容...", + "generating_pictures": "正在生成图片...", + "finding_time_to_post": "正在查找发布时间...", + "write_anything": "随便写点什么", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "你可以写任何你想写的内容,也可以添加链接,我们会为你做研究……", + "output_format": "输出格式", + "add_pictures": "添加图片?", + "7_days": "7天", + "30_days": "30天", + "90_days": "90天", + "start_7_days_free_trial": "开始7天免费试用", + "change_language": "切换语言", + "that_a_wrap": "本帖到此结束!\n\n如果你喜欢这个话题:\n\n1. 关注我 @{{username}},获取更多类似内容\n2. 转发下方推文,与更多人分享本帖\n", + "post_as_images_carousel": "以图片轮播的形式发布", + "save_set": "保存设置", + "separate_post": "将帖子拆分为多个帖子", + "label_who_can_reply_to_this_post": "谁可以回复此帖子?", + "delete_integration": "删除集成", + "start_writing_your_post": "开始写你的帖子以预览", + "billing_join_over": "已有超过", + "billing_entrepreneurs_count": "18,000+位创业者", + "billing_who_use": "正在使用", + "billing_postiz_grow_social": "Postiz来提升他们的社交影响力", + "billing_no_risk_trial": "100%无风险免费试用", + "billing_pay_nothing_7_days": "前7天完全免费", + "billing_cancel_anytime": "随时取消,无需担心", + "billing_choose_plan": "选择一个方案", + "billing_monthly": "按月", + "billing_yearly": "按年", + "billing_20_percent_off": "立减20%", + "billing_features": "功能", + "billing_channel": "频道", + "billing_channels": "频道", + "billing_unlimited": "无限", + "billing_posts_per_month": "每月发布数", + "billing_unlimited_team_members": "无限团队成员", + "billing_ai_auto_complete": "AI自动补全", + "billing_ai_copilots": "AI协作助手", + "billing_ai_autocomplete": "AI自动补全", + "billing_advanced_picture_editor": "高级图片编辑器", + "billing_ai_images_per_month": "每月AI图片生成数", + "billing_ai_videos_per_month": "每月AI视频数", + "billing_billing_address": "账单地址", + "billing_payment": "付款", + "billing_powered_by_stripe": "由Stripe提供支持", + "billing_your_7_day_trial_is": "您的7天试用期", + "billing_100_percent_free": "100%免费", + "billing_ending": "即将结束", + "billing_cancel_anytime_short": "随时取消。", + "billing_pay_0_start_trial": "今日支付$0 - 开始您的免费试用!", + "billing_pay_now": "立即支付", + "billing_per_month": "每月" } From e6f2d87943560fc927d339ff9ec6256f61c6e1a8 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:15:49 +0700 Subject: [PATCH 040/340] feat: extension --- apps/frontend/src/app/(extension)/layout.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/app/(extension)/layout.tsx b/apps/frontend/src/app/(extension)/layout.tsx index 844bdddd..89a192b4 100644 --- a/apps/frontend/src/app/(extension)/layout.tsx +++ b/apps/frontend/src/app/(extension)/layout.tsx @@ -21,12 +21,15 @@ export default async function AppLayout({ children }: { children: ReactNode }) { <head> <link rel="icon" href="/favicon.ico" sizes="any" /> </head> - <body className={clsx(jakartaSans.className, 'dark text-primary !bg-primary')}> + <body + className={clsx(jakartaSans.className, 'dark text-primary !bg-primary')} + > <VariableContextComponent language="en" storageProvider={ process.env.STORAGE_PROVIDER! as 'local' | 'cloudflare' } + stripeClient="" environment={process.env.NODE_ENV!} backendUrl={process.env.NEXT_PUBLIC_BACKEND_URL!} plontoKey={process.env.NEXT_PUBLIC_POLOTNO!} From 59d0df5b5cc06af449ee1788f828027db48cfb83 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:24:14 +0700 Subject: [PATCH 041/340] feat: package upgrade --- package.json | 10 +++++----- pnpm-lock.yaml | 47 +++++++++++++++++++++++------------------------ 2 files changed, 28 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 3e06e95a..b343d87a 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "@mastra/core": "^0.20.2", "@mastra/memory": "^0.15.6", "@mastra/pg": "^0.17.2", - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.22.0", "@nestjs/cli": "10.0.2", "@nestjs/common": "^10.0.2", "@nestjs/core": "^10.0.2", @@ -79,8 +79,8 @@ "@neynar/react": "^0.9.7", "@postiz/wallets": "^0.0.1", "@prisma/client": "6.5.0", - "@sentry/nestjs": "^10.25.0", - "@sentry/nextjs": "^10.25.0", + "@sentry/nestjs": "^10.26.0", + "@sentry/nextjs": "^10.26.0", "@sentry/profiling-node": "^10.25.0", "@sentry/react": "^10.25.0", "@solana/wallet-adapter-react": "^0.15.35", @@ -179,11 +179,11 @@ "music-metadata": "^7.14.0", "nestjs-command": "^3.1.4", "nestjs-real-ip": "^3.0.1", - "next": "^14.2.30", + "next": "^14.2.35", "next-plausible": "^3.12.0", "node-fetch": "^3.3.2", "node-telegram-bot-api": "^0.66.0", - "nodemailer": "^6.9.15", + "nodemailer": "^7.0.11", "nostr-tools": "^2.18.2", "openai": "^6.2.0", "p-limit": "^3.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 299304c1..48d5c868 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,7 +76,7 @@ importers: specifier: ^0.17.2 version: 0.17.8(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(pg-query-stream@4.10.3(pg@8.16.3)) '@modelcontextprotocol/sdk': - specifier: ^1.9.0 + specifier: ^1.22.0 version: 1.22.0(@cfworker/json-schema@4.1.1) '@nestjs/cli': specifier: 10.0.2 @@ -115,11 +115,11 @@ importers: specifier: 6.5.0 version: 6.5.0(prisma@6.5.0(typescript@5.5.4))(typescript@5.5.4) '@sentry/nestjs': - specifier: ^10.25.0 + specifier: ^10.26.0 version: 10.26.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) '@sentry/nextjs': - specifier: ^10.25.0 - version: 10.26.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + specifier: ^10.26.0 + version: 10.26.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) '@sentry/profiling-node': specifier: ^10.25.0 version: 10.26.0 @@ -415,11 +415,11 @@ importers: specifier: ^3.0.1 version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)) next: - specifier: ^14.2.30 - version: 14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + specifier: ^14.2.35 + version: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) next-plausible: specifier: ^3.12.0 - version: 3.12.5(next@14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -427,8 +427,8 @@ importers: specifier: ^0.66.0 version: 0.66.0(request@2.88.2) nodemailer: - specifier: ^6.9.15 - version: 6.10.1 + specifier: ^7.0.11 + version: 7.0.11 nostr-tools: specifier: ^2.18.2 version: 2.18.2(typescript@5.5.4) @@ -3876,8 +3876,8 @@ packages: '@next/env@14.2.16': resolution: {integrity: sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==} - '@next/env@14.2.33': - resolution: {integrity: sha512-CgVHNZ1fRIlxkLhIX22flAZI/HmpDaZ8vwyJ/B0SDPTBuLZ1PJ+DWMjCHhqnExfmSQzA/PbZi8OAc7PAq2w9IA==} + '@next/env@14.2.35': + resolution: {integrity: sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==} '@next/eslint-plugin-next@15.2.1': resolution: {integrity: sha512-6ppeToFd02z38SllzWxayLxjjNfzvc7Wm07gQOKSLjyASvKcXjNStZrLXMHuaWkhjqxe+cnhb2uzfWXm1VEj/Q==} @@ -13142,10 +13142,9 @@ packages: sass: optional: true - next@14.2.33: - resolution: {integrity: sha512-GiKHLsD00t4ACm1p00VgrI0rUFAC9cRDGReKyERlM57aeEZkOQGcZTpIbsGn0b562FTPJWmYfKwplfO9EaT6ng==} + next@14.2.35: + resolution: {integrity: sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==} engines: {node: '>=18.17.0'} - deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 @@ -13235,8 +13234,8 @@ packages: resolution: {integrity: sha512-s4Hrg5q+VPl4/tJVG++pImxF6eb8tNJNj4KnDqAOKL6zGU34lo9RXmyAN158njwGN+v8hdNf8s9fWIYW9hPb5A==} engines: {node: '>=0.12'} - nodemailer@6.10.1: - resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==} + nodemailer@7.0.11: + resolution: {integrity: sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==} engines: {node: '>=6.0.0'} nodemon@3.1.11: @@ -21041,7 +21040,7 @@ snapshots: '@next/env@14.2.16': {} - '@next/env@14.2.33': {} + '@next/env@14.2.35': {} '@next/eslint-plugin-next@15.2.1': dependencies: @@ -23632,7 +23631,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/nextjs@10.26.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/nextjs@10.26.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 @@ -23645,7 +23644,7 @@ snapshots: '@sentry/react': 10.26.0(react@18.3.1) '@sentry/vercel-edge': 10.26.0 '@sentry/webpack-plugin': 4.6.0(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) - next: 14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) resolve: 1.22.8 rollup: 4.53.3 stacktrace-parser: 0.1.11 @@ -33356,9 +33355,9 @@ snapshots: netmask@2.0.2: {} - next-plausible@3.12.5(next@14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-plausible@3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -33390,9 +33389,9 @@ snapshots: - '@babel/core' - babel-plugin-macros - next@14.2.33(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1): + next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1): dependencies: - '@next/env': 14.2.33 + '@next/env': 14.2.35 '@swc/helpers': 0.5.5 busboy: 1.6.0 caniuse-lite: 1.0.30001756 @@ -33497,7 +33496,7 @@ snapshots: - request - supports-color - nodemailer@6.10.1: {} + nodemailer@7.0.11: {} nodemon@3.1.11: dependencies: From 1114bbd8df71fa72a2f03f607f842aecd2225d77 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:29:18 +0700 Subject: [PATCH 042/340] feat: old layout remove --- .../src/components/layout/layout.settings.tsx | 256 ------------------ 1 file changed, 256 deletions(-) delete mode 100644 apps/frontend/src/components/layout/layout.settings.tsx diff --git a/apps/frontend/src/components/layout/layout.settings.tsx b/apps/frontend/src/components/layout/layout.settings.tsx deleted file mode 100644 index a2a3c814..00000000 --- a/apps/frontend/src/components/layout/layout.settings.tsx +++ /dev/null @@ -1,256 +0,0 @@ -'use client'; - -import { ReactNode, useCallback, useEffect } from 'react'; -import { Title } from '@gitroom/frontend/components/layout/title'; -import { ContextWrapper } from '@gitroom/frontend/components/layout/user.context'; -import { TopMenu } from '@gitroom/frontend/components/layout/top.menu'; -import { MantineWrapper } from '@gitroom/react/helpers/mantine.wrapper'; -import { ToolTip } from '@gitroom/frontend/components/layout/top.tip'; -import { ShowMediaBoxModal } from '@gitroom/frontend/components/media/media.component'; -import Image from 'next/image'; -import { Toaster, useToaster } from '@gitroom/react/toaster/toaster'; -import { ShowPostSelector } from '@gitroom/frontend/components/post-url-selector/post.url.selector'; -import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector'; -import NotificationComponent from '@gitroom/frontend/components/notifications/notification.component'; -import Link from 'next/link'; -import useSWR from 'swr'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import utc from 'dayjs/plugin/utc'; -import weekOfYear from 'dayjs/plugin/weekOfYear'; -import isoWeek from 'dayjs/plugin/isoWeek'; -import isBetween from 'dayjs/plugin/isBetween'; -import { ShowLinkedinCompany } from '@gitroom/frontend/components/launches/helpers/linkedin.component'; -import { SettingsComponent } from '@gitroom/frontend/components/layout/settings.component'; -import { Onboarding } from '@gitroom/frontend/components/onboarding/onboarding'; -import { Support } from '@gitroom/frontend/components/layout/support'; -import { ContinueProvider } from '@gitroom/frontend/components/layout/continue.provider'; -import { CopilotKit } from '@copilotkit/react-core'; -import { Impersonate } from '@gitroom/frontend/components/layout/impersonate'; -import clsx from 'clsx'; -import { BillingComponent } from '@gitroom/frontend/components/billing/billing.component'; -import dynamic from 'next/dynamic'; -import { NewSubscription } from '@gitroom/frontend/components/layout/new.subscription'; -import { useVariables } from '@gitroom/react/helpers/variable.context'; -const ModeComponent = dynamic( - () => import('@gitroom/frontend/components/layout/mode.component'), - { - ssr: false, - } -); -import { extend } from 'dayjs'; -import { useSearchParams } from 'next/navigation'; -import { CheckPayment } from '@gitroom/frontend/components/layout/check.payment'; -import { ChromeExtensionComponent } from '@gitroom/frontend/components/layout/chrome.extension.component'; -import { LanguageComponent } from '@gitroom/frontend/components/layout/language.component'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import i18next from '@gitroom/react/translation/i18next'; -import { MediaSettingsLayout } from '@gitroom/frontend/components/launches/helpers/media.settings.component'; -extend(utc); -extend(weekOfYear); -extend(isoWeek); -extend(isBetween); -export const LayoutSettings = ({ children }: { children: ReactNode }) => { - const fetch = useFetch(); - const t = useT(); - - const { isGeneral } = useVariables(); - const { backendUrl, billingEnabled } = useVariables(); - const searchParams = useSearchParams(); - const load = useCallback(async (path: string) => { - return await (await fetch(path)).json(); - }, []); - const { data: user, mutate } = useSWR('/user/self', load, { - revalidateOnFocus: false, - revalidateOnReconnect: false, - revalidateIfStale: false, - refreshWhenOffline: false, - refreshWhenHidden: false, - }); - if (!user) return null; - return ( - <ContextWrapper user={user}> - <CopilotKit - credentials="include" - runtimeUrl={backendUrl + '/copilot/chat'} - showDevConsole={false} - > - <MantineWrapper> - {user.tier === 'FREE' && searchParams.get('check') && ( - <CheckPayment check={searchParams.get('check')!} mutate={mutate} /> - )} - <ToolTip /> - <ShowMediaBoxModal /> - <ShowLinkedinCompany /> - <MediaSettingsLayout /> - <Toaster /> - <ShowPostSelector /> - <NewSubscription /> - <Support /> - <ContinueProvider /> - <div className="min-h-[100vh] w-full max-w-[1440px] mx-auto bg-primary px-6 text-textColor flex flex-col"> - {user?.admin && <Impersonate />} - <nav className="flex items-center justify-between"> - <Link - href="/" - className="text-2xl flex items-center gap-[10px] text-textColor order-1" - > - <div className="min-w-[55px]"> - <Image - src={isGeneral ? '/postiz.svg' : '/logo.svg'} - width={55} - height={53} - alt="Logo" - /> - </div> - <div - className={clsx(!isGeneral ? 'mt-[12px]' : 'min-w-[80px]')} - > - {isGeneral ? ( - <svg - width="80" - height="75" - viewBox="0 0 366 167" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M24.9659 30.4263V43.3825C26.9237 41.3095 29.3998 39.582 32.3941 38.2C35.3885 36.7028 39.0162 35.9543 43.2774 35.9543C47.1931 35.9543 50.8784 36.7028 54.3334 38.2C57.9036 39.6972 61.0131 42.1157 63.6619 45.4555C66.4259 48.6802 68.6141 52.9989 70.2264 58.4118C71.8387 63.8246 72.6449 70.3891 72.6449 78.1053C72.6449 83.6333 72.1266 89.1613 71.0902 94.6893C70.1688 100.217 68.4989 105.169 66.0804 109.546C63.6619 113.922 60.3796 117.492 56.2336 120.256C52.2028 122.905 47.1355 124.23 41.0316 124.23C36.6553 124.23 33.2003 123.654 30.6666 122.502C28.1329 121.235 26.2327 119.796 24.9659 118.183V160.162L0.0898438 166.381V30.4263H24.9659ZM32.7396 109.2C35.734 109.2 38.2676 108.221 40.3406 106.264C42.4136 104.191 44.026 101.542 45.1776 98.3171C46.4445 95.0924 47.3082 91.5222 47.7689 87.6066C48.3447 83.5757 48.6326 79.6025 48.6326 75.6868C48.6326 69.3526 48.0568 64.3429 46.9051 60.6575C45.8686 56.9722 44.6018 54.2658 43.1046 52.5383C41.6075 50.6956 40.1103 49.5439 38.6131 49.0833C37.2311 48.6226 36.137 48.3923 35.3309 48.3923C33.2579 48.3923 31.2425 49.1409 29.2846 50.638C27.3268 52.02 25.8872 54.1506 24.9659 57.0298V105.227C25.5417 106.148 26.463 107.07 27.7299 107.991C28.9967 108.797 30.6666 109.2 32.7396 109.2Z" - fill="currentColor" - /> - <path - d="M188.176 31.4627C191.055 42.5188 193.588 51.5593 195.777 58.5845C197.965 65.4945 199.807 71.3105 201.305 76.0323C202.917 80.7541 204.126 84.9001 204.932 88.4703C205.854 92.0405 206.314 96.0137 206.314 100.39C208.272 99.1232 210.172 97.7988 212.015 96.4168C213.858 94.9196 215.413 93.5376 216.679 92.2708H223.935C220.825 96.9926 217.543 100.908 214.088 104.018C210.633 107.012 207.293 109.661 204.069 111.964C201.996 116.456 198.829 119.623 194.567 121.466C190.306 123.308 185.872 124.23 181.266 124.23C176.083 124.23 171.649 123.539 167.964 122.157C164.279 120.659 161.227 118.702 158.808 116.283C156.505 113.749 154.777 110.87 153.626 107.646C152.474 104.421 151.898 101.023 151.898 97.4533C151.898 93.5376 152.819 90.4857 154.662 88.2975C156.62 85.9942 158.866 84.8426 161.399 84.8426C168.424 84.8426 171.937 87.6641 171.937 93.3073C171.937 95.15 171.304 96.7047 170.037 97.9716C168.77 99.2384 167.158 99.8718 165.2 99.8718C164.278 99.8718 163.3 99.7566 162.263 99.5263C161.342 99.1808 160.593 98.5474 160.017 97.6261C160.939 101.657 162.436 104.824 164.509 107.127C166.697 109.431 169.461 110.582 172.801 110.582C175.68 110.582 177.811 109.891 179.193 108.509C180.575 107.012 181.266 104.478 181.266 100.908C181.266 97.1078 180.92 93.7104 180.229 90.7161C179.653 87.6066 178.732 84.2091 177.465 80.5238C176.198 76.8385 174.644 72.4621 172.801 67.3948C170.958 62.2123 168.885 55.5326 166.582 47.3558C160.823 59.6786 153.222 67.5675 143.779 71.0225C143.779 71.9439 143.779 72.8652 143.779 73.7865C143.894 74.5927 143.952 75.4565 143.952 76.3778C143.952 83.0575 143.376 89.334 142.224 95.2076C141.072 100.966 139.115 106.033 136.351 110.41C133.702 114.671 130.247 118.068 125.986 120.602C121.724 123.02 116.484 124.23 110.265 124.23C106.004 124.23 101.916 123.596 98 122.329C94.1995 120.947 90.8021 118.759 87.8078 115.765C84.8134 112.655 82.3949 108.624 80.5523 103.672C78.8248 98.605 77.961 92.4436 77.961 85.188C77.961 80.2359 78.4793 74.9382 79.5158 69.295C80.5523 63.5367 82.4525 58.1814 85.2165 53.2293C87.9805 48.2771 91.7234 44.1887 96.4453 40.964C101.282 37.6242 107.444 35.9543 114.93 35.9543C122.646 35.9543 128.807 38.0273 133.414 42.1733C138.136 46.3193 141.303 52.9989 142.915 62.2123C146.946 61.2909 150.574 58.5269 153.798 53.9203C157.138 49.1984 160.305 42.8643 163.3 34.9177L188.176 31.4627ZM115.102 107.991C117.521 107.991 119.594 107.185 121.321 105.573C123.164 103.845 124.661 101.542 125.813 98.6626C126.964 95.6682 127.771 92.1556 128.231 88.1248C128.807 84.094 129.095 79.7176 129.095 74.9958V72.75C124.488 71.7135 122.185 68.3161 122.185 62.5578C122.185 58.8724 123.682 56.4539 126.677 55.3023C125.41 51.6169 123.855 49.1984 122.012 48.0468C120.285 46.8951 118.788 46.3193 117.521 46.3193C114.987 46.3193 112.799 47.5285 110.956 49.947C109.229 52.2504 107.789 55.2447 106.638 58.93C105.486 62.5002 104.622 66.4734 104.046 70.8498C103.586 75.2261 103.355 79.4297 103.355 83.4605C103.355 88.6431 103.701 92.8466 104.392 96.0713C105.198 99.296 106.177 101.772 107.329 103.5C108.48 105.227 109.747 106.436 111.129 107.127C112.511 107.703 113.835 107.991 115.102 107.991Z" - fill="currentColor" - /> - <path - d="M239.554 9.52348V36.818H250.092V43.728H239.554V95.5531C239.554 100.39 240.187 103.615 241.454 105.227C242.836 106.724 245.197 107.473 248.537 107.473C251.877 107.473 254.641 106.033 256.829 103.154C259.132 100.275 260.457 96.6471 260.802 92.2708H268.058C267.136 99.296 265.524 104.939 263.221 109.2C260.917 113.346 258.326 116.571 255.447 118.874C252.568 121.062 249.631 122.502 246.637 123.193C243.642 123.884 240.993 124.23 238.69 124.23C229.822 124.23 223.603 121.811 220.033 116.974C216.463 112.022 214.678 105.515 214.678 97.4533V43.728H209.15V36.818H214.678V12.9785L239.554 9.52348Z" - fill="currentColor" - /> - <path - d="M258.833 13.8422C258.833 10.0417 260.158 6.81706 262.806 4.16823C265.455 1.40422 268.68 0.0222168 272.48 0.0222168C276.281 0.0222168 279.506 1.40422 282.154 4.16823C284.918 6.81706 286.3 10.0417 286.3 13.8422C286.3 17.6427 284.918 20.8674 282.154 23.5162C279.506 26.1651 276.281 27.4895 272.48 27.4895C268.68 27.4895 265.455 26.1651 262.806 23.5162C260.158 20.8674 258.833 17.6427 258.833 13.8422ZM285.609 36.818V95.5531C285.609 100.39 286.243 103.615 287.51 105.227C288.892 106.724 291.253 107.473 294.592 107.473C296.09 107.473 297.184 107.358 297.875 107.127C298.681 106.897 299.372 106.667 299.948 106.436C300.063 107.012 300.12 107.588 300.12 108.164C300.12 108.74 300.12 109.315 300.12 109.891C300.12 112.77 299.602 115.131 298.566 116.974C297.644 118.817 296.377 120.314 294.765 121.466C293.268 122.502 291.598 123.193 289.755 123.539C288.028 123.999 286.358 124.23 284.746 124.23C275.878 124.23 269.659 121.811 266.089 116.974C262.518 112.022 260.733 105.515 260.733 97.4533V36.818H285.609ZM351.773 107.473C350.391 107.358 349.354 106.897 348.663 106.091C347.972 105.169 347.627 104.133 347.627 102.981C347.627 101.484 348.26 100.045 349.527 98.6626C350.794 97.1654 352.867 96.4168 355.746 96.4168C358.971 96.4168 361.389 97.5109 363.001 99.6991C364.614 101.772 365.42 104.248 365.42 107.127C365.42 108.97 365.074 110.87 364.383 112.828C363.692 114.671 362.598 116.398 361.101 118.011C359.604 119.508 357.761 120.775 355.573 121.811C353.385 122.732 350.851 123.193 347.972 123.193H300.293L334.152 46.1465H321.369C318.835 46.1465 316.704 46.3193 314.977 46.6648C313.365 46.8951 312.558 47.5285 312.558 48.565C312.558 49.0257 312.674 49.256 312.904 49.256C313.249 49.256 313.595 49.3712 313.94 49.6015C314.401 49.8318 314.747 50.2925 314.977 50.9835C315.322 51.6745 315.495 52.8838 315.495 54.6113C315.495 57.1449 314.689 58.9876 313.077 60.1393C311.579 61.2909 309.852 61.8668 307.894 61.8668C305.591 61.8668 303.345 61.1182 301.157 59.621C299.084 58.0087 298.047 55.5902 298.047 52.3655C298.047 50.638 298.393 48.9105 299.084 47.183C299.775 45.3403 300.811 43.6704 302.193 42.1733C303.575 40.5609 305.303 39.2941 307.376 38.3728C309.449 37.3363 311.867 36.818 314.631 36.818H362.138L329.142 109.891C329.833 109.891 330.812 109.949 332.079 110.064C333.346 110.179 334.67 110.294 336.052 110.41C337.55 110.525 338.989 110.64 340.371 110.755C341.868 110.87 343.193 110.928 344.344 110.928C346.417 110.928 348.145 110.697 349.527 110.237C351.024 109.776 351.773 108.855 351.773 107.473Z" - fill="currentColor" - /> - </svg> - ) : ( - 'Gitroom' - )} - </div> - </Link> - {user?.orgId && - (user.tier !== 'FREE' || !isGeneral || !billingEnabled) ? ( - <TopMenu /> - ) : ( - <></> - )} - <div - id="systray-buttons" - className="flex items-center justify-self-end gap-[8px] order-2 md:order-3" - > - <LanguageComponent /> - <ChromeExtensionComponent /> - <ModeComponent /> - <SettingsComponent /> - <NotificationComponent /> - <OrganizationSelector /> - </div> - </nav> - <div className="flex-1 flex"> - <div className="flex-1 rounded-3xl px-0 py-[17px] flex flex-col"> - {user.tier === 'FREE' && isGeneral && billingEnabled ? ( - <> - <div className="text-center mb-[20px] text-xl [@media(max-width:1024px)]:text-xl"> - <h1 className="text-3xl [@media(max-width:1024px)]:text-xl"> - {t( - 'join_10000_entrepreneurs_who_use_postiz', - 'Join 10,000+ Entrepreneurs Who Use Postiz' - )} - <br /> - {t( - 'to_manage_all_your_social_media_channels', - 'To Manage All Your Social Media Channels' - )} - </h1> - <br /> - {user?.allowTrial && ( - <div className="table mx-auto"> - <div className="flex gap-[5px] items-center"> - <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - > - <path - d="M16.2806 9.21937C16.3504 9.28903 16.4057 9.37175 16.4434 9.46279C16.4812 9.55384 16.5006 9.65144 16.5006 9.75C16.5006 9.84856 16.4812 9.94616 16.4434 10.0372C16.4057 10.1283 16.3504 10.211 16.2806 10.2806L11.0306 15.5306C10.961 15.6004 10.8783 15.6557 10.7872 15.6934C10.6962 15.7312 10.5986 15.7506 10.5 15.7506C10.4014 15.7506 10.3038 15.7312 10.2128 15.6934C10.1218 15.6557 10.039 15.6004 9.96938 15.5306L7.71938 13.2806C7.57865 13.1399 7.49959 12.949 7.49959 12.75C7.49959 12.551 7.57865 12.3601 7.71938 12.2194C7.86011 12.0786 8.05098 11.9996 8.25 11.9996C8.44903 11.9996 8.6399 12.0786 8.78063 12.2194L10.5 13.9397L15.2194 9.21937C15.289 9.14964 15.3718 9.09432 15.4628 9.05658C15.5538 9.01884 15.6514 8.99941 15.75 8.99941C15.8486 8.99941 15.9462 9.01884 16.0372 9.05658C16.1283 9.09432 16.211 9.14964 16.2806 9.21937ZM21.75 12C21.75 13.9284 21.1782 15.8134 20.1068 17.4168C19.0355 19.0202 17.5127 20.2699 15.7312 21.0078C13.9496 21.7458 11.9892 21.9389 10.0979 21.5627C8.20656 21.1865 6.46928 20.2579 5.10571 18.8943C3.74215 17.5307 2.81355 15.7934 2.43735 13.9021C2.06114 12.0108 2.25422 10.0504 2.99218 8.26884C3.73013 6.48726 4.97982 4.96451 6.58319 3.89317C8.18657 2.82183 10.0716 2.25 12 2.25C14.585 2.25273 17.0634 3.28084 18.8913 5.10872C20.7192 6.93661 21.7473 9.41498 21.75 12ZM20.25 12C20.25 10.3683 19.7661 8.77325 18.8596 7.41655C17.9531 6.05984 16.6646 5.00242 15.1571 4.37799C13.6497 3.75357 11.9909 3.59019 10.3905 3.90852C8.79017 4.22685 7.32016 5.01259 6.16637 6.16637C5.01259 7.32015 4.22685 8.79016 3.90853 10.3905C3.5902 11.9908 3.75358 13.6496 4.378 15.1571C5.00242 16.6646 6.05984 17.9531 7.41655 18.8596C8.77326 19.7661 10.3683 20.25 12 20.25C14.1873 20.2475 16.2843 19.3775 17.8309 17.8309C19.3775 16.2843 20.2475 14.1873 20.25 12Z" - fill="#06ff00" - /> - </svg> - </div> - <div> - {t('100_no_risk_trial', '100% no-risk trial')} - </div> - </div> - <div className="flex gap-[5px] items-center"> - <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - > - <path - d="M16.2806 9.21937C16.3504 9.28903 16.4057 9.37175 16.4434 9.46279C16.4812 9.55384 16.5006 9.65144 16.5006 9.75C16.5006 9.84856 16.4812 9.94616 16.4434 10.0372C16.4057 10.1283 16.3504 10.211 16.2806 10.2806L11.0306 15.5306C10.961 15.6004 10.8783 15.6557 10.7872 15.6934C10.6962 15.7312 10.5986 15.7506 10.5 15.7506C10.4014 15.7506 10.3038 15.7312 10.2128 15.6934C10.1218 15.6557 10.039 15.6004 9.96938 15.5306L7.71938 13.2806C7.57865 13.1399 7.49959 12.949 7.49959 12.75C7.49959 12.551 7.57865 12.3601 7.71938 12.2194C7.86011 12.0786 8.05098 11.9996 8.25 11.9996C8.44903 11.9996 8.6399 12.0786 8.78063 12.2194L10.5 13.9397L15.2194 9.21937C15.289 9.14964 15.3718 9.09432 15.4628 9.05658C15.5538 9.01884 15.6514 8.99941 15.75 8.99941C15.8486 8.99941 15.9462 9.01884 16.0372 9.05658C16.1283 9.09432 16.211 9.14964 16.2806 9.21937ZM21.75 12C21.75 13.9284 21.1782 15.8134 20.1068 17.4168C19.0355 19.0202 17.5127 20.2699 15.7312 21.0078C13.9496 21.7458 11.9892 21.9389 10.0979 21.5627C8.20656 21.1865 6.46928 20.2579 5.10571 18.8943C3.74215 17.5307 2.81355 15.7934 2.43735 13.9021C2.06114 12.0108 2.25422 10.0504 2.99218 8.26884C3.73013 6.48726 4.97982 4.96451 6.58319 3.89317C8.18657 2.82183 10.0716 2.25 12 2.25C14.585 2.25273 17.0634 3.28084 18.8913 5.10872C20.7192 6.93661 21.7473 9.41498 21.75 12ZM20.25 12C20.25 10.3683 19.7661 8.77325 18.8596 7.41655C17.9531 6.05984 16.6646 5.00242 15.1571 4.37799C13.6497 3.75357 11.9909 3.59019 10.3905 3.90852C8.79017 4.22685 7.32016 5.01259 6.16637 6.16637C5.01259 7.32015 4.22685 8.79016 3.90853 10.3905C3.5902 11.9908 3.75358 13.6496 4.378 15.1571C5.00242 16.6646 6.05984 17.9531 7.41655 18.8596C8.77326 19.7661 10.3683 20.25 12 20.25C14.1873 20.2475 16.2843 19.3775 17.8309 17.8309C19.3775 16.2843 20.2475 14.1873 20.25 12Z" - fill="#06ff00" - /> - </svg> - </div> - <div> - {t( - 'pay_nothing_for_the_first_7_days', - 'Pay nothing for the first 7 days' - )} - </div> - </div> - <div className="flex gap-[5px] items-center"> - <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - > - <path - d="M16.2806 9.21937C16.3504 9.28903 16.4057 9.37175 16.4434 9.46279C16.4812 9.55384 16.5006 9.65144 16.5006 9.75C16.5006 9.84856 16.4812 9.94616 16.4434 10.0372C16.4057 10.1283 16.3504 10.211 16.2806 10.2806L11.0306 15.5306C10.961 15.6004 10.8783 15.6557 10.7872 15.6934C10.6962 15.7312 10.5986 15.7506 10.5 15.7506C10.4014 15.7506 10.3038 15.7312 10.2128 15.6934C10.1218 15.6557 10.039 15.6004 9.96938 15.5306L7.71938 13.2806C7.57865 13.1399 7.49959 12.949 7.49959 12.75C7.49959 12.551 7.57865 12.3601 7.71938 12.2194C7.86011 12.0786 8.05098 11.9996 8.25 11.9996C8.44903 11.9996 8.6399 12.0786 8.78063 12.2194L10.5 13.9397L15.2194 9.21937C15.289 9.14964 15.3718 9.09432 15.4628 9.05658C15.5538 9.01884 15.6514 8.99941 15.75 8.99941C15.8486 8.99941 15.9462 9.01884 16.0372 9.05658C16.1283 9.09432 16.211 9.14964 16.2806 9.21937ZM21.75 12C21.75 13.9284 21.1782 15.8134 20.1068 17.4168C19.0355 19.0202 17.5127 20.2699 15.7312 21.0078C13.9496 21.7458 11.9892 21.9389 10.0979 21.5627C8.20656 21.1865 6.46928 20.2579 5.10571 18.8943C3.74215 17.5307 2.81355 15.7934 2.43735 13.9021C2.06114 12.0108 2.25422 10.0504 2.99218 8.26884C3.73013 6.48726 4.97982 4.96451 6.58319 3.89317C8.18657 2.82183 10.0716 2.25 12 2.25C14.585 2.25273 17.0634 3.28084 18.8913 5.10872C20.7192 6.93661 21.7473 9.41498 21.75 12ZM20.25 12C20.25 10.3683 19.7661 8.77325 18.8596 7.41655C17.9531 6.05984 16.6646 5.00242 15.1571 4.37799C13.6497 3.75357 11.9909 3.59019 10.3905 3.90852C8.79017 4.22685 7.32016 5.01259 6.16637 6.16637C5.01259 7.32015 4.22685 8.79016 3.90853 10.3905C3.5902 11.9908 3.75358 13.6496 4.378 15.1571C5.00242 16.6646 6.05984 17.9531 7.41655 18.8596C8.77326 19.7661 10.3683 20.25 12 20.25C14.1873 20.2475 16.2843 19.3775 17.8309 17.8309C19.3775 16.2843 20.2475 14.1873 20.25 12Z" - fill="#06ff00" - /> - </svg> - </div> - <div> - {t( - 'cancel_anytime_hassle_free', - 'Cancel anytime, hassle-free' - )} - </div> - </div> - </div> - )} - </div> - <BillingComponent /> - </> - ) : ( - <> - <Title /> - <div className="flex flex-1 flex-col">{children}</div> - </> - )} - </div> - </div> - </div> - </MantineWrapper> - </CopilotKit> - </ContextWrapper> - ); -}; From 9c2abfb47f0b3772d8a359ba7ce8185dd558626e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:36:23 +0700 Subject: [PATCH 043/340] feat: nextjs upgrade --- apps/backend/src/api/routes/agencies.controller.ts | 2 +- apps/cron/src/main.ts | 4 ++-- apps/workers/src/main.ts | 4 ++-- package.json | 2 +- pnpm-lock.yaml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/backend/src/api/routes/agencies.controller.ts b/apps/backend/src/api/routes/agencies.controller.ts index 5eb98c60..e2849f96 100644 --- a/apps/backend/src/api/routes/agencies.controller.ts +++ b/apps/backend/src/api/routes/agencies.controller.ts @@ -10,7 +10,7 @@ import { CreateAgencyDto } from '@gitroom/nestjs-libraries/dtos/agencies/create. export class AgenciesController { constructor(private _agenciesService: AgenciesService) {} @Get('/') - async getAgencyByUsers(@GetUserFromRequest() user: User) { + async getAgencyByUser(@GetUserFromRequest() user: User) { return (await this._agenciesService.getAgencyByUser(user)) || {}; } diff --git a/apps/cron/src/main.ts b/apps/cron/src/main.ts index 3d3ece5b..cc2684f9 100644 --- a/apps/cron/src/main.ts +++ b/apps/cron/src/main.ts @@ -4,9 +4,9 @@ initializeSentry('cron'); import { NestFactory } from '@nestjs/core'; import { CronModule } from './cron.module'; -async function start() { +async function bootstrap() { // some comment again await NestFactory.createApplicationContext(CronModule); } -start(); +bootstrap(); diff --git a/apps/workers/src/main.ts b/apps/workers/src/main.ts index 62173f34..ade2eff0 100644 --- a/apps/workers/src/main.ts +++ b/apps/workers/src/main.ts @@ -8,7 +8,7 @@ import { BullMqServer } from '@gitroom/nestjs-libraries/bull-mq-transport-new/st import { AppModule } from './app/app.module'; -async function start() { +async function bootstrap() { process.env.IS_WORKER = 'true'; // some comment again @@ -22,4 +22,4 @@ async function start() { await app.listen(); } -start(); +bootstrap(); diff --git a/package.json b/package.json index b343d87a..13dd99f1 100644 --- a/package.json +++ b/package.json @@ -179,7 +179,7 @@ "music-metadata": "^7.14.0", "nestjs-command": "^3.1.4", "nestjs-real-ip": "^3.0.1", - "next": "^14.2.35", + "next": "14.2.35", "next-plausible": "^3.12.0", "node-fetch": "^3.3.2", "node-telegram-bot-api": "^0.66.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48d5c868..f698af4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -415,7 +415,7 @@ importers: specifier: ^3.0.1 version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)) next: - specifier: ^14.2.35 + specifier: 14.2.35 version: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) next-plausible: specifier: ^3.12.0 From 3e04076d5db9474bdfc9df02c8842a9998766ebd Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:42:40 +0700 Subject: [PATCH 044/340] feat: check payment --- .../src/components/layout/check.payment.tsx | 11 + .../new-layout/layout.component.tsx | 2 +- pnpm-lock.yaml | 6441 +++++++++-------- 3 files changed, 3313 insertions(+), 3141 deletions(-) diff --git a/apps/frontend/src/components/layout/check.payment.tsx b/apps/frontend/src/components/layout/check.payment.tsx index 318c6345..62917317 100644 --- a/apps/frontend/src/components/layout/check.payment.tsx +++ b/apps/frontend/src/components/layout/check.payment.tsx @@ -8,6 +8,17 @@ export const CheckPayment: FC<{ check: string; mutate: () => void; children: ReactNode; +}> = (props) => { + if (!props.check) { + return <>{props.children}</>; + } + return <CheckPaymentInner {...props} />; +}; + +export const CheckPaymentInner: FC<{ + check: string; + mutate: () => void; + children: ReactNode; }> = (props) => { const [showLoader, setShowLoader] = useState(true); const fetch = useFetch(); diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index cdaecc70..ea51e2e2 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -77,7 +77,7 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => { <MantineWrapper> <ToolTip /> <Toaster /> - <CheckPayment check={searchParams.get('check')!} mutate={mutate}> + <CheckPayment check={searchParams.get('check') || ''} mutate={mutate}> <ShowMediaBoxModal /> <ShowLinkedinCompany /> <MediaSettingsLayout /> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f698af4b..3f28c958 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,22 +11,22 @@ importers: dependencies: '@ag-ui/mastra': specifier: 0.2.0 - version: 0.2.0(@ag-ui/client@0.0.41)(@ag-ui/core@0.0.41)(@copilotkit/runtime@1.10.6(6855d89fee41a5dd20a0f9576445cd9f))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) + version: 0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(7d9d10c1aa1e359346c98e211cce0cbe))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@ai-sdk/openai': specifier: ^2.0.52 - version: 2.0.68(zod@3.25.76) + version: 2.0.88(zod@3.25.76) '@atproto/api': specifier: ^0.15.15 version: 0.15.27 '@aws-sdk/client-s3': specifier: ^3.787.0 - version: 3.934.0 + version: 3.956.0 '@aws-sdk/s3-request-presigner': specifier: ^3.787.0 - version: 3.934.0 + version: 3.956.0 '@casl/ability': specifier: ^6.5.0 - version: 6.7.3 + version: 6.7.5 '@copilotkit/react-core': specifier: 1.10.6 version: 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -38,22 +38,22 @@ importers: version: 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@copilotkit/runtime': specifier: 1.10.6 - version: 1.10.6(6855d89fee41a5dd20a0f9576445cd9f) + version: 1.10.6(7d9d10c1aa1e359346c98e211cce0cbe) '@hookform/resolvers': specifier: ^3.3.4 - version: 3.10.0(react-hook-form@7.66.1(react@18.3.1)) + version: 3.10.0(react-hook-form@7.69.0(react@18.3.1)) '@langchain/community': specifier: ^0.3.40 - version: 0.3.58(23f20e966d4bb4b0ba23179429728315) + version: 0.3.58(d743da2b976a18eaa6701515aaffafa9) '@langchain/core': specifier: ^0.3.44 - version: 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + version: 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) '@langchain/langgraph': specifier: ^0.2.63 - version: 0.2.74(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.0(zod@3.25.76)) + version: 0.2.74(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.0(zod@3.25.76)) '@langchain/openai': specifier: ^0.5.5 - version: 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@mantine/core': specifier: ^5.10.5 version: 5.10.5(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@mantine/hooks@5.10.5(react@18.3.1))(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -71,67 +71,67 @@ importers: version: 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/memory': specifier: ^0.15.6 - version: 0.15.11(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(react@18.3.1)(zod@3.25.76) + version: 0.15.13(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(react@18.3.1)(zod@3.25.76) '@mastra/pg': specifier: ^0.17.2 - version: 0.17.8(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(pg-query-stream@4.10.3(pg@8.16.3)) + version: 0.17.10(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(pg-query-stream@4.10.3(pg@8.16.3)) '@modelcontextprotocol/sdk': specifier: ^1.22.0 - version: 1.22.0(@cfworker/json-schema@4.1.1) + version: 1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76) '@nestjs/cli': specifier: 10.0.2 version: 10.0.2(@swc/cli@0.3.14(@swc/core@1.5.7(@swc/helpers@0.5.13))(chokidar@3.5.3))(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) '@nestjs/common': specifier: ^10.0.2 - version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/core': specifier: ^10.0.2 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/microservices': specifier: ^10.3.1 - version: 10.4.20(@grpc/grpc-js@1.14.1)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.5)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/platform-express': specifier: ^10.0.2 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) '@nestjs/schedule': specifier: ^4.0.0 - version: 4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) '@nestjs/swagger': specifier: ^7.3.0 - version: 7.4.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14) + version: 7.4.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14) '@nestjs/throttler': specifier: ^6.3.0 - version: 6.4.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14) + version: 6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14) '@neynar/nodejs-sdk': specifier: ^2.8.1 - version: 2.46.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.0.9)(class-transformer@0.5.1)(class-validator@0.14.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.46.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@neynar/react': specifier: ^0.9.7 - version: 0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.56.1)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1)(swr@2.3.6(react@18.3.1)) + version: 0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1)) '@postiz/wallets': specifier: ^0.0.1 - version: 0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bs58@6.0.0)(bufferutil@4.0.9)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@prisma/client': specifier: 6.5.0 version: 6.5.0(prisma@6.5.0(typescript@5.5.4))(typescript@5.5.4) '@sentry/nestjs': specifier: ^10.26.0 - version: 10.26.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 10.32.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) '@sentry/nextjs': specifier: ^10.26.0 - version: 10.26.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + version: 10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) '@sentry/profiling-node': specifier: ^10.25.0 - version: 10.26.0 + version: 10.32.1 '@sentry/react': specifier: ^10.25.0 - version: 10.26.0(react@18.3.1) + version: 10.32.1(react@18.3.1) '@solana/wallet-adapter-react': specifier: ^0.15.35 - version: 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/wallet-adapter-react-ui': specifier: ^0.9.35 - version: 0.9.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 0.9.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@stripe/react-stripe-js': specifier: ^5.4.1 version: 5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -146,49 +146,49 @@ importers: version: 5.0.27 '@tailwindcss/postcss': specifier: ^4.1.7 - version: 4.1.17 + version: 4.1.18 '@tiptap/extension-bold': specifier: ^3.0.6 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) '@tiptap/extension-document': specifier: ^3.0.6 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) '@tiptap/extension-heading': specifier: ^3.0.7 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) '@tiptap/extension-history': specifier: ^3.0.7 - version: 3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) '@tiptap/extension-link': specifier: ^3.0.9 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) '@tiptap/extension-list': specifier: ^3.0.7 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) '@tiptap/extension-mention': specifier: ^3.0.7 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)(@tiptap/suggestion@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@tiptap/suggestion@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) '@tiptap/extension-paragraph': specifier: ^3.0.6 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) '@tiptap/extension-text': specifier: ^3.0.6 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) '@tiptap/extension-underline': specifier: ^3.0.6 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) '@tiptap/pm': specifier: ^3.0.6 - version: 3.11.0 + version: 3.14.0 '@tiptap/react': specifier: ^3.0.6 - version: 3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tiptap/starter-kit': specifier: ^3.0.6 - version: 3.11.0 + version: 3.14.0 '@tiptap/suggestion': specifier: ^3.0.7 - version: 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) '@types/bcrypt': specifier: ^5.0.2 version: 5.0.2 @@ -203,7 +203,7 @@ importers: version: 9.0.10 '@types/lodash': specifier: ^4.14.202 - version: 4.17.20 + version: 4.17.21 '@types/md5': specifier: ^2.3.5 version: 2.3.6 @@ -245,7 +245,7 @@ importers: version: 2.4.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@uiw/react-md-editor': specifier: ^4.0.3 - version: 4.0.8(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.0.11(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@uppy/aws-s3': specifier: ^4.1.0 version: 4.3.2(@uppy/core@4.5.3) @@ -299,10 +299,10 @@ importers: version: 6.0.0 bufferutil: specifier: ^4.0.8 - version: 4.0.9 + version: 4.1.0 bullmq: specifier: ^5.12.12 - version: 5.63.2 + version: 5.66.2 canvas: specifier: ^2.11.2 version: 2.11.2 @@ -314,10 +314,10 @@ importers: version: 0.5.1 class-validator: specifier: ^0.14.1 - version: 0.14.2 + version: 0.14.3 class-validator-jsonschema: specifier: ^5.1.0 - version: 5.1.0(class-transformer@0.5.1)(class-validator@0.14.2) + version: 5.1.0(class-transformer@0.5.1)(class-validator@0.14.3) clsx: specifier: ^2.1.0 version: 2.1.1 @@ -344,7 +344,7 @@ importers: version: 8.0.0 emoji-picker-react: specifier: ^4.12.0 - version: 4.15.1(react@18.3.1) + version: 4.16.1(react@18.3.1) evp_bytestokey: specifier: ^1.0.3 version: 1.0.3 @@ -362,13 +362,13 @@ importers: version: 137.1.0 hot-reload-extension-vite: specifier: ^1.0.13 - version: 1.0.13(bufferutil@4.0.9)(utf-8-validate@5.0.10) + version: 1.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) i18n-iso-countries: specifier: ^7.14.0 version: 7.14.0 i18next: specifier: ^25.2.1 - version: 25.6.3(typescript@5.5.4) + version: 25.7.3(typescript@5.5.4) i18next-browser-languagedetector: specifier: ^8.1.0 version: 8.2.0 @@ -386,13 +386,13 @@ importers: version: 2.3.0 jsonwebtoken: specifier: ^9.0.2 - version: 9.0.2 + version: 9.0.3 lodash: specifier: ^4.17.21 version: 4.17.21 mastra: specifier: ^0.13.2 - version: 0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(typescript@5.5.4)(zod@3.25.76) + version: 0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(hono@4.11.1)(typescript@5.5.4)(zod@3.25.76) md5: specifier: ^2.3.0 version: 2.3.0 @@ -410,16 +410,16 @@ importers: version: 7.14.0 nestjs-command: specifier: ^3.1.4 - version: 3.1.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(yargs@17.7.2) + version: 3.1.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(yargs@17.7.2) nestjs-real-ip: specifier: ^3.0.1 - version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)) + version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)) next: specifier: 14.2.35 - version: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + version: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) next-plausible: specifier: ^3.12.0 - version: 3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -431,10 +431,10 @@ importers: version: 7.0.11 nostr-tools: specifier: ^2.18.2 - version: 2.18.2(typescript@5.5.4) + version: 2.19.4(typescript@5.5.4) openai: specifier: ^6.2.0 - version: 6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + version: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) p-limit: specifier: ^3.1.0 version: 3.1.0 @@ -443,10 +443,10 @@ importers: version: 6.0.1 polotno: specifier: ^2.10.5 - version: 2.32.4(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 2.34.1(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) posthog-js: specifier: ^1.178.0 - version: 1.296.1 + version: 1.309.1 react: specifier: 18.3.1 version: 18.3.1 @@ -470,13 +470,13 @@ importers: version: 14.3.8(react@18.3.1) react-hook-form: specifier: ^7.58.1 - version: 7.66.1(react@18.3.1) + version: 7.69.0(react@18.3.1) react-hotkeys-hook: specifier: ^5.1.0 version: 5.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-i18next: specifier: ^15.5.2 - version: 15.7.4(i18next@25.6.3(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 15.7.4(i18next@25.7.3(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) react-loading: specifier: ^2.0.3 version: 2.0.3(prop-types@15.8.1)(react@18.3.1) @@ -494,7 +494,7 @@ importers: version: 1.6.1(react@18.3.1) react-use-keypress: specifier: ^1.3.1 - version: 1.3.1(react@18.3.1) + version: 1.4.0(react@18.3.1) redis: specifier: ^4.6.12 version: 4.7.1 @@ -515,7 +515,7 @@ importers: version: 7.8.2 sass: specifier: ^1.89.2 - version: 1.94.1 + version: 1.97.1 sha256: specifier: ^0.2.0 version: 0.2.0 @@ -542,7 +542,7 @@ importers: version: 11.4.8 swr: specifier: ^2.2.5 - version: 2.3.6(react@18.3.1) + version: 2.3.8(react@18.3.1) tailwind-scrollbar: specifier: ^3.1.0 version: 3.1.0(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4))) @@ -587,13 +587,13 @@ importers: version: 10.0.0 viem: specifier: ^2.22.9 - version: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) webextension-polyfill: specifier: ^0.12.0 version: 0.12.0 ws: specifier: ^8.18.0 - version: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + version: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) yargs: specifier: ^17.7.2 version: 17.7.2 @@ -605,17 +605,17 @@ importers: version: 3.25.76 zustand: specifier: ^5.0.5 - version: 5.0.8(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) + version: 5.0.9(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) devDependencies: '@crxjs/vite-plugin': specifier: ^2.0.0-beta.32 - version: 2.2.1 + version: 2.3.0 '@nestjs/schematics': specifier: ^10.0.1 version: 10.2.3(chokidar@3.5.3)(typescript@5.5.4) '@nestjs/testing': specifier: ^10.0.2 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20) + version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20) '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.7 version: 0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) @@ -633,7 +633,7 @@ importers: version: 1.5.7(@swc/helpers@0.5.13) '@tailwindcss/vite': specifier: ^4.0.17 - version: 4.1.17(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1)) + version: 4.1.18(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) '@testing-library/react': specifier: 15.0.6 version: 15.0.6(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -645,7 +645,7 @@ importers: version: 0.0.319 '@types/cookie-parser': specifier: ^1.4.6 - version: 1.4.10(@types/express@5.0.5) + version: 1.4.10(@types/express@5.0.6) '@types/jest': specifier: 29.5.12 version: 29.5.12 @@ -654,7 +654,7 @@ importers: version: 18.16.9 '@types/node-telegram-bot-api': specifier: ^0.64.7 - version: 0.64.12 + version: 0.64.13 '@types/react': specifier: 18.3.1 version: 18.3.1 @@ -678,7 +678,7 @@ importers: version: 7.18.0(eslint@8.57.0)(typescript@5.5.4) '@vitejs/plugin-react': specifier: ^4.2.0 - version: 4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1)) + version: 4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) '@vitest/coverage-v8': specifier: 1.6.0 version: 1.6.0(vitest@3.1.4) @@ -687,7 +687,7 @@ importers: version: 1.6.0(vitest@3.1.4) autoprefixer: specifier: ^10.4.17 - version: 10.4.22(postcss@8.4.38) + version: 10.4.23(postcss@8.4.38) babel-jest: specifier: 29.7.0 version: 29.7.0(@babel/core@7.28.5) @@ -717,13 +717,13 @@ importers: version: 4.6.0(eslint@8.57.0) fs-extra: specifier: ^11.3.0 - version: 11.3.2 + version: 11.3.3 jest: specifier: 29.7.0 version: 29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)) jest-environment-jsdom: specifier: 29.7.0 - version: 29.7.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10) + version: 29.7.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) jest-environment-node: specifier: ^29.4.1 version: 29.7.0 @@ -735,7 +735,7 @@ importers: version: 4.0.0(@jest/globals@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4) jsdom: specifier: ~22.1.0 - version: 22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10) + version: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) nodemon: specifier: ^3.1.9 version: 3.1.11 @@ -753,25 +753,25 @@ importers: version: 0.10.0 ts-jest: specifier: ^29.1.0 - version: 29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4) + version: 29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4) ts-node: specifier: 10.9.2 version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4) tsup: specifier: ^8.5.0 - version: 8.5.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(jiti@2.6.1)(postcss@8.4.38)(typescript@5.5.4)(yaml@2.8.1) + version: 8.5.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(jiti@2.6.1)(postcss@8.4.38)(typescript@5.5.4)(yaml@2.8.2) typescript: specifier: 5.5.4 version: 5.5.4 vite: specifier: ^6.3.5 - version: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + version: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1)) + version: 5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) vitest: specifier: 3.1.4 - version: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + version: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) apps/backend: {} @@ -808,23 +808,23 @@ packages: '@adraffy/ens-normalize@1.11.1': resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} - '@ag-ui/client@0.0.41': - resolution: {integrity: sha512-aCTuGHm62hjWE0caWhxGAZFSiK42LnORMlYtRhkdIj1Te4eg8LkmpzU6FepQksIk6uBtHNbn82rp0rxpqog7OA==} + '@ag-ui/client@0.0.42': + resolution: {integrity: sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==} '@ag-ui/core@0.0.37': resolution: {integrity: sha512-7bmjPn1Ol0Zo00F+MrPr0eOwH4AFZbhmq/ZMhCsrMILtVYBiBLcLU9QFBpBL3Zm9MCHha8b79N7JE2FzwcMaVA==} - '@ag-ui/core@0.0.41': - resolution: {integrity: sha512-yRSh7fweajRGAJxm2IBpC+hfkXleqq0mQOXntMk/UMEgPKWfSRMt7qHL+3mtrGEaeL4fA/rcE41cVSjFDnYSoQ==} + '@ag-ui/core@0.0.42': + resolution: {integrity: sha512-C2hMg4Gs5oiUDgK9cA2RsTwSSmFZdIsqPklDrFw/Ue+quH6EU3vKp5YoOq7nuaQYO4pO8Em+Z+l5/M5PpcvP1g==} - '@ag-ui/encoder@0.0.41': - resolution: {integrity: sha512-eg+NUppqC/VQVIEDGD1slNEaRZ7YNVWAhfamntwyPYQnAugcY+Q14XIbROWsZ7YNipfX0cO5yb1WxFrgv1YZrw==} + '@ag-ui/encoder@0.0.42': + resolution: {integrity: sha512-97B5MMCSs82t/y41uk2NrLBYFhbvn4kYsKQHMCfy8tjSWubyxh3zP7N9yHo8zJeSPe3WvzTvclyXNiGxSOsorg==} - '@ag-ui/langgraph@0.0.18': - resolution: {integrity: sha512-soWSV8+xR91jMArZUJoRv85UCgTi3Zt3u3gTMZhvs1t6fGFpAi6+hEQ4AqP13Rgvg90IlmIU8MTWo2k0OZDnoA==} + '@ag-ui/langgraph@0.0.21': + resolution: {integrity: sha512-QGGbBB4rPnMtCdflhew7KSGdlVgm/2GzpssuUJ//Gld6xJmCK0yzO8heUggIOwNLcabLnKbvAm0vkxKss7rjWg==} peerDependencies: - '@ag-ui/client': '>=0.0.38' - '@ag-ui/core': '>=0.0.38' + '@ag-ui/client': '>=0.0.42' + '@ag-ui/core': '>=0.0.42' '@ag-ui/mastra@0.2.0': resolution: {integrity: sha512-4d2LwT15I/B87HSeEMqqtdy2n9L+FieAhVmYK6XFSImWnXS4G0YVe+qfncIHMwRnq4XtGM8F9uq6tzaMK8/qdA==} @@ -835,8 +835,8 @@ packages: '@mastra/core': '>=0.20.1' zod: ^3.25.67 - '@ag-ui/proto@0.0.41': - resolution: {integrity: sha512-YlVmS8e53EZuMG68WvjNqzxoa/8NYCy3a8yoWsogPf1iZXa1RZ2WbQTi80xGzUnzluwxGSULlg7m7a1/8eXkkA==} + '@ag-ui/proto@0.0.42': + resolution: {integrity: sha512-NDUwSgMnGEqxZGkWIJ1ge5t3Q7Kiddj360x2JAWaIfv9w+7tDJ0pmgyzf3/SXp605aY2wZiDLBtJ6jKZeg1lFg==} '@ai-sdk/anthropic@2.0.23': resolution: {integrity: sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw==} @@ -868,8 +868,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@2.0.68': - resolution: {integrity: sha512-qUSLFkqgUoFArzBwttu0KWVAZYjbsdZGOklSJXpfZ2nDC61yseHxtcnuG8u6tqKnGXDh4eakEgREDWU2sRht7A==} + '@ai-sdk/openai@2.0.88': + resolution: {integrity: sha512-LlOf83haeZIiRUH1Zw1oEmqUfw5y54227CvndFoBpIkMJwQDGAB3VARUeOJ6iwAWDJjXSz06GdnEnhRU67Yatw==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -886,8 +886,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@3.0.17': - resolution: {integrity: sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw==} + '@ai-sdk/provider-utils@3.0.19': + resolution: {integrity: sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -987,20 +987,26 @@ packages: '@atproto/api@0.15.27': resolution: {integrity: sha512-ok/WGafh1nz4t8pEQGtAF/32x2E2VDWU4af6BajkO5Gky2jp2q6cv6aB2A5yuvNNcc3XkYMYipsqVHVwLPMF9g==} - '@atproto/common-web@0.4.3': - resolution: {integrity: sha512-nRDINmSe4VycJzPo6fP/hEltBcULFxt9Kw7fQk6405FyAWZiTluYHlXOnU7GkQfeUK44OENG1qFTBcmCJ7e8pg==} + '@atproto/common-web@0.4.7': + resolution: {integrity: sha512-vjw2+81KPo2/SAbbARGn64Ln+6JTI0FTI4xk8if0ebBfDxFRmHb2oSN1y77hzNq/ybGHqA2mecfhS03pxC5+lg==} + + '@atproto/lex-data@0.0.3': + resolution: {integrity: sha512-ivo1IpY/EX+RIpxPgCf4cPhQo5bfu4nrpa1vJCt8hCm9SfoonJkDFGa0n4SMw4JnXZoUcGcrJ46L+D8bH6GI2g==} + + '@atproto/lex-json@0.0.3': + resolution: {integrity: sha512-ZVcY7XlRfdPYvQQ2WroKUepee0+NCovrSXgXURM3Xv+n5jflJCoczguROeRr8sN0xvT0ZbzMrDNHCUYKNnxcjw==} '@atproto/lexicon@0.4.14': resolution: {integrity: sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ==} - '@atproto/lexicon@0.5.1': - resolution: {integrity: sha512-y8AEtYmfgVl4fqFxqXAeGvhesiGkxiy3CWoJIfsFDDdTlZUC8DFnZrYhcqkIop3OlCkkljvpSJi1hbeC1tbi8A==} + '@atproto/lexicon@0.6.0': + resolution: {integrity: sha512-5veb8aD+J5M0qszLJ+73KSFsFrJBgAY/nM1TSAJvGY7fNc9ZAT+PSUlmIyrdye9YznAZ07yktalls/TwNV7cHQ==} - '@atproto/syntax@0.4.1': - resolution: {integrity: sha512-CJdImtLAiFO+0z3BWTtxwk6aY5w4t8orHTMVJgkf++QRJWTxPbIFko/0hrkADB7n2EruDxDSeAgfUGehpH6ngw==} + '@atproto/syntax@0.4.2': + resolution: {integrity: sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==} - '@atproto/xrpc@0.7.5': - resolution: {integrity: sha512-MUYNn5d2hv8yVegRL0ccHvTHAVj5JSnW07bkbiaz96UH45lvYNRVwt44z+yYVnb0/mvBzyD3/ZQ55TRGt7fHkA==} + '@atproto/xrpc@0.7.7': + resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} '@aws-crypto/crc32@5.2.0': resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} @@ -1025,159 +1031,163 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-bedrock-agent-runtime@3.934.0': - resolution: {integrity: sha512-3GgDUIazgJwhimHVYhnEMgYcWXWHu/S162phBjOpXzSVjiF+oPGZKkGtJ2Qaq2tFwB6i3GiBwM8dnbGm0e/Bpw==} + '@aws-sdk/client-bedrock-agent-runtime@3.956.0': + resolution: {integrity: sha512-Y4u1BE+aoEPOcX2EdPeuAgrrc8YBAG50tUUkEL8VeLA8h7eCO4fxnuazRZNwlq3PksH3rTOHQoXbcRculqVM4w==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-bedrock-runtime@3.934.0': - resolution: {integrity: sha512-rkVqE9gF72IuQbif9du/4OMuPL23F0/VmG8drJHQB7rpPFti7kDVRACxnd2I2gp7LbcSTuLgwP817xJQ8flJ5w==} + '@aws-sdk/client-bedrock-runtime@3.956.0': + resolution: {integrity: sha512-8cD53FBKn7uvc4QZtYZr87KcuSrEFMwS/O3HC+Y7+disgePlTXxtOo0naCj5O1yVZPuU3FCVLGzxFk5fhbzAJg==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-kendra@3.934.0': - resolution: {integrity: sha512-MiPqty4EMXKspES41DAgDtMMk4JNgMGjfPyATcyjrT73ccBfT8iU6Oz/eCE5FiMrmXyUEHFMI/mNgcYgeIN2zQ==} + '@aws-sdk/client-kendra@3.956.0': + resolution: {integrity: sha512-hnIaKXGpxg/yA21HQZRMZiSsVe2MhY1JQ7kGJzn22y+GzvrvsLI6dyD4/NUMHNi9+wp2saLb4kBaNguW2OxzWA==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-s3@3.934.0': - resolution: {integrity: sha512-dtg77FGTgt8WlqgrRriCOie/SUl0x0cx2itPgK6fkf3pRK0t1betQ0EUZM6VYQcj+hqVMzh/XRcr1TDm5n5eVw==} + '@aws-sdk/client-s3@3.956.0': + resolution: {integrity: sha512-O+Z7PSY9TjaqJcZSDMvVmXBuV/jmFRJIu7ga+9XgWv4+qfjhAX2N2s4kgsRnIdjIO4xgkN3O/BugTCyjIRrIDQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-ses@3.934.0': - resolution: {integrity: sha512-yNxRLhC+zUF/RM/hFSi1z5qAW5NZjAHh9a1UbXN9xRAtvucueCtFvds9NB6WAGhNjr2aNI3GaYnNScHO84dn0w==} + '@aws-sdk/client-ses@3.956.0': + resolution: {integrity: sha512-pot5tKpQcL8EC4MIKicF684FNrVWU6ZUmxdeJpEHb6b0x2B2ggIUGgNEo0nyd7/ga74azKcTmP7rDpE/9Z3yYA==} engines: {node: '>=18.0.0'} - '@aws-sdk/client-sso@3.934.0': - resolution: {integrity: sha512-gsgJevqhY0j3x014ejhXtHLCA6o83FYm3rJoZG7tqoy3DnWerLv/FHaAnHI/+Q+csadqjoFkWGQTOedPoOunzA==} + '@aws-sdk/client-sso@3.956.0': + resolution: {integrity: sha512-TCxCa9B1IMILvk/7sig0fRQzff+M2zBQVZGWOJL8SAZq/gfElIMAf/nYjQwMhXjyq8PFDRGm4GN8ZhNKPeNleQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/core@3.934.0': - resolution: {integrity: sha512-b6k916ZxSrBwQPzeirncTIQXGnhps0HFOUakFt0ZEzjksePYUiEoU/SQ7VeY1j9JeAdJ24ejqddCiyLt99/3lg==} + '@aws-sdk/core@3.956.0': + resolution: {integrity: sha512-BMOCXZNz5z4cR3/SaNHUfeoZQUG/y39bLscdLUgg3RL6mDOhuINIqMc0qc6G3kpwDTLVdXikF4nmx2UrRK9y5A==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-env@3.934.0': - resolution: {integrity: sha512-bnpIGYm7Jy46dxZa1cxMQ1sF0n2iBIT+TpOPHK51sz1N2dYOicUVWUHMDgU2xIFOVcKaqV+GV4VyicMmvDBcBQ==} + '@aws-sdk/credential-provider-env@3.956.0': + resolution: {integrity: sha512-aLJavJMPVTvhmggJ0pcdCKEWJk3sL9QkJkUIEoTzOou7HnxWS66N4sC5e8y27AF2nlnYfIxq3hkEiZlGi/vlfA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-http@3.934.0': - resolution: {integrity: sha512-WJcfFik7MPIgjE8lmuDcCqddHKRMpifzoBzTZWqUJJWYXIy0rDfNzt6pn3/TMLwVgnCGjnXlw6dChTxLzO60RQ==} + '@aws-sdk/credential-provider-http@3.956.0': + resolution: {integrity: sha512-VsKzBNhwT6XJdW3HQX6o4KOHj1MAzSwA8/zCsT9mOGecozw1yeCcQPtlWDSlfsfygKVCXz7fiJzU03yl11NKMA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-ini@3.934.0': - resolution: {integrity: sha512-3vVKGe1F2S09G9kC0ZcpWh09opyrGOgQETllqWbuxlTVd7zBgrZWloItLIvneSDP+dWvdLFUbkD7WDWNCeGiig==} + '@aws-sdk/credential-provider-ini@3.956.0': + resolution: {integrity: sha512-TlDy+IGr0JIRBwnPdV31J1kWXEcfsR3OzcNVWQrguQdHeTw2lU5eft16kdizo6OruqcZRF/LvHBDwAWx4u51ww==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-node@3.934.0': - resolution: {integrity: sha512-nguy36xi8nbH346dJjCmwWtOgfS4VfL7yHP+EEGmma+yg+J7mxgs8kA1NGQdJ8B46GdjlJPpI1P9pm7Pmz7nOw==} + '@aws-sdk/credential-provider-login@3.956.0': + resolution: {integrity: sha512-p2Y62mdIlUpiyi5tvn8cKTja5kq1e3Rm5gm4wpNQ9caTayfkIEXyKrbP07iepTv60Coaylq9Fx6b5En/siAeGA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-process@3.934.0': - resolution: {integrity: sha512-PhvpAgoJ88IOuqlUws9nvHuPex2jK+WS+0s00BQcRTwqPP0jtLT7eql6UfCRduwv2sIy3m1wnWDUubvbpejp/Q==} + '@aws-sdk/credential-provider-node@3.956.0': + resolution: {integrity: sha512-ITjp7uAQh17ljUsCWkPRmLjyFfupGlJVUfTLHnZJ+c7G0P0PDRquaM+fBSh0y33tauHsBa5fGnCCLRo5hy9sGQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-sso@3.934.0': - resolution: {integrity: sha512-7wO86w95V9MZSYo2dunBKruKHdAUmgg9ccOSJSYGnPip1PPBK/rgSgQ8mDlYtFAW3/82bdeM/668QcgLT4+ofA==} + '@aws-sdk/credential-provider-process@3.956.0': + resolution: {integrity: sha512-wpAex+/LGVWkHPchsn9FWy1ahFualIeSYq3ADFc262ljJjrltOWGh3+cu3OK3gTMkX6VEsl+lFvy1P7Bk7cgXA==} engines: {node: '>=18.0.0'} - '@aws-sdk/credential-provider-web-identity@3.934.0': - resolution: {integrity: sha512-hb+lvFxiAPcAvUorB0hrUd1kDjDRXhZgCi5426I8KUpGzZ+ALh8/ep0KXAiYe2yg9ZkyMUbMaMvYYhMFcbXRFA==} + '@aws-sdk/credential-provider-sso@3.956.0': + resolution: {integrity: sha512-IRFSDF32x8TpOEYSGMcGQVJUiYuJaFkek0aCjW0klNIZHBF1YpflVpUarK9DJe4v4ryfVq3c0bqR/JFui8QFmw==} engines: {node: '>=18.0.0'} - '@aws-sdk/eventstream-handler-node@3.930.0': - resolution: {integrity: sha512-0busYTWsruRoUvs4Q4oSPkEQLR+eX80AtmLrW20SycC/OXdHFGj+bP3vhVWJyjDjXjjLwdLPJJhrqfHHrbrXQw==} + '@aws-sdk/credential-provider-web-identity@3.956.0': + resolution: {integrity: sha512-4YkmjwZC+qoUKlVOY9xNx7BTKRdJ1R1/Zjk2QSW5aWtwkk2e07ZUQvUpbW4vGpAxGm1K4EgRcowuSpOsDTh44Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-bucket-endpoint@3.930.0': - resolution: {integrity: sha512-cnCLWeKPYgvV4yRYPFH6pWMdUByvu2cy2BAlfsPpvnm4RaVioztyvxmQj5PmVN5fvWs5w/2d6U7le8X9iye2sA==} + '@aws-sdk/eventstream-handler-node@3.956.0': + resolution: {integrity: sha512-OdnzsiCyMcK9fkMI3II7+q8qu3hY5iK4coV8VjXI5u05UEbg3iosQynlkv0FXztSodPYYwnuZ0lFtIthsUy0Tw==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-eventstream@3.930.0': - resolution: {integrity: sha512-9s9oJ/c+xavDyirkRmPBGQJ3jhRvyAXWvXwttZvUjbpR95Lepaj6Xtqxen3PLOHG1Z+Ma56KKqMdnFxg/Jhf6A==} + '@aws-sdk/middleware-bucket-endpoint@3.956.0': + resolution: {integrity: sha512-+iHH9cnkNZgKkTBnPP9rbapHliKDrOuj7MDz6+wL0NV4N/XGB5tbrd+uDP608FXVeMHcWIIZtWkANADUmAI49w==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-expect-continue@3.930.0': - resolution: {integrity: sha512-5HEQ+JU4DrLNWeY27wKg/jeVa8Suy62ivJHOSUf6e6hZdVIMx0h/kXS1fHEQNNiLu2IzSEP/bFXsKBaW7x7s0g==} + '@aws-sdk/middleware-eventstream@3.956.0': + resolution: {integrity: sha512-xBhNmBCJxB4ho7ATmhniv3PK3qN5ZEgknUI+7XTM/cnQBzuYG5twAQSkGdInzEjygTSTmKpkdBVh67AxKTotAQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.934.0': - resolution: {integrity: sha512-kAV0fhwUhh/CV8hR5iip+du5QSXvIsONERVY/iJPbiBItqsmFaWcwiZE9E+ORJPNyoT/3X17632W33pCweKGDQ==} + '@aws-sdk/middleware-expect-continue@3.956.0': + resolution: {integrity: sha512-97rmalK9x09Darcl6AbShZRXYxWiyCeO8ll1C9rx1xyZMs2DeIKAZ/xuAJ/bywB3l25ls6VqXO4/EuDFJHL8eA==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-host-header@3.930.0': - resolution: {integrity: sha512-x30jmm3TLu7b/b+67nMyoV0NlbnCVT5DI57yDrhXAPCtdgM1KtdLWt45UcHpKOm1JsaIkmYRh2WYu7Anx4MG0g==} + '@aws-sdk/middleware-flexible-checksums@3.956.0': + resolution: {integrity: sha512-Rd/VeVKuw+lQ1oJmJOyXV0flIkp9ouMGAS9QT28ogdQVxXriaheNo754N4z0+8+R6uTcmeojN7dN4jzt51WV2g==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-location-constraint@3.930.0': - resolution: {integrity: sha512-QIGNsNUdRICog+LYqmtJ03PLze6h2KCORXUs5td/hAEjVP5DMmubhtrGg1KhWyctACluUH/E/yrD14p4pRXxwA==} + '@aws-sdk/middleware-host-header@3.956.0': + resolution: {integrity: sha512-JujNJDp/dj1DbsI0ntzhrz2uJ4jpumcKtr743eMpEhdboYjuu/UzY8/7n1h5JbgU9TNXgqE9lgQNa5QPG0Tvsg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-logger@3.930.0': - resolution: {integrity: sha512-vh4JBWzMCBW8wREvAwoSqB2geKsZwSHTa0nSt0OMOLp2PdTYIZDi0ZiVMmpfnjcx9XbS6aSluLv9sKx4RrG46A==} + '@aws-sdk/middleware-location-constraint@3.956.0': + resolution: {integrity: sha512-eANhYRFcVO/lI9tliitSW0DK5H1d9J7BK/9RrRz86bd5zPWteVqqzQRbMUdErVi1nwSbSIAa6YGv/ItYPswe0w==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-recursion-detection@3.933.0': - resolution: {integrity: sha512-qgrMlkVKzTCAdNw2A05DC2sPBo0KRQ7wk+lbYSRJnWVzcrceJhnmhoZVV5PFv7JtchK7sHVcfm9lcpiyd+XaCA==} + '@aws-sdk/middleware-logger@3.956.0': + resolution: {integrity: sha512-Qff39yEOPYgRsm4SrkHOvS0nSoxXILYnC8Akp0uMRi2lOcZVyXL3WCWqIOtI830qVI4GPa796sleKguxx50RHg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-sdk-s3@3.934.0': - resolution: {integrity: sha512-eU2R7pVOhCxnkDzq9mW+xh4WvCA3mdXVUHezIcJNFyKCKKv/c9I4WFcnMnUy+wnCWO2mzN/gwSgQxADkvxfLNQ==} + '@aws-sdk/middleware-recursion-detection@3.956.0': + resolution: {integrity: sha512-/f4JxL2kSCYhy63wovqts6SJkpalSLvuFe78ozt3ClrGoHGyr69o7tPRYx5U7azLgvrIGjsWUyTayeAk3YHIVQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-ssec@3.930.0': - resolution: {integrity: sha512-N2/SvodmaDS6h7CWfuapt3oJyn1T2CBz0CsDIiTDv9cSagXAVFjPdm2g4PFJqrNBeqdDIoYBnnta336HmamWHg==} + '@aws-sdk/middleware-sdk-s3@3.956.0': + resolution: {integrity: sha512-U/+jYb4iowqqpLjB6cSYan0goAMOlh2xg2CPIdSy550o8mYnJtuajBUQ20A9AA9PYKLlEAoCNEysNZkn4o/63g==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-user-agent@3.934.0': - resolution: {integrity: sha512-68giGM2Zm9K6Qas14ws3Qo5wafpn0I8/L64fS9E6Rc6Tu0k+So73hupysw+9ZOzHwQS5FEBUqLOMtbUibAcjNA==} + '@aws-sdk/middleware-ssec@3.956.0': + resolution: {integrity: sha512-1Et0vPoIzfhkUAdNRzu0pC25ZawFqXo5T8xpvbwkfDgfIkeVj+sm9t01iXO3pCOK52OSuLRAy7fiAo/AoHjOYg==} engines: {node: '>=18.0.0'} - '@aws-sdk/middleware-websocket@3.930.0': - resolution: {integrity: sha512-dqMbapt2KH99HG+2+CrIq04HGLOGMiX89lqgu5RxZMBUTxEZlRoAvmpjSYKlB0Co033PwMmWqIRsJVw0KOLMjA==} + '@aws-sdk/middleware-user-agent@3.956.0': + resolution: {integrity: sha512-azH8OJ0AIe3NafaTNvJorG/ALaLNTYwVKtyaSeQKOvaL8TNuBVuDnM5iHCiWryIaRgZotomqycwyfNKLw2D3JQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-websocket@3.956.0': + resolution: {integrity: sha512-yH8D1z5stLDPZPXoDsgzyMIwziMUj6v5riHARJ4cECJBtdREJwDmp56c+iCzkhvtWKqeL/viAlj4Kwe2fAmTxw==} engines: {node: '>= 14.0.0'} - '@aws-sdk/nested-clients@3.934.0': - resolution: {integrity: sha512-kRO61EMrDR4UuPlKAkziG6urcYXlhrFW/Ce5PjWFdjkm0ZOge75OFV1vhf/vE4Pmoop9jaAONX4E5BaIYrIQfg==} + '@aws-sdk/nested-clients@3.956.0': + resolution: {integrity: sha512-GHDQMkxoWpi3eTrhWGmghw0gsZJ5rM1ERHfBFhlhduCdtV3TyhKVmDgFG84KhU8v18dcVpSp3Pu3KwH7j1tgIg==} engines: {node: '>=18.0.0'} - '@aws-sdk/region-config-resolver@3.930.0': - resolution: {integrity: sha512-KL2JZqH6aYeQssu1g1KuWsReupdfOoxD6f1as2VC+rdwYFUu4LfzMsFfXnBvvQWWqQ7rZHWOw1T+o5gJmg7Dzw==} + '@aws-sdk/region-config-resolver@3.956.0': + resolution: {integrity: sha512-byU5XYekW7+rZ3e067y038wlrpnPkdI4fMxcHCHrv+TAfzl8CCk5xLyzerQtXZR8cVPVOXuaYWe1zKW0uCnXUA==} engines: {node: '>=18.0.0'} - '@aws-sdk/s3-request-presigner@3.934.0': - resolution: {integrity: sha512-3VyOm6wa00QqP+0N9s6VOco+4H0PPufZf16uMVfzvmpjaKjXyjBIMYrAhmmxXxNA/xmZiYcmSjQFGwszrn9UHw==} + '@aws-sdk/s3-request-presigner@3.956.0': + resolution: {integrity: sha512-LoqSwHIIYobnly7E6+8ASwlGT1qAAnBzW1xPUlA4gD9ePiV9L9TXwxRwW3WXeaLLQrR3+bi2nXiDxYEiiTsrow==} engines: {node: '>=18.0.0'} - '@aws-sdk/signature-v4-multi-region@3.934.0': - resolution: {integrity: sha512-cLphxVoHapSdouAdLSDEwR2Bktjg5dc11EpSpaLo8jcFpAXhFaDllKBfDfws0EqGY6N2CMqEjqPqxDFzmmQOQA==} + '@aws-sdk/signature-v4-multi-region@3.956.0': + resolution: {integrity: sha512-gejlXPmor08VydGC8bx0Bv4/tPT92eK0WLe2pUPR0AaMXL+5ycDpThAi1vLWjWr0aUjCA7lXx0pMENWlJlYK3A==} engines: {node: '>=18.0.0'} - '@aws-sdk/token-providers@3.934.0': - resolution: {integrity: sha512-M0WEmgXDdUxapSfjplqJoVCBMcn0vQ5Jou0X/XiQwyVDbfvIyNSHUHyMXEIBAew9kVx9sfMMEYz3LXewvQxdCA==} + '@aws-sdk/token-providers@3.956.0': + resolution: {integrity: sha512-I01Q9yDeG9oXge14u/bubtSdBpok/rTsPp2AQwy5xj/5PatRTHPbUTP6tef3AH/lFCAqkI0nncIcgx6zikDdUQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/types@3.930.0': - resolution: {integrity: sha512-we/vaAgwlEFW7IeftmCLlLMw+6hFs3DzZPJw7lVHbj/5HJ0bz9gndxEsS2lQoeJ1zhiiLqAqvXxmM43s0MBg0A==} + '@aws-sdk/types@3.956.0': + resolution: {integrity: sha512-DMRU/p9wAlAJxEjegnLwduCA8YP2pcT/sIJ+17KSF38c5cC6CbBhykwbZLECTo+zYzoFrOqeLbqE6paH8Gx3ug==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-arn-parser@3.893.0': - resolution: {integrity: sha512-u8H4f2Zsi19DGnwj5FSZzDMhytYF/bCh37vAtBsn3cNDL3YG578X5oc+wSX54pM3tOxS+NY7tvOAo52SW7koUA==} + '@aws-sdk/util-arn-parser@3.953.0': + resolution: {integrity: sha512-9hqdKkn4OvYzzaLryq2xnwcrPc8ziY34i9szUdgBfSqEC6pBxbY9/lLXmrgzfwMSL2Z7/v2go4Od0p5eukKLMQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-endpoints@3.930.0': - resolution: {integrity: sha512-M2oEKBzzNAYr136RRc6uqw3aWlwCxqTP1Lawps9E1d2abRPvl1p1ztQmmXp1Ak4rv8eByIZ+yQyKQ3zPdRG5dw==} + '@aws-sdk/util-endpoints@3.956.0': + resolution: {integrity: sha512-xZ5CBoubS4rs9JkFniKNShDtfqxaMUnwaebYMoybZm070q9+omFkQkJYXl7kopTViEgZgQl1sAsAkrawBM8qEQ==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-format-url@3.930.0': - resolution: {integrity: sha512-FW6Im17Zc7F5WT39XUgDOjtJO95Yu8rsmeRHf7z+Y3FamtTSzH4f713BD/qMyJBrZIlFACWlok/Uuvdl5/qtMg==} + '@aws-sdk/util-format-url@3.956.0': + resolution: {integrity: sha512-Piap0XvvmZMtCjeCStuAG/Meq7/U5SR3X+ZDduRYMKlkNtkLcc98e9Sih5AThIJLUdffRS/M+gQRiWvc1sm1ww==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-locate-window@3.893.0': - resolution: {integrity: sha512-T89pFfgat6c8nMmpI8eKjBcDcgJq36+m9oiXbcUzeU55MP9ZuGgBomGjGnHaEyF36jenW9gmg3NfZDm0AO2XPg==} + '@aws-sdk/util-locate-window@3.953.0': + resolution: {integrity: sha512-mPxK+I1LcrgC/RSa3G5AMAn8eN2Ay0VOgw8lSRmV1jCtO+iYvNeCqOdxoJUjOW6I5BA4niIRWqVORuRP07776Q==} engines: {node: '>=18.0.0'} - '@aws-sdk/util-user-agent-browser@3.930.0': - resolution: {integrity: sha512-q6lCRm6UAe+e1LguM5E4EqM9brQlDem4XDcQ87NzEvlTW6GzmNCO0w1jS0XgCFXQHjDxjdlNFX+5sRbHijwklg==} + '@aws-sdk/util-user-agent-browser@3.956.0': + resolution: {integrity: sha512-s8KwYR3HqiGNni7a1DN2P3RUog64QoBQ6VCSzJkHBWb6++8KSOpqeeDkfmEz+22y1LOne+bRrpDGKa0aqOc3rQ==} - '@aws-sdk/util-user-agent-node@3.934.0': - resolution: {integrity: sha512-vPRR4PaqNmuOQJSzq4EAVwFHUaSpPtgDgCEc7AYbArIy+59fckb6JNddlrjx4w4iWbqO0d+7OC5PtRcIk0AcZA==} + '@aws-sdk/util-user-agent-node@3.956.0': + resolution: {integrity: sha512-H0r6ol3Rr63/3xvrUsLqHps+cA7VkM7uCU5NtuTHnMbv3uYYTKf9M2XFHAdVewmmRgssTzvqemrARc8Ji3SNvg==} engines: {node: '>=18.0.0'} peerDependencies: aws-crt: '>=1.0.0' @@ -1185,12 +1195,12 @@ packages: aws-crt: optional: true - '@aws-sdk/xml-builder@3.930.0': - resolution: {integrity: sha512-YIfkD17GocxdmlUVc3ia52QhcWuRIUJonbF8A2CYfcWNV3HzvAqpcPeC0bYUhkK+8e8YO1ARnLKZQE0TlwzorA==} + '@aws-sdk/xml-builder@3.956.0': + resolution: {integrity: sha512-x/IvXUeQYNUEQojpRIQpFt4X7XGxqzjUlXFRdwaTCtTz3q1droXVJvYOhnX3KiMgzeHGlBJfY4Nmq3oZNEUGFw==} engines: {node: '>=18.0.0'} - '@aws/lambda-invoke-store@0.2.0': - resolution: {integrity: sha512-D1jAmAZQYMoPiacfgNf7AWhg3DFN3Wq/vQv3WINt9znwjzHp2x+WzdJFxxj7xZL7V1U79As6G8f7PorMYWBKsQ==} + '@aws/lambda-invoke-store@0.2.2': + resolution: {integrity: sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==} engines: {node: '>=18.0.0'} '@babel/code-frame@7.27.1': @@ -1836,8 +1846,8 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@blueprintjs/colors@5.1.11': - resolution: {integrity: sha512-nSmsgfORb1bYxW4FiMBmFuPPqBK0IbLn1M0hqQCZQ+3hkMJKyQIypBCsw44o4KrpJXbWdS7y2I/p40ZWubYHXw==} + '@blueprintjs/colors@5.1.12': + resolution: {integrity: sha512-7GQWUQ82eLE1te++DC8fRO2B31bsSwia82NLamZfKgjHY9V4zxafMT1DK5gKlmmy0nCjpdcCc+df4aVZMHGLww==} '@blueprintjs/core@5.19.0': resolution: {integrity: sha512-wgQUNX92ffT1A358BeAseRe/MsyYW84U5xX6vo6UV45vHj18S/gOvqOZ1qew2xtAWudAFsxB8jqmrgJRwFsDqg==} @@ -1885,14 +1895,14 @@ packages: openai: ^4.62.1 zod: ^3.23.8 - '@bufbuild/protobuf@2.10.1': - resolution: {integrity: sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg==} + '@bufbuild/protobuf@2.10.2': + resolution: {integrity: sha512-uFsRXwIGyu+r6AMdz+XijIIZJYpoWeYzILt5yZ2d3mCjQrWUTVpVD9WL/jZAbvp+Ed04rOhrsk7FiTcEDseB5A==} - '@cacheable/utils@2.3.1': - resolution: {integrity: sha512-38NJXjIr4W1Sghun8ju+uYWD8h2c61B4dKwfnQHVDFpAJ9oS28RpfqZQJ6Dgd3RceGkILDY9YT+72HJR3LoeSQ==} + '@cacheable/utils@2.3.2': + resolution: {integrity: sha512-8kGE2P+HjfY8FglaOiW+y8qxcaQAfAhVML+i66XJR3YX5FtyDqn6Txctr3K2FrbxLKixRRYYBWMbuGciOhYNDg==} - '@casl/ability@6.7.3': - resolution: {integrity: sha512-A4L28Ko+phJAsTDhRjzCOZWECQWN2jzZnJPnROWWHjJpyMq1h7h9ZqjwS2WbIUa3Z474X1ZPSgW0f1PboZGC0A==} + '@casl/ability@6.7.5': + resolution: {integrity: sha512-NaOHPi9JMn8Kesh+GRkjNKAYkl4q8qMFAlqw7w2yrE+cBQZSbV9GkBGKvgzs3CdzEc5Yl1cn3JwDxxbBN5gjog==} '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} @@ -1941,8 +1951,8 @@ packages: '@copilotkit/shared@1.10.6': resolution: {integrity: sha512-56Rltf4fDBqCpl1ZXARypt5NdE4LTg3tGPPLurZpgPmm31Lv5EAHpfjC7I55vt9A0mXWlTCHtCrpiaAlTyzGJw==} - '@crxjs/vite-plugin@2.2.1': - resolution: {integrity: sha512-SrQY/npF0BNc34DKzA34lHC+aQNlMqbiwoRyzr8EGWngmApwuOAGLHzmGVWIfE8rJmu+TsFC8mKFG4LqLM2+hA==} + '@crxjs/vite-plugin@2.3.0': + resolution: {integrity: sha512-+0CNVGS4bB30OoaF1vUsHVwWU1Lm7MxI0XWY9Fd/Ob+ZVTZgEFNqJ1ZC69IVwQsoYhY0sMQLvpLWiFIuDz8htg==} '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} @@ -2045,8 +2055,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.0': - resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -2057,8 +2067,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.0': - resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -2069,8 +2079,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.0': - resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2081,8 +2091,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.0': - resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2093,8 +2103,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.0': - resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2105,8 +2115,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.0': - resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2117,8 +2127,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.0': - resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2129,8 +2139,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.0': - resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2141,8 +2151,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.0': - resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2153,8 +2163,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.0': - resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2165,8 +2175,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.0': - resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2177,8 +2187,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.0': - resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2189,8 +2199,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.0': - resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2201,8 +2211,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.0': - resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2213,8 +2223,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.0': - resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2225,8 +2235,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.0': - resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2237,8 +2247,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.0': - resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -2249,8 +2259,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.0': - resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2261,8 +2271,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.0': - resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -2273,8 +2283,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.0': - resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2285,8 +2295,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.0': - resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -2297,8 +2307,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.0': - resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2309,8 +2319,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.0': - resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2321,8 +2331,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.0': - resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2333,8 +2343,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.0': - resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2345,8 +2355,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.0': - resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -2378,8 +2388,8 @@ packages: resolution: {integrity: sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==} engines: {node: '>=18'} - '@expo/devcert@1.2.0': - resolution: {integrity: sha512-Uilcv3xGELD5t/b0eM4cxBFEKQRIivB3v7i+VhWLV/gL98aw810unLKKJbGAxAIhY6Ipyz8ChWibFsKFXYwstA==} + '@expo/devcert@1.2.1': + resolution: {integrity: sha512-qC4eaxmKMTmJC2ahwyui6ud8f3W60Ss7pMkpBq40Hu3zyiAaugPXnZ24145U7K36qO9UHdZUVxsCvIpz2RYYCA==} '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} @@ -2429,26 +2439,26 @@ packages: '@fractalwagmi/solana-wallet-adapter@0.1.1': resolution: {integrity: sha512-oTZLEuD+zLKXyhZC5tDRMPKPj8iaxKLxXiCjqRfOo4xmSbS2izGRWLJbKMYYsJysn/OI3UJ3P6CWP8WUWi0dZg==} - '@graphql-tools/executor@1.4.13': - resolution: {integrity: sha512-2hTSRfH2kb4ua0ANOV/K6xUoCZsHAE6igE1bimtWUK7v0bowPIxGRKRPpF8JLbImpsJuTCC4HGOCMy7otg3FIQ==} + '@graphql-tools/executor@1.5.0': + resolution: {integrity: sha512-3HzAxfexmynEWwRB56t/BT+xYKEYLGPvJudR1jfs+XZX8bpfqujEhqVFoxmkpEE8BbFcKuBNoQyGkTi1eFJ+hA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/merge@9.1.5': - resolution: {integrity: sha512-eVcir6nCcOC/Wzv7ZAng3xec3dj6FehE8+h9TvgvUyrDEKVMdFfrO6etRFZ2hucWVcY8S6drx7zQx04N4lPM8Q==} + '@graphql-tools/merge@9.1.6': + resolution: {integrity: sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/schema@10.0.29': - resolution: {integrity: sha512-+Htiupnq6U/AWOEAJerIOGT1pAf4u43Q3n2JmFpqFfYJchz6sKWZ7L9Lpe/NusaaUQty/IOF+eQlNFypEaWxhg==} + '@graphql-tools/schema@10.0.30': + resolution: {integrity: sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/utils@10.10.3': - resolution: {integrity: sha512-2EdYiefeLLxsoeZTukSNZJ0E/Z5NnWBUGK2VJa0DQj1scDhVd93HeT1eW9TszJOYmIh3eWAKLv58ri/1XUmdsQ==} + '@graphql-tools/utils@10.11.0': + resolution: {integrity: sha512-iBFR9GXIs0gCD+yc3hoNswViL1O5josI33dUqiNStFI/MHLCEPduasceAcazRH77YONKNiviHBV8f7OgcT4o2Q==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -2462,12 +2472,12 @@ packages: resolution: {integrity: sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA==} engines: {node: '>=18.0.0'} - '@graphql-yoga/plugin-defer-stream@3.16.2': - resolution: {integrity: sha512-yeyww8E1jfm7Cx16CshEC/Uj2LqGqGqlCyhXWqt18InFiVcI28OiavFhW45qFxLPcc7QYBigRJhODLqGlfkxhQ==} + '@graphql-yoga/plugin-defer-stream@3.18.0': + resolution: {integrity: sha512-VbvQP6hzSNHXXtX+OlK0DrbzQxE4ifP/PQzXUiECCYFOI8manRz+lmEmI1cL+HS6YlppXbtO7Qeb41MNEWLegA==} engines: {node: '>=18.0.0'} peerDependencies: graphql: ^15.2.0 || ^16.0.0 - graphql-yoga: ^5.16.2 + graphql-yoga: ^5.18.0 '@graphql-yoga/subscription@5.0.5': resolution: {integrity: sha512-oCMWOqFs6QV96/NZRt/ZhTQvzjkGB4YohBOpKM4jH/lDT4qb7Lex/aGCxpi/JD9njw3zBBtMqxbaC22+tFHVvw==} @@ -2477,8 +2487,8 @@ packages: resolution: {integrity: sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA==} engines: {node: '>=18.0.0'} - '@grpc/grpc-js@1.14.1': - resolution: {integrity: sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==} + '@grpc/grpc-js@1.14.3': + resolution: {integrity: sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==} engines: {node: '>=12.10.0'} '@grpc/proto-loader@0.8.0': @@ -2493,6 +2503,12 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc + '@hono/node-server@1.19.7': + resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@hookform/resolvers@3.10.0': resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} peerDependencies: @@ -2511,8 +2527,8 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead - '@ibm-cloud/watsonx-ai@1.7.3': - resolution: {integrity: sha512-059l3Rs+FLZTfxIPVUiKLasEC0lkXyTo5HJBwzI1eFZUNjBdJW9BevPB7I5+6msSLoLy7Zbin4W9YarqOydvWg==} + '@ibm-cloud/watsonx-ai@1.7.5': + resolution: {integrity: sha512-j9iQ7cuMbE3fYF+midMDMG16ziZLVZa0IMO3kd2HUFMjIxJ7JN6O29WSt9KaOeDAWRMb7wkN1jcdMcYWpFtytw==} engines: {node: '>=18.0.0'} '@icons/material@0.2.4': @@ -3270,17 +3286,17 @@ packages: peerDependencies: '@langchain/core': '>=0.2.21 <0.4.0' - '@ledgerhq/devices@8.7.0': - resolution: {integrity: sha512-3pSOULPUhClk2oOxa6UnoyXW8+05FK7r6cpq2WiTey4kyIUjmIraO7AX9lhz9IU73dGVtBMdU+NCPPO2RYJaTQ==} + '@ledgerhq/devices@8.7.1': + resolution: {integrity: sha512-xUtqLp4t7PhVKkMG/mCrSzxGQ25JA06UAIxxq07EwuJKyWaey1gSdvqr0TvbTw/fsJNya4FIBYTrUkjH9XpdBQ==} - '@ledgerhq/errors@6.27.0': - resolution: {integrity: sha512-EE2hATONHdNP3YWFe3rZwwpSEzI5oN+q/xTjOulnjHZo84TLjungegEJ54A/Pzld0woulkkeVA27FbW5SAO1aA==} + '@ledgerhq/errors@6.27.1': + resolution: {integrity: sha512-Q6nXkg63kSjxa8DDBtjSGAuiOP/jQVQM/0f+0R/ovilQKuxMIxEMXPfonvhQ2AIP4bqKOzGV1c9Iqnow22w/Zg==} - '@ledgerhq/hw-transport-webhid@6.30.9': - resolution: {integrity: sha512-5if/V1NyOr4JuEX+NB/bxaE92TptpgX+r1mGmzDHG6Gs9/fX/HhKe1GVwwU5C7LvTFrdTgMzEs2aIkpoMM60Ag==} + '@ledgerhq/hw-transport-webhid@6.30.10': + resolution: {integrity: sha512-vPTHhgCNeymlqjcK3VlMCAzEqUC5o6+NTFq2V6ywbKBkT7Wl8jUR2y+Kfba41Pg+DXTUZ4BzgMM6tNNx48ioiA==} - '@ledgerhq/hw-transport@6.31.13': - resolution: {integrity: sha512-MrJRDk74wY980ofiFPRpTHQBbRw1wDuKbdag1zqlO1xtJglymwwY03K2kvBNvkm1RTSCPUp/nAoNG+WThZuuew==} + '@ledgerhq/hw-transport@6.31.14': + resolution: {integrity: sha512-aTKItTLDCN2aoZv6nX6weMslgzT0mI8Hp2FEt2iTomQ6V5r1/BRDRbz2WB1989dZsgBajPRuUWhTZ/Bt6tiijw==} '@ledgerhq/logs@6.13.0': resolution: {integrity: sha512-4+qRW2Pc8V+btL0QEmdB2X+uyx0kOWMWE1/LWsq5sZy3Q5tpi4eItJS6mB0XL3wGW59RQ+8bchNQQ1OW/va8Og==} @@ -3371,14 +3387,14 @@ packages: '@mastra/core': '>=0.20.1-0 <0.22.0-0' zod: ^3.25.0 || ^4.0.0 - '@mastra/memory@0.15.11': - resolution: {integrity: sha512-HxcvJ+JBCEN9f8JQ+mPgxBm2K9Ln52aGsX0OeqG1sgt1vac60PqQi1MbywbiIsLxlMkVFysE2C/7bFh+d6vtjQ==} + '@mastra/memory@0.15.13': + resolution: {integrity: sha512-88RBgT1VIseyvKdkzVJFw88gtFmf/+6vOMwJaeLzSq++oHtoD+A0nkUvCtuX3aYsicpf5IPUBnvEgJ9pH6J/gg==} peerDependencies: '@mastra/core': '>=0.20.1-0 <0.25.0-0' zod: ^3.25.0 || ^4.0.0 - '@mastra/pg@0.17.8': - resolution: {integrity: sha512-5jumeTaasPv7K9zlCwadzTjbXgRnMfEGx3JqzeDEFX7jrwHC9DcMyPP8/DEzgwijVL5r+vDixL8ia5vlYmt/GQ==} + '@mastra/pg@0.17.10': + resolution: {integrity: sha512-+aP8FpDI6Y32ZO4TYqVdxWtoJXc4ZUp0joltRZwUwazond8WB7i7ZTw8BzWoaKsEcEumf5EMoYwxUPJrV90sZA==} peerDependencies: '@mastra/core': '>=0.20.1-0 <0.25.0-0' @@ -3388,8 +3404,8 @@ packages: ai: ^4.0.0 || ^5.0.0 zod: ^3.25.0 || ^4.0.0 - '@mastra/schema-compat@0.11.7': - resolution: {integrity: sha512-F/Q2myO7ar5+p2zRb1anGvLrUCKmdrJraXq9TWsWRp7frrcGUwlewf6Qo9+lp6NSumH0XlC/yvcjzzDwVzaMEw==} + '@mastra/schema-compat@0.11.9': + resolution: {integrity: sha512-LXEChx5n3bcuSFWQ5Wn9K2spLEpzHGf+DCnAeryuecpOo8VGLJ2QCK9Ugsnfjuc6hC0Ha73HvL1AD8zDhjmYOg==} peerDependencies: ai: ^4.0.0 || ^5.0.0 zod: ^3.25.0 || ^4.0.0 @@ -3408,11 +3424,12 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} - '@modelcontextprotocol/sdk@1.22.0': - resolution: {integrity: sha512-VUpl106XVTCpDmTBil2ehgJZjhyLY2QZikzF8NvTXtLRF1CvO5iEE2UNZdVIUer35vFOwMKYeUGbjJtvPWan3g==} + '@modelcontextprotocol/sdk@1.25.1': + resolution: {integrity: sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -3866,8 +3883,8 @@ packages: '@nestjs/platform-express': optional: true - '@nestjs/throttler@6.4.0': - resolution: {integrity: sha512-osL67i0PUuwU5nqSuJjtUJZMkxAnYB4VldgYUMGzvYRJDCqGRFMWbsbzm/CkUtPLRL30I8T74Xgt/OQxnYokiA==} + '@nestjs/throttler@6.5.0': + resolution: {integrity: sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==} peerDependencies: '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 @@ -4095,8 +4112,8 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} - '@openapitools/openapi-generator-cli@2.25.1': - resolution: {integrity: sha512-IY0Vgnv//v2X7+gqAj+jE+CoygdeIx15ZaF2SK+/kQR/sHBJTgxOOc0V+ti2tfxv4pkOILcTcPhmuZIobxPQhg==} + '@openapitools/openapi-generator-cli@2.25.2': + resolution: {integrity: sha512-TXElbW1NXCy0EECXiO5AD2ZzT1dmaCs41Z8t3pBUGaJf8zgF/Lm0P6GRhVEpw29iHBNjZcy8nrgQ1acUfuCdng==} engines: {node: '>=16'} hasBin: true @@ -4111,14 +4128,10 @@ packages: resolution: {integrity: sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==} engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.204.0': - resolution: {integrity: sha512-DqxY8yoAaiBPivoJD4UtgrMS8gEmzZ5lnaxzPojzLVHBGqPxgWm4zcuvcUHZiqQ6kRX2Klel2r9y8cA2HAtqpw==} + '@opentelemetry/api-logs@0.208.0': + resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} engines: {node: '>=8.0.0'} - '@opentelemetry/api-logs@0.57.2': - resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} - engines: {node: '>=14'} - '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -4148,12 +4161,6 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.1.0': - resolution: {integrity: sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==} - engines: {node: ^18.19.0 || >=20.6.0} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.2.0': resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4232,8 +4239,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-amqplib@0.51.0': - resolution: {integrity: sha512-XGmjYwjVRktD4agFnWBWQXo9SiYHKBxR6Ag3MLXwtLE4R99N3a08kGKM5SC1qOFKIELcQDGFEFT9ydXMH00Luw==} + '@opentelemetry/instrumentation-amqplib@0.55.0': + resolution: {integrity: sha512-5ULoU8p+tWcQw5PDYZn8rySptGSLZHNX/7srqo2TioPnAAcvTy6sQFQXsNPrAnyRRtYGMetXVyZUy5OaX1+IfA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4268,8 +4275,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-connect@0.48.0': - resolution: {integrity: sha512-OMjc3SFL4pC16PeK+tDhwP7MRvDPalYCGSvGqUhX5rASkI2H0RuxZHOWElYeXkV0WP+70Gw6JHWac/2Zqwmhdw==} + '@opentelemetry/instrumentation-connect@0.52.0': + resolution: {integrity: sha512-GXPxfNB5szMbV3I9b7kNWSmQBoBzw7MT0ui6iU/p+NIzVx3a06Ri2cdQO7tG9EKb4aKSLmfX9Cw5cKxXqX6Ohg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4286,8 +4293,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-dataloader@0.22.0': - resolution: {integrity: sha512-bXnTcwtngQsI1CvodFkTemrrRSQjAjZxqHVc+CJZTDnidT0T6wt3jkKhnsjU/Kkkc0lacr6VdRpCu2CUWa0OKw==} + '@opentelemetry/instrumentation-dataloader@0.26.0': + resolution: {integrity: sha512-P2BgnFfTOarZ5OKPmYfbXfDFjQ4P9WkQ1Jji7yH5/WwB6Wm/knynAoA1rxbjWcDlYupFkyT0M1j6XLzDzy0aCA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4304,8 +4311,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-express@0.53.0': - resolution: {integrity: sha512-r/PBafQmFYRjuxLYEHJ3ze1iBnP2GDA1nXOSS6E02KnYNZAVjj6WcDA1MSthtdAUUK0XnotHvvWM8/qz7DMO5A==} + '@opentelemetry/instrumentation-express@0.57.0': + resolution: {integrity: sha512-HAdx/o58+8tSR5iW+ru4PHnEejyKrAy9fYFhlEI81o10nYxrGahnMAHWiSjhDC7UQSY3I4gjcPgSKQz4rm/asg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4322,8 +4329,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fs@0.24.0': - resolution: {integrity: sha512-HjIxJ6CBRD770KNVaTdMXIv29Sjz4C1kPCCK5x1Ujpc6SNnLGPqUVyJYZ3LUhhnHAqdbrl83ogVWjCgeT4Q0yw==} + '@opentelemetry/instrumentation-fs@0.28.0': + resolution: {integrity: sha512-FFvg8fq53RRXVBRHZViP+EMxMR03tqzEGpuq55lHNbVPyFklSVfQBN50syPhK5UYYwaStx0eyCtHtbRreusc5g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4334,8 +4341,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-generic-pool@0.48.0': - resolution: {integrity: sha512-TLv/On8pufynNR+pUbpkyvuESVASZZKMlqCm4bBImTpXKTpqXaJJ3o/MUDeMlM91rpen+PEv2SeyOKcHCSlgag==} + '@opentelemetry/instrumentation-generic-pool@0.52.0': + resolution: {integrity: sha512-ISkNcv5CM2IwvsMVL31Tl61/p2Zm2I2NAsYq5SSBgOsOndT0TjnptjufYVScCnD5ZLD1tpl4T3GEYULLYOdIdQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4346,8 +4353,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-graphql@0.52.0': - resolution: {integrity: sha512-3fEJ8jOOMwopvldY16KuzHbRhPk8wSsOTSF0v2psmOCGewh6ad+ZbkTx/xyUK9rUdUMWAxRVU0tFpj4Wx1vkPA==} + '@opentelemetry/instrumentation-graphql@0.56.0': + resolution: {integrity: sha512-IPvNk8AFoVzTAM0Z399t34VDmGDgwT6rIqCUug8P9oAGerl2/PEIYMPOl/rerPGu+q8gSWdmbFSjgg7PDVRd3Q==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4364,8 +4371,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-hapi@0.51.0': - resolution: {integrity: sha512-qyf27DaFNL1Qhbo/da+04MSCw982B02FhuOS5/UF+PMhM61CcOiu7fPuXj8TvbqyReQuJFljXE6UirlvoT/62g==} + '@opentelemetry/instrumentation-hapi@0.55.0': + resolution: {integrity: sha512-prqAkRf9e4eEpy4G3UcR32prKE8NLNlA90TdEU1UsghOTg0jUvs40Jz8LQWFEs5NbLbXHYGzB4CYVkCI8eWEVQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4376,8 +4383,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.204.0': - resolution: {integrity: sha512-1afJYyGRA4OmHTv0FfNTrTAzoEjPQUYgd+8ih/lX0LlZBnGio/O80vxA0lN3knsJPS7FiDrsDrWq25K7oAzbkw==} + '@opentelemetry/instrumentation-http@0.208.0': + resolution: {integrity: sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4388,8 +4395,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-ioredis@0.52.0': - resolution: {integrity: sha512-rUvlyZwI90HRQPYicxpDGhT8setMrlHKokCtBtZgYxQWRF5RBbG4q0pGtbZvd7kyseuHbFpA3I/5z7M8b/5ywg==} + '@opentelemetry/instrumentation-ioredis@0.56.0': + resolution: {integrity: sha512-XSWeqsd3rKSsT3WBz/JKJDcZD4QYElZEa0xVdX8f9dh4h4QgXhKRLorVsVkK3uXFbC2sZKAS2Ds+YolGwD83Dg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4400,8 +4407,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-kafkajs@0.14.0': - resolution: {integrity: sha512-kbB5yXS47dTIdO/lfbbXlzhvHFturbux4EpP0+6H78Lk0Bn4QXiZQW7rmZY1xBCY16mNcCb8Yt0mhz85hTnSVA==} + '@opentelemetry/instrumentation-kafkajs@0.18.0': + resolution: {integrity: sha512-KCL/1HnZN5zkUMgPyOxfGjLjbXjpd4odDToy+7c+UsthIzVLFf99LnfIBE8YSSrYE4+uS7OwJMhvhg3tWjqMBg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4412,8 +4419,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-knex@0.49.0': - resolution: {integrity: sha512-NKsRRT27fbIYL4Ix+BjjP8h4YveyKc+2gD6DMZbr5R5rUeDqfC8+DTfIt3c3ex3BIc5Vvek4rqHnN7q34ZetLQ==} + '@opentelemetry/instrumentation-knex@0.53.0': + resolution: {integrity: sha512-xngn5cH2mVXFmiT1XfQ1aHqq1m4xb5wvU6j9lSgLlihJ1bXzsO543cpDwjrZm2nMrlpddBf55w8+bfS4qDh60g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4424,11 +4431,11 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-koa@0.52.0': - resolution: {integrity: sha512-JJSBYLDx/mNSy8Ibi/uQixu2rH0bZODJa8/cz04hEhRaiZQoeJ5UrOhO/mS87IdgVsHrnBOsZ6vDu09znupyuA==} + '@opentelemetry/instrumentation-koa@0.57.0': + resolution: {integrity: sha512-3JS8PU/D5E3q295mwloU2v7c7/m+DyCqdu62BIzWt+3u9utjxC9QS7v6WmUNuoDN3RM+Q+D1Gpj13ERo+m7CGg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: - '@opentelemetry/api': ^1.3.0 + '@opentelemetry/api': ^1.9.0 '@opentelemetry/instrumentation-lru-memoizer@0.48.0': resolution: {integrity: sha512-KUW29wfMlTPX1wFz+NNrmE7IzN7NWZDrmFWHM/VJcmFEuQGnnBuTIdsP55CnBDxKgQ/qqYFp4udQFNtjeFosPw==} @@ -4436,8 +4443,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-lru-memoizer@0.49.0': - resolution: {integrity: sha512-ctXu+O/1HSadAxtjoEg2w307Z5iPyLOMM8IRNwjaKrIpNAthYGSOanChbk1kqY6zU5CrpkPHGdAT6jk8dXiMqw==} + '@opentelemetry/instrumentation-lru-memoizer@0.53.0': + resolution: {integrity: sha512-LDwWz5cPkWWr0HBIuZUjslyvijljTwmwiItpMTHujaULZCxcYE9eU44Qf/pbVC8TulT0IhZi+RoGvHKXvNhysw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4454,8 +4461,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongodb@0.57.0': - resolution: {integrity: sha512-KD6Rg0KSHWDkik+qjIOWoksi1xqSpix8TSPfquIK1DTmd9OTFb5PHmMkzJe16TAPVEuElUW8gvgP59cacFcrMQ==} + '@opentelemetry/instrumentation-mongodb@0.61.0': + resolution: {integrity: sha512-OV3i2DSoY5M/pmLk+68xr5RvkHU8DRB3DKMzYJdwDdcxeLs62tLbkmRyqJZsYf3Ht7j11rq35pHOWLuLzXL7pQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4466,8 +4473,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongoose@0.51.0': - resolution: {integrity: sha512-gwWaAlhhV2By7XcbyU3DOLMvzsgeaymwP/jktDC+/uPkCmgB61zurwqOQdeiRq9KAf22Y2dtE5ZLXxytJRbEVA==} + '@opentelemetry/instrumentation-mongoose@0.55.0': + resolution: {integrity: sha512-5afj0HfF6aM6Nlqgu6/PPHFk8QBfIe3+zF9FGpX76jWPS0/dujoEYn82/XcLSaW5LPUDW8sni+YeK0vTBNri+w==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4478,8 +4485,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql2@0.51.0': - resolution: {integrity: sha512-zT2Wg22Xn43RyfU3NOUmnFtb5zlDI0fKcijCj9AcK9zuLZ4ModgtLXOyBJSSfO+hsOCZSC1v/Fxwj+nZJFdzLQ==} + '@opentelemetry/instrumentation-mysql2@0.55.0': + resolution: {integrity: sha512-0cs8whQG55aIi20gnK8B7cco6OK6N+enNhW0p5284MvqJ5EPi+I1YlWsWXgzv/V2HFirEejkvKiI4Iw21OqDWg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4490,8 +4497,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql@0.50.0': - resolution: {integrity: sha512-duKAvMRI3vq6u9JwzIipY9zHfikN20bX05sL7GjDeLKr2qV0LQ4ADtKST7KStdGcQ+MTN5wghWbbVdLgNcB3rA==} + '@opentelemetry/instrumentation-mysql@0.54.0': + resolution: {integrity: sha512-bqC1YhnwAeWmRzy1/Xf9cDqxNG2d/JDkaxnqF5N6iJKN1eVWI+vg7NfDkf52/Nggp3tl1jcC++ptC61BD6738A==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4502,8 +4509,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-nestjs-core@0.50.0': - resolution: {integrity: sha512-10u2Gjw260W8vdUem6pM7ENrb8i+UAyrgouhjN7HRdQYh9rcit51tRhgrI52fxTsRjrrBNIItHkX0YM8WnEU2w==} + '@opentelemetry/instrumentation-nestjs-core@0.55.0': + resolution: {integrity: sha512-JFLNhbbEGnnQrMKOYoXx0nNk5N9cPeghu4xP/oup40a7VaSeYruyOiFbg9nkbS4ZQiI8aMuRqUT3Mo4lQjKEKg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4526,8 +4533,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-pg@0.57.0': - resolution: {integrity: sha512-dWLGE+r5lBgm2A8SaaSYDE3OKJ/kwwy5WLyGyzor8PLhUL9VnJRiY6qhp4njwhnljiLtzeffRtG2Mf/YyWLeTw==} + '@opentelemetry/instrumentation-pg@0.61.0': + resolution: {integrity: sha512-UeV7KeTnRSM7ECHa3YscoklhUtTQPs6V6qYpG283AB7xpnPGCUCUfECFT9jFg6/iZOQTt3FHkB1wGTJCNZEvPw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4544,8 +4551,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-redis@0.53.0': - resolution: {integrity: sha512-WUHV8fr+8yo5RmzyU7D5BIE1zwiaNQcTyZPwtxlfr7px6NYYx7IIpSihJK7WA60npWynfxxK1T67RAVF0Gdfjg==} + '@opentelemetry/instrumentation-redis@0.57.0': + resolution: {integrity: sha512-bCxTHQFXzrU3eU1LZnOZQ3s5LURxQPDlU3/upBzlWY77qOI1GZuGofazj3jtzjctMJeBEJhNwIFEgRPBX1kp/Q==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4580,8 +4587,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-tedious@0.23.0': - resolution: {integrity: sha512-3TMTk/9VtlRonVTaU4tCzbg4YqW+Iq/l5VnN2e5whP6JgEg/PKfrGbqQ+CxQWNLfLaQYIUgEZqAn5gk/inh1uQ==} + '@opentelemetry/instrumentation-tedious@0.27.0': + resolution: {integrity: sha512-jRtyUJNZppPBjPae4ZjIQ2eqJbcRaRfJkr0lQLHFmOU/no5A6e9s1OHLd5XZyZoBJ/ymngZitanyRRA5cniseA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4592,8 +4599,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.7.0 - '@opentelemetry/instrumentation-undici@0.15.0': - resolution: {integrity: sha512-sNFGA/iCDlVkNjzTzPRcudmI11vT/WAfAguRdZY9IspCw02N4WSC72zTuQhSMheh2a1gdeM9my1imnKRvEEvEg==} + '@opentelemetry/instrumentation-undici@0.19.0': + resolution: {integrity: sha512-Pst/RhR61A2OoZQZkn6OLpdVpXp6qn3Y92wXa6umfJe9rV640r4bc6SWvw4pPN6DiQqPu2c8gnSSZPDtC6JlpQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.7.0 @@ -4610,18 +4617,12 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.204.0': - resolution: {integrity: sha512-vV5+WSxktzoMP8JoYWKeopChy6G3HKk4UQ2hESCRDUUTZqQ3+nM3u8noVG0LmNfRWwcFBnbZ71GKC7vaYYdJ1g==} + '@opentelemetry/instrumentation@0.208.0': + resolution: {integrity: sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation@0.57.2': - resolution: {integrity: sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': ^1.3.0 - '@opentelemetry/otlp-exporter-base@0.203.0': resolution: {integrity: sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4662,8 +4663,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 - '@opentelemetry/resource-detector-aws@2.8.0': - resolution: {integrity: sha512-L8K5L3bsDKboX7sDofZyRonyK8dfS+CF7ho8YbZ6OrH+d5uyRBsrjuokPzcju1jP2ZzgtpYzhLwzi9zPXyRLlA==} + '@opentelemetry/resource-detector-aws@2.9.0': + resolution: {integrity: sha512-2dk1TuuImatD8n0OyBgghucluGcj2XtnortmJdLH0OffM7cVtat4h7Dcg8IZvP7WrMjbP4ZQQ2cpD1Fhvx6BsA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.0.0 @@ -4756,8 +4757,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 - '@optimize-lodash/rollup-plugin@5.0.2': - resolution: {integrity: sha512-UWBD9/C5jO0rDAbiqrZqiTLPD0LOHG3DzBo8ubLTpNWY9xOz5f5+S2yuxG/7ICk8sx8K6pZ8O/jsAbFgjtfh6w==} + '@optimize-lodash/rollup-plugin@5.1.0': + resolution: {integrity: sha512-dBQYGH8+n4Z/877e61PJteVZEc+U1wfRF6IhKqW0cABRIsqMxpWynyov6M6Sd4IUjru0dnh0EG55X86JO6WakQ==} engines: {node: '>= 18'} peerDependencies: rollup: '>= 4.x' @@ -4878,8 +4879,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.56.1': - resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==} + '@playwright/test@1.57.0': + resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} engines: {node: '>=18'} hasBin: true @@ -4915,8 +4916,8 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@posthog/core@1.5.2': - resolution: {integrity: sha512-iedUP3EnOPPxTA2VaIrsrd29lSZnUV+ZrMnvY56timRVeZAXoYCkmjfIs3KBAsF8OUT5h1GXLSkoQdrV0r31OQ==} + '@posthog/core@1.8.1': + resolution: {integrity: sha512-jfzBtQIk9auRi/biO+G/gumK5KxqsD5wOr7XpYMROE/I3pazjP4zIziinp21iQuIQJMXrDvwt9Af3njgOGwtew==} '@postiz/wallets@0.0.1': resolution: {integrity: sha512-zCkg5ZXHZkyCREvoAtxQAp5IoCYfSQs9xonzyMvV/LoY32KjudV5wc4rb4R7NYNdPTGfcni1R8ETojASnK6oUw==} @@ -4954,8 +4955,8 @@ packages: '@prisma/get-platform@6.5.0': resolution: {integrity: sha512-xYcvyJwNMg2eDptBYFqFLUCfgi+wZLcj6HDMsj0Qw0irvauG4IKmkbywnqwok0B+k+W+p+jThM2DKTSmoPCkzw==} - '@prisma/instrumentation@6.15.0': - resolution: {integrity: sha512-6TXaH6OmDkMOQvOxwLZ8XS51hU2v4A3vmE2pSijCIiGRJYyNeMcL6nMHQMyYdZRD8wl7LF3Wzc+AMPMV/9Oo7A==} + '@prisma/instrumentation@6.19.0': + resolution: {integrity: sha512-QcuYy25pkXM8BJ37wVFBO7Zh34nyRV1GOb2n3lPkkbRYfl4hWl3PTcImP41P0KrzVXfa/45p6eVCos27x3exIg==} peerDependencies: '@opentelemetry/api': ^1.8 @@ -5327,14 +5328,14 @@ packages: '@types/react': optional: true - '@react-aria/focus@3.21.2': - resolution: {integrity: sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==} + '@react-aria/focus@3.21.3': + resolution: {integrity: sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/interactions@3.25.6': - resolution: {integrity: sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==} + '@react-aria/interactions@3.26.0': + resolution: {integrity: sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -5345,8 +5346,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/utils@3.31.0': - resolution: {integrity: sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==} + '@react-aria/utils@3.32.0': + resolution: {integrity: sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -5372,18 +5373,18 @@ packages: peerDependencies: react-native: ^0.0.0-0 || >=0.60 <1.0 - '@react-native/assets-registry@0.82.1': - resolution: {integrity: sha512-B1SRwpntaAcckiatxbjzylvNK562Ayza05gdJCjDQHTiDafa1OABmyB5LHt7qWDOpNkaluD+w11vHF7pBmTpzQ==} + '@react-native/assets-registry@0.83.1': + resolution: {integrity: sha512-AT7/T6UwQqO39bt/4UL5EXvidmrddXrt0yJa7ENXndAv+8yBzMsZn6fyiax6+ERMt9GLzAECikv3lj22cn2wJA==} engines: {node: '>= 20.19.4'} - '@react-native/codegen@0.82.1': - resolution: {integrity: sha512-ezXTN70ygVm9l2m0i+pAlct0RntoV4afftWMGUIeAWLgaca9qItQ54uOt32I/9dBJvzBibT33luIR/pBG0dQvg==} + '@react-native/codegen@0.83.1': + resolution: {integrity: sha512-FpRxenonwH+c2a5X5DZMKUD7sCudHxB3eSQPgV9R+uxd28QWslyAWrpnJM/Az96AEksHnymDzEmzq2HLX5nb+g==} engines: {node: '>= 20.19.4'} peerDependencies: '@babel/core': '*' - '@react-native/community-cli-plugin@0.82.1': - resolution: {integrity: sha512-H/eMdtOy9nEeX7YVeEG1N2vyCoifw3dr9OV8++xfUElNYV7LtSmJ6AqxZUUfxGJRDFPQvaU/8enmJlM/l11VxQ==} + '@react-native/community-cli-plugin@0.83.1': + resolution: {integrity: sha512-FqR1ftydr08PYlRbrDF06eRiiiGOK/hNmz5husv19sK6iN5nHj1SMaCIVjkH/a5vryxEddyFhU6PzO/uf4kOHg==} engines: {node: '>= 20.19.4'} peerDependencies: '@react-native-community/cli': '*' @@ -5394,34 +5395,34 @@ packages: '@react-native/metro-config': optional: true - '@react-native/debugger-frontend@0.82.1': - resolution: {integrity: sha512-a2O6M7/OZ2V9rdavOHyCQ+10z54JX8+B+apYKCQ6a9zoEChGTxUMG2YzzJ8zZJVvYf1ByWSNxv9Se0dca1hO9A==} + '@react-native/debugger-frontend@0.83.1': + resolution: {integrity: sha512-01Rn3goubFvPjHXONooLmsW0FLxJDKIUJNOlOS0cPtmmTIx9YIjxhe/DxwHXGk7OnULd7yl3aYy7WlBsEd5Xmg==} engines: {node: '>= 20.19.4'} - '@react-native/debugger-shell@0.82.1': - resolution: {integrity: sha512-fdRHAeqqPT93bSrxfX+JHPpCXHApfDUdrXMXhoxlPgSzgXQXJDykIViKhtpu0M6slX6xU/+duq+AtP/qWJRpBw==} + '@react-native/debugger-shell@0.83.1': + resolution: {integrity: sha512-d+0w446Hxth5OP/cBHSSxOEpbj13p2zToUy6e5e3tTERNJ8ueGlW7iGwGTrSymNDgXXFjErX+dY4P4/3WokPIQ==} engines: {node: '>= 20.19.4'} - '@react-native/dev-middleware@0.82.1': - resolution: {integrity: sha512-wuOIzms/Qg5raBV6Ctf2LmgzEOCqdP3p1AYN4zdhMT110c39TVMbunpBaJxm0Kbt2HQ762MQViF9naxk7SBo4w==} + '@react-native/dev-middleware@0.83.1': + resolution: {integrity: sha512-QJaSfNRzj3Lp7MmlCRgSBlt1XZ38xaBNXypXAp/3H3OdFifnTZOeYOpFmcpjcXYnDqkxetuwZg8VL65SQhB8dg==} engines: {node: '>= 20.19.4'} - '@react-native/gradle-plugin@0.82.1': - resolution: {integrity: sha512-KkF/2T1NSn6EJ5ALNT/gx0MHlrntFHv8YdooH9OOGl9HQn5NM0ZmQSr86o5utJsGc7ME3R6p3SaQuzlsFDrn8Q==} + '@react-native/gradle-plugin@0.83.1': + resolution: {integrity: sha512-6ESDnwevp1CdvvxHNgXluil5OkqbjkJAkVy7SlpFsMGmVhrSxNAgD09SSRxMNdKsnLtzIvMsFCzyHLsU/S4PtQ==} engines: {node: '>= 20.19.4'} - '@react-native/js-polyfills@0.82.1': - resolution: {integrity: sha512-tf70X7pUodslOBdLN37J57JmDPB/yiZcNDzS2m+4bbQzo8fhx3eG9QEBv5n4fmzqfGAgSB4BWRHgDMXmmlDSVA==} + '@react-native/js-polyfills@0.83.1': + resolution: {integrity: sha512-qgPpdWn/c5laA+3WoJ6Fak8uOm7CG50nBsLlPsF8kbT7rUHIVB9WaP6+GPsoKV/H15koW7jKuLRoNVT7c3Ht3w==} engines: {node: '>= 20.19.4'} - '@react-native/normalize-colors@0.82.1': - resolution: {integrity: sha512-CCfTR1uX+Z7zJTdt3DNX9LUXr2zWXsNOyLbwupW2wmRzrxlHRYfmLgTABzRL/cKhh0Ubuwn15o72MQChvCRaHw==} + '@react-native/normalize-colors@0.83.1': + resolution: {integrity: sha512-84feABbmeWo1kg81726UOlMKAhcQyFXYz2SjRKYkS78QmfhVDhJ2o/ps1VjhFfBz0i/scDwT1XNv9GwmRIghkg==} - '@react-native/virtualized-lists@0.82.1': - resolution: {integrity: sha512-f5zpJg9gzh7JtCbsIwV+4kP3eI0QBuA93JGmwFRd4onQ3DnCjV2J5pYqdWtM95sjSKK1dyik59Gj01lLeKqs1Q==} + '@react-native/virtualized-lists@0.83.1': + resolution: {integrity: sha512-MdmoAbQUTOdicCocm5XAFDJWsswxk7hxa6ALnm6Y88p01HFML0W593hAn6qOt9q6IM1KbAcebtH6oOd4gcQy8w==} engines: {node: '>= 20.19.4'} peerDependencies: - '@types/react': ^19.1.1 + '@types/react': ^19.2.0 react: '*' react-native: '*' peerDependenciesMeta: @@ -5431,8 +5432,8 @@ packages: '@react-stately/flags@3.1.2': resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} - '@react-stately/utils@3.10.8': - resolution: {integrity: sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==} + '@react-stately/utils@3.11.0': + resolution: {integrity: sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -5446,18 +5447,18 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@redis/bloom@5.9.0': - resolution: {integrity: sha512-W9D8yfKTWl4tP8lkC3MRYkMz4OfbuzE/W8iObe0jFgoRmgMfkBV+Vj38gvIqZPImtY0WB34YZkX3amYuQebvRQ==} + '@redis/bloom@5.10.0': + resolution: {integrity: sha512-doIF37ob+l47n0rkpRNgU8n4iacBlKM9xLiP1LtTZTvz8TloJB8qx/MgvhMhKdYG+CvCY2aPBnN2706izFn/4A==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.9.0 + '@redis/client': ^5.10.0 '@redis/client@1.6.1': resolution: {integrity: sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==} engines: {node: '>=14'} - '@redis/client@5.9.0': - resolution: {integrity: sha512-EI0Ti5pojD2p7TmcS7RRa+AJVahdQvP/urpcSbK/K9Rlk6+dwMJTQ354pCNGCwfke8x4yKr5+iH85wcERSkwLQ==} + '@redis/client@5.10.0': + resolution: {integrity: sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==} engines: {node: '>= 18'} '@redis/graph@1.1.1': @@ -5470,33 +5471,33 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@redis/json@5.9.0': - resolution: {integrity: sha512-Bm2jjLYaXdUWPb9RaEywxnjmzw7dWKDZI4MS79mTWPV16R982jVWBj6lY2ZGelJbwxHtEVg4/FSVgYDkuO/MxA==} + '@redis/json@5.10.0': + resolution: {integrity: sha512-B2G8XlOmTPUuZtD44EMGbtoepQG34RCDXLZbjrtON1Djet0t5Ri7/YPXvL9aomXqP8lLTreaprtyLKF4tmXEEA==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.9.0 + '@redis/client': ^5.10.0 '@redis/search@1.2.0': resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} peerDependencies: '@redis/client': ^1.0.0 - '@redis/search@5.9.0': - resolution: {integrity: sha512-jdk2csmJ29DlpvCIb2ySjix2co14/0iwIT3C0I+7ZaToXgPbgBMB+zfEilSuncI2F9JcVxHki0YtLA0xX3VdpA==} + '@redis/search@5.10.0': + resolution: {integrity: sha512-3SVcPswoSfp2HnmWbAGUzlbUPn7fOohVu2weUQ0S+EMiQi8jwjL+aN2p6V3TI65eNfVsJ8vyPvqWklm6H6esmg==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.9.0 + '@redis/client': ^5.10.0 '@redis/time-series@1.1.0': resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} peerDependencies: '@redis/client': ^1.0.0 - '@redis/time-series@5.9.0': - resolution: {integrity: sha512-W6ILxcyOqhnI7ELKjJXOktIg3w4+aBHugDbVpgVLPZ+YDjObis1M0v7ZzwlpXhlpwsfePfipeSK+KWNuymk52w==} + '@redis/time-series@5.10.0': + resolution: {integrity: sha512-cPkpddXH5kc/SdRhF0YG0qtjL+noqFT0AcHbQ6axhsPsO7iqPi1cjxgdkE9TNeKiBUUdCaU1DbqkR/LzbzPBhg==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.9.0 + '@redis/client': ^5.10.0 '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -5614,8 +5615,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.53.3': - resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} cpu: [arm] os: [android] @@ -5624,8 +5625,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.53.3': - resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + '@rollup/rollup-android-arm64@4.54.0': + resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} cpu: [arm64] os: [android] @@ -5634,8 +5635,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.53.3': - resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} cpu: [arm64] os: [darwin] @@ -5644,8 +5645,8 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.3': - resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + '@rollup/rollup-darwin-x64@4.54.0': + resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} cpu: [x64] os: [darwin] @@ -5654,8 +5655,8 @@ packages: cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.53.3': - resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} cpu: [arm64] os: [freebsd] @@ -5664,8 +5665,8 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.3': - resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} cpu: [x64] os: [freebsd] @@ -5674,8 +5675,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': - resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} cpu: [arm] os: [linux] @@ -5684,8 +5685,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.53.3': - resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} cpu: [arm] os: [linux] @@ -5694,8 +5695,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.53.3': - resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} cpu: [arm64] os: [linux] @@ -5704,8 +5705,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.53.3': - resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} cpu: [arm64] os: [linux] @@ -5714,8 +5715,8 @@ packages: cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.53.3': - resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} cpu: [loong64] os: [linux] @@ -5724,8 +5725,8 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.53.3': - resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} cpu: [ppc64] os: [linux] @@ -5734,8 +5735,8 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.53.3': - resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} cpu: [riscv64] os: [linux] @@ -5744,8 +5745,8 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.53.3': - resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} cpu: [riscv64] os: [linux] @@ -5754,8 +5755,8 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.53.3': - resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} cpu: [s390x] os: [linux] @@ -5764,8 +5765,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.53.3': - resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} cpu: [x64] os: [linux] @@ -5774,8 +5775,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.53.3': - resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} cpu: [x64] os: [linux] @@ -5784,8 +5785,8 @@ packages: cpu: [arm64] os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.53.3': - resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} cpu: [arm64] os: [openharmony] @@ -5794,8 +5795,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.53.3': - resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} cpu: [arm64] os: [win32] @@ -5804,13 +5805,13 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.3': - resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.3': - resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} cpu: [x64] os: [win32] @@ -5819,8 +5820,8 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.3': - resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} cpu: [x64] os: [win32] @@ -5882,150 +5883,150 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@sentry-internal/browser-utils@10.26.0': - resolution: {integrity: sha512-rPg1+JZlfp912pZONQAWZzbSaZ9L6R2VrMcCEa+2e2Gqk9um4b+LqF5RQWZsbt5Z0n0azSy/KQ6zAe/zTPXSOg==} + '@sentry-internal/browser-utils@10.32.1': + resolution: {integrity: sha512-sjLLep1es3rTkbtAdTtdpc/a6g7v7bK5YJiZJsUigoJ4NTiFeMI5uIDCxbH/tjJ1q23YE1LzVn7T96I+qBRjHA==} engines: {node: '>=18'} - '@sentry-internal/feedback@10.26.0': - resolution: {integrity: sha512-0vk9eQP0CXD7Y2WkcCIWHaAqnXOAi18/GupgWLnbB2kuQVYVtStWxtW+OWRe8W/XwSnZ5m6JBTVeokuk/O16DQ==} + '@sentry-internal/feedback@10.32.1': + resolution: {integrity: sha512-O24G8jxbfBY1RE/v2qFikPJISVMOrd/zk8FKyl+oUVYdOxU2Ucjk2cR3EQruBFlc7irnL6rT3GPfRZ/kBgLkmQ==} engines: {node: '>=18'} '@sentry-internal/node-cpu-profiler@2.2.0': resolution: {integrity: sha512-oLHVYurqZfADPh5hvmQYS5qx8t0UZzT2u6+/68VXsFruQEOnYJTODKgU3BVLmemRs3WE6kCJjPeFdHVYOQGSzQ==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@10.26.0': - resolution: {integrity: sha512-vs7d/P+8M1L1JVAhhJx2wo15QDhqAipnEQvuRZ6PV7LUcS1un9/Vx49FMxpIkx6JcKADJVwtXrS1sX2hoNT/kw==} + '@sentry-internal/replay-canvas@10.32.1': + resolution: {integrity: sha512-/XGTzWNWVc+B691fIVekV2KeoHFEDA5KftrLFAhEAW7uWOwk/xy3aQX4TYM0LcPm2PBKvoumlAD+Sd/aXk63oA==} engines: {node: '>=18'} - '@sentry-internal/replay@10.26.0': - resolution: {integrity: sha512-FMySQnY2/p0dVtFUBgUO+aMdK2ovqnd7Q/AkvMQUsN/5ulyj6KZx3JX3CqOqRtAr1izoCe4Kh2pi5t//sQmvsg==} + '@sentry-internal/replay@10.32.1': + resolution: {integrity: sha512-KKmLUgIaLRM0VjrMA1ByQTawZyRDYSkG2evvEOVpEtR9F0sumidAQdi7UY71QEKE1RYe/Jcp/3WoaqsMh8tbnQ==} engines: {node: '>=18'} - '@sentry/babel-plugin-component-annotate@4.6.0': - resolution: {integrity: sha512-3soTX50JPQQ51FSbb4qvNBf4z/yP7jTdn43vMTp9E4IxvJ9HKJR7OEuKkCMszrZmWsVABXl02msqO7QisePdiQ==} + '@sentry/babel-plugin-component-annotate@4.6.1': + resolution: {integrity: sha512-aSIk0vgBqv7PhX6/Eov+vlI4puCE0bRXzUG5HdCsHBpAfeMkI8Hva6kSOusnzKqs8bf04hU7s3Sf0XxGTj/1AA==} engines: {node: '>= 14'} - '@sentry/browser@10.26.0': - resolution: {integrity: sha512-uvV4hnkt8bh8yP0disJ0fszy8FdnkyGtzyIVKdeQZbNUefwbDhd3H0KJrAHhJ5ocULMH3B+dipdPmw2QXbEflg==} + '@sentry/browser@10.32.1': + resolution: {integrity: sha512-NPNCXTZ05ZGTFyJdKNqjykpFm+urem0ebosILQiw3C4BxNVNGH4vfYZexyl6prRhmg91oB6GjVNiVDuJiap1gg==} engines: {node: '>=18'} - '@sentry/bundler-plugin-core@4.6.0': - resolution: {integrity: sha512-Fub2XQqrS258jjS8qAxLLU1k1h5UCNJ76i8m4qZJJdogWWaF8t00KnnTyp9TEDJzrVD64tRXS8+HHENxmeUo3g==} + '@sentry/bundler-plugin-core@4.6.1': + resolution: {integrity: sha512-WPeRbnMXm927m4Kr69NTArPfI+p5/34FHftdCRI3LFPMyhZDzz6J3wLy4hzaVUgmMf10eLzmq2HGEMvpQmdynA==} engines: {node: '>= 14'} - '@sentry/cli-darwin@2.58.2': - resolution: {integrity: sha512-MArsb3zLhA2/cbd4rTm09SmTpnEuZCoZOpuZYkrpDw1qzBVJmRFA1W1hGAQ9puzBIk/ubY3EUhhzuU3zN2uD6w==} + '@sentry/cli-darwin@2.58.4': + resolution: {integrity: sha512-kbTD+P4X8O+nsNwPxCywtj3q22ecyRHWff98rdcmtRrvwz8CKi/T4Jxn/fnn2i4VEchy08OWBuZAqaA5Kh2hRQ==} engines: {node: '>=10'} os: [darwin] - '@sentry/cli-linux-arm64@2.58.2': - resolution: {integrity: sha512-ay3OeObnbbPrt45cjeUyQjsx5ain1laj1tRszWj37NkKu55NZSp4QCg1gGBZ0gBGhckI9nInEsmKtix00alw2g==} + '@sentry/cli-linux-arm64@2.58.4': + resolution: {integrity: sha512-0g0KwsOozkLtzN8/0+oMZoOuQ0o7W6O+hx+ydVU1bktaMGKEJLMAWxOQNjsh1TcBbNIXVOKM/I8l0ROhaAb8Ig==} engines: {node: '>=10'} cpu: [arm64] os: [linux, freebsd, android] - '@sentry/cli-linux-arm@2.58.2': - resolution: {integrity: sha512-HU9lTCzcHqCz/7Mt5n+cv+nFuJdc1hGD2h35Uo92GgxX3/IujNvOUfF+nMX9j6BXH6hUt73R5c0Ycq9+a3Parg==} + '@sentry/cli-linux-arm@2.58.4': + resolution: {integrity: sha512-rdQ8beTwnN48hv7iV7e7ZKucPec5NJkRdrrycMJMZlzGBPi56LqnclgsHySJ6Kfq506A2MNuQnKGaf/sBC9REA==} engines: {node: '>=10'} cpu: [arm] os: [linux, freebsd, android] - '@sentry/cli-linux-i686@2.58.2': - resolution: {integrity: sha512-CN9p0nfDFsAT1tTGBbzOUGkIllwS3hygOUyTK7LIm9z+UHw5uNgNVqdM/3Vg+02ymjkjISNB3/+mqEM5osGXdA==} + '@sentry/cli-linux-i686@2.58.4': + resolution: {integrity: sha512-NseoIQAFtkziHyjZNPTu1Gm1opeQHt7Wm1LbLrGWVIRvUOzlslO9/8i6wETUZ6TjlQxBVRgd3Q0lRBG2A8rFYA==} engines: {node: '>=10'} cpu: [x86, ia32] os: [linux, freebsd, android] - '@sentry/cli-linux-x64@2.58.2': - resolution: {integrity: sha512-oX/LLfvWaJO50oBVOn4ZvG2SDWPq0MN8SV9eg5tt2nviq+Ryltfr7Rtoo+HfV+eyOlx1/ZXhq9Wm7OT3cQuz+A==} + '@sentry/cli-linux-x64@2.58.4': + resolution: {integrity: sha512-d3Arz+OO/wJYTqCYlSN3Ktm+W8rynQ/IMtSZLK8nu0ryh5mJOh+9XlXY6oDXw4YlsM8qCRrNquR8iEI1Y/IH+Q==} engines: {node: '>=10'} cpu: [x64] os: [linux, freebsd, android] - '@sentry/cli-win32-arm64@2.58.2': - resolution: {integrity: sha512-+cl3x2HPVMpoSVGVM1IDWlAEREZrrVQj4xBb0TRKII7g3hUxRsAIcsrr7+tSkie++0FuH4go/b5fGAv51OEF3w==} + '@sentry/cli-win32-arm64@2.58.4': + resolution: {integrity: sha512-bqYrF43+jXdDBh0f8HIJU3tbvlOFtGyRjHB8AoRuMQv9TEDUfENZyCelhdjA+KwDKYl48R1Yasb4EHNzsoO83w==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@sentry/cli-win32-i686@2.58.2': - resolution: {integrity: sha512-omFVr0FhzJ8oTJSg1Kf+gjLgzpYklY0XPfLxZ5iiMiYUKwF5uo1RJRdkUOiEAv0IqpUKnmKcmVCLaDxsWclB7Q==} + '@sentry/cli-win32-i686@2.58.4': + resolution: {integrity: sha512-3triFD6jyvhVcXOmGyttf+deKZcC1tURdhnmDUIBkiDPJKGT/N5xa4qAtHJlAB/h8L9jgYih9bvJnvvFVM7yug==} engines: {node: '>=10'} cpu: [x86, ia32] os: [win32] - '@sentry/cli-win32-x64@2.58.2': - resolution: {integrity: sha512-2NAFs9UxVbRztQbgJSP5i8TB9eJQ7xraciwj/93djrSMHSEbJ0vC47TME0iifgvhlHMs5vqETOKJtfbbpQAQFA==} + '@sentry/cli-win32-x64@2.58.4': + resolution: {integrity: sha512-cSzN4PjM1RsCZ4pxMjI0VI7yNCkxiJ5jmWncyiwHXGiXrV1eXYdQ3n1LhUYLZ91CafyprR0OhDcE+RVZ26Qb5w==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@sentry/cli@2.58.2': - resolution: {integrity: sha512-U4u62V4vaTWF+o40Mih8aOpQKqKUbZQt9A3LorIJwaE3tO3XFLRI70eWtW2se1Qmy0RZ74zB14nYcFNFl2t4Rw==} + '@sentry/cli@2.58.4': + resolution: {integrity: sha512-ArDrpuS8JtDYEvwGleVE+FgR+qHaOp77IgdGSacz6SZy6Lv90uX0Nu4UrHCQJz8/xwIcNxSqnN22lq0dH4IqTg==} engines: {node: '>= 10'} hasBin: true - '@sentry/core@10.26.0': - resolution: {integrity: sha512-TjDe5QI37SLuV0q3nMOH8JcPZhv2e85FALaQMIhRILH9Ce6G7xW5GSjmH91NUVq8yc3XtiqYlz/EenEZActc4Q==} + '@sentry/core@10.32.1': + resolution: {integrity: sha512-PH2ldpSJlhqsMj2vCTyU0BI2Fx1oIDhm7Izo5xFALvjVCS0gmlqHt1udu6YlKn8BtpGH6bGzssvv5APrk+OdPQ==} engines: {node: '>=18'} - '@sentry/nestjs@10.26.0': - resolution: {integrity: sha512-bky7+xE/NKZuYd04NR4Tal/LmE1ntGS99tn47cn5HjN4ZrQslPVpribPrq7PGMNgfbsQHSVi3bJGNx35Qp9Scw==} + '@sentry/nestjs@10.32.1': + resolution: {integrity: sha512-StgRg8AojiCbH+Q7uhO/9DOhfpjw6SxtsTWwNoioLfHIx968btdQPhALrHji0xXR8DYDBf+bk99P1KdqgDDh/w==} engines: {node: '>=18'} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - '@sentry/nextjs@10.26.0': - resolution: {integrity: sha512-qvNzoAgJ5Vj8ItaUqUSb1soP9t2pZnvDmI9bTapXmW1BMkdoVDxjQ6cZIhvQmLgiIZtHo6IWe9eohtsj83Ti6A==} + '@sentry/nextjs@10.32.1': + resolution: {integrity: sha512-MlgQiKg9P2clKeyH+ZLdmNiMNfTMs/2DBK9V/enLZvYJd1sy5hmrkAV/NiLxVP0uXAeMEVtrgFMIb64cH7ZcXQ==} engines: {node: '>=18'} peerDependencies: next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0 || ^16.0.0-0 - '@sentry/node-core@10.26.0': - resolution: {integrity: sha512-7OrHVn8XAsq9mMVMlpL18XTKQEVcLOJSo0n2M7QGKfFk/OfVtSFMcUWGqN1qhYtT9aMTr2bjtR5+BI33djnNTQ==} + '@sentry/node-core@10.32.1': + resolution: {integrity: sha512-w56rxdBanBKc832zuwnE+zNzUQ19fPxfHEtOhK8JGPu3aSwQYcIxwz9z52lOx3HN7k/8Fj5694qlT3x/PokhRw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 - '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0 '@opentelemetry/instrumentation': '>=0.57.1 <1' - '@opentelemetry/resources': ^1.30.1 || ^2.1.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/resources': ^1.30.1 || ^2.1.0 || ^2.2.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0 '@opentelemetry/semantic-conventions': ^1.37.0 - '@sentry/node@10.26.0': - resolution: {integrity: sha512-VUwNoKYhRpnHQSj9lty1TgooO+1wcpS1V0K87HU8sZEas5gx3Ujiouk5ocPjlgbKreoYOApgOnEEIq5W/hfQcQ==} + '@sentry/node@10.32.1': + resolution: {integrity: sha512-oxlybzt8QW0lx/QaEj1DcvZDRXkgouewFelu/10dyUwv5So3YvipfvWInda+yMLmn25OggbloDQ0gyScA2jU3g==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.26.0': - resolution: {integrity: sha512-ASJdOwn6NwMH2ZeBrnGJI+l/xkJp1AOiQ5FWkvTqLc/vHX+r3PDMO7c+koecY+LiZxSzZF4IV8sALXfOh6UnwA==} + '@sentry/opentelemetry@10.32.1': + resolution: {integrity: sha512-YLssSz5Y+qPvufrh2cDaTXDoXU8aceOhB+YTjT8/DLF6SOj7Tzen52aAcjNaifawaxEsLCC8O+B+A2iA+BllvA==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 - '@opentelemetry/core': ^1.30.1 || ^2.1.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0 '@opentelemetry/semantic-conventions': ^1.37.0 - '@sentry/profiling-node@10.26.0': - resolution: {integrity: sha512-NgfKgrJc39q/khkWZFph1tiZ1uUadtL8VrBTe+7eB/vogPAuEJa+eH/oCS3/M+Pu2fal61MoCw+2qHDH5JDxQQ==} + '@sentry/profiling-node@10.32.1': + resolution: {integrity: sha512-UDSZayQw4K5wv/XNHoB+i+KrnlCStLb0H2lsypF4dgQFCrHXmbwhMh9ieofVGk5bxdmXoL3lSE+3W9cJbpqy2A==} engines: {node: '>=18'} hasBin: true - '@sentry/react@10.26.0': - resolution: {integrity: sha512-Qi0/FVXAalwQNr8zp0tocViH3+MRelW8ePqj3TdMzapkbXRuh07czdGgw8Zgobqcb7l4rRCRAUo2sl/H3KVkIw==} + '@sentry/react@10.32.1': + resolution: {integrity: sha512-/tX0HeACbAmVP57x8txTrGk/U3fa9pDBaoAtlOrnPv5VS/aC5SGkehXWeTGSAa+ahlOWwp3IF8ILVXRiOoG/Vg==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vercel-edge@10.26.0': - resolution: {integrity: sha512-sHRL9VFvyHfd3lqDSWL+F6LWWX7/B7sUJJOJATffXlvFpbqxWTrgn2mQZtqdpv4hMwupWST0YwKNNizPpNyEQw==} + '@sentry/vercel-edge@10.32.1': + resolution: {integrity: sha512-3hrc7TVs4ZeYSCOZdgmv9D1Bke2osnImfupceW8THecNv3uEUjYbrC2UkS/TFMiVHc9qpYzUnKbsGezMp3Bcaw==} engines: {node: '>=18'} - '@sentry/webpack-plugin@4.6.0': - resolution: {integrity: sha512-i9Yy2kXCbFKlRST09fV1HsI0naJAfeXxoiUPyh5iCgSo2w7ZwEUlk0tJhupnHZzfSa3OSg01+vVNeeyLYM4tdA==} + '@sentry/webpack-plugin@4.6.1': + resolution: {integrity: sha512-CJgT/t2pQWsPsMx9VJ86goU/orCQhL2HhDj5ZYBol6fPPoEGeTqKOPCnv/xsbCAfGSp1uHpyRLTA/Gx96u7VVA==} engines: {node: '>= 14'} peerDependencies: webpack: '>=4.40.0' @@ -6055,8 +6056,8 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@smithy/abort-controller@4.2.5': - resolution: {integrity: sha512-j7HwVkBw68YW8UmFRcjZOmssE77Rvk0GWAIN1oFBhsaovQmZWYCIcGa9/pwRB0ExI8Sk9MWNALTjftjHZea7VA==} + '@smithy/abort-controller@4.2.7': + resolution: {integrity: sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==} engines: {node: '>=18.0.0'} '@smithy/chunked-blob-reader-native@4.2.1': @@ -6067,57 +6068,56 @@ packages: resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.3': - resolution: {integrity: sha512-ezHLe1tKLUxDJo2LHtDuEDyWXolw8WGOR92qb4bQdWq/zKenO5BvctZGrVJBK08zjezSk7bmbKFOXIVyChvDLw==} + '@smithy/config-resolver@4.4.5': + resolution: {integrity: sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==} engines: {node: '>=18.0.0'} - '@smithy/core@3.18.4': - resolution: {integrity: sha512-o5tMqPZILBvvROfC8vC+dSVnWJl9a0u9ax1i1+Bq8515eYjUJqqk5XjjEsDLoeL5dSqGSh6WGdVx1eJ1E/Nwhw==} - engines: {node: '>=18.0.0'} - deprecated: Please upgrade your lockfile to use the latest 3.x version of @smithy/core for various fixes, see https://github.com/smithy-lang/smithy-typescript/blob/main/packages/core/CHANGELOG.md - - '@smithy/credential-provider-imds@4.2.5': - resolution: {integrity: sha512-BZwotjoZWn9+36nimwm/OLIcVe+KYRwzMjfhd4QT7QxPm9WY0HiOV8t/Wlh+HVUif0SBVV7ksq8//hPaBC/okQ==} + '@smithy/core@3.20.0': + resolution: {integrity: sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@4.2.5': - resolution: {integrity: sha512-Ogt4Zi9hEbIP17oQMd68qYOHUzmH47UkK7q7Gl55iIm9oKt27MUGrC5JfpMroeHjdkOliOA4Qt3NQ1xMq/nrlA==} + '@smithy/credential-provider-imds@4.2.7': + resolution: {integrity: sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@4.2.5': - resolution: {integrity: sha512-HohfmCQZjppVnKX2PnXlf47CW3j92Ki6T/vkAT2DhBR47e89pen3s4fIa7otGTtrVxmj7q+IhH0RnC5kpR8wtw==} + '@smithy/eventstream-codec@4.2.7': + resolution: {integrity: sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@4.3.5': - resolution: {integrity: sha512-ibjQjM7wEXtECiT6my1xfiMH9IcEczMOS6xiCQXoUIYSj5b1CpBbJ3VYbdwDy8Vcg5JHN7eFpOCGk8nyZAltNQ==} + '@smithy/eventstream-serde-browser@4.2.7': + resolution: {integrity: sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@4.2.5': - resolution: {integrity: sha512-+elOuaYx6F2H6x1/5BQP5ugv12nfJl66GhxON8+dWVUEDJ9jah/A0tayVdkLRP0AeSac0inYkDz5qBFKfVp2Gg==} + '@smithy/eventstream-serde-config-resolver@4.3.7': + resolution: {integrity: sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@4.2.5': - resolution: {integrity: sha512-G9WSqbST45bmIFaeNuP/EnC19Rhp54CcVdX9PDL1zyEB514WsDVXhlyihKlGXnRycmHNmVv88Bvvt4EYxWef/Q==} + '@smithy/eventstream-serde-node@4.2.7': + resolution: {integrity: sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.6': - resolution: {integrity: sha512-3+RG3EA6BBJ/ofZUeTFJA7mHfSYrZtQIrDP9dI8Lf7X6Jbos2jptuLrAAteDiFVrmbEmLSuRG/bUKzfAXk7dhg==} + '@smithy/eventstream-serde-universal@4.2.7': + resolution: {integrity: sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==} engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@4.2.6': - resolution: {integrity: sha512-8P//tA8DVPk+3XURk2rwcKgYwFvwGwmJH/wJqQiSKwXZtf/LiZK+hbUZmPj/9KzM+OVSwe4o85KTp5x9DUZTjw==} + '@smithy/fetch-http-handler@5.3.8': + resolution: {integrity: sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.5': - resolution: {integrity: sha512-DpYX914YOfA3UDT9CN1BM787PcHfWRBB43fFGCYrZFUH0Jv+5t8yYl+Pd5PW4+QzoGEDvn5d5QIO4j2HyYZQSA==} + '@smithy/hash-blob-browser@4.2.8': + resolution: {integrity: sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==} engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@4.2.5': - resolution: {integrity: sha512-6+do24VnEyvWcGdHXomlpd0m8bfZePpUKBy7m311n+JuRwug8J4dCanJdTymx//8mi0nlkflZBvJe+dEO/O12Q==} + '@smithy/hash-node@4.2.7': + resolution: {integrity: sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.2.5': - resolution: {integrity: sha512-2L2erASEro1WC5nV+plwIMxrTXpvpfzl4e+Nre6vBVRR2HKeGGcvpJyyL3/PpiSg+cJG2KpTmZmq934Olb6e5A==} + '@smithy/hash-stream-node@4.2.7': + resolution: {integrity: sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.2.7': + resolution: {integrity: sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': @@ -6128,76 +6128,76 @@ packages: resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.2.5': - resolution: {integrity: sha512-Bt6jpSTMWfjCtC0s79gZ/WZ1w90grfmopVOWqkI2ovhjpD5Q2XRXuecIPB9689L2+cCySMbaXDhBPU56FKNDNg==} + '@smithy/md5-js@4.2.7': + resolution: {integrity: sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.5': - resolution: {integrity: sha512-Y/RabVa5vbl5FuHYV2vUCwvh/dqzrEY/K2yWPSqvhFUwIY0atLqO4TienjBXakoy4zrKAMCZwg+YEqmH7jaN7A==} + '@smithy/middleware-content-length@4.2.7': + resolution: {integrity: sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.3.11': - resolution: {integrity: sha512-eJXq9VJzEer1W7EQh3HY2PDJdEcEUnv6sKuNt4eVjyeNWcQFS4KmnY+CKkYOIR6tSqarn6bjjCqg1UB+8UJiPQ==} + '@smithy/middleware-endpoint@4.4.1': + resolution: {integrity: sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.11': - resolution: {integrity: sha512-EL5OQHvFOKneJVRgzRW4lU7yidSwp/vRJOe542bHgExN3KNThr1rlg0iE4k4SnA+ohC+qlUxoK+smKeAYPzfAQ==} + '@smithy/middleware-retry@4.4.17': + resolution: {integrity: sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.6': - resolution: {integrity: sha512-VkLoE/z7e2g8pirwisLz8XJWedUSY8my/qrp81VmAdyrhi94T+riBfwP+AOEEFR9rFTSonC/5D2eWNmFabHyGQ==} + '@smithy/middleware-serde@4.2.8': + resolution: {integrity: sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.2.5': - resolution: {integrity: sha512-bYrutc+neOyWxtZdbB2USbQttZN0mXaOyYLIsaTbJhFsfpXyGWUxJpEuO1rJ8IIJm2qH4+xJT0mxUSsEDTYwdQ==} + '@smithy/middleware-stack@4.2.7': + resolution: {integrity: sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.3.5': - resolution: {integrity: sha512-UTurh1C4qkVCtqggI36DGbLB2Kv8UlcFdMXDcWMbqVY2uRg0XmT9Pb4Vj6oSQ34eizO1fvR0RnFV4Axw4IrrAg==} + '@smithy/node-config-provider@4.3.7': + resolution: {integrity: sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.5': - resolution: {integrity: sha512-CMnzM9R2WqlqXQGtIlsHMEZfXKJVTIrqCNoSd/QpAyp+Dw0a1Vps13l6ma1fH8g7zSPNsA59B/kWgeylFuA/lw==} + '@smithy/node-http-handler@4.4.7': + resolution: {integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.2.5': - resolution: {integrity: sha512-8iLN1XSE1rl4MuxvQ+5OSk/Zb5El7NJZ1td6Tn+8dQQHIjp59Lwl6bd0+nzw6SKm2wSSriH2v/I9LPzUic7EOg==} + '@smithy/property-provider@4.2.7': + resolution: {integrity: sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.3.5': - resolution: {integrity: sha512-RlaL+sA0LNMp03bf7XPbFmT5gN+w3besXSWMkA8rcmxLSVfiEXElQi4O2IWwPfxzcHkxqrwBFMbngB8yx/RvaQ==} + '@smithy/protocol-http@5.3.7': + resolution: {integrity: sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.2.5': - resolution: {integrity: sha512-y98otMI1saoajeik2kLfGyRp11e5U/iJYH/wLCh3aTV/XutbGT9nziKGkgCaMD1ghK7p6htHMm6b6scl9JRUWg==} + '@smithy/querystring-builder@4.2.7': + resolution: {integrity: sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.2.5': - resolution: {integrity: sha512-031WCTdPYgiQRYNPXznHXof2YM0GwL6SeaSyTH/P72M1Vz73TvCNH2Nq8Iu2IEPq9QP2yx0/nrw5YmSeAi/AjQ==} + '@smithy/querystring-parser@4.2.7': + resolution: {integrity: sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.2.5': - resolution: {integrity: sha512-8fEvK+WPE3wUAcDvqDQG1Vk3ANLR8Px979te96m84CbKAjBVf25rPYSzb4xU4hlTyho7VhOGnh5i62D/JVF0JQ==} + '@smithy/service-error-classification@4.2.7': + resolution: {integrity: sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.4.0': - resolution: {integrity: sha512-5WmZ5+kJgJDjwXXIzr1vDTG+RhF9wzSODQBfkrQ2VVkYALKGvZX1lgVSxEkgicSAFnFhPj5rudJV0zoinqS0bA==} + '@smithy/shared-ini-file-loader@4.4.2': + resolution: {integrity: sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.3.5': - resolution: {integrity: sha512-xSUfMu1FT7ccfSXkoLl/QRQBi2rOvi3tiBZU2Tdy3I6cgvZ6SEi9QNey+lqps/sJRnogIS+lq+B1gxxbra2a/w==} + '@smithy/signature-v4@5.3.7': + resolution: {integrity: sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.9.7': - resolution: {integrity: sha512-pskaE4kg0P9xNQWihfqlTMyxyFR3CH6Sr6keHYghgyqqDXzjl2QJg5lAzuVe/LzZiOzcbcVtxKYi1/fZPt/3DA==} + '@smithy/smithy-client@4.10.2': + resolution: {integrity: sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==} engines: {node: '>=18.0.0'} - '@smithy/types@4.9.0': - resolution: {integrity: sha512-MvUbdnXDTwykR8cB1WZvNNwqoWVaTRA0RLlLmf/cIFNMM2cKWz01X4Ly6SMC4Kks30r8tT3Cty0jmeWfiuyHTA==} + '@smithy/types@4.11.0': + resolution: {integrity: sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.2.5': - resolution: {integrity: sha512-VaxMGsilqFnK1CeBX+LXnSuaMx4sTL/6znSZh2829txWieazdVxr54HmiyTsIbpOTLcf5nYpq9lpzmwRdxj6rQ==} + '@smithy/url-parser@4.2.7': + resolution: {integrity: sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==} engines: {node: '>=18.0.0'} '@smithy/util-base64@4.3.0': @@ -6224,32 +6224,32 @@ packages: resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.10': - resolution: {integrity: sha512-3iA3JVO1VLrP21FsZZpMCeF93aqP3uIOMvymAT3qHIJz2YlgDeRvNUspFwCNqd/j3qqILQJGtsVQnJZICh/9YA==} + '@smithy/util-defaults-mode-browser@4.3.16': + resolution: {integrity: sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.13': - resolution: {integrity: sha512-PTc6IpnpSGASuzZAgyUtaVfOFpU0jBD2mcGwrgDuHf7PlFgt5TIPxCYBDbFQs06jxgeV3kd/d/sok1pzV0nJRg==} + '@smithy/util-defaults-mode-node@4.2.19': + resolution: {integrity: sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.2.5': - resolution: {integrity: sha512-3O63AAWu2cSNQZp+ayl9I3NapW1p1rR5mlVHcF6hAB1dPZUQFfRPYtplWX/3xrzWthPGj5FqB12taJJCfH6s8A==} + '@smithy/util-endpoints@3.2.7': + resolution: {integrity: sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==} engines: {node: '>=18.0.0'} '@smithy/util-hex-encoding@4.2.0': resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.2.5': - resolution: {integrity: sha512-6Y3+rvBF7+PZOc40ybeZMcGln6xJGVeY60E7jy9Mv5iKpMJpHgRE6dKy9ScsVxvfAYuEX4Q9a65DQX90KaQ3bA==} + '@smithy/util-middleware@4.2.7': + resolution: {integrity: sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.2.5': - resolution: {integrity: sha512-GBj3+EZBbN4NAqJ/7pAhsXdfzdlznOh8PydUijy6FpNIMnHPSMO2/rP4HKu+UFeikJxShERk528oy7GT79YiJg==} + '@smithy/util-retry@4.2.7': + resolution: {integrity: sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.6': - resolution: {integrity: sha512-qWw/UM59TiaFrPevefOZ8CNBKbYEP6wBAIlLqxn3VAIo9rgnTNc4ASbVrqDmhuwI87usnjhdQrxodzAGFFzbRQ==} + '@smithy/util-stream@4.5.8': + resolution: {integrity: sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==} engines: {node: '>=18.0.0'} '@smithy/util-uri-escape@4.2.0': @@ -6264,8 +6264,8 @@ packages: resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@4.2.5': - resolution: {integrity: sha512-Dbun99A3InifQdIrsXZ+QLcC0PGBPAdrl4cj1mTgJvyc9N2zf7QSxg8TBkzsCmGJdE3TLbO9ycwpY0EkWahQ/g==} + '@smithy/util-waiter@4.2.7': + resolution: {integrity: sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==} engines: {node: '>=18.0.0'} '@smithy/uuid@1.1.0': @@ -6631,8 +6631,8 @@ packages: peerDependencies: '@solana/web3.js': '*' - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@storybook/addons@7.6.17': resolution: {integrity: sha512-Ok18Y698Ccyg++MoUNJNHY0cXUvo8ETFIRLJk1g9ElJ70j6kPgNnzW2pAtZkBNmswHtofZ7pT156cj96k/LgfA==} @@ -6640,20 +6640,20 @@ packages: '@storybook/channels@7.6.17': resolution: {integrity: sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==} - '@storybook/channels@7.6.20': - resolution: {integrity: sha512-4hkgPSH6bJclB2OvLnkZOGZW1WptJs09mhQ6j6qLjgBZzL/ZdD6priWSd7iXrmPiN5TzUobkG4P4Dp7FjkiO7A==} + '@storybook/channels@7.6.21': + resolution: {integrity: sha512-899XbW60IXIkWDo90bS5ovjxnFUDgD8B2ZwUEJUmuhIXqQeSg2iJ8uYI699Csei+DoDn5gZYJD+BHbSUuc4g+Q==} '@storybook/client-logger@7.6.17': resolution: {integrity: sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==} - '@storybook/client-logger@7.6.20': - resolution: {integrity: sha512-NwG0VIJQCmKrSaN5GBDFyQgTAHLNishUPLW1NrzqTDNAhfZUoef64rPQlinbopa0H4OXmlB+QxbQIb3ubeXmSQ==} + '@storybook/client-logger@7.6.21': + resolution: {integrity: sha512-NWh32K+N6htmmPfqSPOlA6gy80vFQZLnusK8+/7Hp0sSG//OV5ahlnlSveLUOub2e97CU5EvYUL1xNmSuqk2jQ==} '@storybook/core-events@7.6.17': resolution: {integrity: sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==} - '@storybook/core-events@7.6.20': - resolution: {integrity: sha512-tlVDuVbDiNkvPDFAu+0ou3xBBYbx9zUURQz4G9fAq0ScgBOs/bpzcRrFb4mLpemUViBAd47tfZKdH4MAX45KVQ==} + '@storybook/core-events@7.6.21': + resolution: {integrity: sha512-Ez6bhYuXbEkHVCmnNB/oqN0sQwphsmtPmjYdPMlTtEpVEIXHAw2qOlaDiGakoDHkgrTaxiYvdJrPH0UcEJcWDQ==} '@storybook/csf@0.1.13': resolution: {integrity: sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==} @@ -6667,8 +6667,8 @@ packages: '@storybook/preview-api@7.6.17': resolution: {integrity: sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==} - '@storybook/preview-api@7.6.20': - resolution: {integrity: sha512-3ic2m9LDZEPwZk02wIhNc3n3rNvbi7VDKn52hDXfAxnL5EYm7yDICAkaWcVaTfblru2zn0EDJt7ROpthscTW5w==} + '@storybook/preview-api@7.6.21': + resolution: {integrity: sha512-L5e6VjphfsnJk/kkOIRJzDaTfX5sNpiusocqEbHKTM7c9ZDAuaLPZKluP87AJ0u16UdWMuCu6YaQ6eAakDa9gg==} '@storybook/router@7.6.17': resolution: {integrity: sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==} @@ -6682,8 +6682,8 @@ packages: '@storybook/types@7.6.17': resolution: {integrity: sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==} - '@storybook/types@7.6.20': - resolution: {integrity: sha512-GncdY3x0LpbhmUAAJwXYtJDUQEwfF175gsjH0/fxPkxPoV7Sef9TM41jQLJW/5+6TnZoCZP/+aJZTJtq3ni23Q==} + '@storybook/types@7.6.21': + resolution: {integrity: sha512-rJaBMxzXZOsJpqZGhebFJxOguZQBw5j+MVpqbFBA6vLZPx9wEbDBeVsPUxCxj+V1XkVcrNXf9qfThyJ8ETmLBw==} '@stripe/react-stripe-js@5.4.1': resolution: {integrity: sha512-ipeYcAHa4EPmjwfv0lFE+YDVkOQ0TMKkFWamW+BqmnSkEln/hO8rmxGPPWcd9WjqABx6Ro8Xg4pAS7evCcR9cw==} @@ -6896,65 +6896,65 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} - '@tailwindcss/node@4.1.17': - resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} - '@tailwindcss/oxide-android-arm64@4.1.17': - resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.17': - resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.17': - resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.17': - resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} engines: {node: '>= 10'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': - resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': - resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.17': - resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.17': - resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.17': - resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.17': - resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -6965,38 +6965,38 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': - resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.17': - resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.17': - resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} engines: {node: '>= 10'} - '@tailwindcss/postcss@4.1.17': - resolution: {integrity: sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==} + '@tailwindcss/postcss@4.1.18': + resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==} - '@tailwindcss/vite@4.1.17': - resolution: {integrity: sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==} + '@tailwindcss/vite@4.1.18': + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 - '@tanstack/react-virtual@3.13.12': - resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} + '@tanstack/react-virtual@3.13.13': + resolution: {integrity: sha512-4o6oPMDvQv+9gMi8rE6gWmsOjtUZUYIJHv7EB+GblyYdi8U6OqLl8rhHWIUZSL1dUU2dPwTdTgybCKf9EjIrQg==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.13.12': - resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} + '@tanstack/virtual-core@3.13.13': + resolution: {integrity: sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA==} '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} @@ -7013,172 +7013,172 @@ packages: '@types/react': optional: true - '@tiptap/core@3.11.0': - resolution: {integrity: sha512-kmS7ZVpHm1EMnW1Wmft9H5ZLM7E0G0NGBx+aGEHGDcNxZBXD2ZUa76CuWjIhOGpwsPbELp684ZdpF2JWoNi4Dg==} + '@tiptap/core@3.14.0': + resolution: {integrity: sha512-nm0VWVA1Vq/jaKY3wyRXViL/kf78yMdH7qETpv4qZXDQLU+pdWV3IGoRTQTKESc7d8L1wL/2uCeByLNUJfrSIw==} peerDependencies: - '@tiptap/pm': ^3.11.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-blockquote@3.11.0': - resolution: {integrity: sha512-0H8WVW6Vn4GJ7sQ6wfyDgUU+DqM8fp62g8N0fFPiEhoYtpIYUmCqGhpKnqYR0tet6ofFa648XmA6n2VX7sugzw==} + '@tiptap/extension-blockquote@3.14.0': + resolution: {integrity: sha512-I7aOqcVLHBgCeRtMaMHA+ILSS8Sli46fjFq8477stOpQ79TPiBd6e4SDuFCAu58M94mVLMvlPKF2Eh5IvbIMyQ==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-bold@3.11.0': - resolution: {integrity: sha512-V/c3XYO09Le9GlBGq1MK4c97Fffi0GADQTbZ+LFoi65nUrAwutn5wYnXBcEyWQI6RmFWVDJTieamqtc4j9teyw==} + '@tiptap/extension-bold@3.14.0': + resolution: {integrity: sha512-T4ma6VLoHm9JupglidD3CfZXm89A3HMv99gLplXNizvy1mlr4R3uC3aBqKw6lAP+NoqCqbIgjwc4YYsqZClNwA==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-bubble-menu@3.11.0': - resolution: {integrity: sha512-P3j9lQ+EZ5Zg/isJzLpCPX7bp7WUBmz8GPs/HPlyMyN2su8LqXntITBZr8IP1JNBlB/wR83k/W0XqdC57mG7cA==} + '@tiptap/extension-bubble-menu@3.14.0': + resolution: {integrity: sha512-nraHy+5jumT67J7hWrCuVwVTS2vNj4FpV5kO8epVySBmgEBr/7Pyi4w7mQA1VRVOMdjeN9iypbgQ2rKhpfaoTw==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-bullet-list@3.11.0': - resolution: {integrity: sha512-IKdb1C3bHA1sGPiUcntkL+wHebRg71K5+tgaaRnMw0qmtcpcOQb5zhQOSm5bXUsgCk/WgT04dkZPnpn6Gg1PvQ==} + '@tiptap/extension-bullet-list@3.14.0': + resolution: {integrity: sha512-luqPX4u52hiOAHJ95mYsNE+x+9dZxsM461Xny9d/eTXLjAcnwS7MghjrnpljvyYsSXNiwQtxUyEr4uEZZJ5gIQ==} peerDependencies: - '@tiptap/extension-list': ^3.11.0 + '@tiptap/extension-list': ^3.14.0 - '@tiptap/extension-code-block@3.11.0': - resolution: {integrity: sha512-y01RJVbygDJWYXxZ0SiCYwvUF2X91RANCLSdb8X0qiwVPgNOzsDrrzS/iqoXkiYmM93pJw+ZWelEZxRvxEwsrg==} + '@tiptap/extension-code-block@3.14.0': + resolution: {integrity: sha512-hRSdIhhm3Q9JBMQdKaifRVFnAa4sG+M7l1QcTKR3VSYVy2/oR0U+aiOifi5OvMRBUwhaR71Ro+cMT9FH9s26Kg==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-code@3.11.0': - resolution: {integrity: sha512-5OpR5O4bveHe1KG9CJsto86NgkuerYq3OLY78vzh9uFCLdv7xgXA2aZYJfRMhbZ7hKsR7hHg1etBJUCk+TKsMg==} + '@tiptap/extension-code@3.14.0': + resolution: {integrity: sha512-Sx9yLorzS+oqNmXID4jt0G5tDnsEgU0HtEXPLD3KNt/ltVxWJU0AXwCsp1/Dg0HIDL868vWpJ2jC1t/4oaf9kA==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-document@3.11.0': - resolution: {integrity: sha512-N2G3cwL2Dtur/CgD/byJmFx9T5no6fTO/U462VP3rthQYrRA1AB3TCYqtlwJkmyoxRTNd4qIg4imaPl8ej6Heg==} + '@tiptap/extension-document@3.14.0': + resolution: {integrity: sha512-O3D7/GPB3XrWGy0y/b4LMHiY0eTd+dyIbSdiFtmUnbC/E9lqQLw43GiqvD9Gm6AyKhBA+Z45dKMbaOe1c6eTwQ==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-dropcursor@3.11.0': - resolution: {integrity: sha512-gW/QMGAyiXGSpO+X/lTeiBQn1Or8T8UVB3y9Cv2Lh6zx0SWU+FA28EH+y6s3fm872reN4dH/9rEvMuJjhU/BEw==} + '@tiptap/extension-dropcursor@3.14.0': + resolution: {integrity: sha512-IwHyiZKLjV9WSBlQFS+afMjucIML8wFAKkG8UKCu+CVOe/Qd1ImDGyv6rzPlCmefJkDHIUWS+c2STapJlUD1VQ==} peerDependencies: - '@tiptap/extensions': ^3.11.0 + '@tiptap/extensions': ^3.14.0 - '@tiptap/extension-floating-menu@3.11.0': - resolution: {integrity: sha512-nEHdWZHEJYX1II1oJQ4aeZ8O/Kss4BRbYFXQFGIvPelCfCYEATpUJh3aq3767ARSq40bOWyu+Dcd4SCW0We6Sw==} + '@tiptap/extension-floating-menu@3.14.0': + resolution: {integrity: sha512-+ErwDF74NzX4JV0nXMSIUT9V8FDdo85r0SaBZ8lb2NLmElaA3LDklcNV7SsoKlRcwsAXtFkqQbDwXLNGQLYSPQ==} peerDependencies: '@floating-ui/dom': ^1.0.0 - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-gapcursor@3.11.0': - resolution: {integrity: sha512-lXGEZiYX7k/pEFr8BgDE91vqjLTwuf+qhHLTgIpfhbt562nShLPIDj9Vzu3xrR4fwUAMiUNiLyaeInb8j3I4kg==} + '@tiptap/extension-gapcursor@3.14.0': + resolution: {integrity: sha512-hMg2U59+c9FreYtTvzxx5GWKejdZLRITMLEu4OTfrgQok6uF4qkzGEEqmYqPiHk08TBqAg18Y5bbpyqTsuit9A==} peerDependencies: - '@tiptap/extensions': ^3.11.0 + '@tiptap/extensions': ^3.14.0 - '@tiptap/extension-hard-break@3.11.0': - resolution: {integrity: sha512-NJEHTj++kFOayQXKSQSi9j9eAG33eSiJqai2pf4U+snW94fmb8cYLUurDmfYRe20O6EzBSX0X3GjVlkOz+5b7A==} + '@tiptap/extension-hard-break@3.14.0': + resolution: {integrity: sha512-XKxr8usQp+kFevhDK6Ccmnq1CIkLmPClhKwbt7AClGLKLBtEVAS1qUgcmKudkw8cD8Q2/69twI37LXa23sfuLA==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-heading@3.11.0': - resolution: {integrity: sha512-4Eo67Yo7vsYLkizcMoGdZAR9aHbC7FFTrqfNEd4Em3ajRi0iNqyWMaI90UCYlitDdRdqFlq/njWrMqBOLUgaWQ==} + '@tiptap/extension-heading@3.14.0': + resolution: {integrity: sha512-4xpahSo3b1dN2nwA0XKXLQVz9nZ/vE443a/Y5QLWeXiu3v9wkcMs/5kQ5ysFeDZRBTfVUWBqhngI7zhvDUx2zQ==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-history@3.11.0': - resolution: {integrity: sha512-Q/kuNDCoeH2dZ2P+OqEKnRW047SkrngNq+vSrwQlAKO8osO/eAS7aLzn1NELzE5jLvzOKqUda43bSTKsBeTh+w==} + '@tiptap/extension-history@3.14.0': + resolution: {integrity: sha512-wHDCVXTHcrbOuA6sUFxAZ980/TQa8F6cytP6HfP8J/nDlNDJ/hiov3RIw7kkkHrlHZYG+Qu8EprqFZU6OHAzNQ==} peerDependencies: - '@tiptap/extensions': ^3.11.0 + '@tiptap/extensions': ^3.14.0 - '@tiptap/extension-horizontal-rule@3.11.0': - resolution: {integrity: sha512-FugFHZG+oiMBV6k42hn9NOA4wRNc2b9UeEIMR+XwEMpWJInV4VwSwDvu8JClgkDo8z7FEnker9e51DZ00CLWqg==} + '@tiptap/extension-horizontal-rule@3.14.0': + resolution: {integrity: sha512-65O4T9vPKLUKO1fLowh5jqtfQlH5eaIL7qb/uj5sXMMg8O7TCvBIRkwNuYsFTkJmTk4vBy+fjZ0uwSY3DFkO1g==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-italic@3.11.0': - resolution: {integrity: sha512-WP6wL2b//8bLVdeUCWOpYA7nUStvrAMMD0nRn0F9CEW+l7vH6El2PZFhHmJ9uqXo5MnyugBpARiwgxfoAlef5w==} + '@tiptap/extension-italic@3.14.0': + resolution: {integrity: sha512-Arl5EaG4wdyipwvKjsI7Krlk3OkmqvLfF0YfGwsd5AVDxTiYuiDGgz7RF8J2kttbBeiUTqwME5xpkryQK3F+fg==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-link@3.11.0': - resolution: {integrity: sha512-RoUkGqowVMKLE76KktNOGhzNMyKtwrSDRqeYCe1ODPuOMZvDGexOE8cIuA4A1ODkgN6ji9qE/9Sf8uhpZdH39Q==} + '@tiptap/extension-link@3.14.0': + resolution: {integrity: sha512-xaeJIktD42rJ4t9fbQpKe+yYNZ+YFIK96cp1Kdm0hZHv/8MPMNRiF85TRY+9U1aoyh5uRcspgCj7EKQb2Hs7qg==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-list-item@3.11.0': - resolution: {integrity: sha512-KXTTSBH/T/WW8O1YhK/lVmwlSGh2w2VVucUkMLhgk1VPchahAkn2LfgbgKrCRG/F8M8Jlfvz67iJDo6+bbNqew==} + '@tiptap/extension-list-item@3.14.0': + resolution: {integrity: sha512-19Dcp8HCFdhINmRy0KQLFfz9ZEuVwFWGAAjYG7BvMvkd9k4sJ5vCv5fej59G99rhsc+tCmik77w+SLksOcxwKQ==} peerDependencies: - '@tiptap/extension-list': ^3.11.0 + '@tiptap/extension-list': ^3.14.0 - '@tiptap/extension-list-keymap@3.11.0': - resolution: {integrity: sha512-vm1zGdEqcbQnrGlVXchk1ibmTsyxyfGcGPVWsc4MG+UAFcNfcpAnvCar71BF4RGGPtpzOWdqGkvJENyh0L5/Hw==} + '@tiptap/extension-list-keymap@3.14.0': + resolution: {integrity: sha512-1oPbvNnQjeOxkHZcUbWPx/IY9o4fT3QGk/9A9cIjFrJRD2AHzbYfPDHNHINtg7Bj0jWz74cHvAHcaxP+M27jkA==} peerDependencies: - '@tiptap/extension-list': ^3.11.0 + '@tiptap/extension-list': ^3.14.0 - '@tiptap/extension-list@3.11.0': - resolution: {integrity: sha512-4Ane7VCVZ+GFOQNuy2nMP+SoWH7EemC3geTTqvgHm1H0tbSosxLJAVaZ9dF06F35RJmYCm+jLJUhRVd156eCRQ==} + '@tiptap/extension-list@3.14.0': + resolution: {integrity: sha512-rsjFH0Vd/4UbDsjwMLay7oz72VVu1r35t8ofAzy5587jn5JAjflaZs05XbRRMD2imUTK41dyajVSh8CqSnDEJw==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/extension-mention@3.11.0': - resolution: {integrity: sha512-4y789hKNEvZoNals7PNSGAKThQ+b5nuP/KIEe4wPIfzknjwxzGi0f2YY3L/f+gIhueoZymYpkmhtiRND+wvAWA==} + '@tiptap/extension-mention@3.14.0': + resolution: {integrity: sha512-PGSAKke1pUuGEadqthvSyJLHLh4eVwRoOMsAAmxr1P0UqATpEgVMHAeyEXrRf5FORN0Wu1LzgiuXPFLkhZhfzg==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 - '@tiptap/suggestion': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 + '@tiptap/suggestion': ^3.14.0 - '@tiptap/extension-ordered-list@3.11.0': - resolution: {integrity: sha512-kO8GH4w4Xil+qPiHJLAyILdGHF9hCjkhoVtPD8YEfqK6Qx3bZql5FPySCQNs+MU6rLSCCdam8SUPGY/+SCufqA==} + '@tiptap/extension-ordered-list@3.14.0': + resolution: {integrity: sha512-/fXjVL4JajkJQoc213iiput0bCXC4ztUPUpvNuI62VcgFKHcTvX4eYxED1VflotCx0OdkyY9yYD8PtvyO5lkmA==} peerDependencies: - '@tiptap/extension-list': ^3.11.0 + '@tiptap/extension-list': ^3.14.0 - '@tiptap/extension-paragraph@3.11.0': - resolution: {integrity: sha512-hxgjZOXOqstRTWv+QjWJjK23rD5qzIV9ePlhX3imLeq/MgX0aU9VBDaG5SGKbSjaBNQnpLw6+sABJi3CDP6Z5A==} + '@tiptap/extension-paragraph@3.14.0': + resolution: {integrity: sha512-NFxk2yNo3Cvh9g8evea+yTLNV48se7MbMcVizTnVhobqtBKv793qsb5FM5Hu30Y72FQPNfH+LRoap4XZyBPfVw==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-strike@3.11.0': - resolution: {integrity: sha512-XVP/WMYLrqLBfUsGPu2H9MrOUZLhGUaxtZ3hSRffDi/lsw53x/coZ9eO0FxOB9R7z2ksHWmticIs+0YnKt9LNQ==} + '@tiptap/extension-strike@3.14.0': + resolution: {integrity: sha512-R8BbAhnWpisBml6okMKl98hY4tJjedTTgyTkx8tPabIJ92nS9IURKEk3foWB9uHxdTOBUqTvVT+2ScDf9r6QHg==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-text@3.11.0': - resolution: {integrity: sha512-ELAYm2BuChzZOqDG9B0k3W6zqM4pwNvXkam28KgHGiT2y7Ni68Rb+NXp16uVR+5zR6hkqnQ/BmJSKzAW59MXpA==} + '@tiptap/extension-text@3.14.0': + resolution: {integrity: sha512-XlpnD87LQ7lLcDcBenHgzxv3uivQzPdVHM16CY4lXR4aKDIp2mxjPZr4twHT+cOnRQHc8VYpRgkEo6LLX6VylA==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extension-underline@3.11.0': - resolution: {integrity: sha512-D3PsS/84RlQKFjd5eerMIUioC0mNh4yy1RRV/WbXx6ugu+6T+0hT42gNk9Ap8pDsVQZCk0SHfDyBEUFC2KOwKw==} + '@tiptap/extension-underline@3.14.0': + resolution: {integrity: sha512-zmnWlsi2g/tMlThHby0Je9O+v24j4d+qcXF3nuzLUUaDsGCEtOyC9RzwITft59ViK+Nc2PD2W/J14rsB0j+qoQ==} peerDependencies: - '@tiptap/core': ^3.11.0 + '@tiptap/core': ^3.14.0 - '@tiptap/extensions@3.11.0': - resolution: {integrity: sha512-g43beA73ZMLezez1st9LEwYrRHZ0FLzlsSlOZKk7sdmtHLmuqWHf4oyb0XAHol1HZIdGv104rYaGNgmQXr1ecQ==} + '@tiptap/extensions@3.14.0': + resolution: {integrity: sha512-qQBVKqzU4ZVjRn8W0UbdfE4LaaIgcIWHOMrNnJ+PutrRzQ6ZzhmD/kRONvRWBfG9z3DU7pSKGwVYSR2hztsGuQ==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 - '@tiptap/pm@3.11.0': - resolution: {integrity: sha512-plCQDLCZIOc92cizB8NNhBRN0szvYR3cx9i5IXo6v9Xsgcun8KHNcJkesc2AyeqdIs0BtOJZaqQ9adHThz8UDw==} + '@tiptap/pm@3.14.0': + resolution: {integrity: sha512-xrZmqI5jl4yMeAsu8p8gVP9S3An5h2MBi8BQHNnZmpyzkUrlpd40vlT6u13SWIqVi5ZWhBZ6U3rL7mkVLZuRKg==} - '@tiptap/react@3.11.0': - resolution: {integrity: sha512-SDGei/2DjwmhzsxIQNr6dkB6NxLgXZjQ6hF36NfDm4937r5NLrWrNk5tCsoDQiKZ0DHEzuJ6yZM5C7I7LZLB6w==} + '@tiptap/react@3.14.0': + resolution: {integrity: sha512-Eo/nLyKxHvnLIF4gI2WFhGJiVrqfA6XL9kismVG9NwBNF/NblMDmZZu6Z2SH/ONJQz2Egn7UBPNp3BMq/qZDcg==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tiptap/starter-kit@3.11.0': - resolution: {integrity: sha512-8kMMYqVSZ2Oqji+mY1o9meTjCRWp4DplFegu7APqDEQRhlb6mBI0wNuazYb7FKJIHJTtf0F6cYglJrxpu9c/fA==} + '@tiptap/starter-kit@3.14.0': + resolution: {integrity: sha512-fHsC4oDVzvMU9btg+IUmu/eqPquapjJ341qaNI7cCeSCKjjE6XJEN6WcONLAVId2OZUwML0IX1Jgl+6gJxU9Jw==} - '@tiptap/suggestion@3.11.0': - resolution: {integrity: sha512-4zGU3l1rZ7P112en3HiMXZuKCcZSXdwtdSIfQQV94jNyumg/imYFeYARVsabfv6hFjtEuwbq0ev8y13Bl+1Quw==} + '@tiptap/suggestion@3.14.0': + resolution: {integrity: sha512-B9BQ9Tck8HsISDc6jZmtaSpl8KK69JbKHfU0ntILxgj8aBASElewO+W8WE49JSTxuyJGRgnhGSgaGpM4LhbLAg==} peerDependencies: - '@tiptap/core': ^3.11.0 - '@tiptap/pm': ^3.11.0 + '@tiptap/core': ^3.14.0 + '@tiptap/pm': ^3.14.0 '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} @@ -7359,8 +7359,8 @@ packages: '@types/express@4.17.25': resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} - '@types/express@5.0.5': - resolution: {integrity: sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==} + '@types/express@5.0.6': + resolution: {integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==} '@types/facebook-nodejs-business-sdk@20.0.3': resolution: {integrity: sha512-x/iilSCliq/YqJWTiVoqnTsILF+hluSJdghhZF/cgWXHBNY+rHBGR5oGa3AfVyG5FVEXw/R/0fZAVui87pq7OA==} @@ -7425,8 +7425,8 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - '@types/lodash@4.17.20': - resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/lodash@4.17.21': + resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} '@types/luxon@3.4.2': resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} @@ -7473,8 +7473,8 @@ packages: '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - '@types/node-telegram-bot-api@0.64.12': - resolution: {integrity: sha512-R2g/aJQxcRIOn5nYiuFQid4L/yFbIRsdrhxtijESa1cwAmJq7J+l1ZwXTCNaYyZg+WURd3eLF+htux8Hvwqu2w==} + '@types/node-telegram-bot-api@0.64.13': + resolution: {integrity: sha512-/d3sYpZq4sDwKWAm9XsIsb8MclclJ1yPXZC7uAzZRrJIFY8yg/63oNrLf9CBTlaS/XGR1N66BwibfJQGiWVsqg==} '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -7503,6 +7503,9 @@ packages: '@types/pg@8.15.5': resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==} + '@types/pg@8.15.6': + resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} + '@types/prismjs@1.26.5': resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} @@ -7534,8 +7537,8 @@ packages: '@types/react@18.3.1': resolution: {integrity: sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==} - '@types/readable-stream@4.0.22': - resolution: {integrity: sha512-/FFhJpfCLAPwAcN3mFycNUa77ddnr8jTgF5VmSNetaemWB2cIlfCA9t0YTM3JAT0wOcv8D4tjPo7pkDhK3EJIg==} + '@types/readable-stream@4.0.23': + resolution: {integrity: sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==} '@types/redis@2.8.32': resolution: {integrity: sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==} @@ -7570,12 +7573,12 @@ packages: '@types/serve-static@1.15.10': resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} + '@types/sha256@0.2.2': resolution: {integrity: sha512-uKMaDzyzfcDYGEwTgLh+hmgDMxXWyIVodY8T+qt7A+NYvikW0lmGLMGbQ7BipCB8dzXHa55C9g+Ii/3Lgt1KmA==} - '@types/shimmer@1.2.0': - resolution: {integrity: sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==} - '@types/sortablejs@1.15.9': resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==} @@ -7715,8 +7718,8 @@ packages: react: '>=18.0.0' react-dom: '>=18.0.0' - '@uiw/copy-to-clipboard@1.0.17': - resolution: {integrity: sha512-O2GUHV90Iw2VrSLVLK0OmNIMdZ5fgEg4NhvtwINsX+eZ/Wf6DWD0TdsK9xwV7dNRnK/UI2mQtl0a2/kRgm1m1A==} + '@uiw/copy-to-clipboard@1.0.19': + resolution: {integrity: sha512-AYxzFUBkZrhtExb2QC0C4lFH2+BSx6JVId9iqeGHakBuosqiQHUQaNZCvIBeM97Ucp+nJ22flOh8FBT2pKRRAA==} '@uiw/react-markdown-preview@5.1.5': resolution: {integrity: sha512-DNOqx1a6gJR7Btt57zpGEKTfHRlb7rWbtctMRO2f82wWcuoJsxPBrM+JWebDdOD0LfD8oe2CQvW2ICQJKHQhZg==} @@ -7724,8 +7727,8 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@uiw/react-md-editor@4.0.8': - resolution: {integrity: sha512-S3mOzZeGmJNhzdXJxRTCwsFMDp8nBWeQUf59cK3L6QHzDUHnRoHpcmWpfVRyKGKSg8zaI2+meU5cYWf8kYn3mQ==} + '@uiw/react-md-editor@4.0.11': + resolution: {integrity: sha512-F0OR5O1v54EkZYvJj3ew0I7UqLiPeU34hMAY4MdXS3hI86rruYi5DHVkG/VuvLkUZW7wIETM2QFtZ459gKIjQA==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -8009,14 +8012,14 @@ packages: peerDependencies: '@uppy/core': ^4.5.2 - '@upstash/redis@1.35.6': - resolution: {integrity: sha512-aSEIGJgJ7XUfTYvhQcQbq835re7e/BXjs8Janq6Pvr6LlmTZnyqwT97RziZLO/8AVUL037RLXqqiQC6kCt+5pA==} + '@upstash/redis@1.35.8': + resolution: {integrity: sha512-QqLpVCD9PCPE6hlRzOkz864nfijSdazxtyJLIy9ZeTh6kU2nBIKKfjT5HMHjIRD4BCm6TK1dbUT9pxhFjcvpng==} '@urql/core@5.2.0': resolution: {integrity: sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==} - '@vercel/oidc@3.0.4': - resolution: {integrity: sha512-NUna+D5Kk0UMscKnnFVgHhV4SfuoiE9wFpF8E8RvxHYQv9NB2PRIkQp0eTvu2uVzExFB5eH2/lYPpxw/SrICzg==} + '@vercel/oidc@3.0.5': + resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} engines: {node: '>= 20'} '@vitejs/plugin-react@4.7.0': @@ -8149,11 +8152,9 @@ packages: '@walletconnect/sign-client@2.19.0': resolution: {integrity: sha512-+GkuJzPK9SPq+RZgdKHNOvgRagxh/hhYWFHOeSiGh3DyAQofWuFTq4UrN/MPjKOYswSSBKfIa+iqKYsi4t8zLQ==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/sign-client@2.19.1': resolution: {integrity: sha512-OgBHRPo423S02ceN3lAzcZ3MYb1XuLyTTkKqLmKp/icYZCyRzm3/ynqJDKndiBLJ5LTic0y07LiZilnliYqlvw==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/solana-adapter@0.0.8': resolution: {integrity: sha512-Qb7MT8SdkeBldfUCmF+rYW6vL98mxPuT1yAwww5X2vpx7xEPZvFCoAKnyT5fXu0v56rMxhW3MGejnHyyYdDY7Q==} @@ -8172,11 +8173,9 @@ packages: '@walletconnect/universal-provider@2.19.0': resolution: {integrity: sha512-e9JvadT5F8QwdLmd7qBrmACq04MT7LQEe1m3X2Fzvs3DWo8dzY8QbacnJy4XSv5PCdxMWnua+2EavBk8nrI9QA==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/universal-provider@2.19.1': resolution: {integrity: sha512-4rdLvJ2TGDIieNWW3sZw2MXlX65iHpTuKb5vyvUHQtjIVNLj+7X/09iUAI/poswhtspBK0ytwbH+AIT/nbGpjg==} - deprecated: 'Reliability and performance improvements. See: https://github.com/WalletConnect/walletconnect-monorepo/releases' '@walletconnect/utils@2.19.0': resolution: {integrity: sha512-LZ0D8kevknKfrfA0Sq3Hf3PpmM8oWyNfsyWwFR51t//2LBgtN2Amz5xyoDDJcjLibIbKAxpuo/i0JYAQxz+aPA==} @@ -8305,8 +8304,8 @@ packages: zod: optional: true - abitype@1.1.0: - resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} + abitype@1.2.3: + resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} peerDependencies: typescript: '>=5.0.4' zod: ^3.22.0 || ^4.0.0 @@ -8319,6 +8318,9 @@ packages: abort-controller-x@0.4.3: resolution: {integrity: sha512-VtUwTNU8fpMwvWGn4xE93ywbogTYsuT+AUxAXOeelbXuQVIwNmC5YLeho9sH4vZ4ITW8414TTAOG1nW6uIVHCA==} + abort-controller-x@0.5.0: + resolution: {integrity: sha512-yTt9CI0x+nRfX6BFMenEGP8ooPvErGH6AbFz20C2IeOLIlDsrw/VHpgne3GsCEuTA410IiFiaLVFKmgM4bKEPQ==} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -8601,9 +8603,6 @@ packages: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} - async-limiter@1.0.1: - resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} - async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} @@ -8618,8 +8617,8 @@ packages: resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} engines: {node: '>=4'} - autoprefixer@10.4.22: - resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} + autoprefixer@10.4.23: + resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -8732,8 +8731,8 @@ packages: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true basic-ftp@5.0.5: @@ -8801,8 +8800,12 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + body-parser@1.20.4: + resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} boolbase@1.0.0: @@ -8814,8 +8817,8 @@ packages: bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} - bowser@2.12.1: - resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} + bowser@2.13.1: + resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -8850,8 +8853,8 @@ packages: resolution: {integrity: sha512-C2AUdAJg6rlM2W5QMp2Q4KGQMVBwR1lIimTsUnutJ8bMpW5B52pGpR2gEnNBNwijumDo5FojQ0L9JrXA8m4YEw==} engines: {node: '>= 0.10'} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -8889,15 +8892,15 @@ packages: buffer@6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bufferutil@4.0.9: - resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==} + bufferutil@4.1.0: + resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} - bullmq@5.63.2: - resolution: {integrity: sha512-c1K5gcAh0a+C9lcRXaA1GePDYtmUywCH1pNXkUlZ8lFlqQnrtKyZpcr5aZJcjyZVx6y7t5259ru+ttJFNUQ5kw==} + bullmq@5.66.2: + resolution: {integrity: sha512-0PrkpIakIntkBcPLltPIRWdLC1FTLUa/VhJkmEfobb5YUQjoUwJdmmf7HX+o/vMonS5048JpP+abf9lVRUFEjA==} bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} @@ -8921,8 +8924,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cache-manager@7.2.5: - resolution: {integrity: sha512-Y5LF7olTrcKJn1NoKiWPOvjEiO5DfDVPxqZHETCRMaliC60KBNb4Ge/vEYep5TyaqpXvnpnPPo8zauCe6UzZwA==} + cache-manager@7.2.7: + resolution: {integrity: sha512-TKeeb9nSybk1e9E5yAiPVJ6YKdX9FYhwqqy8fBfVKAFVTJYZUNmeIvwjURW6+UikNsO6l2ta27thYgo/oumDsw==} cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} @@ -8963,8 +8966,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001756: - resolution: {integrity: sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==} + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} @@ -9097,8 +9100,8 @@ packages: class-transformer: ^0.4.0 || ^0.5.0 class-validator: ^0.14.0 - class-validator@0.14.2: - resolution: {integrity: sha512-3kMVRF2io8N8pY1IFIXlho9r8IPUUIfHe2hYVtiebvAzU2XeQFXTv+XI4WX+TnXmtwXMDcjngcpkiPM0O9PvLw==} + class-validator@0.14.3: + resolution: {integrity: sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==} class-variance-authority@0.6.1: resolution: {integrity: sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==} @@ -9351,6 +9354,9 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + cookie-signature@1.0.7: + resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -9477,8 +9483,8 @@ packages: css-select@5.2.2: resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} - css-selector-parser@3.2.0: - resolution: {integrity: sha512-L1bdkNKUP5WYxiW5dW6vA2hd3sL8BdRNLy2FCX0rLVise4eNw9nBdeBuJHxlELieSE2H1f6bYQFfwVUwWCV9rQ==} + css-selector-parser@3.3.0: + resolution: {integrity: sha512-Y2asgMGFqJKF4fq4xHDSlFYIkeVfRsm69lQC1q9kbEsH5XtnINTMrweLkjYMeaUgiXBy/uvKeO/a1JHTNnmB2g==} css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} @@ -9646,8 +9652,8 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.7.0: - resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + dedent@1.7.1: + resolution: {integrity: sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -9903,8 +9909,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.256: - resolution: {integrity: sha512-uqYq1IQhpXXLX+HgiXdyOZml7spy4xfy42yPxcCCRjswp0fYM2X+JwCON07lqnpLEGVCj739B7Yr+FngmHBMEQ==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -9913,8 +9919,8 @@ packages: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - emoji-picker-react@4.15.1: - resolution: {integrity: sha512-RYeAgutdYGbKAyLrOHLsSUubGhg6q8sK889k7G8ofWxzqUzeY2i9WR4+Fj8gsP7oPYTfFPFCG7P6/Tl5TWnqOA==} + emoji-picker-react@4.16.1: + resolution: {integrity: sha512-MrPX0tOCfRL3uYI4of/2GRZ7S6qS7YlacKiF78uFH84/C62vcuHE2DZyv5b4ZJMk0e06es1jjB4e31Bb+YSM8w==} engines: {node: '>=10'} peerDependencies: react: '>=16' @@ -9957,8 +9963,8 @@ packages: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} - enhanced-resolve@5.18.3: - resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} engines: {node: '>=10.13.0'} entities@2.2.0: @@ -9978,8 +9984,8 @@ packages: error-stack-parser@2.1.4: resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} - es-abstract@1.24.0: - resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + es-abstract@1.24.1: + resolution: {integrity: sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw==} engines: {node: '>= 0.4'} es-define-property@1.0.1: @@ -9993,8 +9999,8 @@ packages: es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + es-iterator-helpers@1.2.2: + resolution: {integrity: sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w==} engines: {node: '>= 0.4'} es-module-lexer@0.10.5: @@ -10038,8 +10044,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.0: - resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} hasBin: true @@ -10291,8 +10297,8 @@ packages: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} - execa@9.6.0: - resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + execa@9.6.1: + resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} executable@4.1.1: @@ -10313,8 +10319,8 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} expect@29.7.0: @@ -10334,8 +10340,12 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + express@4.22.1: + resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} + engines: {node: '>= 0.10.0'} + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} exsolve@1.0.8: @@ -10370,6 +10380,9 @@ packages: fast-copy@3.0.2: resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} + fast-copy@4.0.2: + resolution: {integrity: sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==} + fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} @@ -10379,6 +10392,10 @@ packages: fast-diff@1.1.2: resolution: {integrity: sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==} + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} + engines: {node: '>=6.0.0'} + fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} @@ -10519,10 +10536,14 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + finalhandler@1.3.2: + resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-root@1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} @@ -10658,6 +10679,10 @@ packages: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} + fs-extra@11.3.3: + resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + engines: {node: '>=14.14'} + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -10802,10 +10827,9 @@ packages: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true - glob@12.0.0: - resolution: {integrity: sha512-5Qcll1z7IKgHr5g485ePDdHcNQY0k2dtv/bjYy0iuyGxQw2qSOiiXUXJ+AYQpg3HNoUMHqAruX478Jeev7UULw==} + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} engines: {node: 20 || >=22} - hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -10890,8 +10914,8 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - graphql-yoga@5.16.2: - resolution: {integrity: sha512-heaD8ejapeEZ8+8CxB6DbYzkvMfC4gHEXr1Gc2CQCXEb5PVaDcEnQfiThBNic1KLPpuZixqQdJJ0pjcEVc9H7g==} + graphql-yoga@5.18.0: + resolution: {integrity: sha512-xFt1DVXS1BZ3AvjnawAGc5OYieSe56WuQuyk3iEpBwJ3QDZJWQGLmU9z/L5NUZ+pUcyprsz/bOwkYIV96fXt/g==} engines: {node: '>=18.0.0'} peerDependencies: graphql: ^15.2.0 || ^16.0.0 @@ -10981,8 +11005,8 @@ packages: hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - hashery@1.2.0: - resolution: {integrity: sha512-43XJKpwle72Ik5Zpam7MuzRWyNdwwdf6XHlh8wCj2PggvWf+v/Dm5B0dxGZOmddidgeO6Ofu9As/o231Ti/9PA==} + hashery@1.3.0: + resolution: {integrity: sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==} engines: {node: '>=20'} hasown@2.0.2: @@ -11025,8 +11049,8 @@ packages: hast-util-to-jsx-runtime@2.3.6: resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} - hast-util-to-parse5@8.0.0: - resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==} + hast-util-to-parse5@8.0.1: + resolution: {integrity: sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==} hast-util-to-string@3.0.1: resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==} @@ -11052,8 +11076,8 @@ packages: help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - hermes-compiler@0.0.0: - resolution: {integrity: sha512-boVFutx6ME/Km2mB6vvsQcdnazEYYI/jV1pomx1wcFUG/EVqTkr5CU0CW9bKipOA/8Hyu3NYwW3THg2Q1kNCfA==} + hermes-compiler@0.14.0: + resolution: {integrity: sha512-clxa193o+GYYwykWVFfpHduCATz8fR5jvU7ngXpfKHj+E9hr9vjLNtdLSEe8MUbObvVexV3wcyxQ00xTPIrB1Q==} hermes-estree@0.32.0: resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==} @@ -11121,15 +11145,15 @@ packages: zod-openapi: optional: true - hono@4.10.6: - resolution: {integrity: sha512-BIdolzGpDO9MQ4nu3AUuDwHZZ+KViNm+EZ75Ae55eMXMqLVhDFqEMXxtUe9Qh8hjL+pIna/frs2j6Y2yD5Ua/g==} + hono@4.11.1: + resolution: {integrity: sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==} engines: {node: '>=16.9.0'} - hookified@1.13.0: - resolution: {integrity: sha512-6sPYUY8olshgM/1LDNW4QZQN0IqgKhtl/1C8koNZBJrKLBk3AZl6chQtNwpNztvfiApHMEwMHek5rv993PRbWw==} + hookified@1.14.0: + resolution: {integrity: sha512-pi1ynXIMFx/uIIwpWJ/5CEtOHLGtnUB0WhGeeYT+fKcQ+WCQbm3/rrkAXnpfph++PgepNqPdTC2WTj8A6k6zoQ==} - hot-reload-extension-vite@1.0.13: - resolution: {integrity: sha512-bX9Bcvhay+xwVinLb/NdoA85NDAixbZ4xcmOyibvHjDjIzw1qllEGfN4LnoAi1jSfH0Mv5Sf5WrbplJS6IaMmg==} + hot-reload-extension-vite@1.1.0: + resolution: {integrity: sha512-DSuJHbAZ56pbJ7u5iZ2Y0qMBKdgsGYRVm3EL07+3NqA2AhIW8+/o7xgyjEuwT4WA0gxV2wL7EMzabbEzoOBUAw==} html-encoding-sniffer@3.0.0: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} @@ -11167,6 +11191,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@5.0.0: resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} engines: {node: '>= 6'} @@ -11220,17 +11248,17 @@ packages: i18next-resources-to-backend@1.2.1: resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==} - i18next@25.6.3: - resolution: {integrity: sha512-AEQvoPDljhp67a1+NsnG/Wb1Nh6YoSvtrmeEd24sfGn3uujCtXCF3cXpr7ulhMywKNFF7p3TX1u2j7y+caLOJg==} + i18next@25.7.3: + resolution: {integrity: sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==} peerDependencies: typescript: ^5 peerDependenciesMeta: typescript: optional: true - ibm-cloud-sdk-core@5.4.4: - resolution: {integrity: sha512-2zqgHp3W2meNJtommmgnZdouj2dPK4AbNQ4QN7BjNpfsQhWNO4eZbUYo2iD2V3I2k9mAsCjzsM87YuE+mu8gfA==} - engines: {node: '>=18'} + ibm-cloud-sdk-core@5.4.5: + resolution: {integrity: sha512-7ClYtr/Xob83hypKUa1D9N8/ViH71giKQ0kqjHcoyKum6yvwsWAeFA6zf6WTWb+DdZ1XSBrMPhgCCoy0bqReLg==} + engines: {node: '>=20'} iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -11240,8 +11268,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.0: - resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} idb-keyval@6.2.2: @@ -11278,6 +11306,9 @@ packages: import-in-the-middle@1.15.0: resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} + import-in-the-middle@2.0.1: + resolution: {integrity: sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA==} + import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} engines: {node: '>=8'} @@ -11708,12 +11739,8 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.1.1: - resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} - engines: {node: 20 || >=22} - - jayson@4.2.0: - resolution: {integrity: sha512-VfJ9t1YLwacIubLhONk0KFeosUBwstRWQ0IRT1KDjEjnVnSOVHC3uwugyV7L0c7R9lpVyrUGT2XWiBA1UTtpyg==} + jayson@4.3.0: + resolution: {integrity: sha512-AauzHcUcqs8OBnCHOkJY280VaTiCm57AbuO7lqzcw7JapGj50BisE3xhksye4zlTSR1+1tAz67wLTl8tEH1obQ==} engines: {node: '>=8'} hasBin: true @@ -11881,6 +11908,9 @@ packages: jose@5.10.0: resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -11981,6 +12011,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema-walker@2.0.0: resolution: {integrity: sha512-nXN2cMky0Iw7Af28w061hmxaPDaML5/bQD9nwm1lOoIKEGjHcRGxqWe4MfrkYThYAPjSUhmsp4bJNoLAyVn9Xw==} engines: {node: '>=10'} @@ -12034,8 +12067,8 @@ packages: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} - jsonwebtoken@9.0.2: - resolution: {integrity: sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==} + jsonwebtoken@9.0.3: + resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} engines: {node: '>=12', npm: '>=6'} jsprim@1.4.2: @@ -12053,31 +12086,25 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} - jwa@1.4.2: - resolution: {integrity: sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==} - jwa@2.0.1: resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - jws@3.2.2: - resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} - - jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + jws@4.0.1: + resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} jwt-decode@4.0.0: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} - katex@0.16.25: - resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} + katex@0.16.27: + resolution: {integrity: sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==} hasBin: true keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - keyv@5.5.4: - resolution: {integrity: sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==} + keyv@5.5.5: + resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==} keyvaluestorage-interface@1.0.0: resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} @@ -12090,8 +12117,8 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - konva@10.0.9: - resolution: {integrity: sha512-AEqV0XqfE0ouHjcSo/IVAXZUK4jB/r18OlUoIzPFEx/59Vb95Yq1S4Ch5QuDDEXJT21SEpOMb89zSpEpsQWzWg==} + konva@10.0.12: + resolution: {integrity: sha512-DHmkeG5FbW6tLCkbMQTi1ihWycfzljrn0V7umUUuewxx7aoINcI71ksgBX9fTPNXhlsK4/JoMgKwI/iCde+BRw==} langchain@0.3.36: resolution: {integrity: sha512-PqC19KChFF0QlTtYDFgfEbIg+SCnCXox29G8tY62QWfj9bOW7ew2kgWmPw5qoHLOTKOdQPvXET20/1Pdq8vAtQ==} @@ -12151,8 +12178,8 @@ packages: typeorm: optional: true - langsmith@0.3.80: - resolution: {integrity: sha512-BWpbB9/Hkx06S5X4nJE3W5Wm1mH/j6SIqWcM/WAuT+yulohE9knstIJGmBpmSBULb46nCj+cfjRkyF1Nrc4UmA==} + langsmith@0.3.87: + resolution: {integrity: sha512-XXR1+9INH8YX96FKWc5tie0QixWz6tOqAsAKfcJyPkE0xPep+NDz0IQLR32q4bn10QK3LqD2HN6T3n6z1YLW7Q==} peerDependencies: '@opentelemetry/api': '*' '@opentelemetry/exporter-trace-otlp-proto': '*' @@ -12189,8 +12216,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libphonenumber-js@1.12.27: - resolution: {integrity: sha512-8gHhHzzcnY1EF4BS5L/lrjv2VAZWd6ltU7c/sqoktRZSQvZl4g8hrgXtXHXGkSFKFYArFON12zUNJrNVqJ9u4g==} + libphonenumber-js@1.12.33: + resolution: {integrity: sha512-r9kw4OA6oDO4dPXkOrXTkArQAafIKAU71hChInV4FxZ69dxCfbwQGDPzqR5/vea94wU705/3AZroEbSoeVWrQw==} lighthouse-logger@1.4.2: resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} @@ -12318,8 +12345,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash-es@4.17.22: + resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==} lodash._baseiteratee@4.7.0: resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==} @@ -12441,8 +12468,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} lru-cache@4.1.5: @@ -12595,8 +12622,8 @@ packages: mdast-util-to-hast@12.3.0: resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} - mdast-util-to-hast@13.2.0: - resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} mdast-util-to-markdown@2.1.2: resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} @@ -12890,9 +12917,9 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -13063,8 +13090,8 @@ packages: namespace-emitter@2.0.1: resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} - nan@2.23.1: - resolution: {integrity: sha512-r7bBUGKzlqk8oPBDYxt6Z0aEdF1G1rwlMcLk8LCOMbOzf0mG+JUfUzG4fIMWwHWP0iyaLWEQZJmtB7nOHEm/qw==} + nan@2.24.0: + resolution: {integrity: sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==} nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} @@ -13209,8 +13236,8 @@ packages: resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + node-forge@1.3.3: + resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} engines: {node: '>= 6.13.0'} node-gyp-build-optional-packages@5.2.2: @@ -13224,8 +13251,8 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-mock-http@1.0.3: - resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==} + node-mock-http@1.0.4: + resolution: {integrity: sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==} node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} @@ -13257,10 +13284,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} @@ -13268,8 +13291,8 @@ packages: normalize.css@8.0.1: resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} - nostr-tools@2.18.2: - resolution: {integrity: sha512-lUCJQd9YZG3kEvxV5Zgm7qUkBpaeuvFrtqBz4TJLAxHzUn2pE7nmZZRDQmNzp5neEw20tQS3jR16o7XzzF8ncg==} + nostr-tools@2.19.4: + resolution: {integrity: sha512-qVLfoTpZegNYRJo5j+Oi6RPu0AwLP6jcvzcB3ySMnIT5DrAGNXfs5HNBspB/2HiGfH3GY+v6yXkTtcKSBQZwSg==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -13304,8 +13327,8 @@ packages: nullthrows@1.1.1: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} - nwsapi@2.2.22: - resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + nwsapi@2.2.23: + resolution: {integrity: sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==} oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} @@ -13423,8 +13446,8 @@ packages: zod: optional: true - openai@6.9.1: - resolution: {integrity: sha512-vQ5Rlt0ZgB3/BNmTa7bIijYFhz3YBceAA3Z4JuoMSBftBF9YqFHIEhZakSs+O/Ad7EaoEimZvHxD5ylRjN11Lg==} + openai@6.15.0: + resolution: {integrity: sha512-F1Lvs5BoVvmZtzkUEVyh8mDQPPFolq4F+xdsx/DO8Hee8YF3IGAlZqUIsF+DVGhqf4aU0a3bTghsxB6OIsRy1g==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -13468,16 +13491,16 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - ox@0.6.7: - resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} + ox@0.11.1: + resolution: {integrity: sha512-1l1gOLAqg0S0xiN1dH5nkPna8PucrZgrIJOfS49MLNiMevxu07Iz4ZjuJS9N+xifvT+PsZyIptS7WHM8nC+0+A==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: typescript: optional: true - ox@0.9.6: - resolution: {integrity: sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==} + ox@0.6.7: + resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -13775,12 +13798,15 @@ packages: pino-abstract-transport@2.0.0: resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} + pino-abstract-transport@3.0.0: + resolution: {integrity: sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==} + pino-pretty@11.3.0: resolution: {integrity: sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==} hasBin: true - pino-pretty@13.1.2: - resolution: {integrity: sha512-3cN0tCakkT4f3zo9RXDIhy6GTvtYD6bK4CRBLN9j3E/ePqN1tugAXD5rGVfoChW6s0hiek+eyYlLNqc/BG7vBQ==} + pino-pretty@13.1.3: + resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true pino-std-serializers@4.0.0: @@ -13804,8 +13830,8 @@ packages: piscina@4.9.2: resolution: {integrity: sha512-Fq0FERJWFEUpB4eSY59wSNwXD4RYqR+nR/WiEVcZW8IWfVBxJJafcgTEZDQo8k3w0sUarJ8RyVbbUF4GQ2LGbQ==} - pkce-challenge@5.0.0: - resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} pkg-dir@4.2.0: @@ -13818,13 +13844,13 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.56.1: - resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + playwright-core@1.57.0: + resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} engines: {node: '>=18'} hasBin: true - playwright@1.56.1: - resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} + playwright@1.57.0: + resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} engines: {node: '>=18'} hasBin: true @@ -13839,8 +13865,8 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - polotno@2.32.4: - resolution: {integrity: sha512-dOwXQcpYC1eI2ad12uKbq58TX5IQW46JZcHLV3T8ccOb+ZnFpFNFmlZQivyY6u1Y/R9vhB8OW4xZg0qTlYwpRw==} + polotno@2.34.1: + resolution: {integrity: sha512-na8ulXfzIQJonOem6TzH1Knz+nalQr10zAQlAdKfzg8ba/ihKaX6qKWHJdRhCLn1AUzoUtXnp/Eqv8GDTJL5DQ==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -13920,8 +13946,8 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} engines: {node: '>=0.10.0'} postgres-date@1.0.7: @@ -13936,15 +13962,15 @@ packages: resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} engines: {node: '>=12'} - posthog-js@1.296.1: - resolution: {integrity: sha512-Qc0S5dkQHzYzpUu7sfU2JkDkf7AE4wI8Yffog4hKWL9i8YbutuXLhJ1duZlY1V8HyaVVnn6qx5wd55SNwVcfhQ==} + posthog-js@1.309.1: + resolution: {integrity: sha512-JUJcQhYzNNKO0cgnSbowCsVi2RTu75XGZ2EmnTQti4tMGRCTOv/HCnZasdFniBGZ0rLugQkaScYca/84Ta2u5Q==} posthog-node@4.18.0: resolution: {integrity: sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==} engines: {node: '>=15.0.0'} - preact@10.27.2: - resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + preact@10.28.0: + resolution: {integrity: sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -13955,8 +13981,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -14086,8 +14112,8 @@ packages: prosemirror-state@1.4.4: resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} - prosemirror-tables@1.8.1: - resolution: {integrity: sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==} + prosemirror-tables@1.8.4: + resolution: {integrity: sha512-CGr2BK5sLdZx+ARbeLO4HBZYa3qSG3FmwOVmzYs0Zp7n5SkrGqj+1CeNuubFNZEr64yMAQ20SanbFyIyHWZc8w==} prosemirror-trailing-node@3.0.0: resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} @@ -14099,8 +14125,8 @@ packages: prosemirror-transform@1.10.5: resolution: {integrity: sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==} - prosemirror-view@1.41.3: - resolution: {integrity: sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==} + prosemirror-view@1.41.4: + resolution: {integrity: sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==} proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -14242,8 +14268,12 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} - raw-body@3.0.1: - resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + raw-body@2.5.3: + resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} + engines: {node: '>= 0.8'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} react-color@2.19.3: @@ -14303,8 +14333,8 @@ packages: react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - react-hook-form@7.66.1: - resolution: {integrity: sha512-2KnjpgG2Rhbi+CIiIBQQ9Df6sMGH5ExNyFl4Hw9qO7pIqMBR8Bvu9RQyjl3JM4vehzCh9soiNUM/xYMswb2EiA==} + react-hook-form@7.69.0: + resolution: {integrity: sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -14340,8 +14370,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.0: - resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + react-is@19.2.3: + resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} react-konva-utils@2.0.0: resolution: {integrity: sha512-pOb+TF13gFAjfPmUqsE42J4GJ+xhUS97qS32p0NRTqSeqtamWyKJikGa1XeVvV5yItu9SWDo7onL79GGPG96HQ==} @@ -14391,13 +14421,13 @@ packages: react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 - react-native@0.82.1: - resolution: {integrity: sha512-tFAqcU7Z4g49xf/KnyCEzI4nRTu1Opcx05Ov2helr8ZTg1z7AJR/3sr2rZ+AAVlAs2IXk+B0WOxXGmdD3+4czA==} + react-native@0.83.1: + resolution: {integrity: sha512-mL1q5HPq5cWseVhWRLl+Fwvi5z1UO+3vGOpjr+sHFwcUletPRZ5Kv+d0tUfqHmvi73/53NjlQqX1Pyn4GguUfA==} engines: {node: '>= 20.19.4'} hasBin: true peerDependencies: '@types/react': ^19.1.1 - react: ^19.1.1 + react: ^19.2.0 peerDependenciesMeta: '@types/react': optional: true @@ -14460,8 +14490,8 @@ packages: '@types/react': optional: true - react-remove-scroll@2.7.1: - resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} engines: {node: '>=10'} peerDependencies: '@types/react': '*' @@ -14533,10 +14563,10 @@ packages: peerDependencies: react: '>=16.8' - react-use-keypress@1.3.1: - resolution: {integrity: sha512-fo+LQrxviMcZt7efCFPc6CX9/oNEPD+MJ/qSs4nK3/lyRNtquhG9f1J8GQq2VFfIYUVDUdPKz8fGIwErO1Pcuw==} + react-use-keypress@1.4.0: + resolution: {integrity: sha512-8qNnFcWBdtS2bcbSSUqRDaJY/oMHFpnm1/xGNd5GxsoSWkkN4/zd2golR8ZXFOlQ2KzQW8CCV8ipde42H1kUvw==} peerDependencies: - react: ^16.8.0 || 17 || 18 + react: '>=16.8.0' react-window@1.8.11: resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==} @@ -14603,8 +14633,8 @@ packages: redis@4.7.1: resolution: {integrity: sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==} - redis@5.9.0: - resolution: {integrity: sha512-E8dQVLSyH6UE/C9darFuwq4usOPrqfZ1864kI4RFbr5Oj9ioB9qPF0oJMwX7s8mf6sPYrz84x/Dx1PGF3/0EaQ==} + redis@5.10.0: + resolution: {integrity: sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==} engines: {node: '>= 18'} redux@4.2.1: @@ -14658,8 +14688,8 @@ packages: rehype-autolink-headings@7.1.0: resolution: {integrity: sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==} - rehype-ignore@2.0.2: - resolution: {integrity: sha512-BpAT/3lU9DMJ2siYVD/dSR0A/zQgD6Fb+fxkJd4j+wDVy6TYbYpK+FZqu8eM9EuNKGvi4BJR7XTZ/+zF02Dq8w==} + rehype-ignore@2.0.3: + resolution: {integrity: sha512-IzhP6/u/6sm49sdktuYSmeIuObWB+5yC/5eqVws8BhuGA9kY25/byz6uCy/Ravj6lXUShEd2ofHM5MyAIj86Sg==} engines: {node: '>=16'} rehype-parse@9.0.1: @@ -14674,8 +14704,8 @@ packages: rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} - rehype-rewrite@4.0.3: - resolution: {integrity: sha512-y6zPHL2tBqm1TF+lFV8SKH65+DYqkm1CUXipNebTo4l4R/rL4bOLOS+klp9VlcKhytiyYVZB72AA/1F4+lOk9g==} + rehype-rewrite@4.0.4: + resolution: {integrity: sha512-L/FO96EOzSA6bzOam4DVu61/PB3AGKcSPXpa53yMIozoxH4qg1+bVZDF8zh1EsuxtSauAhzt5cCnvoplAaSLrw==} engines: {node: '>=16.0.0'} rehype-slug@6.0.0: @@ -14742,6 +14772,10 @@ packages: resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} engines: {node: '>=8.6.0'} + require-in-the-middle@8.0.1: + resolution: {integrity: sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==} + engines: {node: '>=9.3.0 || >=8.10.0 <9.0.0'} + require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -14855,8 +14889,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.53.3: - resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -14867,8 +14901,8 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - rpc-websockets@9.3.1: - resolution: {integrity: sha512-bY6a+i/lEtBJ/mUxwsCTgevoV1P0foXTVA7UoThzaIWbM+3NDqorf8NBWs5DmqKTFeA1IoNzgvkWjFCPgnzUiQ==} + rpc-websockets@9.3.2: + resolution: {integrity: sha512-VuW2xJDnl1k8n8kjbdRSWawPRkwaVqUQNjE1TdeTawf0y0abGhtVJFTXCLfgpgGDBkO/Fj6kny8Dc/nvOW78MA==} rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} @@ -14941,8 +14975,8 @@ packages: sane-domparser-error@0.2.0: resolution: {integrity: sha512-wxjDV5jty95tNv8N/4WA15UNGqqaor/xX7rnNYY961hifN3bheYoKqtXN+V/M6EUgmUAs6pMul3klwUPMEiVXA==} - sass@1.94.1: - resolution: {integrity: sha512-/YVm5FRQaRlr3oNh2LLFYne1PdPlRZGyKnHh1sLleOqLcohTR4eUUvBjBIqkl1fEXd1MGOHgzJGJh+LgTtV4KQ==} + sass@1.97.1: + resolution: {integrity: sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==} engines: {node: '>=14.0.0'} hasBin: true @@ -14956,8 +14990,8 @@ packages: scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} - scheduler@0.26.0: - resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} schema-utils@3.3.0: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} @@ -15003,8 +15037,12 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + send@0.19.2: + resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} + engines: {node: '>= 0.8.0'} + + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} sentence-case@3.0.4: @@ -15021,8 +15059,12 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + serve-static@1.16.3: + resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} + engines: {node: '>= 0.8.0'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} set-blocking@2.0.0: @@ -15083,9 +15125,6 @@ packages: engines: {node: '>=4'} hasBin: true - shimmer@1.2.1: - resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} - side-channel-list@1.0.0: resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} engines: {node: '>= 0.4'} @@ -15434,8 +15473,8 @@ packages: strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} - strnum@2.1.1: - resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + strnum@2.1.2: + resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} @@ -15486,8 +15525,8 @@ packages: resolution: {integrity: sha512-IMS+L8lXjOLveg5BC/bVZy+36/x2NqMIQmVDhbquDpxLnXugzmz7/yHHFZ7b9YLfqNaBdXwh1lsnAds3g1FnCQ==} engines: {node: '>=10'} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -15528,8 +15567,8 @@ packages: sweetalert2@11.4.8: resolution: {integrity: sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==} - swr@2.3.6: - resolution: {integrity: sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==} + swr@2.3.8: + resolution: {integrity: sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==} peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -15563,8 +15602,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.17: - resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -15580,8 +15619,8 @@ packages: telejson@7.2.0: resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} - terser-webpack-plugin@5.3.14: - resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} + terser-webpack-plugin@5.3.16: + resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} peerDependencies: '@swc/core': '*' @@ -15816,8 +15855,8 @@ packages: resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} engines: {node: '>=8'} - ts-jest@29.4.5: - resolution: {integrity: sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==} + ts-jest@29.4.6: + resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -16073,6 +16112,9 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} + unicode-segmenter@0.14.4: + resolution: {integrity: sha512-pR5VCiCrLrKOL6FRW61jnk9+wyMtKKowq+jyFY9oc6uHbWKhDL4yVRiI4YZPksGMK72Pahh8m0cn/0JvbDDyJg==} + unicode-trie@2.0.0: resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} @@ -16153,8 +16195,8 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - unstorage@1.17.2: - resolution: {integrity: sha512-cKEsD6iBWJgOMJ6vW1ID/SYuqNf8oN4yqRk8OYqaVQ3nnkJXOT1PSpaMh2QfzLs78UN5kSNRD2c/mgjT8tX7+w==} + unstorage@1.17.3: + resolution: {integrity: sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q==} peerDependencies: '@azure/app-configuration': ^1.8.0 '@azure/cosmos': ^4.2.0 @@ -16218,8 +16260,8 @@ packages: untruncate-json@0.0.1: resolution: {integrity: sha512-4W9enDK4X1y1s2S/Rz7ysw6kDuMS3VmRjMFg7GZrNO+98OSe+x5Lh7PKYoVjy3lW/1wmhs6HW0lusnQRHgMarA==} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -16376,8 +16418,8 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} - validator@13.15.23: - resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==} + validator@13.15.26: + resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} engines: {node: '>= 0.10'} valtio@1.13.2: @@ -16423,8 +16465,8 @@ packages: typescript: optional: true - viem@2.39.3: - resolution: {integrity: sha512-s11rPQRvUEdc5qHK3xT4fIk4qvgPAaLwaTFq+EbFlcJJD+Xn3R4mc9H6B6fquEiHl/mdsdbG/uKCnYpoNtHNHw==} + viem@2.43.3: + resolution: {integrity: sha512-zM251fspfSjENCtfmT7cauuD+AA/YAlkFU7cksdEQJxj7wDuO0XFRWRH+RMvfmTFza88B9kug5cKU+Wk2nAjJg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -16539,9 +16581,9 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - weaviate-client@3.9.0: - resolution: {integrity: sha512-7qwg7YONAaT4zWnohLrFdzky+rZegVe76J+Tky/+7tuyvjFpdKgSrdqI/wPDh8aji0ZGZrL4DdGwGfFnZ+uV4w==} - engines: {node: '>=18.0.0'} + weaviate-client@3.10.0: + resolution: {integrity: sha512-PB338DjIwUus1Mq1dxhCc6fEp+yA+aY4H4sSFDS0No/GguEufd6SDhHHVLOYMy2cPgX35dWgEx5jUbG5o3aPZA==} + engines: {node: '>=20.0.0'} web-namespaces@2.0.1: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} @@ -16694,17 +16736,6 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@6.2.3: - resolution: {integrity: sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -16782,8 +16813,8 @@ packages: xmlserializer@0.6.1: resolution: {integrity: sha512-FNb0eEqqUUbnuvxuHqNuKH8qCGKqxu+558Zi8UzOoQk8Z9LdvpONK+v7m3gpKVHrk5Aq+0nNLsKxu/6OYh7Umw==} - xstate@5.24.0: - resolution: {integrity: sha512-h/213ThFfZbOefUWrLc9ZvYggEVBr0jrD2dNxErxNMLQfZRN19v+80TaXFho17hs8Q2E1mULtm/6nv12um0C4A==} + xstate@5.25.0: + resolution: {integrity: sha512-yyWzfhVRoTHNLjLoMmdwZGagAYfmnzpm9gPjlX2MhJZsDojXGqRxODDOi4BsgGRKD46NZRAdcLp6CKOyvQS0Bw==} xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} @@ -16812,8 +16843,8 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + yaml@2.8.2: + resolution: {integrity: sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==} engines: {node: '>= 14.6'} hasBin: true @@ -16869,11 +16900,11 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.1.12: - resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} + zod@4.2.1: + resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} - zustand@5.0.8: - resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + zustand@5.0.9: + resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -16903,20 +16934,20 @@ snapshots: dependencies: '@types/cors': 2.8.19 '@types/express': 4.17.25 - body-parser: 2.2.0 + body-parser: 2.2.1 cors: 2.8.5 - express: 4.21.2 + express: 4.22.1 uuid: 11.1.0 transitivePeerDependencies: - supports-color '@adraffy/ens-normalize@1.11.1': {} - '@ag-ui/client@0.0.41': + '@ag-ui/client@0.0.42': dependencies: - '@ag-ui/core': 0.0.41 - '@ag-ui/encoder': 0.0.41 - '@ag-ui/proto': 0.0.41 + '@ag-ui/core': 0.0.42 + '@ag-ui/encoder': 0.0.42 + '@ag-ui/proto': 0.0.42 '@types/uuid': 10.0.0 compare-versions: 6.1.1 fast-json-patch: 3.1.1 @@ -16930,22 +16961,22 @@ snapshots: rxjs: 7.8.1 zod: 3.25.76 - '@ag-ui/core@0.0.41': + '@ag-ui/core@0.0.42': dependencies: rxjs: 7.8.1 zod: 3.25.76 - '@ag-ui/encoder@0.0.41': + '@ag-ui/encoder@0.0.42': dependencies: - '@ag-ui/core': 0.0.41 - '@ag-ui/proto': 0.0.41 + '@ag-ui/core': 0.0.42 + '@ag-ui/proto': 0.0.42 - '@ag-ui/langgraph@0.0.18(@ag-ui/client@0.0.41)(@ag-ui/core@0.0.41)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ag-ui/langgraph@0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@ag-ui/client': 0.0.41 - '@ag-ui/core': 0.0.41 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ag-ui/client': 0.0.42 + '@ag-ui/core': 0.0.37 + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) partial-json: 0.1.7 rxjs: 7.8.1 transitivePeerDependencies: @@ -16956,12 +16987,12 @@ snapshots: - react - react-dom - '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.41)(@ag-ui/core@0.0.41)(@copilotkit/runtime@1.10.6(6855d89fee41a5dd20a0f9576445cd9f))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': + '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(7d9d10c1aa1e359346c98e211cce0cbe))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': dependencies: - '@ag-ui/client': 0.0.41 - '@ag-ui/core': 0.0.41 + '@ag-ui/client': 0.0.42 + '@ag-ui/core': 0.0.37 '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) - '@copilotkit/runtime': 1.10.6(6855d89fee41a5dd20a0f9576445cd9f) + '@copilotkit/runtime': 1.10.6(7d9d10c1aa1e359346c98e211cce0cbe) '@mastra/client-js': 0.15.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) rxjs: 7.8.1 @@ -16983,10 +17014,10 @@ snapshots: - valibot - zod-openapi - '@ag-ui/proto@0.0.41': + '@ag-ui/proto@0.0.42': dependencies: - '@ag-ui/core': 0.0.41 - '@bufbuild/protobuf': 2.10.1 + '@ag-ui/core': 0.0.42 + '@bufbuild/protobuf': 2.10.2 '@protobuf-ts/protoc': 2.11.1 '@ai-sdk/anthropic@2.0.23(zod@3.25.76)': @@ -16999,7 +17030,7 @@ snapshots: dependencies: '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.10(zod@3.25.76) - '@vercel/oidc': 3.0.4 + '@vercel/oidc': 3.0.5 zod: 3.25.76 '@ai-sdk/google@2.0.17(zod@3.25.76)': @@ -17020,10 +17051,10 @@ snapshots: '@ai-sdk/provider-utils': 3.0.10(zod@3.25.76) zod: 3.25.76 - '@ai-sdk/openai@2.0.68(zod@3.25.76)': + '@ai-sdk/openai@2.0.88(zod@3.25.76)': dependencies: '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.17(zod@3.25.76) + '@ai-sdk/provider-utils': 3.0.19(zod@3.25.76) zod: 3.25.76 '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': @@ -17036,14 +17067,14 @@ snapshots: '@ai-sdk/provider-utils@3.0.10(zod@3.25.76)': dependencies: '@ai-sdk/provider': 2.0.0 - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 eventsource-parser: 3.0.6 zod: 3.25.76 - '@ai-sdk/provider-utils@3.0.17(zod@3.25.76)': + '@ai-sdk/provider-utils@3.0.19(zod@3.25.76)': dependencies: '@ai-sdk/provider': 2.0.0 - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 eventsource-parser: 3.0.6 zod: 3.25.76 @@ -17060,7 +17091,7 @@ snapshots: '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) react: 18.3.1 - swr: 2.3.6(react@18.3.1) + swr: 2.3.8(react@18.3.1) throttleit: 2.1.0 optionalDependencies: zod: 3.25.76 @@ -17175,63 +17206,75 @@ snapshots: '@atproto/api@0.15.27': dependencies: - '@atproto/common-web': 0.4.3 + '@atproto/common-web': 0.4.7 '@atproto/lexicon': 0.4.14 - '@atproto/syntax': 0.4.1 - '@atproto/xrpc': 0.7.5 + '@atproto/syntax': 0.4.2 + '@atproto/xrpc': 0.7.7 await-lock: 2.2.2 multiformats: 9.9.0 tlds: 1.261.0 zod: 3.25.76 - '@atproto/common-web@0.4.3': + '@atproto/common-web@0.4.7': dependencies: - graphemer: 1.4.0 - multiformats: 9.9.0 - uint8arrays: 3.0.0 + '@atproto/lex-data': 0.0.3 + '@atproto/lex-json': 0.0.3 zod: 3.25.76 + '@atproto/lex-data@0.0.3': + dependencies: + '@atproto/syntax': 0.4.2 + multiformats: 9.9.0 + tslib: 2.8.1 + uint8arrays: 3.0.0 + unicode-segmenter: 0.14.4 + + '@atproto/lex-json@0.0.3': + dependencies: + '@atproto/lex-data': 0.0.3 + tslib: 2.8.1 + '@atproto/lexicon@0.4.14': dependencies: - '@atproto/common-web': 0.4.3 - '@atproto/syntax': 0.4.1 + '@atproto/common-web': 0.4.7 + '@atproto/syntax': 0.4.2 iso-datestring-validator: 2.2.2 multiformats: 9.9.0 zod: 3.25.76 - '@atproto/lexicon@0.5.1': + '@atproto/lexicon@0.6.0': dependencies: - '@atproto/common-web': 0.4.3 - '@atproto/syntax': 0.4.1 + '@atproto/common-web': 0.4.7 + '@atproto/syntax': 0.4.2 iso-datestring-validator: 2.2.2 multiformats: 9.9.0 zod: 3.25.76 - '@atproto/syntax@0.4.1': {} + '@atproto/syntax@0.4.2': {} - '@atproto/xrpc@0.7.5': + '@atproto/xrpc@0.7.7': dependencies: - '@atproto/lexicon': 0.5.1 + '@atproto/lexicon': 0.6.0 zod: 3.25.76 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.956.0 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.956.0 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-locate-window': 3.893.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-locate-window': 3.953.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -17240,15 +17283,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-locate-window': 3.893.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-locate-window': 3.953.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.956.0 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -17257,660 +17300,674 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.930.0 + '@aws-sdk/types': 3.956.0 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-bedrock-agent-runtime@3.934.0': + '@aws-sdk/client-bedrock-agent-runtime@3.956.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/eventstream-serde-browser': 4.2.5 - '@smithy/eventstream-serde-config-resolver': 4.3.5 - '@smithy/eventstream-serde-node': 4.2.5 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/eventstream-serde-browser': 4.2.7 + '@smithy/eventstream-serde-config-resolver': 4.3.7 + '@smithy/eventstream-serde-node': 4.2.7 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-bedrock-runtime@3.934.0': + '@aws-sdk/client-bedrock-runtime@3.956.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@aws-sdk/eventstream-handler-node': 3.930.0 - '@aws-sdk/middleware-eventstream': 3.930.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/middleware-websocket': 3.930.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/token-providers': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/eventstream-serde-browser': 4.2.5 - '@smithy/eventstream-serde-config-resolver': 4.3.5 - '@smithy/eventstream-serde-node': 4.2.5 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 + '@aws-sdk/eventstream-handler-node': 3.956.0 + '@aws-sdk/middleware-eventstream': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/middleware-websocket': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/token-providers': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/eventstream-serde-browser': 4.2.7 + '@smithy/eventstream-serde-config-resolver': 4.3.7 + '@smithy/eventstream-serde-node': 4.2.7 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 - '@smithy/util-stream': 4.5.6 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 + '@smithy/util-stream': 4.5.8 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-kendra@3.934.0': + '@aws-sdk/client-kendra@3.956.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-s3@3.934.0': + '@aws-sdk/client-s3@3.956.0': 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.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@aws-sdk/middleware-bucket-endpoint': 3.930.0 - '@aws-sdk/middleware-expect-continue': 3.930.0 - '@aws-sdk/middleware-flexible-checksums': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-location-constraint': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-sdk-s3': 3.934.0 - '@aws-sdk/middleware-ssec': 3.930.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/signature-v4-multi-region': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/eventstream-serde-browser': 4.2.5 - '@smithy/eventstream-serde-config-resolver': 4.3.5 - '@smithy/eventstream-serde-node': 4.2.5 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-blob-browser': 4.2.6 - '@smithy/hash-node': 4.2.5 - '@smithy/hash-stream-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/md5-js': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 + '@aws-sdk/middleware-bucket-endpoint': 3.956.0 + '@aws-sdk/middleware-expect-continue': 3.956.0 + '@aws-sdk/middleware-flexible-checksums': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-location-constraint': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-sdk-s3': 3.956.0 + '@aws-sdk/middleware-ssec': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/signature-v4-multi-region': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/eventstream-serde-browser': 4.2.7 + '@smithy/eventstream-serde-config-resolver': 4.3.7 + '@smithy/eventstream-serde-node': 4.2.7 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-blob-browser': 4.2.8 + '@smithy/hash-node': 4.2.7 + '@smithy/hash-stream-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/md5-js': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 - '@smithy/util-stream': 4.5.6 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 + '@smithy/util-stream': 4.5.8 '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.5 + '@smithy/util-waiter': 4.2.7 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-ses@3.934.0': + '@aws-sdk/client-ses@3.956.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.5 + '@smithy/util-waiter': 4.2.7 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.934.0': + '@aws-sdk/client-sso@3.956.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.934.0': + '@aws-sdk/core@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@aws-sdk/xml-builder': 3.930.0 - '@smithy/core': 3.18.4 - '@smithy/node-config-provider': 4.3.5 - '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/signature-v4': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/xml-builder': 3.956.0 + '@smithy/core': 3.20.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/property-provider': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.5 + '@smithy/util-middleware': 4.2.7 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.934.0': + '@aws-sdk/credential-provider-env@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/property-provider': 4.2.5 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/property-provider': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.934.0': + '@aws-sdk/credential-provider-http@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/node-http-handler': 4.4.5 - '@smithy/property-provider': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/util-stream': 4.5.6 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/node-http-handler': 4.4.7 + '@smithy/property-provider': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/util-stream': 4.5.8 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.934.0': + '@aws-sdk/credential-provider-ini@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/credential-provider-env': 3.934.0 - '@aws-sdk/credential-provider-http': 3.934.0 - '@aws-sdk/credential-provider-process': 3.934.0 - '@aws-sdk/credential-provider-sso': 3.934.0 - '@aws-sdk/credential-provider-web-identity': 3.934.0 - '@aws-sdk/nested-clients': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/credential-provider-imds': 4.2.5 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/credential-provider-env': 3.956.0 + '@aws-sdk/credential-provider-http': 3.956.0 + '@aws-sdk/credential-provider-login': 3.956.0 + '@aws-sdk/credential-provider-process': 3.956.0 + '@aws-sdk/credential-provider-sso': 3.956.0 + '@aws-sdk/credential-provider-web-identity': 3.956.0 + '@aws-sdk/nested-clients': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/credential-provider-imds': 4.2.7 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.934.0': + '@aws-sdk/credential-provider-login@3.956.0': dependencies: - '@aws-sdk/credential-provider-env': 3.934.0 - '@aws-sdk/credential-provider-http': 3.934.0 - '@aws-sdk/credential-provider-ini': 3.934.0 - '@aws-sdk/credential-provider-process': 3.934.0 - '@aws-sdk/credential-provider-sso': 3.934.0 - '@aws-sdk/credential-provider-web-identity': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/credential-provider-imds': 4.2.5 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/nested-clients': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/property-provider': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.934.0': + '@aws-sdk/credential-provider-node@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-sso@3.934.0': - dependencies: - '@aws-sdk/client-sso': 3.934.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/token-providers': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@aws-sdk/credential-provider-env': 3.956.0 + '@aws-sdk/credential-provider-http': 3.956.0 + '@aws-sdk/credential-provider-ini': 3.956.0 + '@aws-sdk/credential-provider-process': 3.956.0 + '@aws-sdk/credential-provider-sso': 3.956.0 + '@aws-sdk/credential-provider-web-identity': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/credential-provider-imds': 4.2.7 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-web-identity@3.934.0': + '@aws-sdk/credential-provider-process@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/nested-clients': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.956.0': + dependencies: + '@aws-sdk/client-sso': 3.956.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/token-providers': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/eventstream-handler-node@3.930.0': + '@aws-sdk/credential-provider-web-identity@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/eventstream-codec': 4.2.5 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/nested-clients': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/eventstream-handler-node@3.956.0': + dependencies: + '@aws-sdk/types': 3.956.0 + '@smithy/eventstream-codec': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.930.0': + '@aws-sdk/middleware-bucket-endpoint@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-arn-parser': 3.893.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-arn-parser': 3.953.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 '@smithy/util-config-provider': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-eventstream@3.930.0': + '@aws-sdk/middleware-eventstream@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.930.0': + '@aws-sdk/middleware-expect-continue@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-flexible-checksums@3.934.0': + '@aws-sdk/middleware-flexible-checksums@3.956.0': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/types': 3.956.0 '@smithy/is-array-buffer': 4.2.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-stream': 4.5.6 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-stream': 4.5.8 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.930.0': + '@aws-sdk/middleware-host-header@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.930.0': + '@aws-sdk/middleware-location-constraint@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.930.0': + '@aws-sdk/middleware-logger@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.933.0': + '@aws-sdk/middleware-recursion-detection@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@aws/lambda-invoke-store': 0.2.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@aws/lambda-invoke-store': 0.2.2 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.934.0': + '@aws-sdk/middleware-sdk-s3@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-arn-parser': 3.893.0 - '@smithy/core': 3.18.4 - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/signature-v4': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-arn-parser': 3.953.0 + '@smithy/core': 3.20.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 '@smithy/util-config-provider': 4.2.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-stream': 4.5.6 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-stream': 4.5.8 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.930.0': + '@aws-sdk/middleware-ssec@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.934.0': + '@aws-sdk/middleware-user-agent@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@smithy/core': 3.18.4 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@smithy/core': 3.20.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.930.0': + '@aws-sdk/middleware-websocket@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-format-url': 3.930.0 - '@smithy/eventstream-codec': 4.2.5 - '@smithy/eventstream-serde-browser': 4.2.5 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/protocol-http': 5.3.5 - '@smithy/signature-v4': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-format-url': 3.956.0 + '@smithy/eventstream-codec': 4.2.7 + '@smithy/eventstream-serde-browser': 4.2.7 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/types': 4.11.0 '@smithy/util-hex-encoding': 4.2.0 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.934.0': + '@aws-sdk/nested-clients@3.956.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.934.0 - '@aws-sdk/middleware-host-header': 3.930.0 - '@aws-sdk/middleware-logger': 3.930.0 - '@aws-sdk/middleware-recursion-detection': 3.933.0 - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/region-config-resolver': 3.930.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-endpoints': 3.930.0 - '@aws-sdk/util-user-agent-browser': 3.930.0 - '@aws-sdk/util-user-agent-node': 3.934.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/core': 3.18.4 - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/hash-node': 4.2.5 - '@smithy/invalid-dependency': 4.2.5 - '@smithy/middleware-content-length': 4.2.5 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-retry': 4.4.11 - '@smithy/middleware-serde': 4.2.6 - '@smithy/middleware-stack': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/node-http-handler': 4.4.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/middleware-host-header': 3.956.0 + '@aws-sdk/middleware-logger': 3.956.0 + '@aws-sdk/middleware-recursion-detection': 3.956.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/region-config-resolver': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-endpoints': 3.956.0 + '@aws-sdk/util-user-agent-browser': 3.956.0 + '@aws-sdk/util-user-agent-node': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/core': 3.20.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/hash-node': 4.2.7 + '@smithy/invalid-dependency': 4.2.7 + '@smithy/middleware-content-length': 4.2.7 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-retry': 4.4.17 + '@smithy/middleware-serde': 4.2.8 + '@smithy/middleware-stack': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/node-http-handler': 4.4.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.10 - '@smithy/util-defaults-mode-node': 4.2.13 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/util-defaults-mode-browser': 4.3.16 + '@smithy/util-defaults-mode-node': 4.2.19 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.930.0': + '@aws-sdk/region-config-resolver@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/config-resolver': 4.4.3 - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.934.0': + '@aws-sdk/s3-request-presigner@3.956.0': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@aws-sdk/util-format-url': 3.930.0 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/protocol-http': 5.3.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 + '@aws-sdk/signature-v4-multi-region': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@aws-sdk/util-format-url': 3.956.0 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/protocol-http': 5.3.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.934.0': + '@aws-sdk/signature-v4-multi-region@3.956.0': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/signature-v4': 5.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/middleware-sdk-s3': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/signature-v4': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.934.0': + '@aws-sdk/token-providers@3.956.0': dependencies: - '@aws-sdk/core': 3.934.0 - '@aws-sdk/nested-clients': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@aws-sdk/core': 3.956.0 + '@aws-sdk/nested-clients': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.930.0': + '@aws-sdk/types@3.956.0': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.893.0': + '@aws-sdk/util-arn-parser@3.953.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.930.0': + '@aws-sdk/util-endpoints@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-endpoints': 3.2.5 + '@aws-sdk/types': 3.956.0 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-endpoints': 3.2.7 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.930.0': + '@aws-sdk/util-format-url@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/querystring-builder': 4.2.5 - '@smithy/types': 4.9.0 + '@aws-sdk/types': 3.956.0 + '@smithy/querystring-builder': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.893.0': + '@aws-sdk/util-locate-window@3.953.0': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.930.0': + '@aws-sdk/util-user-agent-browser@3.956.0': dependencies: - '@aws-sdk/types': 3.930.0 - '@smithy/types': 4.9.0 - bowser: 2.12.1 + '@aws-sdk/types': 3.956.0 + '@smithy/types': 4.11.0 + bowser: 2.13.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.934.0': + '@aws-sdk/util-user-agent-node@3.956.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.934.0 - '@aws-sdk/types': 3.930.0 - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@aws-sdk/middleware-user-agent': 3.956.0 + '@aws-sdk/types': 3.956.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.930.0': + '@aws-sdk/xml-builder@3.956.0': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 fast-xml-parser: 5.2.5 tslib: 2.8.1 - '@aws/lambda-invoke-store@0.2.0': {} + '@aws/lambda-invoke-store@0.2.2': {} '@babel/code-frame@7.27.1': dependencies: @@ -17956,7 +18013,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.0 + browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 @@ -18726,13 +18783,13 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@blueprintjs/colors@5.1.11': + '@blueprintjs/colors@5.1.12': dependencies: tslib: 2.6.3 '@blueprintjs/core@5.19.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@blueprintjs/colors': 5.1.11 + '@blueprintjs/colors': 5.1.12 '@blueprintjs/icons': 5.23.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@popperjs/core': 2.11.8 classnames: 2.5.1 @@ -18782,15 +18839,15 @@ snapshots: transitivePeerDependencies: - encoding - '@browserbasehq/stagehand@1.14.0(@playwright/test@1.56.1)(bufferutil@4.0.9)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@browserbasehq/stagehand@1.14.0(@playwright/test@1.57.0)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@anthropic-ai/sdk': 0.27.3 '@browserbasehq/sdk': 2.6.0 - '@playwright/test': 1.56.1 + '@playwright/test': 1.57.0 deepmerge: 4.3.1 dotenv: 16.6.1 - openai: 6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + openai: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: @@ -18798,14 +18855,14 @@ snapshots: - encoding - utf-8-validate - '@bufbuild/protobuf@2.10.1': {} + '@bufbuild/protobuf@2.10.2': {} - '@cacheable/utils@2.3.1': + '@cacheable/utils@2.3.2': dependencies: - hashery: 1.2.0 - keyv: 5.5.4 + hashery: 1.3.0 + keyv: 5.5.5 - '@casl/ability@6.7.3': + '@casl/ability@6.7.5': dependencies: '@ucast/mongo2js': 1.4.0 @@ -18902,38 +18959,38 @@ snapshots: - encoding - graphql - '@copilotkit/runtime@1.10.6(6855d89fee41a5dd20a0f9576445cd9f)': + '@copilotkit/runtime@1.10.6(7d9d10c1aa1e359346c98e211cce0cbe)': dependencies: - '@ag-ui/client': 0.0.41 - '@ag-ui/core': 0.0.41 - '@ag-ui/encoder': 0.0.41 - '@ag-ui/langgraph': 0.0.18(@ag-ui/client@0.0.41)(@ag-ui/core@0.0.41)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ag-ui/proto': 0.0.41 + '@ag-ui/client': 0.0.42 + '@ag-ui/core': 0.0.37 + '@ag-ui/encoder': 0.0.42 + '@ag-ui/langgraph': 0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ag-ui/proto': 0.0.42 '@anthropic-ai/sdk': 0.57.0 '@copilotkit/shared': 1.10.6 - '@graphql-yoga/plugin-defer-stream': 3.16.2(graphql-yoga@5.16.2(graphql@16.12.0))(graphql@16.12.0) - '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) - '@langchain/community': 0.3.58(4be272a811ea6a146ff99975d81ff9de) - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) - '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1) - '@langchain/openai': 0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@graphql-yoga/plugin-defer-stream': 3.18.0(graphql-yoga@5.18.0(graphql@16.12.0))(graphql@16.12.0) + '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/community': 0.3.58(839bee139a23dbd5124ee69527468f5f) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) + '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1) + '@langchain/openai': 0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@scarf/scarf': 1.4.0 class-transformer: 0.5.1 - class-validator: 0.14.2 - express: 4.21.2 + class-validator: 0.14.3 + express: 4.22.1 graphql: 16.12.0 graphql-scalars: 1.25.0(graphql@16.12.0) - graphql-yoga: 5.16.2(graphql@16.12.0) + graphql-yoga: 5.18.0(graphql@16.12.0) groq-sdk: 0.5.0 - langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) partial-json: 0.1.7 pino: 9.14.0 pino-pretty: 11.3.0 reflect-metadata: 0.2.2 rxjs: 7.8.1 - type-graphql: 2.0.0-rc.1(class-validator@0.14.2)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0) + type-graphql: 2.0.0-rc.1(class-validator@0.14.3)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0) zod: 3.25.76 transitivePeerDependencies: - '@arcjet/redact' @@ -19096,7 +19153,7 @@ snapshots: transitivePeerDependencies: - encoding - '@crxjs/vite-plugin@2.2.1': + '@crxjs/vite-plugin@2.3.0': dependencies: '@rollup/pluginutils': 4.2.1 '@webcomponents/custom-elements': 1.6.0 @@ -19283,157 +19340,157 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.27.0': + '@esbuild/aix-ppc64@0.27.2': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.27.0': + '@esbuild/android-arm64@0.27.2': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.27.0': + '@esbuild/android-arm@0.27.2': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.0': + '@esbuild/android-x64@0.27.2': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.27.0': + '@esbuild/darwin-arm64@0.27.2': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.0': + '@esbuild/darwin-x64@0.27.2': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.0': + '@esbuild/freebsd-arm64@0.27.2': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.27.0': + '@esbuild/freebsd-x64@0.27.2': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.27.0': + '@esbuild/linux-arm64@0.27.2': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.27.0': + '@esbuild/linux-arm@0.27.2': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.27.0': + '@esbuild/linux-ia32@0.27.2': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.27.0': + '@esbuild/linux-loong64@0.27.2': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.0': + '@esbuild/linux-mips64el@0.27.2': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.27.0': + '@esbuild/linux-ppc64@0.27.2': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.27.0': + '@esbuild/linux-riscv64@0.27.2': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.0': + '@esbuild/linux-s390x@0.27.2': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.27.0': + '@esbuild/linux-x64@0.27.2': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.27.0': + '@esbuild/netbsd-arm64@0.27.2': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.0': + '@esbuild/netbsd-x64@0.27.2': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.27.0': + '@esbuild/openbsd-arm64@0.27.2': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.27.0': + '@esbuild/openbsd-x64@0.27.2': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.0': + '@esbuild/openharmony-arm64@0.27.2': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.27.0': + '@esbuild/sunos-x64@0.27.2': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.27.0': + '@esbuild/win32-arm64@0.27.2': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.0': + '@esbuild/win32-ia32@0.27.2': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.27.0': + '@esbuild/win32-x64@0.27.2': optional: true '@eslint-community/eslint-utils@4.9.0(eslint@8.57.0)': @@ -19466,11 +19523,10 @@ snapshots: '@ethereumjs/rlp': 5.0.2 ethereum-cryptography: 2.2.1 - '@expo/devcert@1.2.0': + '@expo/devcert@1.2.1': dependencies: '@expo/sudo-prompt': 9.3.2 debug: 3.2.7 - glob: 10.5.0 transitivePeerDependencies: - supports-color @@ -19522,19 +19578,19 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@fractalwagmi/solana-wallet-adapter@0.1.1(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@fractalwagmi/solana-wallet-adapter@0.1.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@fractalwagmi/popup-connection': 1.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) bs58: 5.0.0 transitivePeerDependencies: - '@solana/web3.js' - react - react-dom - '@graphql-tools/executor@1.4.13(graphql@16.12.0)': + '@graphql-tools/executor@1.5.0(graphql@16.12.0)': dependencies: - '@graphql-tools/utils': 10.10.3(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) '@repeaterjs/repeater': 3.0.6 '@whatwg-node/disposablestack': 0.0.6 @@ -19542,20 +19598,20 @@ snapshots: graphql: 16.12.0 tslib: 2.8.1 - '@graphql-tools/merge@9.1.5(graphql@16.12.0)': + '@graphql-tools/merge@9.1.6(graphql@16.12.0)': dependencies: - '@graphql-tools/utils': 10.10.3(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) graphql: 16.12.0 tslib: 2.8.1 - '@graphql-tools/schema@10.0.29(graphql@16.12.0)': + '@graphql-tools/schema@10.0.30(graphql@16.12.0)': dependencies: - '@graphql-tools/merge': 9.1.5(graphql@16.12.0) - '@graphql-tools/utils': 10.10.3(graphql@16.12.0) + '@graphql-tools/merge': 9.1.6(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) graphql: 16.12.0 tslib: 2.8.1 - '@graphql-tools/utils@10.10.3(graphql@16.12.0)': + '@graphql-tools/utils@10.11.0(graphql@16.12.0)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) '@whatwg-node/promise-helpers': 1.3.2 @@ -19571,11 +19627,11 @@ snapshots: dependencies: tslib: 2.8.1 - '@graphql-yoga/plugin-defer-stream@3.16.2(graphql-yoga@5.16.2(graphql@16.12.0))(graphql@16.12.0)': + '@graphql-yoga/plugin-defer-stream@3.18.0(graphql-yoga@5.18.0(graphql@16.12.0))(graphql@16.12.0)': dependencies: - '@graphql-tools/utils': 10.10.3(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) graphql: 16.12.0 - graphql-yoga: 5.16.2(graphql@16.12.0) + graphql-yoga: 5.18.0(graphql@16.12.0) '@graphql-yoga/subscription@5.0.5': dependencies: @@ -19589,7 +19645,7 @@ snapshots: '@repeaterjs/repeater': 3.0.6 tslib: 2.8.1 - '@grpc/grpc-js@1.14.1': + '@grpc/grpc-js@1.14.3': dependencies: '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 @@ -19604,16 +19660,20 @@ snapshots: '@headlessui/react@2.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react': 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/focus': 3.21.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.25.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-virtual': 3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/focus': 3.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/interactions': 3.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-virtual': 3.13.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) - '@hookform/resolvers@3.10.0(react-hook-form@7.66.1(react@18.3.1))': + '@hono/node-server@1.19.7(hono@4.11.1)': dependencies: - react-hook-form: 7.66.1(react@18.3.1) + hono: 4.11.1 + + '@hookform/resolvers@3.10.0(react-hook-form@7.69.0(react@18.3.1))': + dependencies: + react-hook-form: 7.69.0(react@18.3.1) '@humanwhocodes/config-array@0.11.14': dependencies: @@ -19627,12 +19687,12 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} - '@ibm-cloud/watsonx-ai@1.7.3': + '@ibm-cloud/watsonx-ai@1.7.5': dependencies: '@types/node': 18.16.9 extend: 3.0.2 form-data: 4.0.5 - ibm-cloud-sdk-core: 5.4.4 + ibm-cloud-sdk-core: 5.4.5 transitivePeerDependencies: - supports-color @@ -19718,7 +19778,7 @@ snapshots: '@inquirer/external-editor@1.0.3(@types/node@18.16.9)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.0 + iconv-lite: 0.7.1 optionalDependencies: '@types/node': 18.16.9 @@ -19982,12 +20042,12 @@ snapshots: react-qr-reader: 2.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rxjs: 6.6.7 - '@keystonehq/sol-keyring@0.20.0(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@keystonehq/sol-keyring@0.20.0(bufferutil@4.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: '@keystonehq/bc-ur-registry': 0.5.4 '@keystonehq/bc-ur-registry-sol': 0.9.5 '@keystonehq/sdk': 0.19.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 5.0.0 uuid: 8.3.2 transitivePeerDependencies: @@ -20002,43 +20062,43 @@ snapshots: '@kurkle/color@0.3.4': {} - '@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@aws-sdk/client-bedrock-agent-runtime': 3.934.0 - '@aws-sdk/client-bedrock-runtime': 3.934.0 - '@aws-sdk/client-kendra': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@aws-sdk/client-bedrock-agent-runtime': 3.956.0 + '@aws-sdk/client-bedrock-runtime': 3.956.0 + '@aws-sdk/client-kendra': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) transitivePeerDependencies: - aws-crt - '@langchain/community@0.3.58(23f20e966d4bb4b0ba23179429728315)': + '@langchain/community@0.3.58(839bee139a23dbd5124ee69527468f5f)': dependencies: - '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.56.1)(bufferutil@4.0.9)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) - '@ibm-cloud/watsonx-ai': 1.7.3 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@langchain/weaviate': 0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.57.0)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) + '@ibm-cloud/watsonx-ai': 1.7.5 + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/weaviate': 0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) binary-extensions: 2.3.0 flat: 5.0.2 - ibm-cloud-sdk-core: 5.4.4 + ibm-cloud-sdk-core: 5.4.5 js-yaml: 4.1.1 - langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - langsmith: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) math-expression-evaluator: 2.0.7 - openai: 6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-bedrock-agent-runtime': 3.934.0 - '@aws-sdk/client-bedrock-runtime': 3.934.0 - '@aws-sdk/client-kendra': 3.934.0 - '@aws-sdk/client-s3': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 + '@aws-sdk/client-bedrock-agent-runtime': 3.956.0 + '@aws-sdk/client-bedrock-runtime': 3.956.0 + '@aws-sdk/client-kendra': 3.956.0 + '@aws-sdk/client-s3': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 '@browserbasehq/sdk': 2.6.0 '@smithy/util-utf8': 2.3.0 - '@upstash/redis': 1.35.6 + '@upstash/redis': 1.35.8 cheerio: 1.1.2 crypto-js: 4.2.0 fast-xml-parser: 4.5.3 @@ -20047,14 +20107,14 @@ snapshots: html-to-text: 9.0.5 ignore: 5.3.2 ioredis: 5.8.2 - jsdom: 22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10) - jsonwebtoken: 9.0.2 + jsdom: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) + jsonwebtoken: 9.0.3 lodash: 4.17.21 pg: 8.16.3 - playwright: 1.56.1 + playwright: 1.57.0 redis: 4.7.1 - weaviate-client: 3.9.0 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + weaviate-client: 3.10.0 + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@langchain/anthropic' - '@langchain/aws' @@ -20076,33 +20136,33 @@ snapshots: - handlebars - peggy - '@langchain/community@0.3.58(4be272a811ea6a146ff99975d81ff9de)': + '@langchain/community@0.3.58(d743da2b976a18eaa6701515aaffafa9)': dependencies: - '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.56.1)(bufferutil@4.0.9)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) - '@ibm-cloud/watsonx-ai': 1.7.3 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@langchain/weaviate': 0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.57.0)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) + '@ibm-cloud/watsonx-ai': 1.7.5 + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/weaviate': 0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) binary-extensions: 2.3.0 flat: 5.0.2 - ibm-cloud-sdk-core: 5.4.4 + ibm-cloud-sdk-core: 5.4.5 js-yaml: 4.1.1 - langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - langsmith: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) math-expression-evaluator: 2.0.7 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-bedrock-agent-runtime': 3.934.0 - '@aws-sdk/client-bedrock-runtime': 3.934.0 - '@aws-sdk/client-kendra': 3.934.0 - '@aws-sdk/client-s3': 3.934.0 - '@aws-sdk/credential-provider-node': 3.934.0 + '@aws-sdk/client-bedrock-agent-runtime': 3.956.0 + '@aws-sdk/client-bedrock-runtime': 3.956.0 + '@aws-sdk/client-kendra': 3.956.0 + '@aws-sdk/client-s3': 3.956.0 + '@aws-sdk/credential-provider-node': 3.956.0 '@browserbasehq/sdk': 2.6.0 '@smithy/util-utf8': 2.3.0 - '@upstash/redis': 1.35.6 + '@upstash/redis': 1.35.8 cheerio: 1.1.2 crypto-js: 4.2.0 fast-xml-parser: 4.5.3 @@ -20111,14 +20171,14 @@ snapshots: html-to-text: 9.0.5 ignore: 5.3.2 ioredis: 5.8.2 - jsdom: 22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10) - jsonwebtoken: 9.0.2 + jsdom: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) + jsonwebtoken: 9.0.3 lodash: 4.17.21 pg: 8.16.3 - playwright: 1.56.1 + playwright: 1.57.0 redis: 4.7.1 - weaviate-client: 3.9.0 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + weaviate-client: 3.10.0 + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@langchain/anthropic' - '@langchain/aws' @@ -20140,14 +20200,14 @@ snapshots: - handlebars - peggy - '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))': + '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -20160,14 +20220,14 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))': + '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -20180,66 +20240,66 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/google-common@0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': + '@langchain/google-common@0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: - zod - '@langchain/google-gauth@0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': + '@langchain/google-gauth@0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/google-common': 0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/google-common': 0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) google-auth-library: 8.9.0 transitivePeerDependencies: - encoding - supports-color - zod - '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 - '@langchain/langgraph-sdk@0.0.112(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@langchain/langgraph-sdk@0.0.112(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1)': + '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 18.3.1 - '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@langchain/langgraph@0.2.74(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.0(zod@3.25.76))': + '@langchain/langgraph@0.2.74(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.0(zod@3.25.76))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) - '@langchain/langgraph-sdk': 0.0.112(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/langgraph-sdk': 0.0.112(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: @@ -20248,59 +20308,59 @@ snapshots: - react - react-dom - '@langchain/openai@0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@langchain/openai@0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: - encoding - ws - '@langchain/openai@0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@langchain/openai@0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - openai: 5.23.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 5.23.2(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - ws - '@langchain/textsplitters@0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - '@langchain/weaviate@0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/weaviate@0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 - weaviate-client: 3.9.0 + weaviate-client: 3.10.0 transitivePeerDependencies: - encoding - '@ledgerhq/devices@8.7.0': + '@ledgerhq/devices@8.7.1': dependencies: - '@ledgerhq/errors': 6.27.0 + '@ledgerhq/errors': 6.27.1 '@ledgerhq/logs': 6.13.0 rxjs: 7.8.2 semver: 7.7.3 - '@ledgerhq/errors@6.27.0': {} + '@ledgerhq/errors@6.27.1': {} - '@ledgerhq/hw-transport-webhid@6.30.9': + '@ledgerhq/hw-transport-webhid@6.30.10': dependencies: - '@ledgerhq/devices': 8.7.0 - '@ledgerhq/errors': 6.27.0 - '@ledgerhq/hw-transport': 6.31.13 + '@ledgerhq/devices': 8.7.1 + '@ledgerhq/errors': 6.27.1 + '@ledgerhq/hw-transport': 6.31.14 '@ledgerhq/logs': 6.13.0 - '@ledgerhq/hw-transport@6.31.13': + '@ledgerhq/hw-transport@6.31.14': dependencies: - '@ledgerhq/devices': 8.7.0 - '@ledgerhq/errors': 6.27.0 + '@ledgerhq/devices': 8.7.1 + '@ledgerhq/errors': 6.27.1 '@ledgerhq/logs': 6.13.0 events: 3.3.0 @@ -20439,17 +20499,17 @@ snapshots: ai-v5: ai@5.0.60(zod@3.25.76) date-fns: 3.6.0 dotenv: 16.6.1 - hono: 4.10.6 - hono-openapi: 0.4.8(hono@4.10.6)(openapi-types@12.1.3)(zod@3.25.76) + hono: 4.11.1 + hono-openapi: 0.4.8(hono@4.11.1)(openapi-types@12.1.3)(zod@3.25.76) js-tiktoken: 1.0.21 json-schema: 0.4.0 json-schema-to-zod: 2.7.0 p-map: 7.0.4 pino: 9.14.0 - pino-pretty: 13.1.2 + pino-pretty: 13.1.3 radash: 12.1.1 sift: 17.1.3 - xstate: 5.24.0 + xstate: 5.25.0 zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: @@ -20477,7 +20537,7 @@ snapshots: '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/server': 0.19.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(zod@3.25.76) '@neon-rs/load': 0.1.82 - '@optimize-lodash/rollup-plugin': 5.0.2(rollup@4.50.2) + '@optimize-lodash/rollup-plugin': 5.1.0(rollup@4.50.2) '@rollup/plugin-alias': 5.1.1(rollup@4.50.2) '@rollup/plugin-commonjs': 28.0.9(rollup@4.50.2) '@rollup/plugin-esm-shim': 0.1.8(rollup@4.50.2) @@ -20491,8 +20551,8 @@ snapshots: empathic: 2.0.0 esbuild: 0.25.12 find-workspaces: 0.3.1 - fs-extra: 11.3.2 - hono: 4.10.6 + fs-extra: 11.3.3 + hono: 4.11.1 local-pkg: 1.1.2 resolve-from: 5.0.0 rollup: 4.50.2 @@ -20509,13 +20569,13 @@ snapshots: dependencies: '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) pino: 9.14.0 - pino-pretty: 13.1.2 + pino-pretty: 13.1.3 - '@mastra/mcp@0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(zod@3.25.76)': + '@mastra/mcp@0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(hono@4.11.1)(zod@3.25.76)': dependencies: '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) - '@modelcontextprotocol/sdk': 1.22.0(@cfworker/json-schema@4.1.1) + '@modelcontextprotocol/sdk': 1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76) date-fns: 4.1.0 exit-hook: 4.0.0 fast-deep-equal: 3.1.3 @@ -20526,13 +20586,14 @@ snapshots: transitivePeerDependencies: - '@cfworker/json-schema' - '@types/json-schema' + - hono - supports-color - '@mastra/memory@0.15.11(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(react@18.3.1)(zod@3.25.76)': + '@mastra/memory@0.15.13(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(react@18.3.1)(zod@3.25.76)': dependencies: '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) - '@mastra/schema-compat': 0.11.7(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76) - '@upstash/redis': 1.35.6 + '@mastra/schema-compat': 0.11.9(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76) + '@upstash/redis': 1.35.8 ai: 4.3.19(react@18.3.1)(zod@3.25.76) ai-v5: ai@5.0.60(zod@3.25.76) async-mutex: 0.5.0 @@ -20541,7 +20602,7 @@ snapshots: pg: 8.16.3 pg-pool: 3.10.1(pg@8.16.3) postgres: 3.4.7 - redis: 5.9.0 + redis: 5.10.0 xxhash-wasm: 1.1.0 zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) @@ -20549,7 +20610,7 @@ snapshots: - pg-native - react - '@mastra/pg@0.17.8(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(pg-query-stream@4.10.3(pg@8.16.3))': + '@mastra/pg@0.17.10(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(pg-query-stream@4.10.3(pg@8.16.3))': dependencies: '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) async-mutex: 0.5.0 @@ -20569,10 +20630,11 @@ snapshots: zod-from-json-schema-v3: zod-from-json-schema@0.0.5 zod-to-json-schema: 3.25.0(zod@3.25.76) - '@mastra/schema-compat@0.11.7(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76)': + '@mastra/schema-compat@0.11.9(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76)': dependencies: ai: 4.3.19(react@18.3.1)(zod@3.25.76) json-schema: 0.4.0 + json-schema-to-zod: 2.7.0 zod: 3.25.76 zod-from-json-schema: 0.5.2 zod-from-json-schema-v3: zod-from-json-schema@0.0.5 @@ -20591,8 +20653,9 @@ snapshots: '@microsoft/tsdoc@0.15.1': {} - '@modelcontextprotocol/sdk@1.22.0(@cfworker/json-schema@4.1.1)': + '@modelcontextprotocol/sdk@1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76)': dependencies: + '@hono/node-server': 1.19.7(hono@4.11.1) ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 @@ -20600,15 +20663,18 @@ snapshots: cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 - express: 5.1.0 - express-rate-limit: 7.5.1(express@5.1.0) - pkce-challenge: 5.0.0 - raw-body: 3.0.1 + express: 5.2.1 + express-rate-limit: 7.5.1(express@5.2.1) + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 zod: 3.25.76 zod-to-json-schema: 3.25.0(zod@3.25.76) optionalDependencies: '@cfworker/json-schema': 4.1.1 transitivePeerDependencies: + - hono - supports-color '@mole-inc/bin-wrapper@8.0.1': @@ -20656,7 +20722,7 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-is: 19.2.0 + react-is: 19.2.3 react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) optionalDependencies: '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) @@ -20750,7 +20816,7 @@ snapshots: clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 - react-is: 19.2.0 + react-is: 19.2.3 optionalDependencies: '@types/react': 18.3.1 @@ -20762,7 +20828,7 @@ snapshots: clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 - react-is: 19.2.0 + react-is: 19.2.3 optionalDependencies: '@types/react': 18.3.1 @@ -20847,9 +20913,9 @@ snapshots: '@neon-rs/load@0.1.82': {} - '@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': + '@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) axios: 1.13.2(debug@4.4.3) rxjs: 7.8.2 @@ -20885,7 +20951,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: file-type: 20.4.1 iterare: 1.2.1 @@ -20895,11 +20961,11 @@ snapshots: uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.2 + class-validator: 0.14.3 transitivePeerDependencies: - supports-color - '@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: file-type: 21.1.0 iterare: 1.2.1 @@ -20910,13 +20976,13 @@ snapshots: uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.2 + class-validator: 0.14.3 transitivePeerDependencies: - supports-color - '@nestjs/core@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/core@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -20926,14 +20992,14 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.1)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.5)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) transitivePeerDependencies: - encoding - '@nestjs/core@11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -20943,34 +21009,34 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.1)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.5)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) - '@nestjs/mapped-types@2.0.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)': + '@nestjs/mapped-types@2.0.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.2 + class-validator: 0.14.3 - '@nestjs/microservices@10.4.20(@grpc/grpc-js@1.14.1)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.5)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/microservices@10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) iterare: 1.2.1 reflect-metadata: 0.1.14 rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@grpc/grpc-js': 1.14.1 - cache-manager: 7.2.5 + '@grpc/grpc-js': 1.14.3 + cache-manager: 7.2.7 ioredis: 5.8.2 - '@nestjs/platform-express@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@nestjs/platform-express@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) body-parser: 1.20.3 cors: 2.8.5 express: 4.21.2 @@ -20979,10 +21045,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/schedule@4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@nestjs/schedule@4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) cron: 3.2.1 uuid: 11.0.3 @@ -21008,12 +21074,12 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@7.4.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)': + '@nestjs/swagger@7.4.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)': dependencies: '@microsoft/tsdoc': 0.15.1 - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14) js-yaml: 4.1.0 lodash: 4.17.21 path-to-regexp: 3.3.0 @@ -21021,21 +21087,21 @@ snapshots: swagger-ui-dist: 5.17.14 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.2 + class-validator: 0.14.3 - '@nestjs/testing@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)': + '@nestjs/testing@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) tslib: 2.8.1 optionalDependencies: - '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.1)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.5)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) - '@nestjs/throttler@6.4.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14)': + '@nestjs/throttler@6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 '@next/env@14.2.16': {} @@ -21100,11 +21166,11 @@ snapshots: '@next/swc-win32-x64-msvc@14.2.33': optional: true - '@neynar/nodejs-sdk@2.46.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.0.9)(class-transformer@0.5.1)(class-validator@0.14.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@neynar/nodejs-sdk@2.46.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@openapitools/openapi-generator-cli': 2.25.1(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.2) + '@openapitools/openapi-generator-cli': 2.25.2(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.3) semver: 7.7.3 - viem: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@nestjs/microservices' - '@nestjs/platform-express' @@ -21120,15 +21186,15 @@ snapshots: - utf-8-validate - zod - '@neynar/react@0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.56.1)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1)(swr@2.3.6(react@18.3.1))': + '@neynar/react@0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1))': dependencies: '@pigment-css/react': 0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) hls.js: 1.6.15 - next: 14.2.16(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + next: 14.2.16(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) storybook-source-link: 4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - swr: 2.3.6(react@18.3.1) + swr: 2.3.8(react@18.3.1) transitivePeerDependencies: - '@babel/core' - '@opentelemetry/api' @@ -21221,11 +21287,11 @@ snapshots: '@one-ini/wasm@0.1.1': {} - '@openapitools/openapi-generator-cli@2.25.1(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.2)': + '@openapitools/openapi-generator-cli@2.25.2(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.3)': dependencies: - '@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) - '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxtjs/opencollective': 0.3.2 axios: 1.13.2(debug@4.4.3) chalk: 4.1.2 @@ -21234,7 +21300,7 @@ snapshots: concurrently: 9.2.1 console.table: 0.10.0 fs-extra: 11.3.2 - glob: 12.0.0 + glob: 13.0.0 inquirer: 8.2.7(@types/node@18.16.9) proxy-agent: 6.5.0 reflect-metadata: 0.2.2 @@ -21260,11 +21326,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs@0.204.0': - dependencies: - '@opentelemetry/api': 1.9.0 - - '@opentelemetry/api-logs@0.57.2': + '@opentelemetry/api-logs@0.208.0': dependencies: '@opentelemetry/api': 1.9.0 @@ -21316,7 +21378,7 @@ snapshots: '@opentelemetry/instrumentation-undici': 0.14.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-winston': 0.48.1(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-alibaba-cloud': 0.31.11(@opentelemetry/api@1.9.0) - '@opentelemetry/resource-detector-aws': 2.8.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-aws': 2.9.0(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-azure': 0.10.0(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-container': 0.7.11(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-gcp': 0.37.0(@opentelemetry/api@1.9.0) @@ -21339,11 +21401,6 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 - '@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.38.0 - '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -21351,7 +21408,7 @@ snapshots: '@opentelemetry/exporter-logs-otlp-grpc@0.203.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.14.1 + '@grpc/grpc-js': 1.14.3 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.203.0(@opentelemetry/api@1.9.0) @@ -21381,7 +21438,7 @@ snapshots: '@opentelemetry/exporter-metrics-otlp-grpc@0.203.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.14.1 + '@grpc/grpc-js': 1.14.3 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-metrics-otlp-http': 0.203.0(@opentelemetry/api@1.9.0) @@ -21419,7 +21476,7 @@ snapshots: '@opentelemetry/exporter-trace-otlp-grpc@0.203.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.14.1 + '@grpc/grpc-js': 1.14.3 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.203.0(@opentelemetry/api@1.9.0) @@ -21463,12 +21520,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-amqplib@0.51.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-amqplib@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21517,11 +21573,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-connect@0.48.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-connect@0.52.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 '@types/connect': 3.4.38 transitivePeerDependencies: @@ -21542,10 +21598,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-dataloader@0.22.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-dataloader@0.26.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21565,11 +21621,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.53.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-express@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21591,11 +21647,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-fs@0.24.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-fs@0.28.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21606,10 +21662,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-generic-pool@0.48.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-generic-pool@0.52.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21620,10 +21676,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-graphql@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-graphql@0.56.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21644,11 +21700,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-hapi@0.51.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-hapi@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21663,11 +21719,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-http@0.204.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-http@0.208.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 forwarded-parse: 2.1.2 transitivePeerDependencies: @@ -21682,12 +21738,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-ioredis@0.56.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21699,10 +21754,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-kafkajs@0.14.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-kafkajs@0.18.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21715,10 +21770,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-knex@0.49.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-knex@0.53.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21732,11 +21787,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-koa@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-koa@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21748,10 +21803,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-lru-memoizer@0.49.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-lru-memoizer@0.53.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21772,11 +21827,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongodb@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongodb@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21789,12 +21843,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongoose@0.51.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongoose@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21807,10 +21860,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql2@0.51.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql2@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: @@ -21825,11 +21878,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql@0.50.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql@0.54.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@types/mysql': 2.15.27 transitivePeerDependencies: - supports-color @@ -21842,10 +21894,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-nestjs-core@0.50.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-nestjs-core@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -21879,14 +21931,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-pg@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-pg@0.61.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) - '@types/pg': 8.15.5 + '@types/pg': 8.15.6 '@types/pg-pool': 2.0.6 transitivePeerDependencies: - supports-color @@ -21909,10 +21961,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-redis@0.53.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-redis@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: @@ -21959,11 +22011,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-tedious@0.23.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-tedious@0.27.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color @@ -21976,11 +22027,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-undici@0.15.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-undici@0.19.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 transitivePeerDependencies: - supports-color @@ -22001,24 +22053,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation@0.204.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.204.0 - import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 - transitivePeerDependencies: - - supports-color - - '@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.57.2 - '@types/shimmer': 1.2.0 - import-in-the-middle: 1.15.0 - require-in-the-middle: 7.5.2 - semver: 7.7.3 - shimmer: 1.2.1 + '@opentelemetry/api-logs': 0.208.0 + import-in-the-middle: 2.0.1 + require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -22030,7 +22070,7 @@ snapshots: '@opentelemetry/otlp-grpc-exporter-base@0.203.0(@opentelemetry/api@1.9.0)': dependencies: - '@grpc/grpc-js': 1.14.1 + '@grpc/grpc-js': 1.14.3 '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.203.0(@opentelemetry/api@1.9.0) @@ -22065,7 +22105,7 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resource-detector-aws@2.8.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/resource-detector-aws@2.9.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) @@ -22190,7 +22230,7 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@optimize-lodash/rollup-plugin@5.0.2(rollup@4.50.2)': + '@optimize-lodash/rollup-plugin@5.1.0(rollup@4.50.2)': dependencies: '@optimize-lodash/transform': 3.0.6 '@rollup/pluginutils': 5.3.0(rollup@4.50.2) @@ -22282,10 +22322,10 @@ snapshots: crypto-js: 4.2.0 uuidv4: 6.2.13 - '@particle-network/solana-wallet@1.3.2(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)': + '@particle-network/solana-wallet@1.3.2(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)': dependencies: '@particle-network/auth': 1.3.1 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 6.0.0 '@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4)': @@ -22322,9 +22362,9 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.56.1': + '@playwright/test@1.57.0': dependencies: - playwright: 1.56.1 + playwright: 1.57.0 '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: @@ -22344,48 +22384,48 @@ snapshots: '@popperjs/core@2.11.8': {} - '@posthog/core@1.5.2': + '@posthog/core@1.8.1': dependencies: cross-spawn: 7.0.6 - '@postiz/wallets@0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bs58@6.0.0)(bufferutil@4.0.9)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@postiz/wallets@0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@solana/wallet-adapter-alpha': 0.1.14(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-avana': 0.1.17(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-bitkeep': 0.3.24(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-bitpie': 0.5.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-clover': 0.4.23(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-coin98': 0.5.24(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-coinbase': 0.1.23(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-coinhub': 0.3.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-fractal': 0.1.12(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@solana/wallet-adapter-huobi': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-hyperpay': 0.1.18(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-keystone': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-krystal': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-ledger': 0.9.29(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-mathwallet': 0.9.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-neko': 0.2.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-nightly': 0.1.20(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-nufi': 0.1.21(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-onto': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-particle': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0) - '@solana/wallet-adapter-phantom': 0.9.28(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-safepal': 0.5.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-saifu': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-salmon': 0.1.18(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-sky': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-solflare': 0.6.32(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-solong': 0.9.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-spot': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-tokenary': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-tokenpocket': 0.4.23(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-torus': 0.11.32(@babel/runtime@7.28.4)(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-trust': 0.1.17(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-unsafe-burner': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-walletconnect': 0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@solana/wallet-adapter-xdefi': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-alpha': 0.1.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-avana': 0.1.17(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-bitkeep': 0.3.24(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-bitpie': 0.5.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-clover': 0.4.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-coin98': 0.5.24(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-coinbase': 0.1.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-coinhub': 0.3.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-fractal': 0.1.12(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@solana/wallet-adapter-huobi': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-hyperpay': 0.1.18(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-keystone': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-krystal': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-ledger': 0.9.29(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-mathwallet': 0.9.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-neko': 0.2.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-nightly': 0.1.20(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-nufi': 0.1.21(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-onto': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-particle': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0) + '@solana/wallet-adapter-phantom': 0.9.28(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-safepal': 0.5.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-saifu': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-salmon': 0.1.18(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-sky': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-solflare': 0.6.32(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-solong': 0.9.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-spot': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-tokenary': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-tokenpocket': 0.4.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-torus': 0.11.32(@babel/runtime@7.28.4)(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-trust': 0.1.17(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-unsafe-burner': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-walletconnect': 0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@solana/wallet-adapter-xdefi': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -22426,8 +22466,8 @@ snapshots: '@prisma/config@6.5.0': dependencies: - esbuild: 0.27.0 - esbuild-register: 3.6.0(esbuild@0.27.0) + esbuild: 0.27.2 + esbuild-register: 3.6.0(esbuild@0.27.2) transitivePeerDependencies: - supports-color @@ -22452,16 +22492,16 @@ snapshots: dependencies: '@prisma/debug': 6.5.0 - '@prisma/instrumentation@6.15.0(@opentelemetry/api@1.9.0)': + '@prisma/instrumentation@6.19.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@project-serum/sol-wallet-adapter@0.2.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@project-serum/sol-wallet-adapter@0.2.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 4.0.1 eventemitter3: 4.0.7 @@ -22561,7 +22601,7 @@ snapshots: aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-remove-scroll: 2.7.1(@types/react@18.3.1)(react@18.3.1) + react-remove-scroll: 2.7.2(@types/react@18.3.1)(react@18.3.1) optionalDependencies: '@types/react': 18.3.1 '@types/react-dom': 18.3.0 @@ -22821,20 +22861,20 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@react-aria/focus@3.21.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-aria/focus@3.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@react-aria/interactions': 3.25.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.31.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/interactions': 3.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/utils': 3.32.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-types/shared': 3.32.1(react@18.3.1) '@swc/helpers': 0.5.13 clsx: 2.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@react-aria/interactions@3.25.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-aria/interactions@3.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/utils': 3.31.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/utils': 3.32.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-stately/flags': 3.1.2 '@react-types/shared': 3.32.1(react@18.3.1) '@swc/helpers': 0.5.13 @@ -22846,11 +22886,11 @@ snapshots: '@swc/helpers': 0.5.13 react: 18.3.1 - '@react-aria/utils@3.31.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-aria/utils@3.32.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@react-aria/ssr': 3.9.10(react@18.3.1) '@react-stately/flags': 3.1.2 - '@react-stately/utils': 3.10.8(react@18.3.1) + '@react-stately/utils': 3.11.0(react@18.3.1) '@react-types/shared': 3.32.1(react@18.3.1) '@swc/helpers': 0.5.13 clsx: 2.1.1 @@ -22871,15 +22911,15 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-promise-suspense: 0.3.4 - '@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))': + '@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))': dependencies: merge-options: 3.0.4 - react-native: 0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) optional: true - '@react-native/assets-registry@0.82.1': {} + '@react-native/assets-registry@0.83.1': {} - '@react-native/codegen@0.82.1(@babel/core@7.28.5)': + '@react-native/codegen@0.83.1(@babel/core@7.28.5)': dependencies: '@babel/core': 7.28.5 '@babel/parser': 7.28.5 @@ -22889,13 +22929,13 @@ snapshots: nullthrows: 1.1.1 yargs: 17.7.2 - '@react-native/community-cli-plugin@0.82.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@react-native/community-cli-plugin@0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: - '@react-native/dev-middleware': 0.82.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@react-native/dev-middleware': 0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) debug: 4.4.3(supports-color@5.5.0) invariant: 2.2.4 - metro: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - metro-config: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + metro: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + metro-config: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) metro-core: 0.83.3 semver: 7.7.3 transitivePeerDependencies: @@ -22903,18 +22943,18 @@ snapshots: - supports-color - utf-8-validate - '@react-native/debugger-frontend@0.82.1': {} + '@react-native/debugger-frontend@0.83.1': {} - '@react-native/debugger-shell@0.82.1': + '@react-native/debugger-shell@0.83.1': dependencies: cross-spawn: 7.0.6 fb-dotslash: 0.5.8 - '@react-native/dev-middleware@0.82.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@react-native/dev-middleware@0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.82.1 - '@react-native/debugger-shell': 0.82.1 + '@react-native/debugger-frontend': 0.83.1 + '@react-native/debugger-shell': 0.83.1 chrome-launcher: 0.15.2 chromium-edge-launcher: 0.2.0 connect: 3.7.0 @@ -22922,25 +22962,25 @@ snapshots: invariant: 2.2.4 nullthrows: 1.1.1 open: 7.4.2 - serve-static: 1.16.2 - ws: 6.2.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + serve-static: 1.16.3 + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/gradle-plugin@0.82.1': {} + '@react-native/gradle-plugin@0.83.1': {} - '@react-native/js-polyfills@0.82.1': {} + '@react-native/js-polyfills@0.83.1': {} - '@react-native/normalize-colors@0.82.1': {} + '@react-native/normalize-colors@0.83.1': {} - '@react-native/virtualized-lists@0.82.1(@types/react@18.3.1)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)': + '@react-native/virtualized-lists@0.83.1(@types/react@18.3.1)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) optionalDependencies: '@types/react': 18.3.1 @@ -22948,7 +22988,7 @@ snapshots: dependencies: '@swc/helpers': 0.5.13 - '@react-stately/utils@3.10.8(react@18.3.1)': + '@react-stately/utils@3.11.0(react@18.3.1)': dependencies: '@swc/helpers': 0.5.13 react: 18.3.1 @@ -22961,9 +23001,9 @@ snapshots: dependencies: '@redis/client': 1.6.1 - '@redis/bloom@5.9.0(@redis/client@5.9.0)': + '@redis/bloom@5.10.0(@redis/client@5.10.0)': dependencies: - '@redis/client': 5.9.0 + '@redis/client': 5.10.0 '@redis/client@1.6.1': dependencies: @@ -22971,7 +23011,7 @@ snapshots: generic-pool: 3.9.0 yallist: 4.0.0 - '@redis/client@5.9.0': + '@redis/client@5.10.0': dependencies: cluster-key-slot: 1.1.2 @@ -22983,57 +23023,57 @@ snapshots: dependencies: '@redis/client': 1.6.1 - '@redis/json@5.9.0(@redis/client@5.9.0)': + '@redis/json@5.10.0(@redis/client@5.10.0)': dependencies: - '@redis/client': 5.9.0 + '@redis/client': 5.10.0 '@redis/search@1.2.0(@redis/client@1.6.1)': dependencies: '@redis/client': 1.6.1 - '@redis/search@5.9.0(@redis/client@5.9.0)': + '@redis/search@5.10.0(@redis/client@5.10.0)': dependencies: - '@redis/client': 5.9.0 + '@redis/client': 5.10.0 '@redis/time-series@1.1.0(@redis/client@1.6.1)': dependencies: '@redis/client': 1.6.1 - '@redis/time-series@5.9.0(@redis/client@5.9.0)': + '@redis/time-series@5.10.0(@redis/client@5.10.0)': dependencies: - '@redis/client': 5.9.0 + '@redis/client': 5.10.0 '@remirror/core-constants@3.0.0': {} - '@reown/appkit-common@1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)': + '@reown/appkit-common@1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-common@1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-common@1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@18.3.1)(react@18.3.1) - viem: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23066,13 +23106,13 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) lit: 3.1.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -23103,11 +23143,11 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) lit: 3.1.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -23138,16 +23178,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': + '@reown/appkit-utils@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.2 - '@reown/appkit-wallet': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@18.3.1)(react@18.3.1) - viem: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23176,9 +23216,9 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-wallet@1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) '@reown/appkit-polyfills': 1.7.2 '@walletconnect/logger': 2.1.2 zod: 3.22.4 @@ -23187,20 +23227,20 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit-common': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.2 - '@reown/appkit-scaffold-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) - '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@18.3.1)(react@18.3.1) - viem: 2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23237,9 +23277,9 @@ snapshots: optionalDependencies: rollup: 4.50.2 - '@rollup/plugin-commonjs@28.0.1(rollup@4.53.3)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.54.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@rollup/pluginutils': 5.3.0(rollup@4.54.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -23247,7 +23287,7 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.54.0 '@rollup/plugin-commonjs@28.0.9(rollup@4.50.2)': dependencies: @@ -23301,141 +23341,141 @@ snapshots: optionalDependencies: rollup: 4.50.2 - '@rollup/pluginutils@5.3.0(rollup@4.53.3)': + '@rollup/pluginutils@5.3.0(rollup@4.54.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.3 + rollup: 4.54.0 '@rollup/rollup-android-arm-eabi@4.50.2': optional: true - '@rollup/rollup-android-arm-eabi@4.53.3': + '@rollup/rollup-android-arm-eabi@4.54.0': optional: true '@rollup/rollup-android-arm64@4.50.2': optional: true - '@rollup/rollup-android-arm64@4.53.3': + '@rollup/rollup-android-arm64@4.54.0': optional: true '@rollup/rollup-darwin-arm64@4.50.2': optional: true - '@rollup/rollup-darwin-arm64@4.53.3': + '@rollup/rollup-darwin-arm64@4.54.0': optional: true '@rollup/rollup-darwin-x64@4.50.2': optional: true - '@rollup/rollup-darwin-x64@4.53.3': + '@rollup/rollup-darwin-x64@4.54.0': optional: true '@rollup/rollup-freebsd-arm64@4.50.2': optional: true - '@rollup/rollup-freebsd-arm64@4.53.3': + '@rollup/rollup-freebsd-arm64@4.54.0': optional: true '@rollup/rollup-freebsd-x64@4.50.2': optional: true - '@rollup/rollup-freebsd-x64@4.53.3': + '@rollup/rollup-freebsd-x64@4.54.0': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.50.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': optional: true '@rollup/rollup-linux-arm-musleabihf@4.50.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.3': + '@rollup/rollup-linux-arm-musleabihf@4.54.0': optional: true '@rollup/rollup-linux-arm64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.3': + '@rollup/rollup-linux-arm64-gnu@4.54.0': optional: true '@rollup/rollup-linux-arm64-musl@4.50.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.3': + '@rollup/rollup-linux-arm64-musl@4.54.0': optional: true '@rollup/rollup-linux-loong64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.3': + '@rollup/rollup-linux-loong64-gnu@4.54.0': optional: true '@rollup/rollup-linux-ppc64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.3': + '@rollup/rollup-linux-ppc64-gnu@4.54.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.3': + '@rollup/rollup-linux-riscv64-gnu@4.54.0': optional: true '@rollup/rollup-linux-riscv64-musl@4.50.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.3': + '@rollup/rollup-linux-riscv64-musl@4.54.0': optional: true '@rollup/rollup-linux-s390x-gnu@4.50.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.3': + '@rollup/rollup-linux-s390x-gnu@4.54.0': optional: true '@rollup/rollup-linux-x64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.3': + '@rollup/rollup-linux-x64-gnu@4.54.0': optional: true '@rollup/rollup-linux-x64-musl@4.50.2': optional: true - '@rollup/rollup-linux-x64-musl@4.53.3': + '@rollup/rollup-linux-x64-musl@4.54.0': optional: true '@rollup/rollup-openharmony-arm64@4.50.2': optional: true - '@rollup/rollup-openharmony-arm64@4.53.3': + '@rollup/rollup-openharmony-arm64@4.54.0': optional: true '@rollup/rollup-win32-arm64-msvc@4.50.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.53.3': + '@rollup/rollup-win32-arm64-msvc@4.54.0': optional: true '@rollup/rollup-win32-ia32-msvc@4.50.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.53.3': + '@rollup/rollup-win32-ia32-msvc@4.54.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.53.3': + '@rollup/rollup-win32-x64-gnu@4.54.0': optional: true '@rollup/rollup-win32-x64-msvc@4.50.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.53.3': + '@rollup/rollup-win32-x64-msvc@4.54.0': optional: true '@rtsao/scc@1.1.0': {} @@ -23453,8 +23493,8 @@ snapshots: '@scure/bip32@1.3.1': dependencies: '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.2 - '@scure/base': 1.1.9 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 '@scure/bip32@1.4.0': dependencies: @@ -23470,14 +23510,14 @@ snapshots: '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.7 + '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@scure/bip39@1.2.1': dependencies: - '@noble/hashes': 1.3.2 - '@scure/base': 1.1.9 + '@noble/hashes': 1.3.1 + '@scure/base': 1.1.1 '@scure/bip39@1.3.0': dependencies: @@ -23524,78 +23564,78 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 - '@sentry-internal/browser-utils@10.26.0': + '@sentry-internal/browser-utils@10.32.1': dependencies: - '@sentry/core': 10.26.0 + '@sentry/core': 10.32.1 - '@sentry-internal/feedback@10.26.0': + '@sentry-internal/feedback@10.32.1': dependencies: - '@sentry/core': 10.26.0 + '@sentry/core': 10.32.1 '@sentry-internal/node-cpu-profiler@2.2.0': dependencies: detect-libc: 2.1.2 node-abi: 3.85.0 - '@sentry-internal/replay-canvas@10.26.0': + '@sentry-internal/replay-canvas@10.32.1': dependencies: - '@sentry-internal/replay': 10.26.0 - '@sentry/core': 10.26.0 + '@sentry-internal/replay': 10.32.1 + '@sentry/core': 10.32.1 - '@sentry-internal/replay@10.26.0': + '@sentry-internal/replay@10.32.1': dependencies: - '@sentry-internal/browser-utils': 10.26.0 - '@sentry/core': 10.26.0 + '@sentry-internal/browser-utils': 10.32.1 + '@sentry/core': 10.32.1 - '@sentry/babel-plugin-component-annotate@4.6.0': {} + '@sentry/babel-plugin-component-annotate@4.6.1': {} - '@sentry/browser@10.26.0': + '@sentry/browser@10.32.1': dependencies: - '@sentry-internal/browser-utils': 10.26.0 - '@sentry-internal/feedback': 10.26.0 - '@sentry-internal/replay': 10.26.0 - '@sentry-internal/replay-canvas': 10.26.0 - '@sentry/core': 10.26.0 + '@sentry-internal/browser-utils': 10.32.1 + '@sentry-internal/feedback': 10.32.1 + '@sentry-internal/replay': 10.32.1 + '@sentry-internal/replay-canvas': 10.32.1 + '@sentry/core': 10.32.1 - '@sentry/bundler-plugin-core@4.6.0': + '@sentry/bundler-plugin-core@4.6.1': dependencies: '@babel/core': 7.28.5 - '@sentry/babel-plugin-component-annotate': 4.6.0 - '@sentry/cli': 2.58.2 + '@sentry/babel-plugin-component-annotate': 4.6.1 + '@sentry/cli': 2.58.4 dotenv: 16.6.1 find-up: 5.0.0 - glob: 9.3.5 + glob: 10.5.0 magic-string: 0.30.8 unplugin: 1.0.1 transitivePeerDependencies: - encoding - supports-color - '@sentry/cli-darwin@2.58.2': + '@sentry/cli-darwin@2.58.4': optional: true - '@sentry/cli-linux-arm64@2.58.2': + '@sentry/cli-linux-arm64@2.58.4': optional: true - '@sentry/cli-linux-arm@2.58.2': + '@sentry/cli-linux-arm@2.58.4': optional: true - '@sentry/cli-linux-i686@2.58.2': + '@sentry/cli-linux-i686@2.58.4': optional: true - '@sentry/cli-linux-x64@2.58.2': + '@sentry/cli-linux-x64@2.58.4': optional: true - '@sentry/cli-win32-arm64@2.58.2': + '@sentry/cli-win32-arm64@2.58.4': optional: true - '@sentry/cli-win32-i686@2.58.2': + '@sentry/cli-win32-i686@2.58.4': optional: true - '@sentry/cli-win32-x64@2.58.2': + '@sentry/cli-win32-x64@2.58.4': optional: true - '@sentry/cli@2.58.2': + '@sentry/cli@2.58.4': dependencies: https-proxy-agent: 5.0.1 node-fetch: 2.7.0 @@ -23603,50 +23643,50 @@ snapshots: proxy-from-env: 1.1.0 which: 2.0.2 optionalDependencies: - '@sentry/cli-darwin': 2.58.2 - '@sentry/cli-linux-arm': 2.58.2 - '@sentry/cli-linux-arm64': 2.58.2 - '@sentry/cli-linux-i686': 2.58.2 - '@sentry/cli-linux-x64': 2.58.2 - '@sentry/cli-win32-arm64': 2.58.2 - '@sentry/cli-win32-i686': 2.58.2 - '@sentry/cli-win32-x64': 2.58.2 + '@sentry/cli-darwin': 2.58.4 + '@sentry/cli-linux-arm': 2.58.4 + '@sentry/cli-linux-arm64': 2.58.4 + '@sentry/cli-linux-i686': 2.58.4 + '@sentry/cli-linux-x64': 2.58.4 + '@sentry/cli-win32-arm64': 2.58.4 + '@sentry/cli-win32-i686': 2.58.4 + '@sentry/cli-win32-x64': 2.58.4 transitivePeerDependencies: - encoding - supports-color - '@sentry/core@10.26.0': {} + '@sentry/core@10.32.1': {} - '@sentry/nestjs@10.26.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@sentry/nestjs@10.32.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': 0.50.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.55.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 - '@sentry/core': 10.26.0 - '@sentry/node': 10.26.0 + '@sentry/core': 10.32.1 + '@sentry/node': 10.32.1 transitivePeerDependencies: - supports-color - '@sentry/nextjs@10.26.0(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/nextjs@10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 - '@rollup/plugin-commonjs': 28.0.1(rollup@4.53.3) - '@sentry-internal/browser-utils': 10.26.0 - '@sentry/bundler-plugin-core': 4.6.0 - '@sentry/core': 10.26.0 - '@sentry/node': 10.26.0 - '@sentry/opentelemetry': 10.26.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - '@sentry/react': 10.26.0(react@18.3.1) - '@sentry/vercel-edge': 10.26.0 - '@sentry/webpack-plugin': 4.6.0(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) - next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + '@rollup/plugin-commonjs': 28.0.1(rollup@4.54.0) + '@sentry-internal/browser-utils': 10.32.1 + '@sentry/bundler-plugin-core': 4.6.1 + '@sentry/core': 10.32.1 + '@sentry/node': 10.32.1 + '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + '@sentry/react': 10.32.1(react@18.3.1) + '@sentry/vercel-edge': 10.32.1 + '@sentry/webpack-plugin': 4.6.1(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) resolve: 1.22.8 - rollup: 4.53.3 + rollup: 4.54.0 stacktrace-parser: 0.1.11 transitivePeerDependencies: - '@opentelemetry/context-async-hooks' @@ -23657,95 +23697,95 @@ snapshots: - supports-color - webpack - '@sentry/node-core@10.26.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': + '@sentry/node-core@10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': dependencies: '@apm-js-collab/tracing-hooks': 0.3.1 '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 - '@sentry/core': 10.26.0 - '@sentry/opentelemetry': 10.26.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - import-in-the-middle: 1.15.0 + '@sentry/core': 10.32.1 + '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + import-in-the-middle: 2.0.1 transitivePeerDependencies: - supports-color - '@sentry/node@10.26.0': + '@sentry/node@10.32.1': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-amqplib': 0.51.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.48.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-dataloader': 0.22.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.53.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fs': 0.24.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-generic-pool': 0.48.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.51.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.204.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-kafkajs': 0.14.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-knex': 0.49.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-lru-memoizer': 0.49.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.51.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.50.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.51.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis': 0.53.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-tedious': 0.23.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-undici': 0.15.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.52.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.56.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.18.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.61.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.27.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.19.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 - '@prisma/instrumentation': 6.15.0(@opentelemetry/api@1.9.0) - '@sentry/core': 10.26.0 - '@sentry/node-core': 10.26.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.204.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - '@sentry/opentelemetry': 10.26.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - import-in-the-middle: 1.15.0 + '@prisma/instrumentation': 6.19.0(@opentelemetry/api@1.9.0) + '@sentry/core': 10.32.1 + '@sentry/node-core': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) + import-in-the-middle: 2.0.1 minimatch: 9.0.5 transitivePeerDependencies: - supports-color - '@sentry/opentelemetry@10.26.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': + '@sentry/opentelemetry@10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.38.0 - '@sentry/core': 10.26.0 + '@sentry/core': 10.32.1 - '@sentry/profiling-node@10.26.0': + '@sentry/profiling-node@10.32.1': dependencies: '@sentry-internal/node-cpu-profiler': 2.2.0 - '@sentry/core': 10.26.0 - '@sentry/node': 10.26.0 + '@sentry/core': 10.32.1 + '@sentry/node': 10.32.1 transitivePeerDependencies: - supports-color - '@sentry/react@10.26.0(react@18.3.1)': + '@sentry/react@10.32.1(react@18.3.1)': dependencies: - '@sentry/browser': 10.26.0 - '@sentry/core': 10.26.0 + '@sentry/browser': 10.32.1 + '@sentry/core': 10.32.1 hoist-non-react-statics: 3.3.2 react: 18.3.1 - '@sentry/vercel-edge@10.26.0': + '@sentry/vercel-edge@10.32.1': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@sentry/core': 10.26.0 + '@sentry/core': 10.32.1 - '@sentry/webpack-plugin@4.6.0(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/webpack-plugin@4.6.1(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: - '@sentry/bundler-plugin-core': 4.6.0 + '@sentry/bundler-plugin-core': 4.6.1 unplugin: 1.0.1 uuid: 9.0.1 webpack: 5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) @@ -23776,9 +23816,9 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@smithy/abort-controller@4.2.5': + '@smithy/abort-controller@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 '@smithy/chunked-blob-reader-native@4.2.1': @@ -23790,97 +23830,97 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/config-resolver@4.4.3': + '@smithy/config-resolver@4.4.5': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 '@smithy/util-config-provider': 4.2.0 - '@smithy/util-endpoints': 3.2.5 - '@smithy/util-middleware': 4.2.5 + '@smithy/util-endpoints': 3.2.7 + '@smithy/util-middleware': 4.2.7 tslib: 2.8.1 - '@smithy/core@3.18.4': + '@smithy/core@3.20.0': dependencies: - '@smithy/middleware-serde': 4.2.6 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/middleware-serde': 4.2.8 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 '@smithy/util-base64': 4.3.0 '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-stream': 4.5.6 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-stream': 4.5.8 '@smithy/util-utf8': 4.2.0 '@smithy/uuid': 1.1.0 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.2.5': + '@smithy/credential-provider-imds@4.2.7': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/property-provider': 4.2.5 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 + '@smithy/node-config-provider': 4.3.7 + '@smithy/property-provider': 4.2.7 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 tslib: 2.8.1 - '@smithy/eventstream-codec@4.2.5': + '@smithy/eventstream-codec@4.2.7': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 '@smithy/util-hex-encoding': 4.2.0 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.2.5': + '@smithy/eventstream-serde-browser@4.2.7': dependencies: - '@smithy/eventstream-serde-universal': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/eventstream-serde-universal': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.3.5': + '@smithy/eventstream-serde-config-resolver@4.3.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/eventstream-serde-node@4.2.5': + '@smithy/eventstream-serde-node@4.2.7': dependencies: - '@smithy/eventstream-serde-universal': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/eventstream-serde-universal': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.2.5': + '@smithy/eventstream-serde-universal@4.2.7': dependencies: - '@smithy/eventstream-codec': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/eventstream-codec': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.6': + '@smithy/fetch-http-handler@5.3.8': dependencies: - '@smithy/protocol-http': 5.3.5 - '@smithy/querystring-builder': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/querystring-builder': 4.2.7 + '@smithy/types': 4.11.0 '@smithy/util-base64': 4.3.0 tslib: 2.8.1 - '@smithy/hash-blob-browser@4.2.6': + '@smithy/hash-blob-browser@4.2.8': dependencies: '@smithy/chunked-blob-reader': 5.2.0 '@smithy/chunked-blob-reader-native': 4.2.1 - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/hash-node@4.2.5': + '@smithy/hash-node@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 '@smithy/util-buffer-from': 4.2.0 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/hash-stream-node@4.2.5': + '@smithy/hash-stream-node@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/invalid-dependency@4.2.5': + '@smithy/invalid-dependency@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': @@ -23891,126 +23931,126 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/md5-js@4.2.5': + '@smithy/md5-js@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.5': + '@smithy/middleware-content-length@4.2.7': dependencies: - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.3.11': + '@smithy/middleware-endpoint@4.4.1': dependencies: - '@smithy/core': 3.18.4 - '@smithy/middleware-serde': 4.2.6 - '@smithy/node-config-provider': 4.3.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 - '@smithy/url-parser': 4.2.5 - '@smithy/util-middleware': 4.2.5 + '@smithy/core': 3.20.0 + '@smithy/middleware-serde': 4.2.8 + '@smithy/node-config-provider': 4.3.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 + '@smithy/url-parser': 4.2.7 + '@smithy/util-middleware': 4.2.7 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.11': + '@smithy/middleware-retry@4.4.17': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/service-error-classification': 4.2.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 - '@smithy/util-middleware': 4.2.5 - '@smithy/util-retry': 4.2.5 + '@smithy/node-config-provider': 4.3.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/service-error-classification': 4.2.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 + '@smithy/util-middleware': 4.2.7 + '@smithy/util-retry': 4.2.7 '@smithy/uuid': 1.1.0 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.6': + '@smithy/middleware-serde@4.2.8': dependencies: - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/middleware-stack@4.2.5': + '@smithy/middleware-stack@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.3.5': + '@smithy/node-config-provider@4.3.7': dependencies: - '@smithy/property-provider': 4.2.5 - '@smithy/shared-ini-file-loader': 4.4.0 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.7 + '@smithy/shared-ini-file-loader': 4.4.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.5': + '@smithy/node-http-handler@4.4.7': dependencies: - '@smithy/abort-controller': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/querystring-builder': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/abort-controller': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/querystring-builder': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/property-provider@4.2.5': + '@smithy/property-provider@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/protocol-http@5.3.5': + '@smithy/protocol-http@5.3.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/querystring-builder@4.2.5': + '@smithy/querystring-builder@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 '@smithy/util-uri-escape': 4.2.0 tslib: 2.8.1 - '@smithy/querystring-parser@4.2.5': + '@smithy/querystring-parser@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/service-error-classification@4.2.5': + '@smithy/service-error-classification@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 - '@smithy/shared-ini-file-loader@4.4.0': + '@smithy/shared-ini-file-loader@4.4.2': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/signature-v4@5.3.5': + '@smithy/signature-v4@5.3.7': dependencies: '@smithy/is-array-buffer': 4.2.0 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-middleware': 4.2.5 + '@smithy/util-middleware': 4.2.7 '@smithy/util-uri-escape': 4.2.0 '@smithy/util-utf8': 4.2.0 tslib: 2.8.1 - '@smithy/smithy-client@4.9.7': + '@smithy/smithy-client@4.10.2': dependencies: - '@smithy/core': 3.18.4 - '@smithy/middleware-endpoint': 4.3.11 - '@smithy/middleware-stack': 4.2.5 - '@smithy/protocol-http': 5.3.5 - '@smithy/types': 4.9.0 - '@smithy/util-stream': 4.5.6 + '@smithy/core': 3.20.0 + '@smithy/middleware-endpoint': 4.4.1 + '@smithy/middleware-stack': 4.2.7 + '@smithy/protocol-http': 5.3.7 + '@smithy/types': 4.11.0 + '@smithy/util-stream': 4.5.8 tslib: 2.8.1 - '@smithy/types@4.9.0': + '@smithy/types@4.11.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.2.5': + '@smithy/url-parser@4.2.7': dependencies: - '@smithy/querystring-parser': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/querystring-parser': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 '@smithy/util-base64@4.3.0': @@ -24041,49 +24081,49 @@ snapshots: dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.10': + '@smithy/util-defaults-mode-browser@4.3.16': dependencies: - '@smithy/property-provider': 4.2.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 + '@smithy/property-provider': 4.2.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.13': + '@smithy/util-defaults-mode-node@4.2.19': dependencies: - '@smithy/config-resolver': 4.4.3 - '@smithy/credential-provider-imds': 4.2.5 - '@smithy/node-config-provider': 4.3.5 - '@smithy/property-provider': 4.2.5 - '@smithy/smithy-client': 4.9.7 - '@smithy/types': 4.9.0 + '@smithy/config-resolver': 4.4.5 + '@smithy/credential-provider-imds': 4.2.7 + '@smithy/node-config-provider': 4.3.7 + '@smithy/property-provider': 4.2.7 + '@smithy/smithy-client': 4.10.2 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/util-endpoints@3.2.5': + '@smithy/util-endpoints@3.2.7': dependencies: - '@smithy/node-config-provider': 4.3.5 - '@smithy/types': 4.9.0 + '@smithy/node-config-provider': 4.3.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 '@smithy/util-hex-encoding@4.2.0': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.2.5': + '@smithy/util-middleware@4.2.7': dependencies: - '@smithy/types': 4.9.0 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/util-retry@4.2.5': + '@smithy/util-retry@4.2.7': dependencies: - '@smithy/service-error-classification': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/service-error-classification': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 - '@smithy/util-stream@4.5.6': + '@smithy/util-stream@4.5.8': dependencies: - '@smithy/fetch-http-handler': 5.3.6 - '@smithy/node-http-handler': 4.4.5 - '@smithy/types': 4.9.0 + '@smithy/fetch-http-handler': 5.3.8 + '@smithy/node-http-handler': 4.4.7 + '@smithy/types': 4.11.0 '@smithy/util-base64': 4.3.0 '@smithy/util-buffer-from': 4.2.0 '@smithy/util-hex-encoding': 4.2.0 @@ -24104,10 +24144,10 @@ snapshots: '@smithy/util-buffer-from': 4.2.0 tslib: 2.8.1 - '@smithy/util-waiter@4.2.5': + '@smithy/util-waiter@4.2.7': dependencies: - '@smithy/abort-controller': 4.2.5 - '@smithy/types': 4.9.0 + '@smithy/abort-controller': 4.2.7 + '@smithy/types': 4.11.0 tslib: 2.8.1 '@smithy/uuid@1.1.0': @@ -24116,10 +24156,10 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@solana-mobile/mobile-wallet-adapter-protocol-web3js@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/mobile-wallet-adapter-protocol-web3js@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 5.0.0 js-base64: 3.7.8 transitivePeerDependencies: @@ -24129,14 +24169,14 @@ snapshots: - react-native - typescript - '@solana-mobile/mobile-wallet-adapter-protocol@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/mobile-wallet-adapter-protocol@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: '@solana/codecs-strings': 4.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) - '@solana/wallet-standard': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) + '@solana/wallet-standard': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) '@solana/wallet-standard-util': 1.1.2 '@wallet-standard/core': 1.1.1 js-base64: 3.7.8 - react-native: 0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@solana/wallet-adapter-base' - '@solana/web3.js' @@ -24145,25 +24185,25 @@ snapshots: - react - typescript - '@solana-mobile/wallet-adapter-mobile@2.2.5(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/wallet-adapter-mobile@2.2.5(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/mobile-wallet-adapter-protocol-web3js': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana-mobile/wallet-standard-mobile': 0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana-mobile/mobile-wallet-adapter-protocol-web3js': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/wallet-standard-mobile': 0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': 1.3.0 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) js-base64: 3.7.8 optionalDependencies: - '@react-native-async-storage/async-storage': 1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)) + '@react-native-async-storage/async-storage': 1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - react - react-native - typescript - '@solana-mobile/wallet-standard-mobile@0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/wallet-standard-mobile@0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/wallet-standard-chains': 1.1.1 '@solana/wallet-standard-features': 1.3.0 '@wallet-standard/base': 1.1.0 @@ -24225,20 +24265,20 @@ snapshots: commander: 14.0.1 typescript: 5.5.4 - '@solana/wallet-adapter-alpha@0.1.14(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-alpha@0.1.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-avana@0.1.17(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-avana@0.1.17(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-base-ui@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana/wallet-adapter-base-ui@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) react: 18.3.1 transitivePeerDependencies: - bs58 @@ -24246,70 +24286,70 @@ snapshots: - react-native - typescript - '@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-standard-features': 1.3.0 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 eventemitter3: 5.0.1 - '@solana/wallet-adapter-bitkeep@0.3.24(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-bitkeep@0.3.24(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-bitpie@0.5.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-bitpie@0.5.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-clover@0.4.23(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-clover@0.4.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-coin98@0.5.24(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-coin98@0.5.24(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 6.0.0 buffer: 6.0.3 - '@solana/wallet-adapter-coinbase@0.1.23(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-coinbase@0.1.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-coinhub@0.3.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-coinhub@0.3.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-fractal@0.1.12(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@solana/wallet-adapter-fractal@0.1.12(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@fractalwagmi/solana-wallet-adapter': 0.1.1(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@fractalwagmi/solana-wallet-adapter': 0.1.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) transitivePeerDependencies: - react - react-dom - '@solana/wallet-adapter-huobi@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-huobi@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-hyperpay@0.1.18(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-hyperpay@0.1.18(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-keystone@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@solana/wallet-adapter-keystone@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: - '@keystonehq/sol-keyring': 0.20.0(bufferutil@4.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@keystonehq/sol-keyring': 0.20.0(bufferutil@4.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -24319,64 +24359,64 @@ snapshots: - typescript - utf-8-validate - '@solana/wallet-adapter-krystal@0.1.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-krystal@0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-ledger@0.9.29(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-ledger@0.9.29(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@ledgerhq/devices': 8.7.0 - '@ledgerhq/hw-transport': 6.31.13 - '@ledgerhq/hw-transport-webhid': 6.30.9 - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@ledgerhq/devices': 8.7.1 + '@ledgerhq/hw-transport': 6.31.14 + '@ledgerhq/hw-transport-webhid': 6.30.10 + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) buffer: 6.0.3 - '@solana/wallet-adapter-mathwallet@0.9.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-mathwallet@0.9.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-neko@0.2.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-neko@0.2.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-nightly@0.1.20(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-nightly@0.1.20(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-nufi@0.1.21(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-nufi@0.1.21(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-onto@0.1.11(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-onto@0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-particle@0.1.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)': + '@solana/wallet-adapter-particle@0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)': dependencies: - '@particle-network/solana-wallet': 1.3.2(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@particle-network/solana-wallet': 1.3.2(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) transitivePeerDependencies: - bs58 - '@solana/wallet-adapter-phantom@0.9.28(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-phantom@0.9.28(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-react-ui@0.9.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana/wallet-adapter-react-ui@0.9.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-base-ui': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base-ui': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) transitivePeerDependencies: @@ -24385,12 +24425,12 @@ snapshots: - react-native - typescript - '@solana/wallet-adapter-react@0.15.39(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana/wallet-adapter-react@0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/wallet-adapter-mobile': 2.2.5(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@18.3.1) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana-mobile/wallet-adapter-mobile': 2.2.5(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@18.3.1) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) react: 18.3.1 transitivePeerDependencies: - bs58 @@ -24398,61 +24438,61 @@ snapshots: - react-native - typescript - '@solana/wallet-adapter-safepal@0.5.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-safepal@0.5.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-saifu@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-saifu@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-salmon@0.1.18(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-salmon@0.1.18(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - salmon-adapter-sdk: 1.1.1(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + salmon-adapter-sdk: 1.1.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-sky@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-sky@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-solflare@0.6.32(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-solflare@0.6.32(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-chains': 1.1.1 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solflare-wallet/metamask-sdk': 1.0.3(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solflare-wallet/sdk': 1.4.2(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solflare-wallet/metamask-sdk': 1.0.3(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solflare-wallet/sdk': 1.4.2(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@wallet-standard/wallet': 1.1.0 - '@solana/wallet-adapter-solong@0.9.22(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-solong@0.9.22(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-spot@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-spot@0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-tokenary@0.1.16(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-tokenary@0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-tokenpocket@0.4.23(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-tokenpocket@0.4.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-torus@0.11.32(@babel/runtime@7.28.4)(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@solana/wallet-adapter-torus@0.11.32(@babel/runtime@7.28.4)(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@toruslabs/solana-embed': 2.1.0(@babel/runtime@7.28.4)(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@toruslabs/solana-embed': 2.1.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) assert: 2.1.0 crypto-browserify: 3.12.1 process: 0.11.10 @@ -24466,24 +24506,24 @@ snapshots: - typescript - utf-8-validate - '@solana/wallet-adapter-trust@0.1.17(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-trust@0.1.17(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-unsafe-burner@0.1.11(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-unsafe-burner@0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: '@noble/curves': 1.9.7 - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': 1.3.0 '@solana/wallet-standard-util': 1.1.2 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-walletconnect@0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@solana/wallet-adapter-walletconnect@0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/solana-adapter': 0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/solana-adapter': 0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -24512,10 +24552,10 @@ snapshots: - utf-8-validate - zod - '@solana/wallet-adapter-xdefi@0.1.11(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solana/wallet-adapter-xdefi@0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@solana/wallet-standard-chains@1.1.1': dependencies: @@ -24538,36 +24578,36 @@ snapshots: '@solana/wallet-standard-chains': 1.1.1 '@solana/wallet-standard-features': 1.3.0 - '@solana/wallet-standard-wallet-adapter-base@1.1.4(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)': + '@solana/wallet-standard-wallet-adapter-base@1.1.4(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-chains': 1.1.1 '@solana/wallet-standard-features': 1.3.0 '@solana/wallet-standard-util': 1.1.2 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@wallet-standard/app': 1.1.0 '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 '@wallet-standard/wallet': 1.1.0 bs58: 5.0.0 - '@solana/wallet-standard-wallet-adapter-base@1.1.4(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)': + '@solana/wallet-standard-wallet-adapter-base@1.1.4(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-chains': 1.1.1 '@solana/wallet-standard-features': 1.3.0 '@solana/wallet-standard-util': 1.1.2 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@wallet-standard/app': 1.1.0 '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 '@wallet-standard/wallet': 1.1.0 bs58: 6.0.0 - '@solana/wallet-standard-wallet-adapter-react@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1)': + '@solana/wallet-standard-wallet-adapter-react@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0) '@wallet-standard/app': 1.1.0 '@wallet-standard/base': 1.1.0 react: 18.3.1 @@ -24575,10 +24615,10 @@ snapshots: - '@solana/web3.js' - bs58 - '@solana/wallet-standard-wallet-adapter-react@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@18.3.1)': + '@solana/wallet-standard-wallet-adapter-react@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@18.3.1)': dependencies: - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0) '@wallet-standard/app': 1.1.0 '@wallet-standard/base': 1.1.0 react: 18.3.1 @@ -24586,27 +24626,27 @@ snapshots: - '@solana/web3.js' - bs58 - '@solana/wallet-standard-wallet-adapter@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1)': + '@solana/wallet-standard-wallet-adapter@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1)': dependencies: - '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0) - '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) + '@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0) + '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) transitivePeerDependencies: - '@solana/wallet-adapter-base' - '@solana/web3.js' - bs58 - react - '@solana/wallet-standard@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1)': + '@solana/wallet-standard@1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1)': dependencies: '@solana/wallet-standard-core': 1.1.2 - '@solana/wallet-standard-wallet-adapter': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) + '@solana/wallet-standard-wallet-adapter': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) transitivePeerDependencies: - '@solana/wallet-adapter-base' - '@solana/web3.js' - bs58 - react - '@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.28.4 '@noble/curves': 1.9.7 @@ -24619,9 +24659,9 @@ snapshots: bs58: 4.0.1 buffer: 6.0.3 fast-stable-stringify: 1.0.0 - jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + jayson: 4.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 9.3.1 + rpc-websockets: 9.3.2 superstruct: 2.0.2 transitivePeerDependencies: - bufferutil @@ -24629,23 +24669,23 @@ snapshots: - typescript - utf-8-validate - '@solflare-wallet/metamask-sdk@1.0.3(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solflare-wallet/metamask-sdk@1.0.3(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: '@solana/wallet-standard-features': 1.3.0 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@wallet-standard/base': 1.1.0 bs58: 5.0.0 eventemitter3: 5.0.1 uuid: 9.0.1 - '@solflare-wallet/sdk@1.4.2(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))': + '@solflare-wallet/sdk@1.4.2(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 5.0.0 eventemitter3: 5.0.1 uuid: 9.0.1 - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} '@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -24665,10 +24705,10 @@ snapshots: telejson: 7.2.0 tiny-invariant: 1.3.3 - '@storybook/channels@7.6.20': + '@storybook/channels@7.6.21': dependencies: - '@storybook/client-logger': 7.6.20 - '@storybook/core-events': 7.6.20 + '@storybook/client-logger': 7.6.21 + '@storybook/core-events': 7.6.21 '@storybook/global': 5.0.0 qs: 6.14.0 telejson: 7.2.0 @@ -24678,7 +24718,7 @@ snapshots: dependencies: '@storybook/global': 5.0.0 - '@storybook/client-logger@7.6.20': + '@storybook/client-logger@7.6.21': dependencies: '@storybook/global': 5.0.0 @@ -24686,7 +24726,7 @@ snapshots: dependencies: ts-dedent: 2.2.0 - '@storybook/core-events@7.6.20': + '@storybook/core-events@7.6.21': dependencies: ts-dedent: 2.2.0 @@ -24733,14 +24773,14 @@ snapshots: ts-dedent: 2.2.0 util-deprecate: 1.0.2 - '@storybook/preview-api@7.6.20': + '@storybook/preview-api@7.6.21': dependencies: - '@storybook/channels': 7.6.20 - '@storybook/client-logger': 7.6.20 - '@storybook/core-events': 7.6.20 + '@storybook/channels': 7.6.21 + '@storybook/client-logger': 7.6.21 + '@storybook/core-events': 7.6.21 '@storybook/csf': 0.1.13 '@storybook/global': 5.0.0 - '@storybook/types': 7.6.20 + '@storybook/types': 7.6.21 '@types/qs': 6.14.0 dequal: 2.0.3 lodash: 4.17.21 @@ -24772,9 +24812,9 @@ snapshots: '@types/express': 4.17.25 file-system-cache: 2.3.0 - '@storybook/types@7.6.20': + '@storybook/types@7.6.21': dependencies: - '@storybook/channels': 7.6.20 + '@storybook/channels': 7.6.21 '@types/babel__core': 7.20.5 '@types/express': 4.17.25 file-system-cache: 2.3.0 @@ -24994,89 +25034,89 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tailwindcss/node@4.1.17': + '@tailwindcss/node@4.1.18': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.18.4 jiti: 2.6.1 lightningcss: 1.30.2 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.1.17 + tailwindcss: 4.1.18 - '@tailwindcss/oxide-android-arm64@4.1.17': + '@tailwindcss/oxide-android-arm64@4.1.18': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.17': + '@tailwindcss/oxide-darwin-arm64@4.1.18': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.17': + '@tailwindcss/oxide-darwin-x64@4.1.18': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.17': + '@tailwindcss/oxide-freebsd-x64@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.17': + '@tailwindcss/oxide-linux-x64-musl@4.1.18': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.17': + '@tailwindcss/oxide-wasm32-wasi@4.1.18': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': optional: true - '@tailwindcss/oxide@4.1.17': + '@tailwindcss/oxide@4.1.18': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.17 - '@tailwindcss/oxide-darwin-arm64': 4.1.17 - '@tailwindcss/oxide-darwin-x64': 4.1.17 - '@tailwindcss/oxide-freebsd-x64': 4.1.17 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 - '@tailwindcss/oxide-linux-x64-musl': 4.1.17 - '@tailwindcss/oxide-wasm32-wasi': 4.1.17 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 - '@tailwindcss/postcss@4.1.17': + '@tailwindcss/postcss@4.1.18': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.17 - '@tailwindcss/oxide': 4.1.17 + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 postcss: 8.5.6 - tailwindcss: 4.1.17 + tailwindcss: 4.1.18 - '@tailwindcss/vite@4.1.17(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1))': + '@tailwindcss/vite@4.1.18(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2))': dependencies: - '@tailwindcss/node': 4.1.17 - '@tailwindcss/oxide': 4.1.17 - tailwindcss: 4.1.17 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + tailwindcss: 4.1.18 + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) - '@tanstack/react-virtual@3.13.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-virtual@3.13.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/virtual-core': 3.13.12 + '@tanstack/virtual-core': 3.13.13 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tanstack/virtual-core@3.13.12': {} + '@tanstack/virtual-core@3.13.13': {} '@testing-library/dom@10.4.1': dependencies: @@ -25099,129 +25139,129 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@tiptap/core@3.11.0(@tiptap/pm@3.11.0)': + '@tiptap/core@3.14.0(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/pm': 3.11.0 + '@tiptap/pm': 3.14.0 - '@tiptap/extension-blockquote@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-blockquote@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-bold@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-bold@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-bubble-menu@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extension-bubble-menu@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: '@floating-ui/dom': 1.7.4 - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 optional: true - '@tiptap/extension-bullet-list@3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-bullet-list@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extension-list': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-code-block@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extension-code-block@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 - '@tiptap/extension-code@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-code@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-document@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-document@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-dropcursor@3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-dropcursor@3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extensions': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-floating-menu@3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extension-floating-menu@3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: '@floating-ui/dom': 1.7.4 - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 optional: true - '@tiptap/extension-gapcursor@3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-gapcursor@3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extensions': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-hard-break@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-hard-break@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-heading@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-heading@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-history@3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-history@3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extensions': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-horizontal-rule@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extension-horizontal-rule@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 - '@tiptap/extension-italic@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-italic@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-link@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extension-link@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 linkifyjs: 4.3.2 - '@tiptap/extension-list-item@3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-list-item@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extension-list': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-list-keymap@3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-list-keymap@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extension-list': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 - '@tiptap/extension-mention@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)(@tiptap/suggestion@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-mention@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@tiptap/suggestion@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 - '@tiptap/suggestion': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 + '@tiptap/suggestion': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-ordered-list@3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0))': + '@tiptap/extension-ordered-list@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/extension-list': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-paragraph@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-paragraph@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-strike@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-strike@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-text@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-text@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-underline@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))': + '@tiptap/extension-underline@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 - '@tiptap/pm@3.11.0': + '@tiptap/pm@3.14.0': dependencies: prosemirror-changeset: 2.3.1 prosemirror-collab: 1.3.1 @@ -25237,59 +25277,59 @@ snapshots: prosemirror-schema-basic: 1.2.4 prosemirror-schema-list: 1.5.1 prosemirror-state: 1.4.4 - prosemirror-tables: 1.8.1 - prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3) + prosemirror-tables: 1.8.4 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4) prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 - '@tiptap/react@3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tiptap/react@3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 '@types/react': 18.3.1 '@types/react-dom': 18.3.0 '@types/use-sync-external-store': 0.0.6 - fast-deep-equal: 3.1.3 + fast-equals: 5.4.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: - '@tiptap/extension-bubble-menu': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) - '@tiptap/extension-floating-menu': 3.11.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) + '@tiptap/extension-bubble-menu': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-floating-menu': 3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) transitivePeerDependencies: - '@floating-ui/dom' - '@tiptap/starter-kit@3.11.0': + '@tiptap/starter-kit@3.14.0': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/extension-blockquote': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-bold': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-bullet-list': 3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) - '@tiptap/extension-code': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-code-block': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) - '@tiptap/extension-document': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-dropcursor': 3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) - '@tiptap/extension-gapcursor': 3.11.0(@tiptap/extensions@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) - '@tiptap/extension-hard-break': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-heading': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-horizontal-rule': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) - '@tiptap/extension-italic': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-link': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) - '@tiptap/extension-list': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) - '@tiptap/extension-list-item': 3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) - '@tiptap/extension-list-keymap': 3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) - '@tiptap/extension-ordered-list': 3.11.0(@tiptap/extension-list@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)) - '@tiptap/extension-paragraph': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-strike': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-text': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extension-underline': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0)) - '@tiptap/extensions': 3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/extension-blockquote': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-bold': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-bullet-list': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + '@tiptap/extension-code': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-code-block': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-document': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-dropcursor': 3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + '@tiptap/extension-gapcursor': 3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + '@tiptap/extension-hard-break': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-heading': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-horizontal-rule': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-italic': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-link': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-list-item': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + '@tiptap/extension-list-keymap': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + '@tiptap/extension-ordered-list': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + '@tiptap/extension-paragraph': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-strike': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-text': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extension-underline': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 - '@tiptap/suggestion@3.11.0(@tiptap/core@3.11.0(@tiptap/pm@3.11.0))(@tiptap/pm@3.11.0)': + '@tiptap/suggestion@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': dependencies: - '@tiptap/core': 3.11.0(@tiptap/pm@3.11.0) - '@tiptap/pm': 3.11.0 + '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/pm': 3.14.0 '@tokenizer/inflate@0.2.7': dependencies: @@ -25313,17 +25353,17 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@toruslabs/base-controllers@5.11.0(@babel/runtime@7.28.4)(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@toruslabs/base-controllers@5.11.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.28.4 '@ethereumjs/util': 9.1.0 - '@toruslabs/broadcast-channel': 10.0.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@toruslabs/broadcast-channel': 10.0.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.4) '@toruslabs/openlogin-jrpc': 8.3.0(@babel/runtime@7.28.4) '@toruslabs/openlogin-utils': 8.2.1(@babel/runtime@7.28.4) async-mutex: 0.5.0 bignumber.js: 9.3.1 - bowser: 2.12.1 + bowser: 2.13.1 jwt-decode: 4.0.0 loglevel: 1.9.2 transitivePeerDependencies: @@ -25332,14 +25372,14 @@ snapshots: - supports-color - utf-8-validate - '@toruslabs/broadcast-channel@10.0.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@toruslabs/broadcast-channel@10.0.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.28.4 '@toruslabs/eccrypto': 4.0.0 '@toruslabs/metadata-helpers': 5.1.0(@babel/runtime@7.28.4) loglevel: 1.9.2 oblivious-set: 1.4.0 - socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + socket.io-client: 4.8.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) unload: 2.4.1 transitivePeerDependencies: - '@sentry/types' @@ -25389,16 +25429,16 @@ snapshots: base64url: 3.0.1 color: 4.2.3 - '@toruslabs/solana-embed@2.1.0(@babel/runtime@7.28.4)(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@toruslabs/solana-embed@2.1.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.28.4 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@toruslabs/base-controllers': 5.11.0(@babel/runtime@7.28.4)(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@toruslabs/base-controllers': 5.11.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(utf-8-validate@5.0.10) '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.4) '@toruslabs/openlogin-jrpc': 8.3.0(@babel/runtime@7.28.4) eth-rpc-errors: 4.0.3 fast-deep-equal: 3.1.3 - lodash-es: 4.17.21 + lodash-es: 4.17.22 loglevel: 1.9.2 pump: 3.0.3 transitivePeerDependencies: @@ -25471,7 +25511,7 @@ snapshots: '@types/cache-manager@5.0.0': dependencies: - cache-manager: 7.2.5 + cache-manager: 7.2.7 '@types/cacheable-request@6.0.3': dependencies: @@ -25495,9 +25535,9 @@ snapshots: dependencies: '@types/node': 18.16.9 - '@types/cookie-parser@1.4.10(@types/express@5.0.5)': + '@types/cookie-parser@1.4.10(@types/express@5.0.6)': dependencies: - '@types/express': 5.0.5 + '@types/express': 5.0.6 '@types/cors@2.8.19': dependencies: @@ -25546,11 +25586,11 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.10 - '@types/express@5.0.5': + '@types/express@5.0.6': dependencies: '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 5.1.0 - '@types/serve-static': 1.15.10 + '@types/serve-static': 2.2.0 '@types/facebook-nodejs-business-sdk@20.0.3': {} @@ -25618,7 +25658,7 @@ snapshots: '@types/linkify-it@5.0.0': {} - '@types/lodash@4.17.20': {} + '@types/lodash@4.17.21': {} '@types/luxon@3.4.2': {} @@ -25653,7 +25693,7 @@ snapshots: '@types/multer@1.4.13': dependencies: - '@types/express': 5.0.5 + '@types/express': 5.0.6 '@types/multipipe@3.0.5': dependencies: @@ -25668,7 +25708,7 @@ snapshots: '@types/node': 18.16.9 form-data: 4.0.5 - '@types/node-telegram-bot-api@0.64.12': + '@types/node-telegram-bot-api@0.64.13': dependencies: '@types/node': 18.16.9 '@types/request': 2.48.13 @@ -25683,7 +25723,7 @@ snapshots: '@types/nodemailer@6.4.21': dependencies: - '@aws-sdk/client-ses': 3.934.0 + '@aws-sdk/client-ses': 3.956.0 '@types/node': 18.16.9 transitivePeerDependencies: - aws-crt @@ -25706,6 +25746,12 @@ snapshots: pg-protocol: 1.10.3 pg-types: 2.2.0 + '@types/pg@8.15.6': + dependencies: + '@types/node': 18.16.9 + pg-protocol: 1.10.3 + pg-types: 2.2.0 + '@types/prismjs@1.26.5': {} '@types/prop-types@15.7.15': {} @@ -25735,7 +25781,7 @@ snapshots: '@types/prop-types': 15.7.15 csstype: 3.2.3 - '@types/readable-stream@4.0.22': + '@types/readable-stream@4.0.23': dependencies: '@types/node': 18.16.9 @@ -25779,12 +25825,15 @@ snapshots: '@types/node': 18.16.9 '@types/send': 0.17.6 + '@types/serve-static@2.2.0': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 18.16.9 + '@types/sha256@0.2.2': dependencies: '@types/node': 18.16.9 - '@types/shimmer@1.2.0': {} - '@types/sortablejs@1.15.9': {} '@types/stack-utils@2.0.3': {} @@ -25939,21 +25988,21 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@uiw/copy-to-clipboard@1.0.17': {} + '@uiw/copy-to-clipboard@1.0.19': {} '@uiw/react-markdown-preview@5.1.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.4 - '@uiw/copy-to-clipboard': 1.0.17 + '@uiw/copy-to-clipboard': 1.0.19 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-markdown: 9.0.3(@types/react@18.3.1)(react@18.3.1) rehype-attr: 3.0.3 rehype-autolink-headings: 7.1.0 - rehype-ignore: 2.0.2 + rehype-ignore: 2.0.3 rehype-prism-plus: 2.0.0 rehype-raw: 7.0.0 - rehype-rewrite: 4.0.3 + rehype-rewrite: 4.0.4 rehype-slug: 6.0.0 remark-gfm: 4.0.1 remark-github-blockquote-alert: 1.3.1 @@ -25962,7 +26011,7 @@ snapshots: - '@types/react' - supports-color - '@uiw/react-md-editor@4.0.8(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@uiw/react-md-editor@4.0.11(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.4 '@uiw/react-markdown-preview': 5.1.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -26039,7 +26088,7 @@ snapshots: dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/aws-s3@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26053,7 +26102,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/companion-client@4.5.2(@uppy/core@4.5.3)': dependencies: @@ -26072,7 +26121,7 @@ snapshots: '@uppy/webcam': 4.3.2(@uppy/core@4.5.3) clsx: 2.1.1 dequal: 2.0.3 - preact: 10.27.2 + preact: 10.28.0 pretty-bytes: 6.1.1 '@uppy/compressor@2.3.2(@uppy/core@4.5.3)': @@ -26081,7 +26130,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 compressorjs: 1.2.1 - preact: 10.27.2 + preact: 10.28.0 promise-queue: 2.2.5 '@uppy/core@4.5.3': @@ -26093,7 +26142,7 @@ snapshots: mime-match: 1.0.2 namespace-emitter: 2.0.1 nanoid: 5.1.6 - preact: 10.27.2 + preact: 10.28.0 '@uppy/dashboard@4.4.3(@uppy/core@4.5.3)': dependencies: @@ -26107,14 +26156,14 @@ snapshots: classnames: 2.5.1 lodash: 4.17.21 nanoid: 5.1.6 - preact: 10.27.2 + preact: 10.28.0 shallow-equal: 3.1.0 '@uppy/drag-drop@4.2.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/dropbox@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26122,7 +26171,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/facebook@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26130,13 +26179,13 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/file-input@4.2.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/google-drive@4.4.2(@uppy/core@4.5.3)': dependencies: @@ -26144,20 +26193,20 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/image-editor@3.4.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 cropperjs: 1.6.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/informer@4.3.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/instagram@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26165,7 +26214,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/onedrive@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26173,13 +26222,13 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/progress-bar@4.3.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/provider-views@4.5.3(@uppy/core@4.5.3)': dependencies: @@ -26188,14 +26237,14 @@ snapshots: classnames: 2.5.1 nanoid: 5.1.6 p-queue: 8.1.1 - preact: 10.27.2 + preact: 10.28.0 '@uppy/react@4.5.2(@uppy/core@4.5.3)(@uppy/dashboard@4.4.3(@uppy/core@4.5.3))(@uppy/drag-drop@4.2.2(@uppy/core@4.5.3))(@uppy/file-input@4.2.2(@uppy/core@4.5.3))(@uppy/progress-bar@4.3.2(@uppy/core@4.5.3))(@uppy/screen-capture@4.4.2(@uppy/core@4.5.3))(@uppy/status-bar@4.2.3(@uppy/core@4.5.3))(@uppy/webcam@4.3.2(@uppy/core@4.5.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@uppy/components': 0.3.2 '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) @@ -26226,7 +26275,7 @@ snapshots: dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/status-bar@4.2.3(@uppy/core@4.5.3)': dependencies: @@ -26234,7 +26283,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 classnames: 2.5.1 - preact: 10.27.2 + preact: 10.28.0 '@uppy/store-default@4.3.2': {} @@ -26266,7 +26315,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 '@uppy/url@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26274,19 +26323,19 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 nanoid: 5.1.6 - preact: 10.27.2 + preact: 10.28.0 '@uppy/utils@6.2.2': dependencies: lodash: 4.17.21 - preact: 10.27.2 + preact: 10.28.0 '@uppy/webcam@4.3.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 is-mobile: 4.0.0 - preact: 10.27.2 + preact: 10.28.0 '@uppy/xhr-upload@4.4.2(@uppy/core@4.5.3)': dependencies: @@ -26300,9 +26349,9 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.27.2 + preact: 10.28.0 - '@upstash/redis@1.35.6': + '@upstash/redis@1.35.8': dependencies: uncrypto: 0.1.3 @@ -26313,9 +26362,9 @@ snapshots: transitivePeerDependencies: - graphql - '@vercel/oidc@3.0.4': {} + '@vercel/oidc@3.0.5': {} - '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) @@ -26323,7 +26372,7 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -26342,7 +26391,7 @@ snapshots: std-env: 3.10.0 strip-literal: 2.1.1 test-exclude: 6.0.0 - vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -26353,13 +26402,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1))': + '@vitest/mocker@3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) '@vitest/pretty-format@3.1.4': dependencies: @@ -26393,7 +26442,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.1.1 sirv: 2.0.4 - vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) '@vitest/utils@1.6.0': dependencies: @@ -26435,21 +26484,21 @@ snapshots: dependencies: '@wallet-standard/base': 1.1.0 - '@walletconnect/core@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 events: 3.3.0 lodash.isequal: 4.5.0 @@ -26479,21 +26528,21 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -26564,23 +26613,23 @@ snapshots: '@walletconnect/jsonrpc-types': 1.0.4 tslib: 1.14.1 - '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + '@walletconnect/jsonrpc-ws-connection@1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/safe-json': 1.0.2 events: 3.3.0 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate - '@walletconnect/keyvaluestorage@1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2)': + '@walletconnect/keyvaluestorage@1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2)': dependencies: '@walletconnect/safe-json': 1.0.2 idb-keyval: 6.2.2 - unstorage: 1.17.2(@upstash/redis@1.35.6)(idb-keyval@6.2.2)(ioredis@5.8.2) + unstorage: 1.17.3(@upstash/redis@1.35.8)(idb-keyval@6.2.2)(ioredis@5.8.2) optionalDependencies: - '@react-native-async-storage/async-storage': 1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)) + '@react-native-async-storage/async-storage': 1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -26622,16 +26671,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -26658,16 +26707,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -26694,13 +26743,13 @@ snapshots: - utf-8-validate - zod - '@walletconnect/solana-adapter@0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/solana-adapter@0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -26734,12 +26783,12 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/types@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2)': + '@walletconnect/types@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -26763,12 +26812,12 @@ snapshots: - ioredis - uploadthing - '@walletconnect/types@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2)': + '@walletconnect/types@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -26792,18 +26841,18 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 lodash: 4.17.21 transitivePeerDependencies: @@ -26832,18 +26881,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -26872,25 +26921,25 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 detect-browser: 5.3.0 elliptic: 6.6.1 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -26916,18 +26965,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(bufferutil@4.0.9)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.6)(ioredis@5.8.2) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 bs58: 6.0.0 @@ -26935,7 +26984,7 @@ snapshots: elliptic: 6.6.1 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -27134,18 +27183,20 @@ snapshots: typescript: 5.5.4 zod: 3.25.76 - abitype@1.1.0(typescript@5.5.4)(zod@3.22.4): + abitype@1.2.3(typescript@5.5.4)(zod@3.22.4): optionalDependencies: typescript: 5.5.4 zod: 3.22.4 - abitype@1.1.0(typescript@5.5.4)(zod@3.25.76): + abitype@1.2.3(typescript@5.5.4)(zod@3.25.76): optionalDependencies: typescript: 5.5.4 zod: 3.25.76 abort-controller-x@0.4.3: {} + abort-controller-x@0.5.0: {} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -27161,7 +27212,7 @@ snapshots: accepts@2.0.0: dependencies: - mime-types: 3.0.1 + mime-types: 3.0.2 negotiator: 1.0.0 acorn-globals@7.0.1: @@ -27338,7 +27389,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 is-string: 1.1.1 @@ -27355,7 +27406,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -27363,7 +27414,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -27373,7 +27424,7 @@ snapshots: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 es-shim-unscopables: 1.1.0 @@ -27382,21 +27433,21 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-shim-unscopables: 1.1.0 @@ -27405,7 +27456,7 @@ snapshots: array-buffer-byte-length: 1.0.2 call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 @@ -27448,8 +27499,6 @@ snapshots: async-function@1.0.0: {} - async-limiter@1.0.1: {} - async-mutex@0.5.0: dependencies: tslib: 2.8.1 @@ -27460,12 +27509,11 @@ snapshots: attr-accept@2.2.5: {} - autoprefixer@10.4.22(postcss@8.4.38): + autoprefixer@10.4.23(postcss@8.4.38): dependencies: - browserslist: 4.28.0 - caniuse-lite: 1.0.30001756 + browserslist: 4.28.1 + caniuse-lite: 1.0.30001761 fraction.js: 5.3.4 - normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.4.38 postcss-value-parser: 4.2.0 @@ -27607,7 +27655,7 @@ snapshots: base64url@3.0.1: {} - baseline-browser-mapping@2.8.29: {} + baseline-browser-mapping@2.9.11: {} basic-ftp@5.0.5: {} @@ -27687,16 +27735,33 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.0: + body-parser@1.20.4: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 2.5.3 + type-is: 1.6.18 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + body-parser@2.2.1: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3(supports-color@5.5.0) - http-errors: 2.0.0 - iconv-lite: 0.6.3 + http-errors: 2.0.1 + iconv-lite: 0.7.1 on-finished: 2.4.1 qs: 6.14.0 - raw-body: 3.0.1 + raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: - supports-color @@ -27711,7 +27776,7 @@ snapshots: bottleneck@2.19.5: {} - bowser@2.12.1: {} + bowser@2.13.1: {} brace-expansion@1.1.12: dependencies: @@ -27772,13 +27837,13 @@ snapshots: readable-stream: 2.3.8 safe-buffer: 5.2.1 - browserslist@4.28.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001756 - electron-to-chromium: 1.5.256 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) + update-browserslist-db: 1.2.3(browserslist@4.28.1) bs-logger@0.2.6: dependencies: @@ -27822,7 +27887,7 @@ snapshots: base64-js: 1.5.1 ieee754: 1.2.1 - bufferutil@4.0.9: + bufferutil@4.1.0: dependencies: node-gyp-build: 4.8.4 @@ -27830,7 +27895,7 @@ snapshots: dependencies: semver: 7.7.3 - bullmq@5.63.2: + bullmq@5.66.2: dependencies: cron-parser: 4.9.0 ioredis: 5.8.2 @@ -27846,9 +27911,9 @@ snapshots: dependencies: run-applescript: 7.1.0 - bundle-require@5.1.0(esbuild@0.27.0): + bundle-require@5.1.0(esbuild@0.27.2): dependencies: - esbuild: 0.27.0 + esbuild: 0.27.2 load-tsconfig: 0.2.5 busboy@1.6.0: @@ -27859,10 +27924,10 @@ snapshots: cac@6.7.14: {} - cache-manager@7.2.5: + cache-manager@7.2.7: dependencies: - '@cacheable/utils': 2.3.1 - keyv: 5.5.4 + '@cacheable/utils': 2.3.2 + keyv: 5.5.5 cacheable-lookup@5.0.4: {} @@ -27906,12 +27971,12 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001756: {} + caniuse-lite@1.0.30001761: {} canvas@2.11.2: dependencies: '@mapbox/node-pre-gyp': 1.0.11 - nan: 2.23.1 + nan: 2.24.0 simple-get: 3.1.1 transitivePeerDependencies: - encoding @@ -28076,21 +28141,21 @@ snapshots: class-transformer@0.5.1: {} - class-validator-jsonschema@5.1.0(class-transformer@0.5.1)(class-validator@0.14.2): + class-validator-jsonschema@5.1.0(class-transformer@0.5.1)(class-validator@0.14.3): dependencies: class-transformer: 0.5.1 - class-validator: 0.14.2 + class-validator: 0.14.3 lodash.groupby: 4.6.0 lodash.merge: 4.6.2 openapi3-ts: 3.2.0 reflect-metadata: 0.2.2 tslib: 2.8.1 - class-validator@0.14.2: + class-validator@0.14.3: dependencies: '@types/validator': 13.15.10 - libphonenumber-js: 1.12.27 - validator: 13.15.23 + libphonenumber-js: 1.12.33 + validator: 13.15.26 class-variance-authority@0.6.1: dependencies: @@ -28318,6 +28383,8 @@ snapshots: cookie-signature@1.0.6: {} + cookie-signature@1.0.7: {} + cookie-signature@1.2.2: {} cookie@0.7.1: {} @@ -28330,7 +28397,7 @@ snapshots: core-js-compat@3.47.0: dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 core-js-pure@3.47.0: {} @@ -28482,7 +28549,7 @@ snapshots: domutils: 3.2.2 nth-check: 2.1.1 - css-selector-parser@3.2.0: {} + css-selector-parser@3.3.0: {} css-tree@2.2.1: dependencies: @@ -28617,7 +28684,7 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@1.7.0(babel-plugin-macros@3.1.0): + dedent@1.7.1(babel-plugin-macros@3.1.0): optionalDependencies: babel-plugin-macros: 3.1.0 @@ -28873,7 +28940,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.256: {} + electron-to-chromium@1.5.267: {} elliptic@6.6.1: dependencies: @@ -28887,7 +28954,7 @@ snapshots: emittery@0.13.1: {} - emoji-picker-react@4.15.1(react@18.3.1): + emoji-picker-react@4.16.1(react@18.3.1): dependencies: flairup: 1.0.0 react: 18.3.1 @@ -28915,12 +28982,12 @@ snapshots: dependencies: once: 1.4.0 - engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + engine.io-client@6.6.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7 engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -28929,7 +28996,7 @@ snapshots: engine.io-parser@5.2.3: {} - enhanced-resolve@5.18.3: + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -28948,7 +29015,7 @@ snapshots: dependencies: stackframe: 1.3.4 - es-abstract@1.24.0: + es-abstract@1.24.1: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 @@ -29021,12 +29088,12 @@ snapshots: isarray: 2.0.5 stop-iteration-iterator: 1.1.0 - es-iterator-helpers@1.2.1: + es-iterator-helpers@1.2.2: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 @@ -29073,10 +29140,10 @@ snapshots: dependencies: es6-promise: 4.2.8 - esbuild-register@3.6.0(esbuild@0.27.0): + esbuild-register@3.6.0(esbuild@0.27.2): dependencies: debug: 4.4.3(supports-color@5.5.0) - esbuild: 0.27.0 + esbuild: 0.27.2 transitivePeerDependencies: - supports-color @@ -29109,34 +29176,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.0: + esbuild@0.27.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.0 - '@esbuild/android-arm': 0.27.0 - '@esbuild/android-arm64': 0.27.0 - '@esbuild/android-x64': 0.27.0 - '@esbuild/darwin-arm64': 0.27.0 - '@esbuild/darwin-x64': 0.27.0 - '@esbuild/freebsd-arm64': 0.27.0 - '@esbuild/freebsd-x64': 0.27.0 - '@esbuild/linux-arm': 0.27.0 - '@esbuild/linux-arm64': 0.27.0 - '@esbuild/linux-ia32': 0.27.0 - '@esbuild/linux-loong64': 0.27.0 - '@esbuild/linux-mips64el': 0.27.0 - '@esbuild/linux-ppc64': 0.27.0 - '@esbuild/linux-riscv64': 0.27.0 - '@esbuild/linux-s390x': 0.27.0 - '@esbuild/linux-x64': 0.27.0 - '@esbuild/netbsd-arm64': 0.27.0 - '@esbuild/netbsd-x64': 0.27.0 - '@esbuild/openbsd-arm64': 0.27.0 - '@esbuild/openbsd-x64': 0.27.0 - '@esbuild/openharmony-arm64': 0.27.0 - '@esbuild/sunos-x64': 0.27.0 - '@esbuild/win32-arm64': 0.27.0 - '@esbuild/win32-ia32': 0.27.0 - '@esbuild/win32-x64': 0.27.0 + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 escalade@3.2.0: {} @@ -29343,7 +29410,7 @@ snapshots: array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 + es-iterator-helpers: 1.2.2 eslint: 8.57.0 estraverse: 5.3.0 hasown: 2.0.2 @@ -29515,7 +29582,7 @@ snapshots: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - execa@9.6.0: + execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 cross-spawn: 7.0.6 @@ -29542,7 +29609,7 @@ snapshots: exit@0.1.2: {} - expect-type@1.2.2: {} + expect-type@1.3.0: {} expect@29.7.0: dependencies: @@ -29554,9 +29621,9 @@ snapshots: exponential-backoff@3.1.3: {} - express-rate-limit@7.5.1(express@5.1.0): + express-rate-limit@7.5.1(express@5.2.1): dependencies: - express: 5.1.0 + express: 5.2.1 express@4.21.2: dependencies: @@ -29594,23 +29661,60 @@ snapshots: transitivePeerDependencies: - supports-color - express@5.1.0: + express@4.22.1: + dependencies: + accepts: 1.3.8 + array-flatten: 1.1.1 + body-parser: 1.20.4 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.0.7 + debug: 2.6.9 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.3.2 + fresh: 0.5.2 + http-errors: 2.0.1 + merge-descriptors: 1.0.3 + methods: 1.1.2 + on-finished: 2.4.1 + parseurl: 1.3.3 + path-to-regexp: 0.1.12 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + safe-buffer: 5.2.1 + send: 0.19.2 + serve-static: 1.16.3 + setprototypeof: 1.2.0 + statuses: 2.0.2 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.0 + body-parser: 2.2.1 content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 debug: 4.4.3(supports-color@5.5.0) + depd: 2.0.0 encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 2.1.0 + finalhandler: 2.1.1 fresh: 2.0.0 - http-errors: 2.0.0 + http-errors: 2.0.1 merge-descriptors: 2.0.0 - mime-types: 3.0.1 + mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 @@ -29618,8 +29722,8 @@ snapshots: qs: 6.14.0 range-parser: 1.2.1 router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 statuses: 2.0.2 type-is: 2.0.1 vary: 1.1.2 @@ -29661,12 +29765,16 @@ snapshots: fast-copy@3.0.2: {} + fast-copy@4.0.2: {} + fast-deep-equal@2.0.1: {} fast-deep-equal@3.1.3: {} fast-diff@1.1.2: {} + fast-equals@5.4.0: {} + fast-glob@3.3.1: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -29705,7 +29813,7 @@ snapshots: fast-xml-parser@5.2.5: dependencies: - strnum: 2.1.1 + strnum: 2.1.2 fastestsmallesttextencoderdecoder@1.0.22: {} @@ -29827,7 +29935,19 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@2.1.0: + finalhandler@1.3.2: + dependencies: + debug: 2.6.9 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.1: dependencies: debug: 4.4.3(supports-color@5.5.0) encodeurl: 2.0.0 @@ -29858,7 +29978,7 @@ snapshots: dependencies: fast-glob: 3.3.3 pkg-types: 1.3.1 - yaml: 2.8.1 + yaml: 2.8.2 first-match@0.0.1: {} @@ -29866,7 +29986,7 @@ snapshots: dependencies: magic-string: 0.30.21 mlly: 1.8.0 - rollup: 4.53.3 + rollup: 4.54.0 flairup@1.0.0: {} @@ -30003,6 +30123,12 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 + fs-extra@11.3.3: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -30176,13 +30302,10 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@12.0.0: + glob@13.0.0: dependencies: - foreground-child: 3.3.1 - jackspeak: 4.1.1 minimatch: 10.1.1 minipass: 7.1.2 - package-json-from-dist: 1.0.1 path-scurry: 2.0.1 glob@7.2.3: @@ -30230,7 +30353,7 @@ snapshots: gaxios: 5.1.3 gcp-metadata: 5.3.0 gtoken: 6.1.2 - jws: 4.0.0 + jws: 4.0.1 lru-cache: 6.0.0 transitivePeerDependencies: - encoding @@ -30243,7 +30366,7 @@ snapshots: gaxios: 6.7.1 gcp-metadata: 6.1.1 gtoken: 7.1.0 - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - encoding - supports-color @@ -30252,7 +30375,7 @@ snapshots: google-p12-pem@4.0.1: dependencies: - node-forge: 1.3.1 + node-forge: 1.3.3 googleapis-common@7.2.0: dependencies: @@ -30314,13 +30437,13 @@ snapshots: graphql: 16.12.0 tslib: 2.8.1 - graphql-yoga@5.16.2(graphql@16.12.0): + graphql-yoga@5.18.0(graphql@16.12.0): dependencies: '@envelop/core': 5.4.0 '@envelop/instrumentation': 1.0.0 - '@graphql-tools/executor': 1.4.13(graphql@16.12.0) - '@graphql-tools/schema': 10.0.29(graphql@16.12.0) - '@graphql-tools/utils': 10.10.3(graphql@16.12.0) + '@graphql-tools/executor': 1.5.0(graphql@16.12.0) + '@graphql-tools/schema': 10.0.30(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.12.0) '@graphql-yoga/logger': 2.0.1 '@graphql-yoga/subscription': 5.0.5 '@whatwg-node/fetch': 0.10.13 @@ -30349,7 +30472,7 @@ snapshots: dependencies: gaxios: 5.1.3 google-p12-pem: 4.0.1 - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - encoding - supports-color @@ -30357,7 +30480,7 @@ snapshots: gtoken@7.1.0: dependencies: gaxios: 6.7.1 - jws: 4.0.0 + jws: 4.0.1 transitivePeerDependencies: - encoding - supports-color @@ -30369,7 +30492,7 @@ snapshots: defu: 6.1.4 destr: 2.0.5 iron-webcrypto: 1.2.1 - node-mock-http: 1.0.3 + node-mock-http: 1.0.4 radix3: 1.1.2 ufo: 1.6.1 uncrypto: 0.1.3 @@ -30439,9 +30562,9 @@ snapshots: inherits: 2.0.4 minimalistic-assert: 1.0.1 - hashery@1.2.0: + hashery@1.3.0: dependencies: - hookified: 1.13.0 + hookified: 1.14.0 hasown@2.0.2: dependencies: @@ -30495,9 +30618,9 @@ snapshots: '@types/unist': 3.0.3 '@ungap/structured-clone': 1.3.0 hast-util-from-parse5: 8.0.3 - hast-util-to-parse5: 8.0.0 + hast-util-to-parse5: 8.0.1 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 parse5: 7.3.0 unist-util-position: 5.0.0 unist-util-visit: 5.0.0 @@ -30511,7 +30634,7 @@ snapshots: '@types/unist': 3.0.3 bcp-47-match: 2.0.3 comma-separated-tokens: 2.0.3 - css-selector-parser: 3.2.0 + css-selector-parser: 3.3.0 devlop: 1.1.0 direction: 2.0.1 hast-util-has-property: 3.0.0 @@ -30531,7 +30654,7 @@ snapshots: comma-separated-tokens: 2.0.3 hast-util-whitespace: 3.0.0 html-void-elements: 3.0.0 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 stringify-entities: 4.0.4 @@ -30557,12 +30680,12 @@ snapshots: transitivePeerDependencies: - supports-color - hast-util-to-parse5@8.0.0: + hast-util-to-parse5@8.0.1: dependencies: '@types/hast': 3.0.4 comma-separated-tokens: 2.0.3 devlop: 1.1.0 - property-information: 6.5.0 + property-information: 7.1.0 space-separated-tokens: 2.0.2 web-namespaces: 2.0.1 zwitch: 2.0.4 @@ -30608,7 +30731,7 @@ snapshots: help-me@5.0.0: {} - hermes-compiler@0.0.0: {} + hermes-compiler@0.14.0: {} hermes-estree@0.32.0: {} @@ -30632,21 +30755,21 @@ snapshots: dependencies: react-is: 16.13.1 - hono-openapi@0.4.8(hono@4.10.6)(openapi-types@12.1.3)(zod@3.25.76): + hono-openapi@0.4.8(hono@4.11.1)(openapi-types@12.1.3)(zod@3.25.76): dependencies: json-schema-walker: 2.0.0 openapi-types: 12.1.3 optionalDependencies: - hono: 4.10.6 + hono: 4.11.1 zod: 3.25.76 - hono@4.10.6: {} + hono@4.11.1: {} - hookified@1.13.0: {} + hookified@1.14.0: {} - hot-reload-extension-vite@1.0.13(bufferutil@4.0.9)(utf-8-validate@5.0.10): + hot-reload-extension-vite@1.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -30699,6 +30822,14 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 @@ -30767,13 +30898,13 @@ snapshots: dependencies: '@babel/runtime': 7.28.4 - i18next@25.6.3(typescript@5.5.4): + i18next@25.7.3(typescript@5.5.4): dependencies: '@babel/runtime': 7.28.4 optionalDependencies: typescript: 5.5.4 - ibm-cloud-sdk-core@5.4.4: + ibm-cloud-sdk-core@5.4.5: dependencies: '@types/debug': 4.1.12 '@types/node': 18.19.130 @@ -30786,7 +30917,7 @@ snapshots: file-type: 16.5.4 form-data: 4.0.5 isstream: 0.1.2 - jsonwebtoken: 9.0.2 + jsonwebtoken: 9.0.3 mime-types: 2.1.35 retry-axios: 2.6.0(axios@1.13.2) tough-cookie: 4.1.4 @@ -30801,7 +30932,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.0: + iconv-lite@0.7.1: dependencies: safer-buffer: 2.1.2 @@ -30837,6 +30968,13 @@ snapshots: cjs-module-lexer: 1.4.3 module-details-from-path: 1.0.4 + import-in-the-middle@2.0.1: + dependencies: + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + cjs-module-lexer: 1.4.3 + module-details-from-path: 1.0.4 + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 @@ -31206,17 +31344,17 @@ snapshots: isobject@3.0.1: {} - isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) - isows@1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isows@1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) - isows@1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isows@1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) isstream@0.1.2: {} @@ -31293,11 +31431,7 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jackspeak@4.1.1: - dependencies: - '@isaacs/cliui': 8.0.2 - - jayson@4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + jayson@4.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@types/connect': 3.4.38 '@types/node': 12.20.55 @@ -31306,11 +31440,11 @@ snapshots: delay: 5.0.0 es6-promisify: 5.0.0 eyes: 0.1.8 - isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isomorphic-ws: 4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10)) json-stringify-safe: 5.0.1 stream-json: 1.9.1 uuid: 8.3.2 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -31330,7 +31464,7 @@ snapshots: '@types/node': 18.16.9 chalk: 4.1.2 co: 4.6.0 - dedent: 1.7.0(babel-plugin-macros@3.1.0) + dedent: 1.7.1(babel-plugin-macros@3.1.0) is-generator-fn: 2.1.0 jest-each: 29.7.0 jest-matcher-utils: 29.7.0 @@ -31416,7 +31550,7 @@ snapshots: jest-util: 29.7.0 pretty-format: 29.7.0 - jest-environment-jsdom@29.7.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10): + jest-environment-jsdom@29.7.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10): dependencies: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 @@ -31425,7 +31559,7 @@ snapshots: '@types/node': 18.16.9 jest-mock: 29.7.0 jest-util: 29.7.0 - jsdom: 20.0.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10) + jsdom: 20.0.3(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) optionalDependencies: canvas: 2.11.2 transitivePeerDependencies: @@ -31667,6 +31801,8 @@ snapshots: jose@5.10.0: {} + jose@6.1.3: {} + joycon@3.1.1: {} jpeg-exif@1.1.4: {} @@ -31716,7 +31852,7 @@ snapshots: jsc-safe-url@0.2.4: {} - jsdom@20.0.3(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10): + jsdom@20.0.3(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10): dependencies: abab: 2.0.6 acorn: 8.15.0 @@ -31732,7 +31868,7 @@ snapshots: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 + nwsapi: 2.2.23 parse5: 7.3.0 saxes: 6.0.0 symbol-tree: 3.2.4 @@ -31742,7 +31878,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) xml-name-validator: 4.0.0 optionalDependencies: canvas: 2.11.2 @@ -31751,7 +31887,7 @@ snapshots: - supports-color - utf-8-validate - jsdom@22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10): + jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10): dependencies: abab: 2.0.6 cssstyle: 3.0.0 @@ -31763,7 +31899,7 @@ snapshots: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.22 + nwsapi: 2.2.23 parse5: 7.3.0 rrweb-cssom: 0.6.0 saxes: 6.0.0 @@ -31774,7 +31910,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 12.0.1 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) xml-name-validator: 4.0.0 optionalDependencies: canvas: 2.11.2 @@ -31799,6 +31935,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json-schema-walker@2.0.0: dependencies: '@apidevtools/json-schema-ref-parser': 11.9.3 @@ -31848,9 +31986,9 @@ snapshots: jsonpointer@5.0.1: {} - jsonwebtoken@9.0.2: + jsonwebtoken@9.0.3: dependencies: - jws: 3.2.2 + jws: 4.0.1 lodash.includes: 4.3.0 lodash.isboolean: 3.0.3 lodash.isinteger: 4.0.4 @@ -31884,31 +32022,20 @@ snapshots: object.assign: 4.1.7 object.values: 1.2.1 - jwa@1.4.2: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - jwa@2.0.1: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - jws@3.2.2: - dependencies: - jwa: 1.4.2 - safe-buffer: 5.2.1 - - jws@4.0.0: + jws@4.0.1: dependencies: jwa: 2.0.1 safe-buffer: 5.2.1 jwt-decode@4.0.0: {} - katex@0.16.25: + katex@0.16.27: dependencies: commander: 8.3.0 @@ -31916,7 +32043,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - keyv@5.5.4: + keyv@5.5.5: dependencies: '@keyv/serialize': 1.1.1 @@ -31926,24 +32053,24 @@ snapshots: kleur@4.1.5: {} - konva@10.0.9: {} + konva@10.0.12: {} - langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) js-tiktoken: 1.0.21 js-yaml: 4.1.1 jsonpointer: 5.0.1 - langsmith: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 - yaml: 2.8.1 + yaml: 2.8.2 zod: 3.25.76 optionalDependencies: - '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) axios: 1.13.2(debug@4.4.3) cheerio: 1.1.2 handlebars: 4.7.8 @@ -31954,22 +32081,22 @@ snapshots: - openai - ws - langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) js-tiktoken: 1.0.21 js-yaml: 4.1.1 jsonpointer: 5.0.1 - langsmith: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 - yaml: 2.8.1 + yaml: 2.8.2 zod: 3.25.76 optionalDependencies: - '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) axios: 1.13.2(debug@4.4.3) cheerio: 1.1.2 handlebars: 4.7.8 @@ -31980,35 +32107,33 @@ snapshots: - openai - ws - langsmith@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): + langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 console-table-printer: 2.15.0 p-queue: 6.6.2 - p-retry: 4.6.2 semver: 7.7.3 uuid: 10.0.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-proto': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) - langsmith@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): + langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 console-table-printer: 2.15.0 p-queue: 6.6.2 - p-retry: 4.6.2 semver: 7.7.3 uuid: 10.0.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-proto': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) language-subtag-registry@0.3.23: {} @@ -32029,7 +32154,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.12.27: {} + libphonenumber-js@1.12.33: {} lighthouse-logger@1.4.2: dependencies: @@ -32144,7 +32269,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.21: {} + lodash-es@4.17.22: {} lodash._baseiteratee@4.7.0: dependencies: @@ -32244,7 +32369,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.2: {} + lru-cache@11.2.4: {} lru-cache@4.1.5: dependencies: @@ -32320,14 +32445,14 @@ snapshots: marky@1.3.0: {} - mastra@0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(typescript@5.5.4)(zod@3.25.76): + mastra@0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(hono@4.11.1)(typescript@5.5.4)(zod@3.25.76): dependencies: '@clack/prompts': 0.11.0 - '@expo/devcert': 1.2.0 + '@expo/devcert': 1.2.1 '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/deployer': 0.19.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(typescript@5.5.4)(zod@3.25.76) '@mastra/loggers': 0.10.19(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)) - '@mastra/mcp': 0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(zod@3.25.76) + '@mastra/mcp': 0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(hono@4.11.1)(zod@3.25.76) '@opentelemetry/auto-instrumentations-node': 0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)) '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-grpc': 0.203.0(@opentelemetry/api@1.9.0) @@ -32340,13 +32465,13 @@ snapshots: '@webcontainer/env': 1.1.1 commander: 12.1.0 dotenv: 16.6.1 - execa: 9.6.0 - fs-extra: 11.3.2 + execa: 9.6.1 + fs-extra: 11.3.3 get-port: 7.1.0 open: 10.2.0 picocolors: 1.1.1 posthog-node: 4.18.0 - prettier: 3.6.2 + prettier: 3.7.4 shell-quote: 1.8.3 strip-json-comments: 5.0.3 tcp-port-used: 1.0.2 @@ -32359,6 +32484,7 @@ snapshots: - '@types/json-schema' - debug - encoding + - hono - supports-color - typescript @@ -32553,7 +32679,7 @@ snapshots: unist-util-position: 4.0.4 unist-util-visit: 4.1.2 - mdast-util-to-hast@13.2.0: + mdast-util-to-hast@13.2.1: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 @@ -32644,16 +32770,16 @@ snapshots: transitivePeerDependencies: - supports-color - metro-config@0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + metro-config@0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: connect: 3.7.0 flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + metro: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) metro-cache: 0.83.3 metro-core: 0.83.3 metro-runtime: 0.83.3 - yaml: 2.8.1 + yaml: 2.8.2 transitivePeerDependencies: - bufferutil - supports-color @@ -32730,14 +32856,14 @@ snapshots: transitivePeerDependencies: - supports-color - metro-transform-worker@0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + metro-transform-worker@0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@babel/core': 7.28.5 '@babel/generator': 7.28.5 '@babel/parser': 7.28.5 '@babel/types': 7.28.5 flow-enums-runtime: 0.0.6 - metro: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + metro: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) metro-babel-transformer: 0.83.3 metro-cache: 0.83.3 metro-cache-key: 0.83.3 @@ -32750,7 +32876,7 @@ snapshots: - supports-color - utf-8-validate - metro@0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + metro@0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@babel/code-frame': 7.27.1 '@babel/core': 7.28.5 @@ -32776,7 +32902,7 @@ snapshots: metro-babel-transformer: 0.83.3 metro-cache: 0.83.3 metro-cache-key: 0.83.3 - metro-config: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + metro-config: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) metro-core: 0.83.3 metro-file-map: 0.83.3 metro-resolver: 0.83.3 @@ -32784,13 +32910,13 @@ snapshots: metro-source-map: 0.83.3 metro-symbolicate: 0.83.3 metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + metro-transform-worker: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) mime-types: 2.1.35 nullthrows: 1.1.1 serialize-error: 2.1.0 source-map: 0.5.7 throat: 5.0.0 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) yargs: 17.7.2 transitivePeerDependencies: - bufferutil @@ -32897,7 +33023,7 @@ snapshots: dependencies: '@types/katex': 0.16.7 devlop: 1.1.0 - katex: 0.16.25 + katex: 0.16.27 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 @@ -33153,7 +33279,7 @@ snapshots: dependencies: mime-db: 1.52.0 - mime-types@3.0.1: + mime-types@3.0.2: dependencies: mime-db: 1.54.0 @@ -33225,14 +33351,14 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 - mobx-react-lite@4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1): + mobx-react-lite@4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1): dependencies: mobx: 6.15.0 react: 18.3.1 use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: react-dom: 18.3.1(react@18.3.1) - react-native: 0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) mobx-state-tree@7.0.2(mobx@6.15.0)(typescript@5.5.4): dependencies: @@ -33322,7 +33448,7 @@ snapshots: namespace-emitter@2.0.1: {} - nan@2.23.1: {} + nan@2.24.0: {} nanoid@3.3.11: {} @@ -33340,33 +33466,33 @@ snapshots: neo-async@2.6.2: {} - nestjs-command@3.1.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(yargs@17.7.2): + nestjs-command@3.1.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(yargs@17.7.2): dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) lodash.compact: 3.0.1 lodash.flattendeep: 4.4.0 yargs: 17.7.2 - nestjs-real-ip@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)): + nestjs-real-ip@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)): dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@supercharge/request-ip': 1.2.0 netmask@2.0.2: {} - next-plausible@3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-plausible@3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1) + next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.16(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1): + next@14.2.16(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1): dependencies: '@next/env': 14.2.16 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001756 + caniuse-lite: 1.0.30001761 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -33383,18 +33509,18 @@ snapshots: '@next/swc-win32-ia32-msvc': 14.2.16 '@next/swc-win32-x64-msvc': 14.2.16 '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.56.1 - sass: 1.94.1 + '@playwright/test': 1.57.0 + sass: 1.97.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.56.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.94.1): + next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1): dependencies: '@next/env': 14.2.35 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001756 + caniuse-lite: 1.0.30001761 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -33411,8 +33537,8 @@ snapshots: '@next/swc-win32-ia32-msvc': 14.2.33 '@next/swc-win32-x64-msvc': 14.2.33 '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.56.1 - sass: 1.94.1 + '@playwright/test': 1.57.0 + sass: 1.97.1 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -33428,7 +33554,7 @@ snapshots: nice-grpc@2.1.14: dependencies: - '@grpc/grpc-js': 1.14.1 + '@grpc/grpc-js': 1.14.3 abort-controller-x: 0.4.3 nice-grpc-common: 2.0.2 @@ -33466,7 +33592,7 @@ snapshots: fetch-blob: 3.2.0 formdata-polyfill: 4.0.10 - node-forge@1.3.1: {} + node-forge@1.3.3: {} node-gyp-build-optional-packages@5.2.2: dependencies: @@ -33477,7 +33603,7 @@ snapshots: node-int64@0.4.0: {} - node-mock-http@1.0.3: {} + node-mock-http@1.0.4: {} node-releases@2.0.27: {} @@ -33521,13 +33647,11 @@ snapshots: normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - normalize-url@6.1.0: {} normalize.css@8.0.1: {} - nostr-tools@2.18.2(typescript@5.5.4): + nostr-tools@2.19.4(typescript@5.5.4): dependencies: '@noble/ciphers': 0.5.3 '@noble/curves': 1.2.0 @@ -33569,7 +33693,7 @@ snapshots: nullthrows@1.1.1: {} - nwsapi@2.2.22: {} + nwsapi@2.2.23: {} oauth-sign@0.9.0: {} @@ -33610,19 +33734,19 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 object.hasown@1.1.4: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 object.omit@3.0.0: @@ -33676,7 +33800,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.104.0(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): dependencies: '@types/node': 18.16.9 '@types/node-fetch': 2.6.13 @@ -33686,26 +33810,26 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0 optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - encoding - openai@5.23.2(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@5.23.2(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 - openai@6.9.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 openapi-types@12.1.3: {} openapi3-ts@3.2.0: dependencies: - yaml: 2.8.1 + yaml: 2.8.2 optionator@0.9.4: dependencies: @@ -33747,13 +33871,43 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 + ox@0.11.1(typescript@5.5.4)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.5.4)(zod@3.22.4) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + + ox@0.11.1(typescript@5.5.4)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/ciphers': 1.3.0 + '@noble/curves': 1.9.1 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.5.4)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + ox@0.6.7(typescript@5.5.4)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 abitype: 1.0.8(typescript@5.5.4)(zod@3.25.76) eventemitter3: 5.0.1 optionalDependencies: @@ -33761,36 +33915,6 @@ snapshots: transitivePeerDependencies: - zod - ox@0.9.6(typescript@5.5.4)(zod@3.22.4): - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.5.4)(zod@3.22.4) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - zod - - ox@0.9.6(typescript@5.5.4)(zod@3.25.76): - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.5.4)(zod@3.25.76) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - zod - p-cancelable@2.1.1: {} p-finally@1.0.0: {} @@ -33972,7 +34096,7 @@ snapshots: path-scurry@2.0.1: dependencies: - lru-cache: 11.2.2 + lru-cache: 11.2.4 minipass: 7.1.2 path-to-regexp@0.1.12: {} @@ -34052,7 +34176,7 @@ snapshots: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 - postgres-bytea: 1.0.0 + postgres-bytea: 1.0.1 postgres-date: 1.0.7 postgres-interval: 1.2.0 @@ -34089,6 +34213,10 @@ snapshots: dependencies: split2: 4.2.0 + pino-abstract-transport@3.0.0: + dependencies: + split2: 4.2.0 + pino-pretty@11.3.0: dependencies: colorette: 2.0.20 @@ -34106,17 +34234,17 @@ snapshots: sonic-boom: 4.2.0 strip-json-comments: 3.1.1 - pino-pretty@13.1.2: + pino-pretty@13.1.3: dependencies: colorette: 2.0.20 dateformat: 4.6.3 - fast-copy: 3.0.2 + fast-copy: 4.0.2 fast-safe-stringify: 2.1.1 help-me: 5.0.0 joycon: 3.1.1 minimist: 1.2.8 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 + pino-abstract-transport: 3.0.0 pump: 3.0.3 secure-json-parse: 4.1.0 sonic-boom: 4.2.0 @@ -34160,7 +34288,7 @@ snapshots: optionalDependencies: '@napi-rs/nice': 1.1.1 - pkce-challenge@5.0.0: {} + pkce-challenge@5.0.1: {} pkg-dir@4.2.0: dependencies: @@ -34178,11 +34306,11 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 - playwright-core@1.56.1: {} + playwright-core@1.57.0: {} - playwright@1.56.1: + playwright@1.57.0: dependencies: - playwright-core: 1.56.1 + playwright-core: 1.57.0 optionalDependencies: fsevents: 2.3.2 @@ -34192,7 +34320,7 @@ snapshots: pngjs@5.0.0: {} - polotno@2.32.4(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): + polotno@2.34.1(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): dependencies: '@blueprintjs/core': 5.19.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@blueprintjs/icons': 5.23.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -34203,10 +34331,10 @@ snapshots: functions-have-names: 1.2.3 gifuct-js: 2.1.2 gradient-parser: 1.1.1 - konva: 10.0.9 + konva: 10.0.12 mensch: 0.3.4 mobx: 6.15.0 - mobx-react-lite: 4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) + mobx-react-lite: 4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) mobx-state-tree: 7.0.2(mobx@6.15.0)(typescript@5.5.4) nanoid: 3.3.11 quill: 1.3.7 @@ -34214,13 +34342,13 @@ snapshots: react: 18.3.1 react-color: 2.19.3(react@18.3.1) react-dom: 18.3.1(react@18.3.1) - react-konva: 18.2.14(@types/react@18.3.1)(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-konva-utils: 2.0.0(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + react-konva: 18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-konva-utils: 2.0.0(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) react-sortablejs: 6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6) react-window: 1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) sortablejs: 1.15.6 svg-round-corners: 0.4.3 - swr: 2.3.6(react@18.3.1) + swr: 2.3.8(react@18.3.1) use-image: 1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@types/react' @@ -34245,18 +34373,18 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.6)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)): dependencies: lilconfig: 3.1.3 - yaml: 2.8.1 + yaml: 2.8.2 optionalDependencies: postcss: 8.5.6 ts-node: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4) - postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.4.38)(yaml@2.8.1): + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.4.38)(yaml@2.8.2): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.6.1 postcss: 8.4.38 - yaml: 2.8.1 + yaml: 2.8.2 postcss-nested@6.2.0(postcss@8.5.6): dependencies: @@ -34290,7 +34418,7 @@ snapshots: postgres-array@2.0.0: {} - postgres-bytea@1.0.0: {} + postgres-bytea@1.0.1: {} postgres-date@1.0.7: {} @@ -34300,12 +34428,12 @@ snapshots: postgres@3.4.7: {} - posthog-js@1.296.1: + posthog-js@1.309.1: dependencies: - '@posthog/core': 1.5.2 + '@posthog/core': 1.8.1 core-js: 3.47.0 fflate: 0.4.8 - preact: 10.27.2 + preact: 10.28.0 web-vitals: 4.2.4 posthog-node@4.18.0: @@ -34314,13 +34442,13 @@ snapshots: transitivePeerDependencies: - debug - preact@10.27.2: {} + preact@10.28.0: {} prelude-ls@1.2.1: {} prettier@2.8.8: {} - prettier@3.6.2: {} + prettier@3.7.4: {} pretty-bytes@6.1.1: {} @@ -34420,20 +34548,20 @@ snapshots: dependencies: prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 prosemirror-gapcursor@1.4.0: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 prosemirror-history@1.5.0: dependencies: prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 rope-sequence: 1.3.4 prosemirror-inputrules@1.5.1: @@ -34477,29 +34605,29 @@ snapshots: dependencies: prosemirror-model: 1.25.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 - prosemirror-tables@1.8.1: + prosemirror-tables@1.8.4: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 - prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.3): + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4): dependencies: '@remirror/core-constants': 3.0.0 escape-string-regexp: 4.0.0 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-view: 1.41.3 + prosemirror-view: 1.41.4 prosemirror-transform@1.10.5: dependencies: prosemirror-model: 1.25.4 - prosemirror-view@1.41.3: + prosemirror-view@1.41.4: dependencies: prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 @@ -34678,18 +34806,25 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - raw-body@3.0.1: + raw-body@2.5.3: dependencies: bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.7.0 + http-errors: 2.0.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.1 unpipe: 1.0.0 react-color@2.19.3(react@18.3.1): dependencies: '@icons/material': 0.2.4(react@18.3.1) lodash: 4.17.21 - lodash-es: 4.17.21 + lodash-es: 4.17.22 material-colors: 1.2.6 prop-types: 15.8.1 react: 18.3.1 @@ -34705,10 +34840,10 @@ snapshots: dependencies: react: 18.3.1 - react-devtools-core@6.1.5(bufferutil@4.0.9)(utf-8-validate@5.0.10): + react-devtools-core@6.1.5(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: shell-quote: 1.8.3 - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -34749,7 +34884,7 @@ snapshots: react-fast-compare@3.2.2: {} - react-hook-form@7.66.1(react@18.3.1): + react-hook-form@7.69.0(react@18.3.1): dependencies: react: 18.3.1 @@ -34758,15 +34893,15 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-i18next@15.7.4(i18next@25.6.3(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): + react-i18next@15.7.4(i18next@25.7.3(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): dependencies: '@babel/runtime': 7.28.4 html-parse-stringify: 3.0.1 - i18next: 25.6.3(typescript@5.5.4) + i18next: 25.7.3(typescript@5.5.4) react: 18.3.1 optionalDependencies: react-dom: 18.3.1(react@18.3.1) - react-native: 0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) typescript: 5.5.4 react-is@16.13.1: {} @@ -34775,21 +34910,21 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.0: {} + react-is@19.2.3: {} - react-konva-utils@2.0.0(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + react-konva-utils@2.0.0(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: - konva: 10.0.9 + konva: 10.0.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-konva: 18.2.14(@types/react@18.3.1)(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-konva: 18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) use-image: 1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@types/react-reconciler': 0.28.9(@types/react@18.3.1) its-fine: 1.2.5(@types/react@18.3.1)(react@18.3.1) - konva: 10.0.9 + konva: 10.0.12 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-reconciler: 0.29.2(react@18.3.1) @@ -34812,7 +34947,7 @@ snapshots: devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 react: 18.3.1 remark-parse: 11.0.0 remark-rehype: 11.1.2 @@ -34851,7 +34986,7 @@ snapshots: devlop: 1.1.0 hast-util-to-jsx-runtime: 2.3.6 html-url-attributes: 3.0.1 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 react: 18.3.1 remark-parse: 11.0.0 remark-rehype: 11.1.2 @@ -34870,16 +35005,16 @@ snapshots: react-lifecycles-compat: 3.0.4 warning: 4.0.3 - react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10): + react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native/assets-registry': 0.82.1 - '@react-native/codegen': 0.82.1(@babel/core@7.28.5) - '@react-native/community-cli-plugin': 0.82.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@react-native/gradle-plugin': 0.82.1 - '@react-native/js-polyfills': 0.82.1 - '@react-native/normalize-colors': 0.82.1 - '@react-native/virtualized-lists': 0.82.1(@types/react@18.3.1)(react-native@0.82.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) + '@react-native/assets-registry': 0.83.1 + '@react-native/codegen': 0.83.1(@babel/core@7.28.5) + '@react-native/community-cli-plugin': 0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@react-native/gradle-plugin': 0.83.1 + '@react-native/js-polyfills': 0.83.1 + '@react-native/normalize-colors': 0.83.1 + '@react-native/virtualized-lists': 0.83.1(@types/react@18.3.1)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 @@ -34889,7 +35024,7 @@ snapshots: commander: 12.1.0 flow-enums-runtime: 0.0.6 glob: 7.2.3 - hermes-compiler: 0.0.0 + hermes-compiler: 0.14.0 invariant: 2.2.4 jest-environment-node: 29.7.0 memoize-one: 5.2.1 @@ -34899,14 +35034,14 @@ snapshots: pretty-format: 29.7.0 promise: 8.3.0 react: 18.3.1 - react-devtools-core: 6.1.5(bufferutil@4.0.9)(utf-8-validate@5.0.10) + react-devtools-core: 6.1.5(bufferutil@4.1.0)(utf-8-validate@5.0.10) react-refresh: 0.14.2 regenerator-runtime: 0.13.11 - scheduler: 0.26.0 + scheduler: 0.27.0 semver: 7.7.3 stacktrace-parser: 0.1.11 whatwg-fetch: 3.6.20 - ws: 6.2.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) yargs: 17.7.2 optionalDependencies: '@types/react': 18.3.1 @@ -34971,7 +35106,7 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - react-remove-scroll@2.7.1(@types/react@18.3.1)(react@18.3.1): + react-remove-scroll@2.7.2(@types/react@18.3.1)(react@18.3.1): dependencies: react: 18.3.1 react-remove-scroll-bar: 2.3.8(@types/react@18.3.1)(react@18.3.1) @@ -35049,10 +35184,9 @@ snapshots: dependencies: react: 18.3.1 - react-use-keypress@1.3.1(react@18.3.1): + react-use-keypress@1.4.0(react@18.3.1): dependencies: react: 18.3.1 - tiny-invariant: 1.3.3 react-window@1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: @@ -35131,13 +35265,13 @@ snapshots: '@redis/search': 1.2.0(@redis/client@1.6.1) '@redis/time-series': 1.1.0(@redis/client@1.6.1) - redis@5.9.0: + redis@5.10.0: dependencies: - '@redis/bloom': 5.9.0(@redis/client@5.9.0) - '@redis/client': 5.9.0 - '@redis/json': 5.9.0(@redis/client@5.9.0) - '@redis/search': 5.9.0(@redis/client@5.9.0) - '@redis/time-series': 5.9.0(@redis/client@5.9.0) + '@redis/bloom': 5.10.0(@redis/client@5.10.0) + '@redis/client': 5.10.0 + '@redis/json': 5.10.0(@redis/client@5.10.0) + '@redis/search': 5.10.0(@redis/client@5.10.0) + '@redis/time-series': 5.10.0(@redis/client@5.10.0) redux@4.2.1: dependencies: @@ -35151,7 +35285,7 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -35217,7 +35351,7 @@ snapshots: unified: 11.0.5 unist-util-visit: 5.0.0 - rehype-ignore@2.0.2: + rehype-ignore@2.0.3: dependencies: hast-util-select: 6.0.4 unified: 11.0.5 @@ -35253,7 +35387,7 @@ snapshots: hast-util-raw: 9.1.0 vfile: 6.0.3 - rehype-rewrite@4.0.3: + rehype-rewrite@4.0.4: dependencies: hast-util-select: 6.0.4 unified: 11.0.5 @@ -35332,7 +35466,7 @@ snapshots: dependencies: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - mdast-util-to-hast: 13.2.0 + mdast-util-to-hast: 13.2.1 unified: 11.0.5 vfile: 6.0.3 @@ -35386,6 +35520,13 @@ snapshots: transitivePeerDependencies: - supports-color + require-in-the-middle@8.0.1: + dependencies: + debug: 4.4.3(supports-color@5.5.0) + module-details-from-path: 1.0.4 + transitivePeerDependencies: + - supports-color + require-main-filename@2.0.0: {} requires-port@1.0.0: {} @@ -35511,32 +35652,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.50.2 fsevents: 2.3.3 - rollup@4.53.3: + rollup@4.54.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.3 - '@rollup/rollup-android-arm64': 4.53.3 - '@rollup/rollup-darwin-arm64': 4.53.3 - '@rollup/rollup-darwin-x64': 4.53.3 - '@rollup/rollup-freebsd-arm64': 4.53.3 - '@rollup/rollup-freebsd-x64': 4.53.3 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 - '@rollup/rollup-linux-arm-musleabihf': 4.53.3 - '@rollup/rollup-linux-arm64-gnu': 4.53.3 - '@rollup/rollup-linux-arm64-musl': 4.53.3 - '@rollup/rollup-linux-loong64-gnu': 4.53.3 - '@rollup/rollup-linux-ppc64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-gnu': 4.53.3 - '@rollup/rollup-linux-riscv64-musl': 4.53.3 - '@rollup/rollup-linux-s390x-gnu': 4.53.3 - '@rollup/rollup-linux-x64-gnu': 4.53.3 - '@rollup/rollup-linux-x64-musl': 4.53.3 - '@rollup/rollup-openharmony-arm64': 4.53.3 - '@rollup/rollup-win32-arm64-msvc': 4.53.3 - '@rollup/rollup-win32-ia32-msvc': 4.53.3 - '@rollup/rollup-win32-x64-gnu': 4.53.3 - '@rollup/rollup-win32-x64-msvc': 4.53.3 + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 fsevents: 2.3.3 rope-sequence@1.3.4: {} @@ -35551,7 +35692,7 @@ snapshots: transitivePeerDependencies: - supports-color - rpc-websockets@9.3.1: + rpc-websockets@9.3.2: dependencies: '@swc/helpers': 0.5.13 '@types/uuid': 8.3.4 @@ -35559,9 +35700,9 @@ snapshots: buffer: 6.0.3 eventemitter3: 5.0.1 uuid: 8.3.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.1.0 utf-8-validate: 5.0.10 rrweb-cssom@0.6.0: {} @@ -35630,15 +35771,15 @@ snapshots: safer-buffer@2.1.2: {} - salmon-adapter-sdk@1.1.1(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)): + salmon-adapter-sdk@1.1.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)): dependencies: - '@project-serum/sol-wallet-adapter': 0.2.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@project-serum/sol-wallet-adapter': 0.2.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) eventemitter3: 4.0.7 sane-domparser-error@0.2.0: {} - sass@1.94.1: + sass@1.97.1: dependencies: chokidar: 4.0.3 immutable: 5.1.4 @@ -35656,7 +35797,7 @@ snapshots: dependencies: loose-envify: 1.4.0 - scheduler@0.26.0: {} + scheduler@0.27.0: {} schema-utils@3.3.0: dependencies: @@ -35713,15 +35854,33 @@ snapshots: transitivePeerDependencies: - supports-color - send@1.2.0: + send@0.19.2: + dependencies: + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.1 + mime: 1.6.0 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + send@1.2.1: dependencies: debug: 4.4.3(supports-color@5.5.0) encodeurl: 2.0.0 escape-html: 1.0.3 etag: 1.8.1 fresh: 2.0.0 - http-errors: 2.0.0 - mime-types: 3.0.1 + http-errors: 2.0.1 + mime-types: 3.0.2 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 @@ -35750,12 +35909,21 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@2.2.0: + serve-static@1.16.3: dependencies: encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 1.2.0 + send: 0.19.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 transitivePeerDependencies: - supports-color @@ -35844,8 +36012,6 @@ snapshots: interpret: 1.4.0 rechoir: 0.6.2 - shimmer@1.2.1: {} - side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 @@ -35921,7 +36087,7 @@ snapshots: dependencies: '@juggle/resize-observer': 3.4.0 '@types/is-hotkey': 0.1.10 - '@types/lodash': 4.17.20 + '@types/lodash': 4.17.21 direction: 1.0.4 is-hotkey: 0.1.8 is-plain-object: 5.0.0 @@ -35947,11 +36113,11 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + socket.io-client@4.8.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7 - engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + engine.io-client: 6.6.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -36080,7 +36246,7 @@ snapshots: storybook-source-link@4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@storybook/addons': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/preview-api': 7.6.20 + '@storybook/preview-api': 7.6.21 '@types/react': 18.3.1 react-error-boundary: 4.1.2(react@18.3.1) optionalDependencies: @@ -36125,14 +36291,14 @@ snapshots: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 call-bound: 1.0.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-errors: 1.3.0 es-object-atoms: 1.1.1 get-intrinsic: 1.3.0 @@ -36146,7 +36312,7 @@ snapshots: string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 string.prototype.trim@1.2.10: dependencies: @@ -36154,7 +36320,7 @@ snapshots: call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 - es-abstract: 1.24.0 + es-abstract: 1.24.1 es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 @@ -36221,7 +36387,7 @@ snapshots: strnum@1.1.2: {} - strnum@2.1.1: {} + strnum@2.1.2: {} strtok3@10.3.4: dependencies: @@ -36269,20 +36435,20 @@ snapshots: subtitle@4.2.2-alpha.0: dependencies: '@types/multipipe': 3.0.5 - '@types/readable-stream': 4.0.22 + '@types/readable-stream': 4.0.23 multipipe: 4.0.0 readable-stream: 4.7.0 split2: 3.2.2 strip-bom: 4.0.0 - sucrase@3.35.0: + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.5.0 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.15 ts-interface-checker: 0.1.13 superstruct@2.0.2: {} @@ -36319,7 +36485,7 @@ snapshots: sweetalert2@11.4.8: {} - swr@2.3.6(react@18.3.1): + swr@2.3.8(react@18.3.1): dependencies: dequal: 2.0.3 react: 18.3.1 @@ -36364,11 +36530,11 @@ snapshots: postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 - sucrase: 3.35.0 + sucrase: 3.35.1 transitivePeerDependencies: - ts-node - tailwindcss@4.1.17: {} + tailwindcss@4.1.18: {} tapable@2.3.0: {} @@ -36392,7 +36558,7 @@ snapshots: dependencies: memoizerific: 1.11.3 - terser-webpack-plugin@5.3.14(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 @@ -36600,7 +36766,7 @@ snapshots: dependencies: tslib: 2.8.1 - ts-jest@29.4.5(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4): + ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -36648,7 +36814,7 @@ snapshots: tsconfig-paths-webpack-plugin@4.0.1: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.18.4 tsconfig-paths: 4.2.0 tsconfig-paths@3.15.0: @@ -36670,22 +36836,22 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(jiti@2.6.1)(postcss@8.4.38)(typescript@5.5.4)(yaml@2.8.1): + tsup@8.5.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(jiti@2.6.1)(postcss@8.4.38)(typescript@5.5.4)(yaml@2.8.2): dependencies: - bundle-require: 5.1.0(esbuild@0.27.0) + bundle-require: 5.1.0(esbuild@0.27.2) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 debug: 4.4.3(supports-color@5.5.0) - esbuild: 0.27.0 + esbuild: 0.27.2 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.4.38)(yaml@2.8.1) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.4.38)(yaml@2.8.2) resolve-from: 5.0.0 - rollup: 4.53.3 + rollup: 4.54.0 source-map: 0.7.6 - sucrase: 3.35.0 + sucrase: 3.35.1 tinyexec: 0.3.2 tinyglobby: 0.2.15 tree-kill: 1.2.2 @@ -36754,7 +36920,7 @@ snapshots: type-fest@4.41.0: {} - type-graphql@2.0.0-rc.1(class-validator@0.14.2)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0): + type-graphql@2.0.0-rc.1(class-validator@0.14.3)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0): dependencies: '@graphql-yoga/subscription': 5.0.5 '@types/node': 18.16.9 @@ -36765,7 +36931,7 @@ snapshots: semver: 7.7.3 tslib: 2.8.1 optionalDependencies: - class-validator: 0.14.2 + class-validator: 0.14.3 type-is@1.6.18: dependencies: @@ -36776,7 +36942,7 @@ snapshots: dependencies: content-type: 1.0.5 media-typer: 1.1.0 - mime-types: 3.0.1 + mime-types: 3.0.2 typed-array-buffer@1.0.3: dependencies: @@ -36873,6 +37039,8 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} + unicode-segmenter@0.14.4: {} + unicode-trie@2.0.0: dependencies: pako: 0.2.9 @@ -37007,7 +37175,7 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - unstorage@1.17.2(@upstash/redis@1.35.6)(idb-keyval@6.2.2)(ioredis@5.8.2): + unstorage@1.17.3(@upstash/redis@1.35.8)(idb-keyval@6.2.2)(ioredis@5.8.2): dependencies: anymatch: 3.1.3 chokidar: 4.0.3 @@ -37018,15 +37186,15 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.1 optionalDependencies: - '@upstash/redis': 1.35.6 + '@upstash/redis': 1.35.8 idb-keyval: 6.2.2 ioredis: 5.8.2 untruncate-json@0.0.1: {} - update-browserslist-db@1.1.4(browserslist@4.28.0): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.28.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -37161,7 +37329,7 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 - validator@13.15.23: {} + validator@13.15.26: {} valtio@1.13.2(@types/react@18.3.1)(react@18.3.1): dependencies: @@ -37207,16 +37375,16 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - viem@2.23.2(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.23.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 abitype: 1.0.8(typescript@5.5.4)(zod@3.25.76) - isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + isows: 1.0.6(ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) ox: 0.6.7(typescript@5.5.4)(zod@3.25.76) - ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -37224,16 +37392,16 @@ snapshots: - utf-8-validate - zod - viem@2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): + viem@2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.5.4)(zod@3.22.4) - isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.5.4)(zod@3.22.4) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.2.3(typescript@5.5.4)(zod@3.22.4) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.11.1(typescript@5.5.4)(zod@3.22.4) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -37241,16 +37409,16 @@ snapshots: - utf-8-validate - zod - viem@2.39.3(bufferutil@4.0.9)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.5.4)(zod@3.25.76) - isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.6(typescript@5.5.4)(zod@3.25.76) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.2.3(typescript@5.5.4)(zod@3.25.76) + isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + ox: 0.11.1(typescript@5.5.4)(zod@3.25.76) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -37258,13 +37426,13 @@ snapshots: - utf-8-validate - zod - vite-node@3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1): + vite-node@3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3(supports-color@5.5.0) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -37279,38 +37447,38 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)): dependencies: debug: 4.4.3(supports-color@5.5.0) globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.5.4) optionalDependencies: - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - supports-color - typescript - vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1): + vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.53.3 + rollup: 4.54.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 18.16.9 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 - sass: 1.94.1 + sass: 1.97.1 terser: 5.44.1 - yaml: 2.8.1 + yaml: 2.8.2 - vitest@3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1): + vitest@3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2): dependencies: '@vitest/expect': 3.1.4 - '@vitest/mocker': 3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1)) + '@vitest/mocker': 3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.1.4 '@vitest/snapshot': 3.1.4 @@ -37318,7 +37486,7 @@ snapshots: '@vitest/utils': 3.1.4 chai: 5.3.3 debug: 4.4.3(supports-color@5.5.0) - expect-type: 1.2.2 + expect-type: 1.3.0 magic-string: 0.30.21 pathe: 2.0.3 std-env: 3.10.0 @@ -37327,15 +37495,15 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) - vite-node: 3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.94.1)(terser@5.44.1)(yaml@2.8.1) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vite-node: 3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 18.16.9 '@vitest/ui': 1.6.0(vitest@3.1.4) happy-dom: 15.11.7 - jsdom: 22.1.0(bufferutil@4.0.9)(canvas@2.11.2)(utf-8-validate@5.0.10) + jsdom: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) transitivePeerDependencies: - jiti - less @@ -37377,9 +37545,9 @@ snapshots: dependencies: defaults: 1.0.4 - weaviate-client@3.9.0: + weaviate-client@3.10.0: dependencies: - abort-controller-x: 0.4.3 + abort-controller-x: 0.5.0 graphql: 16.12.0 graphql-request: 6.1.0(graphql@16.12.0) long: 5.3.2 @@ -37419,9 +37587,9 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-assertions: 1.9.0(acorn@8.15.0) - browserslist: 4.28.0 + browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.3 + enhanced-resolve: 5.18.4 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -37433,7 +37601,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 3.3.0 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + terser-webpack-plugin: 5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -37572,31 +37740,24 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@6.2.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - async-limiter: 1.0.1 + ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: - bufferutil: 4.0.9 - utf-8-validate: 5.0.10 - - ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.0.9 + bufferutil: 4.1.0 utf-8-validate: 5.0.10 wsl-utils@0.1.0: @@ -37620,7 +37781,7 @@ snapshots: xmlserializer@0.6.1: {} - xstate@5.24.0: {} + xstate@5.25.0: {} xtend@4.0.2: {} @@ -37638,7 +37799,7 @@ snapshots: yaml@1.10.2: {} - yaml@2.8.1: {} + yaml@2.8.2: {} yargs-parser@18.1.3: dependencies: @@ -37694,7 +37855,7 @@ snapshots: zod-from-json-schema@0.5.2: dependencies: - zod: 4.1.12 + zod: 4.2.1 zod-to-json-schema@3.25.0(zod@3.25.76): dependencies: @@ -37704,9 +37865,9 @@ snapshots: zod@3.25.76: {} - zod@4.1.12: {} + zod@4.2.1: {} - zustand@5.0.8(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): + zustand@5.0.9(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): optionalDependencies: '@types/react': 18.3.1 immer: 9.0.21 From 8d0b4a125a9786588a673df7abd15d5a41392ee5 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 17:51:27 +0700 Subject: [PATCH 045/340] feat: upgrade old packages --- package.json | 4 +- pnpm-lock.yaml | 813 +++++++------------------------------------------ 2 files changed, 112 insertions(+), 705 deletions(-) diff --git a/package.json b/package.json index 13dd99f1..d03c8c89 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,8 @@ "@nestjs/schedule": "^4.0.0", "@nestjs/swagger": "^7.3.0", "@nestjs/throttler": "^6.3.0", - "@neynar/nodejs-sdk": "^2.8.1", - "@neynar/react": "^0.9.7", + "@neynar/nodejs-sdk": "^3.112.0", + "@neynar/react": "^1.2.22", "@postiz/wallets": "^0.0.1", "@prisma/client": "6.5.0", "@sentry/nestjs": "^10.26.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f28c958..150c387f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,7 +11,7 @@ importers: dependencies: '@ag-ui/mastra': specifier: 0.2.0 - version: 0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(7d9d10c1aa1e359346c98e211cce0cbe))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) + version: 0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@copilotkit/runtime@1.10.6(c9e743140f0883eda50bf44e0628c06c))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@ai-sdk/openai': specifier: ^2.0.52 version: 2.0.88(zod@3.25.76) @@ -38,7 +38,7 @@ importers: version: 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@copilotkit/runtime': specifier: 1.10.6 - version: 1.10.6(7d9d10c1aa1e359346c98e211cce0cbe) + version: 1.10.6(c9e743140f0883eda50bf44e0628c06c) '@hookform/resolvers': specifier: ^3.3.4 version: 3.10.0(react-hook-form@7.69.0(react@18.3.1)) @@ -103,11 +103,11 @@ importers: specifier: ^6.3.0 version: 6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14) '@neynar/nodejs-sdk': - specifier: ^2.8.1 - version: 2.46.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + specifier: ^3.112.0 + version: 3.112.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@neynar/react': - specifier: ^0.9.7 - version: 0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1)) + specifier: ^1.2.22 + version: 1.2.22(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(swr@2.3.8(react@18.3.1))(typescript@5.5.4) '@postiz/wallets': specifier: ^0.0.1 version: 0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -2394,6 +2394,17 @@ packages: '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} + '@farcaster/miniapp-core@0.4.1': + resolution: {integrity: sha512-20FxHTRToYUKx7CQ8PvIy9OoQ6XjdmF1pRMS7dsj37qdqjVDeEkYoK8yXwnoReZoJRcYwIg8P3i6V8bTWNR5mg==} + + '@farcaster/miniapp-sdk@0.2.1': + resolution: {integrity: sha512-2SnDeOtDdlN1lGQt7UyH2jkrZRQDOkmhcrlzNWazYChyPh9XfV8+9fMS+Lr/E2pEws9Q40Xl9POrCzdpUC19lg==} + + '@farcaster/quick-auth@0.0.6': + resolution: {integrity: sha512-tiZndhpfDtEhaKlkmS5cVDuS+A/tafqZT3y9I44rC69m3beJok6e8dIH2JhxVy3EvOWTyTBnrmNn6GOOh+qK6A==} + peerDependencies: + typescript: 5.8.3 + '@fastify/busboy@3.2.0': resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} @@ -3498,16 +3509,6 @@ packages: '@types/react': optional: true - '@mui/private-theming@6.4.9': - resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@mui/styled-engine@5.18.0': resolution: {integrity: sha512-BN/vKV/O6uaQh2z5rXV+MBlVrEkwoS/TK75rFQ2mjxA7+NBo8qtTAOA4UaM0XeJfn7kh2wZ+xQw2HAx0u+TiBg==} engines: {node: '>=12.0.0'} @@ -3521,19 +3522,6 @@ packages: '@emotion/styled': optional: true - '@mui/styled-engine@6.5.0': - resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@mui/system@5.18.0': resolution: {integrity: sha512-ojZGVcRWqWhu557cdO3pWHloIGJdzVtxs3rk0F9L+x55LsUjcMUVkEhiF7E4TMxZoF9MmIHGGs0ZX3FDLAf0Xw==} engines: {node: '>=12.0.0'} @@ -3550,22 +3538,6 @@ packages: '@types/react': optional: true - '@mui/system@6.5.0': - resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true - '@mui/types@7.2.24': resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} peerDependencies: @@ -3584,16 +3556,6 @@ packages: '@types/react': optional: true - '@mui/utils@6.4.9': - resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true - '@napi-rs/nice-android-arm-eabi@1.1.1': resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} engines: {node: '>= 10'} @@ -3890,135 +3852,77 @@ packages: '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 reflect-metadata: ^0.1.13 || ^0.2.0 - '@next/env@14.2.16': - resolution: {integrity: sha512-fLrX5TfJzHCbnZ9YUSnGW63tMV3L4nSfhgOQ0iCcX21Pt+VSTDuaLsSuL8J/2XAiVA5AnzvXDpf6pMs60QxOag==} - '@next/env@14.2.35': resolution: {integrity: sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==} '@next/eslint-plugin-next@15.2.1': resolution: {integrity: sha512-6ppeToFd02z38SllzWxayLxjjNfzvc7Wm07gQOKSLjyASvKcXjNStZrLXMHuaWkhjqxe+cnhb2uzfWXm1VEj/Q==} - '@next/swc-darwin-arm64@14.2.16': - resolution: {integrity: sha512-uFT34QojYkf0+nn6MEZ4gIWQ5aqGF11uIZ1HSxG+cSbj+Mg3+tYm8qXYd3dKN5jqKUm5rBVvf1PBRO/MeQ6rxw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - '@next/swc-darwin-arm64@14.2.33': resolution: {integrity: sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.16': - resolution: {integrity: sha512-mCecsFkYezem0QiZlg2bau3Xul77VxUD38b/auAjohMA22G9KTJneUYMv78vWoCCFkleFAhY1NIvbyjj1ncG9g==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - '@next/swc-darwin-x64@14.2.33': resolution: {integrity: sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.16': - resolution: {integrity: sha512-yhkNA36+ECTC91KSyZcgWgKrYIyDnXZj8PqtJ+c2pMvj45xf7y/HrgI17hLdrcYamLfVt7pBaJUMxADtPaczHA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - '@next/swc-linux-arm64-gnu@14.2.33': resolution: {integrity: sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.16': - resolution: {integrity: sha512-X2YSyu5RMys8R2lA0yLMCOCtqFOoLxrq2YbazFvcPOE4i/isubYjkh+JCpRmqYfEuCVltvlo+oGfj/b5T2pKUA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - '@next/swc-linux-arm64-musl@14.2.33': resolution: {integrity: sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.16': - resolution: {integrity: sha512-9AGcX7VAkGbc5zTSa+bjQ757tkjr6C/pKS7OK8cX7QEiK6MHIIezBLcQ7gQqbDW2k5yaqba2aDtaBeyyZh1i6Q==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - '@next/swc-linux-x64-gnu@14.2.33': resolution: {integrity: sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.16': - resolution: {integrity: sha512-Klgeagrdun4WWDaOizdbtIIm8khUDQJ/5cRzdpXHfkbY91LxBXeejL4kbZBrpR/nmgRrQvmz4l3OtttNVkz2Sg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - '@next/swc-linux-x64-musl@14.2.33': resolution: {integrity: sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.16': - resolution: {integrity: sha512-PwW8A1UC1Y0xIm83G3yFGPiOBftJK4zukTmk7DI1CebyMOoaVpd8aSy7K6GhobzhkjYvqS/QmzcfsWG2Dwizdg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - '@next/swc-win32-arm64-msvc@14.2.33': resolution: {integrity: sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.16': - resolution: {integrity: sha512-jhPl3nN0oKEshJBNDAo0etGMzv0j3q3VYorTSFqH1o3rwv1MQRdor27u1zhkgsHPNeY1jxcgyx1ZsCkDD1IHgg==} - engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - '@next/swc-win32-ia32-msvc@14.2.33': resolution: {integrity: sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.16': - resolution: {integrity: sha512-OA7NtfxgirCjfqt+02BqxC3MIgM/JaGjw9tOe4fyZgPsqfseNiMPnCRP44Pfs+Gpo9zPN+SXaFsgP6vk8d571A==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - '@next/swc-win32-x64-msvc@14.2.33': resolution: {integrity: sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@neynar/nodejs-sdk@2.46.0': - resolution: {integrity: sha512-9VVdC23CZfUW5SJDhxmoB2kkIxAOEaxsEX5FXX5GRFqpspVlLFmjX28d8kd0LRbbpCrC3a0gc91qFJ1m/6phuA==} + '@neynar/nodejs-sdk@3.112.0': + resolution: {integrity: sha512-kBCCs9UXw/K0qd7HBmhgIt13QK28lrFHseFX5RjIXbygd+8CGue/1ORTzFgkOHUbcEzImLfD5YTXvbD1OJAfnw==} engines: {node: '>=19.9.0'} - '@neynar/react@0.9.7': - resolution: {integrity: sha512-y2P6rJ16vGB5iaIRCeEVTvwuOEJ5HNZ3uO3Ks4skSFCMLquFqx9fo4zPD0sgL6scQuhrtHXW0kmZYWvh3m1pQQ==} + '@neynar/react@1.2.22': + resolution: {integrity: sha512-HZWN1CvHQ8Hjg42hKZSM6JlnHG49f6UfAyCjTd2qGieAKvmrVOkQXJb48s48T1TmAi2ujMLvVadgst03VTkH7g==} peerDependencies: - '@pigment-css/react': ^0.0.9 - hls.js: ^1.5.13 - react: ^18.3.0 - react-dom: ^18.3.0 - swr: ^2.2.5 + '@farcaster/miniapp-sdk': '>=0.1.6 <1.0.0' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + swr: ^2.3.2 '@ngraveio/bc-ur@1.1.13': resolution: {integrity: sha512-j73akJMV4+vLR2yQ4AphPIT5HZmxVjn/LxpL7YHoINnXoH6ccc90Zzck6/n6a3bCXOVZwBxq+YHwbAKRV+P8Zg==} @@ -4867,11 +4771,6 @@ packages: '@solana/web3.js': ^1.50.1 bs58: ^4.0.1 - '@pigment-css/react@0.0.9': - resolution: {integrity: sha512-mGiZ+Dvy01+HJMQKdtDJWn+ZGuq22o4Drh9Q5o3lVOU+GPrsR6LEWW8HQZ63WAWpSPsXtXRsWyudgIVO+oHQmQ==} - peerDependencies: - react: ^17.0.0 || ^18.0.0 - '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -6634,57 +6533,6 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@storybook/addons@7.6.17': - resolution: {integrity: sha512-Ok18Y698Ccyg++MoUNJNHY0cXUvo8ETFIRLJk1g9ElJ70j6kPgNnzW2pAtZkBNmswHtofZ7pT156cj96k/LgfA==} - - '@storybook/channels@7.6.17': - resolution: {integrity: sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==} - - '@storybook/channels@7.6.21': - resolution: {integrity: sha512-899XbW60IXIkWDo90bS5ovjxnFUDgD8B2ZwUEJUmuhIXqQeSg2iJ8uYI699Csei+DoDn5gZYJD+BHbSUuc4g+Q==} - - '@storybook/client-logger@7.6.17': - resolution: {integrity: sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==} - - '@storybook/client-logger@7.6.21': - resolution: {integrity: sha512-NWh32K+N6htmmPfqSPOlA6gy80vFQZLnusK8+/7Hp0sSG//OV5ahlnlSveLUOub2e97CU5EvYUL1xNmSuqk2jQ==} - - '@storybook/core-events@7.6.17': - resolution: {integrity: sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==} - - '@storybook/core-events@7.6.21': - resolution: {integrity: sha512-Ez6bhYuXbEkHVCmnNB/oqN0sQwphsmtPmjYdPMlTtEpVEIXHAw2qOlaDiGakoDHkgrTaxiYvdJrPH0UcEJcWDQ==} - - '@storybook/csf@0.1.13': - resolution: {integrity: sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==} - - '@storybook/global@5.0.0': - resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} - - '@storybook/manager-api@7.6.17': - resolution: {integrity: sha512-IJIV1Yc6yw1dhCY4tReHCfBnUKDqEBnMyHp3mbXpsaHxnxJZrXO45WjRAZIKlQKhl/Ge1CrnznmHRCmYgqmrWg==} - - '@storybook/preview-api@7.6.17': - resolution: {integrity: sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==} - - '@storybook/preview-api@7.6.21': - resolution: {integrity: sha512-L5e6VjphfsnJk/kkOIRJzDaTfX5sNpiusocqEbHKTM7c9ZDAuaLPZKluP87AJ0u16UdWMuCu6YaQ6eAakDa9gg==} - - '@storybook/router@7.6.17': - resolution: {integrity: sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==} - - '@storybook/theming@7.6.17': - resolution: {integrity: sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - - '@storybook/types@7.6.17': - resolution: {integrity: sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==} - - '@storybook/types@7.6.21': - resolution: {integrity: sha512-rJaBMxzXZOsJpqZGhebFJxOguZQBw5j+MVpqbFBA6vLZPx9wEbDBeVsPUxCxj+V1XkVcrNXf9qfThyJ8ETmLBw==} - '@stripe/react-stripe-js@5.4.1': resolution: {integrity: sha512-ipeYcAHa4EPmjwfv0lFE+YDVkOQ0TMKkFWamW+BqmnSkEln/hO8rmxGPPWcd9WjqABx6Ro8Xg4pAS7evCcR9cw==} peerDependencies: @@ -8264,18 +8112,6 @@ packages: resolution: {integrity: sha512-QxI+HQfJeI/UscFNCTcSri6nrHP25mtyAMbhEri7W2ctdb3EsorPuJz7IovSgNjvKVs73dg9Fmayewx1O2xOxA==} engines: {node: '>=18.0.0'} - '@wyw-in-js/processor-utils@0.5.5': - resolution: {integrity: sha512-L3IcAfoowhM0fw9Cnv2CNzfjWNLKpYl2CFqam6NvwpiXNR1kXz/GpO0AOiKvCs5h4Ps5kWxE2e8knXLpk8q/2g==} - engines: {node: '>=16.0.0'} - - '@wyw-in-js/shared@0.5.5': - resolution: {integrity: sha512-Wnvp3RGfynHk81lrp/0fA+Yv5yuIr2Ej13N3lawQeqbK4KlMag3P9npyIljGrEiwK2Bv4byHuXsJFgLI0Fo8bw==} - engines: {node: '>=16.0.0'} - - '@wyw-in-js/transform@0.5.5': - resolution: {integrity: sha512-XMZjhS8poHpxfPg41rkc6eh3Mr2BZAFM7OzYN4jPZUicpJKv7uQAU2dLEqnyDcDllo04LbZIryb2fXwpr+pqPw==} - engines: {node: '>=16.0.0'} - '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -8658,12 +8494,6 @@ packages: peerDependencies: '@babel/core': ^7.8.0 - babel-merge@3.0.0: - resolution: {integrity: sha512-eBOBtHnzt9xvnjpYNI5HmaPp/b2vMveE5XggzqHnQeHJ8mFIBrBv6WZEVIj5jJ2uwTItkqKo9gWzEEcBxEq0yw==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - peerDependencies: - '@babel/core': ^7.0.0 - babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} @@ -9210,6 +9040,9 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + comlink@4.4.2: + resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} + comma-separated-tokens@1.0.8: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} @@ -9503,10 +9336,6 @@ packages: engines: {node: '>=4'} hasBin: true - cssjanus@2.3.0: - resolution: {integrity: sha512-ZZXXn51SnxRxAZ6fdY7mBDPmA4OZd83q/J9Gdqz3YmE9TUq+9tZl+tdOnCi7PpNygI6PEkehj9rgifv5+W8a5A==} - engines: {node: '>=10.0.0'} - csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -9675,10 +9504,6 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - deepmerge@2.2.1: - resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} - engines: {node: '>=0.10.0'} - deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -10489,9 +10314,6 @@ packages: resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==} engines: {node: '>= 12'} - file-system-cache@2.3.0: - resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==} - file-type@16.5.4: resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} engines: {node: '>=10'} @@ -10671,10 +10493,6 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} - fs-extra@11.1.1: - resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} - engines: {node: '>=14.14'} - fs-extra@11.3.2: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} @@ -11475,10 +11293,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true - is-extendable@1.0.1: - resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} - engines: {node: '>=0.10.0'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -11567,10 +11381,6 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} - is-plain-object@2.0.4: - resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} - engines: {node: '>=0.10.0'} - is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -11673,10 +11483,6 @@ packages: iso-datestring-validator@2.2.2: resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} - isobject@3.0.1: - resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} - engines: {node: '>=0.10.0'} - isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: @@ -12535,9 +12341,6 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} - map-or-similar@1.5.0: - resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} - markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true @@ -12658,9 +12461,6 @@ packages: memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - memoizerific@1.11.3: - resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} - mensch@0.3.4: resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} @@ -12995,6 +12795,14 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mipd@0.0.7: + resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + mixwith@0.1.1: resolution: {integrity: sha512-DQsf/liljH/9e+94jR+xfK8vlKceeKdOM9H9UEXLwGuvEEpO6debNtJ9yt1ZKzPKPrwqGxzMdu0BR1fnQb6i4A==} @@ -13150,25 +12958,6 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - next@14.2.16: - resolution: {integrity: sha512-LcO7WnFu6lYSvCzZoo1dB+IO0xXz5uEv52HF1IUN0IqVTUIZGHuuR10I5efiLadGt+4oZqTcNZyVVEem/TM5nA==} - engines: {node: '>=18.17.0'} - deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details. - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - sass: - optional: true - next@14.2.35: resolution: {integrity: sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==} engines: {node: '>=18.17.0'} @@ -13377,10 +13166,6 @@ packages: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} - object.omit@3.0.0: - resolution: {integrity: sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==} - engines: {node: '>=0.10.0'} - object.values@1.2.1: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} @@ -13499,6 +13284,14 @@ packages: typescript: optional: true + ox@0.4.4: + resolution: {integrity: sha512-oJPEeCDs9iNiPs6J0rTx+Y0KGeCGyCAA3zo94yZhm8G5WpOxrwUtn2Ie/Y8IyARSqqY/j9JTKA3Fc1xs1DvFnw==} + peerDependencies: + typescript: '>=5.4.0' + peerDependenciesMeta: + typescript: + optional: true + ox@0.6.7: resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} peerDependencies: @@ -14248,9 +14041,6 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} - ramda@0.29.0: - resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} - randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -14325,11 +14115,6 @@ packages: peerDependencies: react: '>= 16.8 || 18.0.0' - react-error-boundary@4.1.2: - resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==} - peerDependencies: - react: '>=16.13.1' - react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} @@ -15341,21 +15126,6 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - store2@2.14.4: - resolution: {integrity: sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==} - - storybook-source-link@4.0.1: - resolution: {integrity: sha512-eafQgCxZ5/0ZIumT/3W/LVhFgl3Z/OeBxRkmOKkLJx9F1gpZmwNhT04IvgcSScc810ovKK1IdZ1pogkefi1s/A==} - peerDependencies: - '@storybook/addons': '*' - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - react: - optional: true - react-dom: - optional: true - stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} @@ -15510,17 +15280,9 @@ packages: babel-plugin-macros: optional: true - stylis-plugin-rtl@2.1.1: - resolution: {integrity: sha512-q6xIkri6fBufIO/sV55md2CbgS5c6gg9EhSVATtHHCdOnbN/jcI0u3lYhNVeuI65c4lQPo67g8xmq5jrREvzlg==} - peerDependencies: - stylis: 4.x - stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - stylis@4.3.6: - resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} - subtitle@4.2.2-alpha.0: resolution: {integrity: sha512-IMS+L8lXjOLveg5BC/bVZy+36/x2NqMIQmVDhbquDpxLnXugzmz7/yHHFZ7b9YLfqNaBdXwh1lsnAds3g1FnCQ==} engines: {node: '>=10'} @@ -15579,9 +15341,6 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synchronous-promise@2.0.17: - resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} - tabbable@6.3.0: resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} @@ -15616,9 +15375,6 @@ packages: tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} - telejson@7.2.0: - resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} - terser-webpack-plugin@5.3.16: resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} @@ -15688,9 +15444,6 @@ packages: tiny-invariant@1.2.0: resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==} - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} @@ -15825,10 +15578,6 @@ packages: peerDependencies: typescript: '>=4.2.0' - ts-dedent@2.2.0: - resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} - engines: {node: '>=6.10'} - ts-error@1.0.6: resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==} @@ -15851,10 +15600,6 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-invariant@0.10.3: - resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} - engines: {node: '>=8'} - ts-jest@29.4.6: resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -16971,10 +16716,10 @@ snapshots: '@ag-ui/core': 0.0.42 '@ag-ui/proto': 0.0.42 - '@ag-ui/langgraph@0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ag-ui/langgraph@0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ag-ui/client': 0.0.42 - '@ag-ui/core': 0.0.37 + '@ag-ui/core': 0.0.42 '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) partial-json: 0.1.7 @@ -16987,12 +16732,12 @@ snapshots: - react - react-dom - '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(7d9d10c1aa1e359346c98e211cce0cbe))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': + '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@copilotkit/runtime@1.10.6(c9e743140f0883eda50bf44e0628c06c))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': dependencies: '@ag-ui/client': 0.0.42 - '@ag-ui/core': 0.0.37 + '@ag-ui/core': 0.0.42 '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) - '@copilotkit/runtime': 1.10.6(7d9d10c1aa1e359346c98e211cce0cbe) + '@copilotkit/runtime': 1.10.6(c9e743140f0883eda50bf44e0628c06c) '@mastra/client-js': 0.15.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) rxjs: 7.8.1 @@ -18959,12 +18704,12 @@ snapshots: - encoding - graphql - '@copilotkit/runtime@1.10.6(7d9d10c1aa1e359346c98e211cce0cbe)': + '@copilotkit/runtime@1.10.6(c9e743140f0883eda50bf44e0628c06c)': dependencies: '@ag-ui/client': 0.0.42 - '@ag-ui/core': 0.0.37 + '@ag-ui/core': 0.0.42 '@ag-ui/encoder': 0.0.42 - '@ag-ui/langgraph': 0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ag-ui/langgraph': 0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ag-ui/proto': 0.0.42 '@anthropic-ai/sdk': 0.57.0 '@copilotkit/shared': 1.10.6 @@ -19532,6 +19277,37 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} + '@farcaster/miniapp-core@0.4.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': + dependencies: + '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + ox: 0.4.4(typescript@5.5.4)(zod@3.25.76) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + + '@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + dependencies: + '@farcaster/miniapp-core': 0.4.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@farcaster/quick-auth': 0.0.6(typescript@5.5.4) + comlink: 4.4.2 + eventemitter3: 5.0.1 + ox: 0.4.4(typescript@5.5.4)(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - encoding + - typescript + - utf-8-validate + - zod + + '@farcaster/quick-auth@0.0.6(typescript@5.5.4)': + dependencies: + jose: 5.10.0 + typescript: 5.5.4 + zod: 3.25.76 + '@fastify/busboy@3.2.0': {} '@floating-ui/core@1.7.3': @@ -20738,15 +20514,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@mui/private-theming@6.4.9(@types/react@18.3.1)(react@18.3.1)': - dependencies: - '@babel/runtime': 7.28.4 - '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) - prop-types: 15.8.1 - react: 18.3.1 - optionalDependencies: - '@types/react': 18.3.1 - '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.4 @@ -20759,19 +20526,6 @@ snapshots: '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) - '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)': - dependencies: - '@babel/runtime': 7.28.4 - '@emotion/cache': 11.14.0 - '@emotion/serialize': 1.3.3 - '@emotion/sheet': 1.4.0 - csstype: 3.2.3 - prop-types: 15.8.1 - react: 18.3.1 - optionalDependencies: - '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) - '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': dependencies: '@babel/runtime': 7.28.4 @@ -20788,22 +20542,6 @@ snapshots: '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) '@types/react': 18.3.1 - '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': - dependencies: - '@babel/runtime': 7.28.4 - '@mui/private-theming': 6.4.9(@types/react@18.3.1)(react@18.3.1) - '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1) - '@mui/types': 7.2.24(@types/react@18.3.1) - '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) - clsx: 2.1.1 - csstype: 3.2.3 - prop-types: 15.8.1 - react: 18.3.1 - optionalDependencies: - '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) - '@types/react': 18.3.1 - '@mui/types@7.2.24(@types/react@18.3.1)': optionalDependencies: '@types/react': 18.3.1 @@ -20820,18 +20558,6 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@mui/utils@6.4.9(@types/react@18.3.1)(react@18.3.1)': - dependencies: - '@babel/runtime': 7.28.4 - '@mui/types': 7.2.24(@types/react@18.3.1) - '@types/prop-types': 15.7.15 - clsx: 2.1.1 - prop-types: 15.8.1 - react: 18.3.1 - react-is: 19.2.3 - optionalDependencies: - '@types/react': 18.3.1 - '@napi-rs/nice-android-arm-eabi@1.1.1': optional: true @@ -21104,71 +20830,43 @@ snapshots: '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 - '@next/env@14.2.16': {} - '@next/env@14.2.35': {} '@next/eslint-plugin-next@15.2.1': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@14.2.16': - optional: true - '@next/swc-darwin-arm64@14.2.33': optional: true - '@next/swc-darwin-x64@14.2.16': - optional: true - '@next/swc-darwin-x64@14.2.33': optional: true - '@next/swc-linux-arm64-gnu@14.2.16': - optional: true - '@next/swc-linux-arm64-gnu@14.2.33': optional: true - '@next/swc-linux-arm64-musl@14.2.16': - optional: true - '@next/swc-linux-arm64-musl@14.2.33': optional: true - '@next/swc-linux-x64-gnu@14.2.16': - optional: true - '@next/swc-linux-x64-gnu@14.2.33': optional: true - '@next/swc-linux-x64-musl@14.2.16': - optional: true - '@next/swc-linux-x64-musl@14.2.33': optional: true - '@next/swc-win32-arm64-msvc@14.2.16': - optional: true - '@next/swc-win32-arm64-msvc@14.2.33': optional: true - '@next/swc-win32-ia32-msvc@14.2.16': - optional: true - '@next/swc-win32-ia32-msvc@14.2.33': optional: true - '@next/swc-win32-x64-msvc@14.2.16': - optional: true - '@next/swc-win32-x64-msvc@14.2.33': optional: true - '@neynar/nodejs-sdk@2.46.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@neynar/nodejs-sdk@3.112.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@openapitools/openapi-generator-cli': 2.25.2(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.3) + axios: 1.13.2(debug@4.4.3) semver: 7.7.3 viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -21186,22 +20884,16 @@ snapshots: - utf-8-validate - zod - '@neynar/react@0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1))': + '@neynar/react@1.2.22(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(swr@2.3.8(react@18.3.1))(typescript@5.5.4)': dependencies: - '@pigment-css/react': 0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) + '@farcaster/miniapp-sdk': 0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) hls.js: 1.6.15 - next: 14.2.16(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) + mipd: 0.0.7(typescript@5.5.4) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook-source-link: 4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) swr: 2.3.8(react@18.3.1) transitivePeerDependencies: - - '@babel/core' - - '@opentelemetry/api' - - '@playwright/test' - - '@storybook/addons' - - babel-plugin-macros - - sass + - typescript '@ngraveio/bc-ur@1.1.13': dependencies: @@ -22328,35 +22020,6 @@ snapshots: '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 6.0.0 - '@pigment-css/react@0.0.9(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 - '@emotion/css': 11.13.5 - '@emotion/is-prop-valid': 1.4.0 - '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) - '@emotion/serialize': 1.3.3 - '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) - '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) - '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) - '@wyw-in-js/processor-utils': 0.5.5 - '@wyw-in-js/shared': 0.5.5 - '@wyw-in-js/transform': 0.5.5(typescript@5.5.4) - clsx: 2.1.1 - cssesc: 3.0.0 - csstype: 3.2.3 - lodash: 4.17.21 - react: 18.3.1 - stylis: 4.3.6 - stylis-plugin-rtl: 2.1.1(stylis@4.3.6) - transitivePeerDependencies: - - '@types/react' - - supports-color - - typescript - '@pinojs/redact@0.4.0': {} '@pkgjs/parseargs@0.11.0': @@ -24687,138 +24350,6 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@storybook/manager-api': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/preview-api': 7.6.17 - '@storybook/types': 7.6.17 - transitivePeerDependencies: - - react - - react-dom - - '@storybook/channels@7.6.17': - dependencies: - '@storybook/client-logger': 7.6.17 - '@storybook/core-events': 7.6.17 - '@storybook/global': 5.0.0 - qs: 6.14.0 - telejson: 7.2.0 - tiny-invariant: 1.3.3 - - '@storybook/channels@7.6.21': - dependencies: - '@storybook/client-logger': 7.6.21 - '@storybook/core-events': 7.6.21 - '@storybook/global': 5.0.0 - qs: 6.14.0 - telejson: 7.2.0 - tiny-invariant: 1.3.3 - - '@storybook/client-logger@7.6.17': - dependencies: - '@storybook/global': 5.0.0 - - '@storybook/client-logger@7.6.21': - dependencies: - '@storybook/global': 5.0.0 - - '@storybook/core-events@7.6.17': - dependencies: - ts-dedent: 2.2.0 - - '@storybook/core-events@7.6.21': - dependencies: - ts-dedent: 2.2.0 - - '@storybook/csf@0.1.13': - dependencies: - type-fest: 2.19.0 - - '@storybook/global@5.0.0': {} - - '@storybook/manager-api@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@storybook/channels': 7.6.17 - '@storybook/client-logger': 7.6.17 - '@storybook/core-events': 7.6.17 - '@storybook/csf': 0.1.13 - '@storybook/global': 5.0.0 - '@storybook/router': 7.6.17 - '@storybook/theming': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/types': 7.6.17 - dequal: 2.0.3 - lodash: 4.17.21 - memoizerific: 1.11.3 - store2: 2.14.4 - telejson: 7.2.0 - ts-dedent: 2.2.0 - transitivePeerDependencies: - - react - - react-dom - - '@storybook/preview-api@7.6.17': - dependencies: - '@storybook/channels': 7.6.17 - '@storybook/client-logger': 7.6.17 - '@storybook/core-events': 7.6.17 - '@storybook/csf': 0.1.13 - '@storybook/global': 5.0.0 - '@storybook/types': 7.6.17 - '@types/qs': 6.14.0 - dequal: 2.0.3 - lodash: 4.17.21 - memoizerific: 1.11.3 - qs: 6.14.0 - synchronous-promise: 2.0.17 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - - '@storybook/preview-api@7.6.21': - dependencies: - '@storybook/channels': 7.6.21 - '@storybook/client-logger': 7.6.21 - '@storybook/core-events': 7.6.21 - '@storybook/csf': 0.1.13 - '@storybook/global': 5.0.0 - '@storybook/types': 7.6.21 - '@types/qs': 6.14.0 - dequal: 2.0.3 - lodash: 4.17.21 - memoizerific: 1.11.3 - qs: 6.14.0 - synchronous-promise: 2.0.17 - ts-dedent: 2.2.0 - util-deprecate: 1.0.2 - - '@storybook/router@7.6.17': - dependencies: - '@storybook/client-logger': 7.6.17 - memoizerific: 1.11.3 - qs: 6.14.0 - - '@storybook/theming@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': - dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) - '@storybook/client-logger': 7.6.17 - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - - '@storybook/types@7.6.17': - dependencies: - '@storybook/channels': 7.6.17 - '@types/babel__core': 7.20.5 - '@types/express': 4.17.25 - file-system-cache: 2.3.0 - - '@storybook/types@7.6.21': - dependencies: - '@storybook/channels': 7.6.21 - '@types/babel__core': 7.20.5 - '@types/express': 4.17.25 - file-system-cache: 2.3.0 - '@stripe/react-stripe-js@5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@stripe/stripe-js': 8.6.0 @@ -27132,42 +26663,6 @@ snapshots: '@whatwg-node/promise-helpers': 1.3.2 tslib: 2.8.1 - '@wyw-in-js/processor-utils@0.5.5': - dependencies: - '@babel/generator': 7.28.5 - '@wyw-in-js/shared': 0.5.5 - transitivePeerDependencies: - - supports-color - - '@wyw-in-js/shared@0.5.5': - dependencies: - debug: 4.4.3(supports-color@5.5.0) - find-up: 5.0.0 - minimatch: 9.0.5 - transitivePeerDependencies: - - supports-color - - '@wyw-in-js/transform@0.5.5(typescript@5.5.4)': - dependencies: - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - '@wyw-in-js/processor-utils': 0.5.5 - '@wyw-in-js/shared': 0.5.5 - babel-merge: 3.0.0(@babel/core@7.28.5) - cosmiconfig: 8.3.6(typescript@5.5.4) - happy-dom: 15.11.7 - source-map: 0.7.6 - stylis: 4.3.6 - ts-invariant: 0.10.3 - transitivePeerDependencies: - - supports-color - - typescript - '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -27555,12 +27050,6 @@ snapshots: transitivePeerDependencies: - supports-color - babel-merge@3.0.0(@babel/core@7.28.5): - dependencies: - '@babel/core': 7.28.5 - deepmerge: 2.2.1 - object.omit: 3.0.0 - babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 @@ -28252,6 +27741,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + comlink@4.4.2: {} + comma-separated-tokens@1.0.8: {} comma-separated-tokens@2.0.3: {} @@ -28565,8 +28056,6 @@ snapshots: cssesc@3.0.0: {} - cssjanus@2.3.0: {} - csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -28722,8 +28211,6 @@ snapshots: deep-is@0.1.4: {} - deepmerge@2.2.1: {} - deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -29860,11 +29347,6 @@ snapshots: dependencies: tslib: 2.8.1 - file-system-cache@2.3.0: - dependencies: - fs-extra: 11.1.1 - ramda: 0.29.0 - file-type@16.5.4: dependencies: readable-web-to-node-stream: 3.0.4 @@ -30111,12 +29593,6 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-extra@11.1.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 @@ -30511,6 +29987,7 @@ snapshots: entities: 4.5.0 webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 + optional: true har-schema@2.0.0: {} @@ -31182,10 +30659,6 @@ snapshots: is-docker@3.0.0: {} - is-extendable@1.0.1: - dependencies: - is-plain-object: 2.0.4 - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -31251,10 +30724,6 @@ snapshots: is-plain-obj@4.1.0: {} - is-plain-object@2.0.4: - dependencies: - isobject: 3.0.1 - is-plain-object@5.0.0: {} is-potential-custom-element-name@1.0.1: {} @@ -31342,8 +30811,6 @@ snapshots: iso-datestring-validator@2.2.2: {} - isobject@3.0.1: {} - isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -32430,8 +31897,6 @@ snapshots: dependencies: tmpl: 1.0.5 - map-or-similar@1.5.0: {} - markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -32727,10 +32192,6 @@ snapshots: memoize-one@5.2.1: {} - memoizerific@1.11.3: - dependencies: - map-or-similar: 1.5.0 - mensch@0.3.4: {} merge-descriptors@1.0.3: {} @@ -33336,6 +32797,10 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + mipd@0.0.7(typescript@5.5.4): + optionalDependencies: + typescript: 5.5.4 + mixwith@0.1.1: {} mkdirp@0.5.6: @@ -33487,34 +32952,6 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.16(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1): - dependencies: - '@next/env': 14.2.16 - '@swc/helpers': 0.5.5 - busboy: 1.6.0 - caniuse-lite: 1.0.30001761 - graceful-fs: 4.2.11 - postcss: 8.4.31 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.28.5)(babel-plugin-macros@3.1.0)(react@18.3.1) - optionalDependencies: - '@next/swc-darwin-arm64': 14.2.16 - '@next/swc-darwin-x64': 14.2.16 - '@next/swc-linux-arm64-gnu': 14.2.16 - '@next/swc-linux-arm64-musl': 14.2.16 - '@next/swc-linux-x64-gnu': 14.2.16 - '@next/swc-linux-x64-musl': 14.2.16 - '@next/swc-win32-arm64-msvc': 14.2.16 - '@next/swc-win32-ia32-msvc': 14.2.16 - '@next/swc-win32-x64-msvc': 14.2.16 - '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.57.0 - sass: 1.97.1 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1): dependencies: '@next/env': 14.2.35 @@ -33749,10 +33186,6 @@ snapshots: es-abstract: 1.24.1 es-object-atoms: 1.1.1 - object.omit@3.0.0: - dependencies: - is-extendable: 1.0.1 - object.values@1.2.1: dependencies: call-bind: 1.0.8 @@ -33901,6 +33334,20 @@ snapshots: transitivePeerDependencies: - zod + ox@0.4.4(typescript@5.5.4)(zod@3.25.76): + dependencies: + '@adraffy/ens-normalize': 1.11.1 + '@noble/curves': 1.9.7 + '@noble/hashes': 1.8.0 + '@scure/bip32': 1.7.0 + '@scure/bip39': 1.6.0 + abitype: 1.2.3(typescript@5.5.4)(zod@3.25.76) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - zod + ox@0.6.7(typescript@5.5.4)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -34779,8 +34226,6 @@ snapshots: radix3@1.1.2: {} - ramda@0.29.0: {} - randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -34877,11 +34322,6 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 - react-error-boundary@4.1.2(react@18.3.1): - dependencies: - '@babel/runtime': 7.28.4 - react: 18.3.1 - react-fast-compare@3.2.2: {} react-hook-form@7.69.0(react@18.3.1): @@ -36241,18 +35681,6 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - store2@2.14.4: {} - - storybook-source-link@4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@storybook/addons': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/preview-api': 7.6.21 - '@types/react': 18.3.1 - react-error-boundary: 4.1.2(react@18.3.1) - optionalDependencies: - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - stream-browserify@3.0.0: dependencies: inherits: 2.0.4 @@ -36423,15 +35851,8 @@ snapshots: '@babel/core': 7.28.5 babel-plugin-macros: 3.1.0 - stylis-plugin-rtl@2.1.1(stylis@4.3.6): - dependencies: - cssjanus: 2.3.0 - stylis: 4.3.6 - stylis@4.2.0: {} - stylis@4.3.6: {} - subtitle@4.2.2-alpha.0: dependencies: '@types/multipipe': 3.0.5 @@ -36495,8 +35916,6 @@ snapshots: symbol-tree@3.2.4: {} - synchronous-promise@2.0.17: {} - tabbable@6.3.0: {} tailwind-merge@1.14.0: {} @@ -36554,10 +35973,6 @@ snapshots: transitivePeerDependencies: - supports-color - telejson@7.2.0: - dependencies: - memoizerific: 1.11.3 - terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -36619,8 +36034,6 @@ snapshots: tiny-invariant@1.2.0: {} - tiny-invariant@1.3.3: {} - tiny-warning@1.0.3: {} tinybench@2.9.0: {} @@ -36748,8 +36161,6 @@ snapshots: dependencies: typescript: 5.5.4 - ts-dedent@2.2.0: {} - ts-error@1.0.6: {} ts-essentials@10.1.1(typescript@5.5.4): @@ -36762,10 +36173,6 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-invariant@0.10.3: - dependencies: - tslib: 2.8.1 - ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 From eaec9bf585946b744a76815574d75530ab627f7d Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 18:04:36 +0700 Subject: [PATCH 046/340] feat: neyner fix --- package.json | 1 + pnpm-lock.yaml | 264 ++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 264 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d03c8c89..721942b8 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@nestjs/throttler": "^6.3.0", "@neynar/nodejs-sdk": "^3.112.0", "@neynar/react": "^1.2.22", + "@pigment-css/react": "^0.0.30", "@postiz/wallets": "^0.0.1", "@prisma/client": "6.5.0", "@sentry/nestjs": "^10.26.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 150c387f..6fd7cb86 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -108,6 +108,9 @@ importers: '@neynar/react': specifier: ^1.2.22 version: 1.2.22(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(swr@2.3.8(react@18.3.1))(typescript@5.5.4) + '@pigment-css/react': + specifier: ^0.0.30 + version: 0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) '@postiz/wallets': specifier: ^0.0.1 version: 0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -3509,6 +3512,16 @@ packages: '@types/react': optional: true + '@mui/private-theming@6.4.9': + resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/styled-engine@5.18.0': resolution: {integrity: sha512-BN/vKV/O6uaQh2z5rXV+MBlVrEkwoS/TK75rFQ2mjxA7+NBo8qtTAOA4UaM0XeJfn7kh2wZ+xQw2HAx0u+TiBg==} engines: {node: '>=12.0.0'} @@ -3522,6 +3535,19 @@ packages: '@emotion/styled': optional: true + '@mui/styled-engine@6.5.0': + resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/system@5.18.0': resolution: {integrity: sha512-ojZGVcRWqWhu557cdO3pWHloIGJdzVtxs3rk0F9L+x55LsUjcMUVkEhiF7E4TMxZoF9MmIHGGs0ZX3FDLAf0Xw==} engines: {node: '>=12.0.0'} @@ -3538,6 +3564,22 @@ packages: '@types/react': optional: true + '@mui/system@6.5.0': + resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + '@mui/types@7.2.24': resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==} peerDependencies: @@ -3556,6 +3598,16 @@ packages: '@types/react': optional: true + '@mui/utils@6.4.9': + resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@napi-rs/nice-android-arm-eabi@1.1.1': resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} engines: {node: '>= 10'} @@ -4771,6 +4823,12 @@ packages: '@solana/web3.js': ^1.50.1 bs58: ^4.0.1 + '@pigment-css/react@0.0.30': + resolution: {integrity: sha512-aNvpOgbv+M9+YV2wKk3CIyiiiF+8S6KJJKDKGzhFWOVWeQFZBgTOjBHhL/0SyAnCOVjDg2sSXOEElIdEQywXKQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + '@pinojs/redact@0.4.0': resolution: {integrity: sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==} @@ -8112,6 +8170,18 @@ packages: resolution: {integrity: sha512-QxI+HQfJeI/UscFNCTcSri6nrHP25mtyAMbhEri7W2ctdb3EsorPuJz7IovSgNjvKVs73dg9Fmayewx1O2xOxA==} engines: {node: '>=18.0.0'} + '@wyw-in-js/processor-utils@0.5.5': + resolution: {integrity: sha512-L3IcAfoowhM0fw9Cnv2CNzfjWNLKpYl2CFqam6NvwpiXNR1kXz/GpO0AOiKvCs5h4Ps5kWxE2e8knXLpk8q/2g==} + engines: {node: '>=16.0.0'} + + '@wyw-in-js/shared@0.5.5': + resolution: {integrity: sha512-Wnvp3RGfynHk81lrp/0fA+Yv5yuIr2Ej13N3lawQeqbK4KlMag3P9npyIljGrEiwK2Bv4byHuXsJFgLI0Fo8bw==} + engines: {node: '>=16.0.0'} + + '@wyw-in-js/transform@0.5.5': + resolution: {integrity: sha512-XMZjhS8poHpxfPg41rkc6eh3Mr2BZAFM7OzYN4jPZUicpJKv7uQAU2dLEqnyDcDllo04LbZIryb2fXwpr+pqPw==} + engines: {node: '>=16.0.0'} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -8494,6 +8564,12 @@ packages: peerDependencies: '@babel/core': ^7.8.0 + babel-merge@3.0.0: + resolution: {integrity: sha512-eBOBtHnzt9xvnjpYNI5HmaPp/b2vMveE5XggzqHnQeHJ8mFIBrBv6WZEVIj5jJ2uwTItkqKo9gWzEEcBxEq0yw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + peerDependencies: + '@babel/core': ^7.0.0 + babel-plugin-istanbul@6.1.1: resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} engines: {node: '>=8'} @@ -9336,6 +9412,10 @@ packages: engines: {node: '>=4'} hasBin: true + cssjanus@2.3.0: + resolution: {integrity: sha512-ZZXXn51SnxRxAZ6fdY7mBDPmA4OZd83q/J9Gdqz3YmE9TUq+9tZl+tdOnCi7PpNygI6PEkehj9rgifv5+W8a5A==} + engines: {node: '>=10.0.0'} + csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -9504,6 +9584,10 @@ packages: deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deepmerge@2.2.1: + resolution: {integrity: sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==} + engines: {node: '>=0.10.0'} + deepmerge@4.3.1: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} @@ -11293,6 +11377,10 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} hasBin: true + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -11381,6 +11469,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + is-plain-object@5.0.0: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} @@ -11483,6 +11575,10 @@ packages: iso-datestring-validator@2.2.2: resolution: {integrity: sha512-yLEMkBbLZTlVQqOnQ4FiMujR6T4DEcCb1xizmvXS+OxuhwcbtynoosRzdMA69zZCShCNAbi+gJ71FxZBBXx1SA==} + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + isomorphic-ws@4.0.1: resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} peerDependencies: @@ -13166,6 +13262,10 @@ packages: resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} engines: {node: '>= 0.4'} + object.omit@3.0.0: + resolution: {integrity: sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==} + engines: {node: '>=0.10.0'} + object.values@1.2.1: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} @@ -15280,9 +15380,17 @@ packages: babel-plugin-macros: optional: true + stylis-plugin-rtl@2.1.1: + resolution: {integrity: sha512-q6xIkri6fBufIO/sV55md2CbgS5c6gg9EhSVATtHHCdOnbN/jcI0u3lYhNVeuI65c4lQPo67g8xmq5jrREvzlg==} + peerDependencies: + stylis: 4.x + stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + subtitle@4.2.2-alpha.0: resolution: {integrity: sha512-IMS+L8lXjOLveg5BC/bVZy+36/x2NqMIQmVDhbquDpxLnXugzmz7/yHHFZ7b9YLfqNaBdXwh1lsnAds3g1FnCQ==} engines: {node: '>=10'} @@ -15600,6 +15708,10 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-invariant@0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + ts-jest@29.4.6: resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} @@ -20514,6 +20626,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 + '@mui/private-theming@6.4.9(@types/react@18.3.1)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.1 + '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.28.4 @@ -20526,6 +20647,19 @@ snapshots: '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) + '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) + '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': dependencies: '@babel/runtime': 7.28.4 @@ -20542,6 +20676,22 @@ snapshots: '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) '@types/react': 18.3.1 + '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/private-theming': 6.4.9(@types/react@18.3.1)(react@18.3.1) + '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.24(@types/react@18.3.1) + '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.2.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) + '@types/react': 18.3.1 + '@mui/types@7.2.24(@types/react@18.3.1)': optionalDependencies: '@types/react': 18.3.1 @@ -20558,6 +20708,18 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 + '@mui/utils@6.4.9(@types/react@18.3.1)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.28.4 + '@mui/types': 7.2.24(@types/react@18.3.1) + '@types/prop-types': 15.7.15 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 19.2.3 + optionalDependencies: + '@types/react': 18.3.1 + '@napi-rs/nice-android-arm-eabi@1.1.1': optional: true @@ -22020,6 +22182,36 @@ snapshots: '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 6.0.0 + '@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@emotion/css': 11.13.5 + '@emotion/is-prop-valid': 1.4.0 + '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) + '@emotion/serialize': 1.3.3 + '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) + '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) + '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) + '@wyw-in-js/processor-utils': 0.5.5 + '@wyw-in-js/shared': 0.5.5 + '@wyw-in-js/transform': 0.5.5(typescript@5.5.4) + clsx: 2.1.1 + cssesc: 3.0.0 + csstype: 3.2.3 + lodash: 4.17.21 + prop-types: 15.8.1 + react: 18.3.1 + stylis: 4.3.6 + stylis-plugin-rtl: 2.1.1(stylis@4.3.6) + transitivePeerDependencies: + - '@types/react' + - supports-color + - typescript + '@pinojs/redact@0.4.0': {} '@pkgjs/parseargs@0.11.0': @@ -26663,6 +26855,42 @@ snapshots: '@whatwg-node/promise-helpers': 1.3.2 tslib: 2.8.1 + '@wyw-in-js/processor-utils@0.5.5': + dependencies: + '@babel/generator': 7.28.5 + '@wyw-in-js/shared': 0.5.5 + transitivePeerDependencies: + - supports-color + + '@wyw-in-js/shared@0.5.5': + dependencies: + debug: 4.4.3(supports-color@5.5.0) + find-up: 5.0.0 + minimatch: 9.0.5 + transitivePeerDependencies: + - supports-color + + '@wyw-in-js/transform@0.5.5(typescript@5.5.4)': + dependencies: + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@wyw-in-js/processor-utils': 0.5.5 + '@wyw-in-js/shared': 0.5.5 + babel-merge: 3.0.0(@babel/core@7.28.5) + cosmiconfig: 8.3.6(typescript@5.5.4) + happy-dom: 15.11.7 + source-map: 0.7.6 + stylis: 4.3.6 + ts-invariant: 0.10.3 + transitivePeerDependencies: + - supports-color + - typescript + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -27050,6 +27278,12 @@ snapshots: transitivePeerDependencies: - supports-color + babel-merge@3.0.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + deepmerge: 2.2.1 + object.omit: 3.0.0 + babel-plugin-istanbul@6.1.1: dependencies: '@babel/helper-plugin-utils': 7.27.1 @@ -28056,6 +28290,8 @@ snapshots: cssesc@3.0.0: {} + cssjanus@2.3.0: {} + csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -28211,6 +28447,8 @@ snapshots: deep-is@0.1.4: {} + deepmerge@2.2.1: {} + deepmerge@4.3.1: {} default-browser-id@5.0.1: {} @@ -29987,7 +30225,6 @@ snapshots: entities: 4.5.0 webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 - optional: true har-schema@2.0.0: {} @@ -30659,6 +30896,10 @@ snapshots: is-docker@3.0.0: {} + is-extendable@1.0.1: + dependencies: + is-plain-object: 2.0.4 + is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -30724,6 +30965,10 @@ snapshots: is-plain-obj@4.1.0: {} + is-plain-object@2.0.4: + dependencies: + isobject: 3.0.1 + is-plain-object@5.0.0: {} is-potential-custom-element-name@1.0.1: {} @@ -30811,6 +31056,8 @@ snapshots: iso-datestring-validator@2.2.2: {} + isobject@3.0.1: {} + isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) @@ -33186,6 +33433,10 @@ snapshots: es-abstract: 1.24.1 es-object-atoms: 1.1.1 + object.omit@3.0.0: + dependencies: + is-extendable: 1.0.1 + object.values@1.2.1: dependencies: call-bind: 1.0.8 @@ -35851,8 +36102,15 @@ snapshots: '@babel/core': 7.28.5 babel-plugin-macros: 3.1.0 + stylis-plugin-rtl@2.1.1(stylis@4.3.6): + dependencies: + cssjanus: 2.3.0 + stylis: 4.3.6 + stylis@4.2.0: {} + stylis@4.3.6: {} + subtitle@4.2.2-alpha.0: dependencies: '@types/multipipe': 3.0.5 @@ -36173,6 +36431,10 @@ snapshots: ts-interface-checker@0.1.13: {} + ts-invariant@0.10.3: + dependencies: + tslib: 2.8.1 + ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 From d43690e688029f133fe5419ce2ef55b1888d7ef4 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 18:28:34 +0700 Subject: [PATCH 047/340] feat: force nextjs version --- package.json | 5 +- pnpm-lock.yaml | 397 +++++++++++++++++++++++++++++++++++++------------ 2 files changed, 306 insertions(+), 96 deletions(-) diff --git a/package.json b/package.json index 721942b8..0ad0d907 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "@nestjs/swagger": "^7.3.0", "@nestjs/throttler": "^6.3.0", "@neynar/nodejs-sdk": "^3.112.0", - "@neynar/react": "^1.2.22", + "@neynar/react": "^0.9.7", "@pigment-css/react": "^0.0.30", "@postiz/wallets": "^0.0.1", "@prisma/client": "6.5.0", @@ -310,6 +310,9 @@ "outputName": "junit.xml" }, "pnpm": { + "overrides": { + "next": "14.2.35" + }, "onlyBuiltDependencies": [ "bcrypt" ] diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6fd7cb86..cee5dac1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false injectWorkspacePackages: true +overrides: + next: 14.2.35 + importers: .: @@ -106,8 +109,8 @@ importers: specifier: ^3.112.0 version: 3.112.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@neynar/react': - specifier: ^1.2.22 - version: 1.2.22(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(swr@2.3.8(react@18.3.1))(typescript@5.5.4) + specifier: ^0.9.7 + version: 0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1)) '@pigment-css/react': specifier: ^0.0.30 version: 0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) @@ -2397,17 +2400,6 @@ packages: '@expo/sudo-prompt@9.3.2': resolution: {integrity: sha512-HHQigo3rQWKMDzYDLkubN5WQOYXJJE2eNqIQC2axC2iO3mHdwnIR7FgZVvHWtBwAdzBgAP0ECp8KqS8TiMKvgw==} - '@farcaster/miniapp-core@0.4.1': - resolution: {integrity: sha512-20FxHTRToYUKx7CQ8PvIy9OoQ6XjdmF1pRMS7dsj37qdqjVDeEkYoK8yXwnoReZoJRcYwIg8P3i6V8bTWNR5mg==} - - '@farcaster/miniapp-sdk@0.2.1': - resolution: {integrity: sha512-2SnDeOtDdlN1lGQt7UyH2jkrZRQDOkmhcrlzNWazYChyPh9XfV8+9fMS+Lr/E2pEws9Q40Xl9POrCzdpUC19lg==} - - '@farcaster/quick-auth@0.0.6': - resolution: {integrity: sha512-tiZndhpfDtEhaKlkmS5cVDuS+A/tafqZT3y9I44rC69m3beJok6e8dIH2JhxVy3EvOWTyTBnrmNn6GOOh+qK6A==} - peerDependencies: - typescript: 5.8.3 - '@fastify/busboy@3.2.0': resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} @@ -3968,13 +3960,14 @@ packages: resolution: {integrity: sha512-kBCCs9UXw/K0qd7HBmhgIt13QK28lrFHseFX5RjIXbygd+8CGue/1ORTzFgkOHUbcEzImLfD5YTXvbD1OJAfnw==} engines: {node: '>=19.9.0'} - '@neynar/react@1.2.22': - resolution: {integrity: sha512-HZWN1CvHQ8Hjg42hKZSM6JlnHG49f6UfAyCjTd2qGieAKvmrVOkQXJb48s48T1TmAi2ujMLvVadgst03VTkH7g==} + '@neynar/react@0.9.7': + resolution: {integrity: sha512-y2P6rJ16vGB5iaIRCeEVTvwuOEJ5HNZ3uO3Ks4skSFCMLquFqx9fo4zPD0sgL6scQuhrtHXW0kmZYWvh3m1pQQ==} peerDependencies: - '@farcaster/miniapp-sdk': '>=0.1.6 <1.0.0' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - swr: ^2.3.2 + '@pigment-css/react': ^0.0.9 + hls.js: ^1.5.13 + react: ^18.3.0 + react-dom: ^18.3.0 + swr: ^2.2.5 '@ngraveio/bc-ur@1.1.13': resolution: {integrity: sha512-j73akJMV4+vLR2yQ4AphPIT5HZmxVjn/LxpL7YHoINnXoH6ccc90Zzck6/n6a3bCXOVZwBxq+YHwbAKRV+P8Zg==} @@ -5939,7 +5932,7 @@ packages: resolution: {integrity: sha512-MlgQiKg9P2clKeyH+ZLdmNiMNfTMs/2DBK9V/enLZvYJd1sy5hmrkAV/NiLxVP0uXAeMEVtrgFMIb64cH7ZcXQ==} engines: {node: '>=18'} peerDependencies: - next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0 || ^16.0.0-0 + next: 14.2.35 '@sentry/node-core@10.32.1': resolution: {integrity: sha512-w56rxdBanBKc832zuwnE+zNzUQ19fPxfHEtOhK8JGPu3aSwQYcIxwz9z52lOx3HN7k/8Fj5694qlT3x/PokhRw==} @@ -6591,6 +6584,57 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@storybook/addons@7.6.17': + resolution: {integrity: sha512-Ok18Y698Ccyg++MoUNJNHY0cXUvo8ETFIRLJk1g9ElJ70j6kPgNnzW2pAtZkBNmswHtofZ7pT156cj96k/LgfA==} + + '@storybook/channels@7.6.17': + resolution: {integrity: sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==} + + '@storybook/channels@7.6.21': + resolution: {integrity: sha512-899XbW60IXIkWDo90bS5ovjxnFUDgD8B2ZwUEJUmuhIXqQeSg2iJ8uYI699Csei+DoDn5gZYJD+BHbSUuc4g+Q==} + + '@storybook/client-logger@7.6.17': + resolution: {integrity: sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==} + + '@storybook/client-logger@7.6.21': + resolution: {integrity: sha512-NWh32K+N6htmmPfqSPOlA6gy80vFQZLnusK8+/7Hp0sSG//OV5ahlnlSveLUOub2e97CU5EvYUL1xNmSuqk2jQ==} + + '@storybook/core-events@7.6.17': + resolution: {integrity: sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==} + + '@storybook/core-events@7.6.21': + resolution: {integrity: sha512-Ez6bhYuXbEkHVCmnNB/oqN0sQwphsmtPmjYdPMlTtEpVEIXHAw2qOlaDiGakoDHkgrTaxiYvdJrPH0UcEJcWDQ==} + + '@storybook/csf@0.1.13': + resolution: {integrity: sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==} + + '@storybook/global@5.0.0': + resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} + + '@storybook/manager-api@7.6.17': + resolution: {integrity: sha512-IJIV1Yc6yw1dhCY4tReHCfBnUKDqEBnMyHp3mbXpsaHxnxJZrXO45WjRAZIKlQKhl/Ge1CrnznmHRCmYgqmrWg==} + + '@storybook/preview-api@7.6.17': + resolution: {integrity: sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==} + + '@storybook/preview-api@7.6.21': + resolution: {integrity: sha512-L5e6VjphfsnJk/kkOIRJzDaTfX5sNpiusocqEbHKTM7c9ZDAuaLPZKluP87AJ0u16UdWMuCu6YaQ6eAakDa9gg==} + + '@storybook/router@7.6.17': + resolution: {integrity: sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==} + + '@storybook/theming@7.6.17': + resolution: {integrity: sha512-ZbaBt3KAbmBtfjNqgMY7wPMBshhSJlhodyMNQypv+95xLD/R+Az6aBYbpVAOygLaUQaQk4ar7H/Ww6lFIoiFbA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + + '@storybook/types@7.6.17': + resolution: {integrity: sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==} + + '@storybook/types@7.6.21': + resolution: {integrity: sha512-rJaBMxzXZOsJpqZGhebFJxOguZQBw5j+MVpqbFBA6vLZPx9wEbDBeVsPUxCxj+V1XkVcrNXf9qfThyJ8ETmLBw==} + '@stripe/react-stripe-js@5.4.1': resolution: {integrity: sha512-ipeYcAHa4EPmjwfv0lFE+YDVkOQ0TMKkFWamW+BqmnSkEln/hO8rmxGPPWcd9WjqABx6Ro8Xg4pAS7evCcR9cw==} peerDependencies: @@ -9116,9 +9160,6 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} - comlink@4.4.2: - resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} - comma-separated-tokens@1.0.8: resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} @@ -10398,6 +10439,9 @@ packages: resolution: {integrity: sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==} engines: {node: '>= 12'} + file-system-cache@2.3.0: + resolution: {integrity: sha512-l4DMNdsIPsVnKrgEXbJwDJsA5mB8rGwHYERMgqQx/xAUtChPJMre1bXBzDEqqVbWv9AIbFezXMxeEkZDSrXUOQ==} + file-type@16.5.4: resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} engines: {node: '>=10'} @@ -10577,6 +10621,10 @@ packages: resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} engines: {node: '>=12'} + fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + fs-extra@11.3.2: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} @@ -12437,6 +12485,9 @@ packages: makeerror@1.0.12: resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + map-or-similar@1.5.0: + resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} + markdown-it@14.1.0: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true @@ -12557,6 +12608,9 @@ packages: memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} + memoizerific@1.11.3: + resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} + mensch@0.3.4: resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} @@ -12891,14 +12945,6 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} - mipd@0.0.7: - resolution: {integrity: sha512-aAPZPNDQ3uMTdKbuO2YmAw2TxLHO0moa4YKAyETM/DTj5FloZo+a+8tU+iv4GmW+sOxKLSRwcSFuczk+Cpt6fg==} - peerDependencies: - typescript: '>=5.0.4' - peerDependenciesMeta: - typescript: - optional: true - mixwith@0.1.1: resolution: {integrity: sha512-DQsf/liljH/9e+94jR+xfK8vlKceeKdOM9H9UEXLwGuvEEpO6debNtJ9yt1ZKzPKPrwqGxzMdu0BR1fnQb6i4A==} @@ -13050,7 +13096,7 @@ packages: next-plausible@3.12.5: resolution: {integrity: sha512-l1YMuTI9akb2u7z4hyTuxXpudy8KfSteRNXCYpWpnhAoBjaWQlv6sITai1TwcR7wWvVW8DFbLubvMQAsirAjcA==} peerDependencies: - next: '^11.1.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 ' + next: 14.2.35 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -13384,14 +13430,6 @@ packages: typescript: optional: true - ox@0.4.4: - resolution: {integrity: sha512-oJPEeCDs9iNiPs6J0rTx+Y0KGeCGyCAA3zo94yZhm8G5WpOxrwUtn2Ie/Y8IyARSqqY/j9JTKA3Fc1xs1DvFnw==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - ox@0.6.7: resolution: {integrity: sha512-17Gk/eFsFRAZ80p5eKqv89a57uXjd3NgIf1CaXojATPBuujVc/fQSVhBeAU9JCRB+k7J50WQAyWTxK19T9GgbA==} peerDependencies: @@ -14141,6 +14179,9 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + ramda@0.29.0: + resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} + randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} @@ -14215,6 +14256,11 @@ packages: peerDependencies: react: '>= 16.8 || 18.0.0' + react-error-boundary@4.1.2: + resolution: {integrity: sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==} + peerDependencies: + react: '>=16.13.1' + react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} @@ -15226,6 +15272,21 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} + store2@2.14.4: + resolution: {integrity: sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==} + + storybook-source-link@4.0.1: + resolution: {integrity: sha512-eafQgCxZ5/0ZIumT/3W/LVhFgl3Z/OeBxRkmOKkLJx9F1gpZmwNhT04IvgcSScc810ovKK1IdZ1pogkefi1s/A==} + peerDependencies: + '@storybook/addons': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + react: + optional: true + react-dom: + optional: true + stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} @@ -15449,6 +15510,9 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synchronous-promise@2.0.17: + resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} + tabbable@6.3.0: resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} @@ -15483,6 +15547,9 @@ packages: tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} + telejson@7.2.0: + resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} + terser-webpack-plugin@5.3.16: resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} engines: {node: '>= 10.13.0'} @@ -15552,6 +15619,9 @@ packages: tiny-invariant@1.2.0: resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-warning@1.0.3: resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} @@ -15686,6 +15756,10 @@ packages: peerDependencies: typescript: '>=4.2.0' + ts-dedent@2.2.0: + resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==} + engines: {node: '>=6.10'} + ts-error@1.0.6: resolution: {integrity: sha512-tLJxacIQUM82IR7JO1UUkKlYuUTmoY9HBJAmNWFzheSlDS5SPMcNIepejHJa4BpPQLAcbRhRf3GDJzyj6rbKvA==} @@ -19389,37 +19463,6 @@ snapshots: '@expo/sudo-prompt@9.3.2': {} - '@farcaster/miniapp-core@0.4.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': - dependencies: - '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - ox: 0.4.4(typescript@5.5.4)(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - '@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@farcaster/miniapp-core': 0.4.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@farcaster/quick-auth': 0.0.6(typescript@5.5.4) - comlink: 4.4.2 - eventemitter3: 5.0.1 - ox: 0.4.4(typescript@5.5.4)(zod@3.25.76) - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - zod - - '@farcaster/quick-auth@0.0.6(typescript@5.5.4)': - dependencies: - jose: 5.10.0 - typescript: 5.5.4 - zod: 3.25.76 - '@fastify/busboy@3.2.0': {} '@floating-ui/core@1.7.3': @@ -21046,16 +21089,22 @@ snapshots: - utf-8-validate - zod - '@neynar/react@1.2.22(@farcaster/miniapp-sdk@0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(swr@2.3.8(react@18.3.1))(typescript@5.5.4)': + '@neynar/react@0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1))': dependencies: - '@farcaster/miniapp-sdk': 0.2.1(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@pigment-css/react': 0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) hls.js: 1.6.15 - mipd: 0.0.7(typescript@5.5.4) + next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + storybook-source-link: 4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) swr: 2.3.8(react@18.3.1) transitivePeerDependencies: - - typescript + - '@babel/core' + - '@opentelemetry/api' + - '@playwright/test' + - '@storybook/addons' + - babel-plugin-macros + - sass '@ngraveio/bc-ur@1.1.13': dependencies: @@ -24542,6 +24591,138 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/manager-api': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/preview-api': 7.6.17 + '@storybook/types': 7.6.17 + transitivePeerDependencies: + - react + - react-dom + + '@storybook/channels@7.6.17': + dependencies: + '@storybook/client-logger': 7.6.17 + '@storybook/core-events': 7.6.17 + '@storybook/global': 5.0.0 + qs: 6.14.0 + telejson: 7.2.0 + tiny-invariant: 1.3.3 + + '@storybook/channels@7.6.21': + dependencies: + '@storybook/client-logger': 7.6.21 + '@storybook/core-events': 7.6.21 + '@storybook/global': 5.0.0 + qs: 6.14.0 + telejson: 7.2.0 + tiny-invariant: 1.3.3 + + '@storybook/client-logger@7.6.17': + dependencies: + '@storybook/global': 5.0.0 + + '@storybook/client-logger@7.6.21': + dependencies: + '@storybook/global': 5.0.0 + + '@storybook/core-events@7.6.17': + dependencies: + ts-dedent: 2.2.0 + + '@storybook/core-events@7.6.21': + dependencies: + ts-dedent: 2.2.0 + + '@storybook/csf@0.1.13': + dependencies: + type-fest: 2.19.0 + + '@storybook/global@5.0.0': {} + + '@storybook/manager-api@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@storybook/channels': 7.6.17 + '@storybook/client-logger': 7.6.17 + '@storybook/core-events': 7.6.17 + '@storybook/csf': 0.1.13 + '@storybook/global': 5.0.0 + '@storybook/router': 7.6.17 + '@storybook/theming': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/types': 7.6.17 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + store2: 2.14.4 + telejson: 7.2.0 + ts-dedent: 2.2.0 + transitivePeerDependencies: + - react + - react-dom + + '@storybook/preview-api@7.6.17': + dependencies: + '@storybook/channels': 7.6.17 + '@storybook/client-logger': 7.6.17 + '@storybook/core-events': 7.6.17 + '@storybook/csf': 0.1.13 + '@storybook/global': 5.0.0 + '@storybook/types': 7.6.17 + '@types/qs': 6.14.0 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.14.0 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + + '@storybook/preview-api@7.6.21': + dependencies: + '@storybook/channels': 7.6.21 + '@storybook/client-logger': 7.6.21 + '@storybook/core-events': 7.6.21 + '@storybook/csf': 0.1.13 + '@storybook/global': 5.0.0 + '@storybook/types': 7.6.21 + '@types/qs': 6.14.0 + dequal: 2.0.3 + lodash: 4.17.21 + memoizerific: 1.11.3 + qs: 6.14.0 + synchronous-promise: 2.0.17 + ts-dedent: 2.2.0 + util-deprecate: 1.0.2 + + '@storybook/router@7.6.17': + dependencies: + '@storybook/client-logger': 7.6.17 + memoizerific: 1.11.3 + qs: 6.14.0 + + '@storybook/theming@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@18.3.1) + '@storybook/client-logger': 7.6.17 + '@storybook/global': 5.0.0 + memoizerific: 1.11.3 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@storybook/types@7.6.17': + dependencies: + '@storybook/channels': 7.6.17 + '@types/babel__core': 7.20.5 + '@types/express': 4.17.25 + file-system-cache: 2.3.0 + + '@storybook/types@7.6.21': + dependencies: + '@storybook/channels': 7.6.21 + '@types/babel__core': 7.20.5 + '@types/express': 4.17.25 + file-system-cache: 2.3.0 + '@stripe/react-stripe-js@5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@stripe/stripe-js': 8.6.0 @@ -27975,8 +28156,6 @@ snapshots: dependencies: delayed-stream: 1.0.0 - comlink@4.4.2: {} - comma-separated-tokens@1.0.8: {} comma-separated-tokens@2.0.3: {} @@ -29585,6 +29764,11 @@ snapshots: dependencies: tslib: 2.8.1 + file-system-cache@2.3.0: + dependencies: + fs-extra: 11.1.1 + ramda: 0.29.0 + file-type@16.5.4: dependencies: readable-web-to-node-stream: 3.0.4 @@ -29831,6 +30015,12 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 + fs-extra@11.1.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 @@ -32144,6 +32334,8 @@ snapshots: dependencies: tmpl: 1.0.5 + map-or-similar@1.5.0: {} + markdown-it@14.1.0: dependencies: argparse: 2.0.1 @@ -32439,6 +32631,10 @@ snapshots: memoize-one@5.2.1: {} + memoizerific@1.11.3: + dependencies: + map-or-similar: 1.5.0 + mensch@0.3.4: {} merge-descriptors@1.0.3: {} @@ -33044,10 +33240,6 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 - mipd@0.0.7(typescript@5.5.4): - optionalDependencies: - typescript: 5.5.4 - mixwith@0.1.1: {} mkdirp@0.5.6: @@ -33585,20 +33777,6 @@ snapshots: transitivePeerDependencies: - zod - ox@0.4.4(typescript@5.5.4)(zod@3.25.76): - dependencies: - '@adraffy/ens-normalize': 1.11.1 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.2.3(typescript@5.5.4)(zod@3.25.76) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.5.4 - transitivePeerDependencies: - - zod - ox@0.6.7(typescript@5.5.4)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 @@ -34477,6 +34655,8 @@ snapshots: radix3@1.1.2: {} + ramda@0.29.0: {} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 @@ -34573,6 +34753,11 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 + react-error-boundary@4.1.2(react@18.3.1): + dependencies: + '@babel/runtime': 7.28.4 + react: 18.3.1 + react-fast-compare@3.2.2: {} react-hook-form@7.69.0(react@18.3.1): @@ -35932,6 +36117,18 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 + store2@2.14.4: {} + + storybook-source-link@4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@storybook/addons': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@storybook/preview-api': 7.6.21 + '@types/react': 18.3.1 + react-error-boundary: 4.1.2(react@18.3.1) + optionalDependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + stream-browserify@3.0.0: dependencies: inherits: 2.0.4 @@ -36174,6 +36371,8 @@ snapshots: symbol-tree@3.2.4: {} + synchronous-promise@2.0.17: {} + tabbable@6.3.0: {} tailwind-merge@1.14.0: {} @@ -36231,6 +36430,10 @@ snapshots: transitivePeerDependencies: - supports-color + telejson@7.2.0: + dependencies: + memoizerific: 1.11.3 + terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -36292,6 +36495,8 @@ snapshots: tiny-invariant@1.2.0: {} + tiny-invariant@1.3.3: {} + tiny-warning@1.0.3: {} tinybench@2.9.0: {} @@ -36419,6 +36624,8 @@ snapshots: dependencies: typescript: 5.5.4 + ts-dedent@2.2.0: {} + ts-error@1.0.6: {} ts-essentials@10.1.1(typescript@5.5.4): From 0690d0b80b03bed3b860d995b552c14f729d3d78 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 18:33:23 +0700 Subject: [PATCH 048/340] feat: logo fix --- apps/frontend/src/app/(app)/auth/layout.tsx | 5 +++-- .../src/components/auth/providers/farcaster.provider.tsx | 2 +- .../frontend/src/components/launches/generator/generator.tsx | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/app/(app)/auth/layout.tsx b/apps/frontend/src/app/(app)/auth/layout.tsx index 1ee91cff..729e7aa6 100644 --- a/apps/frontend/src/app/(app)/auth/layout.tsx +++ b/apps/frontend/src/app/(app)/auth/layout.tsx @@ -5,6 +5,7 @@ import { ReactNode } from 'react'; import Image from 'next/image'; import loadDynamic from 'next/dynamic'; import { TestimonialComponent } from '@gitroom/frontend/components/auth/testimonial.component'; +import { LogoTextComponent } from '@gitroom/frontend/components/ui/logo-text.component'; const ReturnUrlComponent = loadDynamic(() => import('./return.url.component')); export default async function AuthLayout({ children, @@ -18,8 +19,8 @@ export default async function AuthLayout({ {/*<style>{`html, body {overflow-x: hidden;}`}</style>*/} <ReturnUrlComponent /> <div className="flex flex-col py-[40px] px-[20px] flex-1 lg:w-[600px] lg:flex-none rounded-[12px] text-white p-[12px] bg-[#1A1919]"> - <div className="w-full max-w-[440px] mx-auto justify-center gap-[20px] h-full flex flex-col"> - <Image width={100} height={33} src="/logo-text.svg" alt="Postiz" /> + <div className="w-full max-w-[440px] mx-auto justify-center gap-[20px] h-full flex flex-col text-white"> + <LogoTextComponent /> <div className="flex">{children}</div> </div> </div> diff --git a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx index 2bfbfbc7..26e0d13f 100644 --- a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx +++ b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx @@ -35,7 +35,7 @@ export const ButtonCaster: FC<{ viewBox="0 0 24 24" fill="none" > - <g clip-path="url(#clip0_2026_31786)"> + <g clipPath="url(#clip0_2026_31786)"> <path d="M18.75 0H5.25C2.35051 0 0 2.35051 0 5.25V18.75C0 21.6495 2.35051 24 5.25 24H18.75C21.6495 24 24 21.6495 24 18.75V5.25C24 2.35051 21.6495 0 18.75 0Z" fill="#7C65C1" diff --git a/apps/frontend/src/components/launches/generator/generator.tsx b/apps/frontend/src/components/launches/generator/generator.tsx index 5003407e..6669a514 100644 --- a/apps/frontend/src/components/launches/generator/generator.tsx +++ b/apps/frontend/src/components/launches/generator/generator.tsx @@ -329,7 +329,7 @@ export const GeneratorComponent = () => { viewBox="0 0 20 20" fill="none" > - <g clip-path="url(#clip0_1930_7370)"> + <g clipPath="url(#clip0_1930_7370)"> <path d="M5.41675 10.8337L6.07046 12.1411C6.2917 12.5836 6.40232 12.8048 6.55011 12.9965C6.68124 13.1667 6.83375 13.3192 7.00388 13.4503C7.19559 13.5981 7.41684 13.7087 7.85932 13.9299L9.16675 14.5837L7.85932 15.2374C7.41684 15.4586 7.19559 15.5692 7.00388 15.717C6.83375 15.8482 6.68124 16.0007 6.55011 16.1708C6.40232 16.3625 6.2917 16.5837 6.07046 17.0262L5.41675 18.3337L4.76303 17.0262C4.54179 16.5837 4.43117 16.3625 4.28339 16.1708C4.15225 16.0007 3.99974 15.8482 3.82962 15.717C3.6379 15.5692 3.41666 15.4586 2.97418 15.2374L1.66675 14.5837L2.97418 13.9299C3.41666 13.7087 3.6379 13.5981 3.82962 13.4503C3.99974 13.3192 4.15225 13.1667 4.28339 12.9965C4.43117 12.8048 4.54179 12.5836 4.76303 12.1411L5.41675 10.8337Z" stroke="white" From 21f0b64344fa16c52dadd1d45b864a6cd9efec35 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 19:02:50 +0700 Subject: [PATCH 049/340] feat: trailing --- .../billing/first.billing.component.tsx | 94 +++++++++++++------ 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index ea42234b..1381b5e2 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -22,6 +22,7 @@ import { FAQSection, } from '@gitroom/frontend/components/billing/faq.component'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useUser } from '@gitroom/frontend/components/layout/user.context'; const ModeComponent = dynamic( () => import('@gitroom/frontend/components/layout/mode.component'), @@ -42,6 +43,7 @@ const EmbeddedBilling = dynamic( export const FirstBillingComponent = () => { const { stripeClient } = useVariables(); + const user = useUser(); const [stripe, setStripe] = useState<null | Promise<Stripe>>(null); const [tier, setTier] = useState('STANDARD'); const [period, setPeriod] = useState('MONTHLY'); @@ -105,29 +107,48 @@ export const FirstBillingComponent = () => { <div className="flex-1 py-[40px] flex flex-col pe-[40px]"> <div className="text-[36px] font-[600] leading-[110%] whitespace-pre-line"> {t('billing_join_over', 'Join Over')}{' '} - <span className="text-[#FC69FF]">{t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')}</span> {t('billing_who_use', 'who use')}{'\n'}{t('billing_postiz_grow_social', 'Postiz To Grow Their Social Presence')} + <span className="text-[#FC69FF]"> + {t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')} + </span>{' '} + {t('billing_who_use', 'who use')} + {'\n'} + {t( + 'billing_postiz_grow_social', + 'Postiz To Grow Their Social Presence' + )} </div> - <div className="flex mt-[34px] mb-[10px]"> - <div className="flex gap-[8px]"> - <div> - <CheckIconComponent /> + {!!user?.allowTrial && ( + <div className="flex mt-[34px] mb-[10px]"> + <div className="flex gap-[8px]"> + <div> + <CheckIconComponent /> + </div> + <div> + {t('billing_no_risk_trial', '100% No-Risk Free Trial')} + </div> </div> - <div>{t('billing_no_risk_trial', '100% No-Risk Free Trial')}</div> - </div> - <div className="flex-1 flex gap-[8px] justify-center"> - <div> - <CheckIconComponent /> + <div className="flex-1 flex gap-[8px] justify-center"> + <div> + <CheckIconComponent /> + </div> + <div> + {t( + 'billing_pay_nothing_7_days', + 'Pay NOTHING for the first 7-days' + )} + </div> </div> - <div>{t('billing_pay_nothing_7_days', 'Pay NOTHING for the first 7-days')}</div> - </div> - <div className="flex gap-[8px]"> - <div> - <CheckIconComponent /> + <div className="flex gap-[8px]"> + <div> + <CheckIconComponent /> + </div> + <div> + {t('billing_cancel_anytime', 'Cancel anytime, hassle-free')} + </div> </div> - <div>{t('billing_cancel_anytime', 'Cancel anytime, hassle-free')}</div> </div> - </div> + )} {!isLoading && data && stripe ? ( <> @@ -141,7 +162,9 @@ export const FirstBillingComponent = () => { <div className="flex flex-col ps-[40px] border-l border-newColColor py-[40px]"> <div className="top-[20px] sticky"> <div className="flex mb-[24px]"> - <div className="flex-1 text-[24px] font-[700]">{t('billing_choose_plan', 'Choose a Plan')}</div> + <div className="flex-1 text-[24px] font-[700]"> + {t('billing_choose_plan', 'Choose a Plan')} + </div> <div className="h-[44px] px-[6px] flex items-center justify-center gap-[12px] border border-newColColor rounded-[12px] select-none"> <div className={clsx( @@ -203,7 +226,9 @@ export const FirstBillingComponent = () => { )} </div> <div className="flex flex-col mt-[54px] gap-[24px]"> - <div className="text-[24px] font-[700]">{t('billing_features', 'Features')}</div> + <div className="text-[24px] font-[700]"> + {t('billing_features', 'Features')} + </div> <BillingFeatures tier={tier} /> </div> </div> @@ -225,28 +250,43 @@ export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => { const currentPricing = pricing[tier]; const channelsOr = currentPricing.channel; const list: FeatureItem[] = []; - + list.push({ key: channelsOr === 1 ? 'billing_channel' : 'billing_channels', defaultValue: channelsOr === 1 ? 'channel' : 'channels', prefix: channelsOr, }); - + list.push({ key: 'billing_posts_per_month', defaultValue: 'posts per month', - prefix: currentPricing.posts_per_month > 10000 ? 'unlimited' : currentPricing.posts_per_month, + prefix: + currentPricing.posts_per_month > 10000 + ? 'unlimited' + : currentPricing.posts_per_month, }); - + if (currentPricing.team_members) { - list.push({ key: 'billing_unlimited_team_members', defaultValue: 'Unlimited team members' }); + list.push({ + key: 'billing_unlimited_team_members', + defaultValue: 'Unlimited team members', + }); } if (currentPricing?.ai) { - list.push({ key: 'billing_ai_auto_complete', defaultValue: 'AI auto-complete' }); + list.push({ + key: 'billing_ai_auto_complete', + defaultValue: 'AI auto-complete', + }); list.push({ key: 'billing_ai_copilots', defaultValue: 'AI copilots' }); - list.push({ key: 'billing_ai_autocomplete', defaultValue: 'AI Autocomplete' }); + list.push({ + key: 'billing_ai_autocomplete', + defaultValue: 'AI Autocomplete', + }); } - list.push({ key: 'billing_advanced_picture_editor', defaultValue: 'Advanced Picture Editor' }); + list.push({ + key: 'billing_advanced_picture_editor', + defaultValue: 'Advanced Picture Editor', + }); if (currentPricing?.image_generator) { list.push({ key: 'billing_ai_images_per_month', From f4dec2ed266112560d2c44709edae5a9a23da092 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 19:17:10 +0700 Subject: [PATCH 050/340] feat: powered by --- apps/frontend/src/components/billing/embedded.billing.tsx | 2 +- i18n.lock | 2 +- .../src/translation/locales/ar/translation.json | 2 +- .../src/translation/locales/bn/translation.json | 2 +- .../src/translation/locales/de/translation.json | 2 +- .../src/translation/locales/en/translation.json | 2 +- .../src/translation/locales/es/translation.json | 2 +- .../src/translation/locales/fr/translation.json | 2 +- .../src/translation/locales/he/translation.json | 2 +- .../src/translation/locales/it/translation.json | 2 +- .../src/translation/locales/ja/translation.json | 2 +- .../src/translation/locales/ko/translation.json | 2 +- .../src/translation/locales/pt/translation.json | 2 +- .../src/translation/locales/ru/translation.json | 2 +- .../src/translation/locales/tr/translation.json | 2 +- .../src/translation/locales/vi/translation.json | 2 +- .../src/translation/locales/zh/translation.json | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index 05d465ae..ef46aa4d 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -136,7 +136,7 @@ const StripeInputs = () => { <PaymentElement id="payment-element" options={{ layout: 'tabs' }} /> {checkout.type === 'loading' ? null : ( <div className="mt-[24px] flex gap-[10px]"> - <div>{t('billing_powered_by_stripe', 'Powered by Stripe')}</div> + <div>{t('billing_powered_by_stripe', 'Secure payments processed by Stripe')}</div> <Image src="/stripe.svg" alt="Stripe" width={20} height={20} /> </div> )} diff --git a/i18n.lock b/i18n.lock index 05afe097..e4f03d7f 100644 --- a/i18n.lock +++ b/i18n.lock @@ -532,7 +532,7 @@ checksums: billing_ai_videos_per_month: c786199d8dc9bded54fab8f92c350b19 billing_billing_address: 48980a775bfc7292b0c4f9b74b4d352e billing_payment: 95142d3fd8b6a6f551aba771842e9c11 - billing_powered_by_stripe: 4b1f500613fe28f3cc17cb003ed0caf6 + billing_powered_by_stripe: c7c2723003ae04f17ae0c5bee195aeb6 billing_your_7_day_trial_is: 4b59fb559f5fd520668974e909e3479c billing_100_percent_free: 6616fd6ee152264c06dd2537f6347e66 billing_ending: f66133a14aa7d86ea2453df97de12cd5 diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index 871a9ae3..a060b06d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "فيديوهات بالذكاء الاصطناعي شهريًا", "billing_billing_address": "عنوان الفاتورة", "billing_payment": "الدفع", - "billing_powered_by_stripe": "بدعم من سترايب", + "billing_powered_by_stripe": "مدفوعات آمنة تتم معالجتها عبر سترايب", "billing_your_7_day_trial_is": "فترة التجربة المجانية لمدة 7 أيام الخاصة بك هي", "billing_100_percent_free": "مجانية 100%", "billing_ending": "تنتهي", diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index d985a5a9..78430231 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "প্রতি মাসে এআই ভিডিও", "billing_billing_address": "বিলিং ঠিকানা", "billing_payment": "পেমেন্ট", - "billing_powered_by_stripe": "স্ট্রাইপ দ্বারা পরিচালিত", + "billing_powered_by_stripe": "নিরাপদ পেমেন্ট Stripe দ্বারা প্রক্রিয়াজাত করা হয়", "billing_your_7_day_trial_is": "আপনার ৭ দিনের ট্রায়াল", "billing_100_percent_free": "১০০% ফ্রি", "billing_ending": "শেষ হচ্ছে", diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index 10ac1cc0..162a6e38 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "KI-Videos pro Monat", "billing_billing_address": "Rechnungsadresse", "billing_payment": "Zahlung", - "billing_powered_by_stripe": "Bereitgestellt von Stripe", + "billing_powered_by_stripe": "Sichere Zahlungen, abgewickelt von Stripe", "billing_your_7_day_trial_is": "Ihre 7-tägige Testphase ist", "billing_100_percent_free": "100% kostenlos", "billing_ending": "endet", diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index d1029414..c6aedaf5 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "AI Videos per month", "billing_billing_address": "Billing Address", "billing_payment": "Payment", - "billing_powered_by_stripe": "Powered by Stripe", + "billing_powered_by_stripe": "Secure payments processed by Stripe", "billing_your_7_day_trial_is": "Your 7-day trial is", "billing_100_percent_free": "100% free", "billing_ending": "ending", diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index ab1f97a3..e07584ad 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Videos de IA por mes", "billing_billing_address": "Dirección de facturación", "billing_payment": "Pago", - "billing_powered_by_stripe": "Desarrollado por Stripe", + "billing_powered_by_stripe": "Pagos seguros procesados por Stripe", "billing_your_7_day_trial_is": "Tu prueba de 7 días es", "billing_100_percent_free": "100% gratis", "billing_ending": "finaliza", diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index 6de13f4b..ca1d5e3f 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Vidéos IA par mois", "billing_billing_address": "Adresse de facturation", "billing_payment": "Paiement", - "billing_powered_by_stripe": "Propulsé par Stripe", + "billing_powered_by_stripe": "Paiements sécurisés traités par Stripe", "billing_your_7_day_trial_is": "Votre essai de 7 jours est", "billing_100_percent_free": "100% gratuit", "billing_ending": "se termine", diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index db8266e9..2d59ee8a 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "סרטוני בינה מלאכותית בחודש", "billing_billing_address": "כתובת לחיוב", "billing_payment": "תשלום", - "billing_powered_by_stripe": "מופעל על ידי Stripe", + "billing_powered_by_stripe": "תשלומים מאובטחים מעובדים על ידי Stripe", "billing_your_7_day_trial_is": "תקופת הניסיון שלך ל-7 ימים היא", "billing_100_percent_free": "100% חינם", "billing_ending": "מסתיימת", diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index d16c070d..4e71853d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Video AI al mese", "billing_billing_address": "Indirizzo di fatturazione", "billing_payment": "Pagamento", - "billing_powered_by_stripe": "Offerto da Stripe", + "billing_powered_by_stripe": "Pagamenti sicuri elaborati da Stripe", "billing_your_7_day_trial_is": "La tua prova di 7 giorni è", "billing_100_percent_free": "100% gratuita", "billing_ending": "in scadenza", diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index fd28c538..a19dc3a0 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "月あたりのAI動画数", "billing_billing_address": "請求先住所", "billing_payment": "支払い", - "billing_powered_by_stripe": "Stripeによって提供されています", + "billing_powered_by_stripe": "Stripeによって処理される安全な支払い", "billing_your_7_day_trial_is": "7日間の無料トライアルは", "billing_100_percent_free": "完全に無料", "billing_ending": "終了します", diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index cbc86399..63572a0f 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "월별 AI 비디오", "billing_billing_address": "청구 주소", "billing_payment": "결제", - "billing_powered_by_stripe": "Stripe로 제공됨", + "billing_powered_by_stripe": "Stripe를 통한 안전한 결제가 처리됩니다", "billing_your_7_day_trial_is": "7일 무료 체험이", "billing_100_percent_free": "100% 무료", "billing_ending": "종료됩니다", diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index 09ee5205..7d298126 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Vídeos de IA por mês", "billing_billing_address": "Endereço de cobrança", "billing_payment": "Pagamento", - "billing_powered_by_stripe": "Desenvolvido por Stripe", + "billing_powered_by_stripe": "Pagamentos seguros processados pela Stripe", "billing_your_7_day_trial_is": "Seu teste de 7 dias é", "billing_100_percent_free": "100% gratuito", "billing_ending": "terminando", diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index cb59cf5d..ccbda6bc 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "ИИ-видео в месяц", "billing_billing_address": "Платёжный адрес", "billing_payment": "Платёж", - "billing_powered_by_stripe": "Работает на Stripe", + "billing_powered_by_stripe": "Безопасные платежи обрабатываются через Stripe", "billing_your_7_day_trial_is": "Ваш 7-дневный пробный период", "billing_100_percent_free": "100% бесплатно", "billing_ending": "заканчивается", diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index a695c60a..95fb3617 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Aylık Yapay Zeka Videoları", "billing_billing_address": "Fatura Adresi", "billing_payment": "Ödeme", - "billing_powered_by_stripe": "Stripe ile desteklenmektedir", + "billing_powered_by_stripe": "Güvenli ödemeler Stripe tarafından işlenir", "billing_your_7_day_trial_is": "7 günlük deneme süreniz", "billing_100_percent_free": "%100 ücretsiz", "billing_ending": "sona eriyor", diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 24b95ceb..74088c49 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Video AI mỗi tháng", "billing_billing_address": "Địa chỉ thanh toán", "billing_payment": "Thanh toán", - "billing_powered_by_stripe": "Được hỗ trợ bởi Stripe", + "billing_powered_by_stripe": "Thanh toán an toàn được xử lý bởi Stripe", "billing_your_7_day_trial_is": "Dùng thử 7 ngày của bạn là", "billing_100_percent_free": "Miễn phí 100%", "billing_ending": "kết thúc", diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index 8b8e636f..5f22272a 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "每月AI视频数", "billing_billing_address": "账单地址", "billing_payment": "付款", - "billing_powered_by_stripe": "由Stripe提供支持", + "billing_powered_by_stripe": "安全支付由Stripe处理", "billing_your_7_day_trial_is": "您的7天试用期", "billing_100_percent_free": "100%免费", "billing_ending": "即将结束", From 63c3e1c23394f008102c9b1326e12d7913efdb99 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 21:15:14 +0700 Subject: [PATCH 051/340] fix: tolt --- .../src/components/billing/first.billing.component.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index 1381b5e2..928d2f4a 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -23,6 +23,7 @@ import { } from '@gitroom/frontend/components/billing/faq.component'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; +import { useTolt } from '@gitroom/frontend/components/layout/tolt.script'; const ModeComponent = dynamic( () => import('@gitroom/frontend/components/layout/mode.component'), @@ -49,6 +50,7 @@ export const FirstBillingComponent = () => { const [period, setPeriod] = useState('MONTHLY'); const fetch = useFetch(); const t = useT(); + const tolt = useTolt(); useEffect(() => { setStripe(loadStripe(stripeClient)); @@ -61,6 +63,7 @@ export const FirstBillingComponent = () => { body: JSON.stringify({ billing: tier, period: period, + tolt: tolt(), }), }) ).json(); From ec623a678eed61bf2e53d8ab2567d77982056bdc Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 22 Dec 2025 23:44:03 +0700 Subject: [PATCH 052/340] fix: discord support --- apps/frontend/src/components/new-layout/layout.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index ea51e2e2..d34db3aa 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -96,6 +96,7 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => { <FirstBillingComponent /> ) : ( <div className="flex-1 flex gap-[8px]"> + <Support /> <div className="flex flex-col bg-newBgColorInner w-[80px] rounded-[12px]"> <div className={clsx( @@ -106,7 +107,6 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => { <div className="flex flex-col h-full gap-[32px] flex-1 py-[12px]"> <Logo /> <TopMenu /> - <Support /> </div> </div> </div> From 37c45743a8c0a1e2a3c85d133d9406ab3714b96c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 25 Dec 2025 18:29:12 +0700 Subject: [PATCH 053/340] feat: refactor for creation modal --- apps/frontend/src/app/colors.scss | 8 + apps/frontend/src/app/global.scss | 49 +- .../src/components/launches/ai.image.tsx | 42 +- .../src/components/launches/ai.video.tsx | 42 +- .../src/components/launches/calendar.tsx | 4 +- .../launches/general.preview.component.tsx | 2 +- .../launches/helpers/date.picker.tsx | 25 +- .../launches/information.component.tsx | 205 +++++ .../components/launches/repeat.component.tsx | 129 ++- .../components/launches/select.customer.tsx | 130 ++- .../components/launches/tags.component.tsx | 317 ++++++- .../src/components/launches/up.down.arrow.tsx | 31 +- .../src/components/layout/drop.files.tsx | 4 +- .../src/components/layout/new-modal.tsx | 47 +- .../src/components/layout/support.tsx | 1 + .../src/components/media/media.component.tsx | 780 ++++++++++-------- .../src/components/media/new.uploader.tsx | 4 +- .../src/components/new-launch/a.component.tsx | 40 +- .../components/new-launch/add.edit.modal.tsx | 14 +- .../components/new-launch/add.post.button.tsx | 21 +- .../src/components/new-launch/bold.text.tsx | 35 +- .../new-launch/bullets.component.tsx | 19 +- .../src/components/new-launch/editor.tsx | 564 +++++++++---- .../new-launch/heading.component.tsx | 23 +- .../components/new-launch/manage.modal.tsx | 592 ++++++++++++- .../new-launch/picks.socials.component.tsx | 40 +- .../providers/high.order.provider.tsx | 59 +- .../providers/show.all.providers.tsx | 7 - .../components/new-launch/select.current.tsx | 80 +- .../src/components/new-launch/u.text.tsx | 39 +- .../new-layout/layout.component.tsx | 1 - apps/frontend/src/components/sets/sets.tsx | 7 +- apps/frontend/src/components/signature.tsx | 43 +- .../third-parties/third-party.media.tsx | 12 +- apps/frontend/tailwind.config.js | 10 + .../database/prisma/media/media.repository.ts | 9 +- 36 files changed, 2522 insertions(+), 913 deletions(-) create mode 100644 apps/frontend/src/components/launches/information.component.tsx diff --git a/apps/frontend/src/app/colors.scss b/apps/frontend/src/app/colors.scss index c0082f70..9ae668e9 100644 --- a/apps/frontend/src/app/colors.scss +++ b/apps/frontend/src/app/colors.scss @@ -1,7 +1,11 @@ :root { .dark { + --new-back-drop: #000; + --new-settings: #242323; + --new-border: #252525; --new-bgColor: #0e0e0e; --new-bgColorInner: #1a1919; + --new-sep: #454444; --new-bgLineColor: #212121; --new-textItemFocused: #1a1919; --new-textItemBlur: #999999; @@ -26,6 +30,10 @@ --popup-color: rgba(65, 64, 66, 0.3); } .light { + --new-back-drop: #2d1b57; + --new-settings: #ECEEF1; + --new-sep: #D5D9DD; + --new-border: #EAECEE; --new-bgColor: #f0f2f4; --new-bgColorInner: #ffffff; --new-bgLineColor: #e7e9eb; diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index a5f342b9..5a524d1f 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -679,6 +679,9 @@ html[dir='rtl'] [dir='ltr'] { .blur-xs { filter: blur(4px); } +.blur-s { + filter: blur(5px); +} .agent { .copilotKitInputContainer { @@ -736,7 +739,45 @@ html[dir='rtl'] [dir='ltr'] { background: transparent !important; } -//html body iframe[title="Stripe developer tools frame"] { -// display: none !important; -// height: 0 !important; -//} \ No newline at end of file +.menu-shadow { + border-radius: 12px; + box-shadow: 0 8px 30px 0 rgba(0, 0, 0, 0.5); +} + +.post-now { + box-shadow: -33px 57px 18px 0 rgba(0, 0, 0, 0.01), -21px 36px 17px 0 rgba(0, 0, 0, 0.06), -12px 20px 14px 0 rgba(0, 0, 0, 0.20), -5px 9px 11px 0 rgba(0, 0, 0, 0.34), -1px 2px 6px 0 rgba(0, 0, 0, 0.39); +} + +.uppyChange .uppy-Dashboard-inner { + width: 100% !important; +} + +.btnSub:disabled .arrow-change { + display: none !important; +} +.btnSub:disabled + button { + display: none !important; +} + +.tiptap p.is-editor-empty:first-child::before { + color: #adb5bd; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} + +.w8-max { + width: calc(100% / 6); + max-width: calc(100% / 6); +} + +.withp3 { + width: calc(100% + 9px); + height: calc(100% + 6px); +} + +.forceChange .changeColor { + background: var(--new-btn-primary) !important; + color: #fff !important; +} \ No newline at end of file diff --git a/apps/frontend/src/components/launches/ai.image.tsx b/apps/frontend/src/components/launches/ai.image.tsx index 9cfa00f8..611af14a 100644 --- a/apps/frontend/src/components/launches/ai.image.tsx +++ b/apps/frontend/src/components/launches/ai.image.tsx @@ -28,7 +28,7 @@ export const AiImage: FC<{ const t = useT(); const { value, onChange } = props; const [loading, setLoading] = useState(false); - const setLocked = useLaunchStore(p => p.setLocked); + const setLocked = useLaunchStore((p) => p.setLocked); const fetch = useFetch(); const generateImage = useCallback( (type: string) => async () => { @@ -59,7 +59,7 @@ ${type} ); return ( <div className="relative group"> - <Button + <div {...(value.length < 30 ? { 'data-tooltip-id': 'tooltip', @@ -68,13 +68,13 @@ ${type} } : {})} className={clsx( - 'relative ms-[10px] rounded-[4px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-newBgLineColor bg-newColColor', - value.length < 30 && 'opacity-25' + 'cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]', + value.length < 30 && 'opacity-50' )} > {loading && ( <div className="absolute start-[50%] -translate-x-[50%]"> - <Loading height={30} width={30} type="spin" color="#fff" /> + <Loading height={15} width={15} type="spin" color="#fff" /> </div> )} <div @@ -86,24 +86,32 @@ ${type} <div> <svg xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" + width="16" + height="16" + viewBox="0 0 16 16" fill="none" > - <path - d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z" - fill="currentColor" - /> + <g clip-path="url(#clip0_2352_53053)"> + <path + d="M8.33333 2.00033H5.2C4.07989 2.00033 3.51984 2.00033 3.09202 2.21831C2.71569 2.41006 2.40973 2.71602 2.21799 3.09234C2 3.52017 2 4.08022 2 5.20032V10.8003C2 11.9204 2 12.4805 2.21799 12.9083C2.40973 13.2846 2.71569 13.5906 3.09202 13.7823C3.51984 14.0003 4.07989 14.0003 5.2 14.0003H11.3333C11.9533 14.0003 12.2633 14.0003 12.5176 13.9322C13.2078 13.7472 13.7469 13.2081 13.9319 12.518C14 12.2636 14 11.9536 14 11.3337M7 5.66699C7 6.40337 6.40305 7.00033 5.66667 7.00033C4.93029 7.00033 4.33333 6.40337 4.33333 5.66699C4.33333 4.93061 4.93029 4.33366 5.66667 4.33366C6.40305 4.33366 7 4.93061 7 5.66699ZM9.99336 7.94576L4.3541 13.0724C4.03691 13.3607 3.87831 13.5049 3.86429 13.6298C3.85213 13.738 3.89364 13.8454 3.97546 13.9173C4.06985 14.0003 4.28419 14.0003 4.71286 14.0003H10.9707C11.9301 14.0003 12.4098 14.0003 12.7866 13.8391C13.2596 13.6368 13.6365 13.2599 13.8388 12.7869C14 12.4101 14 11.9304 14 10.971C14 10.6482 14 10.4867 13.9647 10.3364C13.9204 10.1475 13.8353 9.97056 13.7155 9.81792C13.6202 9.69646 13.4941 9.59562 13.242 9.39396L11.3772 7.9021C11.1249 7.70026 10.9988 7.59935 10.8599 7.56373C10.7374 7.53234 10.6086 7.53641 10.4884 7.57545C10.352 7.61975 10.2324 7.72842 9.99336 7.94576ZM13 1.01074L12.5932 1.82425C12.4556 2.09958 12.3868 2.23724 12.2948 2.35653C12.2132 2.46238 12.1183 2.55728 12.0125 2.63887C11.8932 2.73083 11.7555 2.79966 11.4802 2.93732L10.6667 3.34408L11.4802 3.75083C11.7555 3.88849 11.8932 3.95732 12.0125 4.04928C12.1183 4.13087 12.2132 4.22577 12.2948 4.33162C12.3868 4.45091 12.4556 4.58857 12.5932 4.8639L13 5.67741L13.4068 4.8639C13.5444 4.58857 13.6132 4.45091 13.7052 4.33162C13.7868 4.22577 13.8817 4.13087 13.9875 4.04928C14.1068 3.95732 14.2445 3.88849 14.5198 3.75083L15.3333 3.34408L14.5198 2.93732C14.2445 2.79966 14.1068 2.73083 13.9875 2.63887C13.8817 2.55728 13.7868 2.46238 13.7052 2.35653C13.6132 2.23724 13.5444 2.09958 13.4068 1.82425L13 1.01074Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2352_53053"> + <rect width="16" height="16" fill="currentColor" /> + </clipPath> + </defs> </svg> </div> - <div className="text-[12px] font-[500] !text-current"> - {t('ai', 'AI')} Image - </div> + <div className="text-[10px] font-[600] iconBreak:hidden block">{t('ai', 'AI')} Image</div> </div> - </Button> + </div> {value.length >= 30 && !loading && ( - <div className="text-[12px] ms-[10px] -mt-[10px] w-[200px] absolute top-[100%] z-[500] start-0 hidden group-hover:block"> + <div className="text-[12px] -mt-[10px] w-[200px] absolute bottom-[100%] z-[500] start-0 hidden group-hover:block"> <ul className="cursor-pointer rounded-[4px] border border-dashed mt-[3px] p-[5px] border-newBgLineColor bg-newColColor"> {list.map((p) => ( <li onClick={generateImage(p)} key={p} className="hover:bg-sixth"> diff --git a/apps/frontend/src/components/launches/ai.video.tsx b/apps/frontend/src/components/launches/ai.video.tsx index ed0fd2c4..5b37f389 100644 --- a/apps/frontend/src/components/launches/ai.video.tsx +++ b/apps/frontend/src/components/launches/ai.video.tsx @@ -44,9 +44,9 @@ export const Modal: FC<{ setLocked(true); const customParams = form.getValues(); - if (!await form.trigger()) { + if (!(await form.trigger())) { toaster.show('Please fill all required fields', 'warning'); - return ; + return; } try { const image = await fetch(`/media/generate-video`, { @@ -199,7 +199,7 @@ export const AiVideo: FC<{ /> )} <div className="relative group"> - <Button + <div {...(value.length < 30 ? { 'data-tooltip-id': 'tooltip', @@ -208,8 +208,8 @@ export const AiVideo: FC<{ } : {})} className={clsx( - 'relative ms-[10px] rounded-[4px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-newBgLineColor bg-newColColor', - value.length < 30 && 'opacity-25' + 'cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]', + value.length < 30 && 'opacity-50' )} > {loading && ( @@ -226,24 +226,32 @@ export const AiVideo: FC<{ <div> <svg xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" + width="16" + height="16" + viewBox="0 0 16 16" fill="none" > - <path - d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z" - fill="currentColor" - /> + <g clip-path="url(#clip0_2352_53058)"> + <path + d="M8.06916 14.1663V2.04134M4.97208 14.1663V11.1351M4.97208 5.07259V2.04134M11.1662 14.1663V11.1351M9.09973 2.02152L4.8482 2.04134C3.80748 2.04134 3.28712 2.04134 2.88962 2.23957C2.53997 2.41394 2.25569 2.69218 2.07754 3.0344C1.875 3.42345 1.875 3.93275 1.875 4.95134L1.875 11.2563C1.875 12.2749 1.875 12.7842 2.07754 13.1733C2.25569 13.5155 2.53997 13.7937 2.88962 13.9681C3.28712 14.1663 3.80748 14.1663 4.8482 14.1663H11.2901C12.3308 14.1663 12.8512 14.1663 13.2487 13.9681C13.5984 13.7937 13.8826 13.5155 14.0608 13.1733C14.2633 12.7842 14.2633 12.2749 14.2633 11.2563V7.61426M1.875 5.07259L9.09973 5.06116M1.875 11.1351H14.2633M12.8141 1.20801L12.3949 2.02152C12.253 2.29684 12.1821 2.4345 12.0873 2.55379C12.0032 2.65965 11.9054 2.75455 11.7963 2.83614C11.6734 2.92809 11.5315 2.99692 11.2478 3.13458L10.4094 3.54134L11.2478 3.9481C11.5315 4.08576 11.6734 4.15459 11.7963 4.24654C11.9054 4.32814 12.0032 4.42303 12.0873 4.52889C12.1821 4.64818 12.253 4.78584 12.3949 5.06116L12.8141 5.87467L13.2333 5.06116C13.3751 4.78584 13.4461 4.64818 13.5408 4.52889C13.6249 4.42303 13.7227 4.32814 13.8318 4.24654C13.9548 4.15459 14.0966 4.08576 14.3804 3.9481L15.2188 3.54134L14.3804 3.13458C14.0966 2.99692 13.9548 2.92809 13.8318 2.83614C13.7227 2.75455 13.6249 2.65965 13.5408 2.55379C13.4461 2.4345 13.3751 2.29684 13.2333 2.02152L12.8141 1.20801Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2352_53058"> + <rect width="16" height="16" fill="currentColor" /> + </clipPath> + </defs> </svg> </div> - <div className="text-[12px] font-[500] !text-current"> - {t('ai', 'AI')} Video - </div> + <div className="text-[10px] font-[600] iconBreak:hidden block">{t('ai', 'AI')} Video</div> </div> - </Button> + </div> {value.length >= 30 && !loading && ( - <div className="text-[12px] ms-[10px] -mt-[10px] w-[200px] absolute top-[100%] z-[500] start-0 hidden group-hover:block"> + <div className="text-[12px] -mt-[10px] w-[200px] absolute bottom-[100%] z-[500] start-0 hidden group-hover:block"> <ul className="cursor-pointer rounded-[4px] border border-dashed border-newBgLineColor bg-newColColor mt-[3px] p-[5px]"> {data.map((p: any) => ( <li diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 01516c90..1edb19da 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -471,6 +471,7 @@ export const CalendarColumn: FC<{ closeOnEscape: false, withCloseButton: false, askClose: true, + fullScreen: true, classNames: { modal: 'w-[100%] max-w-[1400px] text-textColor', }, @@ -551,9 +552,10 @@ export const CalendarColumn: FC<{ closeOnEscape: false, withCloseButton: false, removeLayout: true, + fullScreen: true, askClose: true, classNames: { - modal: 'w-[100%] max-w-[1400px] text-textColor', + modal: 'fixed left-0 top-0 w-full h-full', }, id: 'add-edit-modal', children: ( diff --git a/apps/frontend/src/components/launches/general.preview.component.tsx b/apps/frontend/src/components/launches/general.preview.component.tsx index 403293c8..4a85731c 100644 --- a/apps/frontend/src/components/launches/general.preview.component.tsx +++ b/apps/frontend/src/components/launches/general.preview.component.tsx @@ -49,7 +49,7 @@ export const GeneralPreviewComponent: FC<{ }); return ( - <div className={clsx('w-full md:w-[555px] px-[16px]')}> + <div className={clsx('w-full md:w-[555px]')}> <div className="w-full h-full relative flex flex-col"> {renderContent.map((value, index) => ( <div diff --git a/apps/frontend/src/components/launches/helpers/date.picker.tsx b/apps/frontend/src/components/launches/helpers/date.picker.tsx index 2d4b2831..2abebc7d 100644 --- a/apps/frontend/src/components/launches/helpers/date.picker.tsx +++ b/apps/frontend/src/components/launches/helpers/date.picker.tsx @@ -34,31 +34,34 @@ export const DatePicker: FC<{ ); return ( <div - className="flex gap-[8px] items-center relative px-[16px] select-none" + className="px-[16px] border border-newTextColor/10 rounded-[8px] justify-center flex gap-[8px] items-center relative h-[44px] text-[15px] font-[600] ml-[7px] select-none flex-1" onClick={changeShow} ref={ref} > - <div className="cursor-pointer"> - {date.format(isUSCitizen() ? 'MM/DD/YYYY hh:mm A' : 'DD/MM/YYYY HH:mm')} - </div> <div className="cursor-pointer"> <svg xmlns="http://www.w3.org/2000/svg" - width="20" - height="21" - viewBox="0 0 20 21" + width="17" + height="19" + viewBox="0 0 17 19" fill="none" > <path - d="M16.25 3H14.375V2.375C14.375 2.20924 14.3092 2.05027 14.1919 1.93306C14.0747 1.81585 13.9158 1.75 13.75 1.75C13.5842 1.75 13.4253 1.81585 13.3081 1.93306C13.1908 2.05027 13.125 2.20924 13.125 2.375V3H6.875V2.375C6.875 2.20924 6.80915 2.05027 6.69194 1.93306C6.57473 1.81585 6.41576 1.75 6.25 1.75C6.08424 1.75 5.92527 1.81585 5.80806 1.93306C5.69085 2.05027 5.625 2.20924 5.625 2.375V3H3.75C3.41848 3 3.10054 3.1317 2.86612 3.36612C2.6317 3.60054 2.5 3.91848 2.5 4.25V16.75C2.5 17.0815 2.6317 17.3995 2.86612 17.6339C3.10054 17.8683 3.41848 18 3.75 18H16.25C16.5815 18 16.8995 17.8683 17.1339 17.6339C17.3683 17.3995 17.5 17.0815 17.5 16.75V4.25C17.5 3.91848 17.3683 3.60054 17.1339 3.36612C16.8995 3.1317 16.5815 3 16.25 3ZM16.25 6.75H3.75V4.25H5.625V4.875C5.625 5.04076 5.69085 5.19973 5.80806 5.31694C5.92527 5.43415 6.08424 5.5 6.25 5.5C6.41576 5.5 6.57473 5.43415 6.69194 5.31694C6.80915 5.19973 6.875 5.04076 6.875 4.875V4.25H13.125V4.875C13.125 5.04076 13.1908 5.19973 13.3081 5.31694C13.4253 5.43415 13.5842 5.5 13.75 5.5C13.9158 5.5 14.0747 5.43415 14.1919 5.31694C14.3092 5.19973 14.375 5.04076 14.375 4.875V4.25H16.25V6.75Z" - fill="#B69DEC" + d="M15.75 7.41667H0.75M11.5833 0.75V4.08333M4.91667 0.75V4.08333M4.75 17.4167H11.75C13.1501 17.4167 13.8502 17.4167 14.385 17.1442C14.8554 16.9045 15.2378 16.522 15.4775 16.0516C15.75 15.5169 15.75 14.8168 15.75 13.4167V6.41667C15.75 5.01654 15.75 4.31647 15.4775 3.78169C15.2378 3.31129 14.8554 2.92883 14.385 2.68915C13.8502 2.41667 13.1501 2.41667 11.75 2.41667H4.75C3.34987 2.41667 2.6498 2.41667 2.11502 2.68915C1.64462 2.92883 1.26217 3.31129 1.02248 3.78169C0.75 4.31647 0.75 5.01654 0.75 6.41667V13.4167C0.75 14.8168 0.75 15.5169 1.02248 16.0516C1.26217 16.522 1.64462 16.9045 2.11502 17.1442C2.6498 17.4167 3.34987 17.4167 4.75 17.4167Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> </div> + <div className="cursor-pointer"> + {date.format(isUSCitizen() ? 'MM/DD/YYYY hh:mm A' : 'DD/MM/YYYY HH:mm')} + </div> {open && ( <div onClick={(e) => e.stopPropagation()} - className="animate-normalFadeDown absolute top-[100%] mt-[16px] end-0 bg-sixth border border-tableBorder text-textColor rounded-[16px] z-[300] p-[16px] flex flex-col" + className="animate-fadeIn absolute bottom-[100%] mb-[16px] start-[50%] -translate-x-[50%] bg-sixth border border-tableBorder text-textColor rounded-[16px] z-[300] p-[16px] flex flex-col" > <Calendar onChange={changeDate('date')} @@ -92,7 +95,7 @@ export const DatePicker: FC<{ defaultValue={date.toDate()} /> <Button className="mt-[12px]" onClick={changeShow}> - {t('save', 'Save')} + {t('close', 'Close')} </Button> </div> )} diff --git a/apps/frontend/src/components/launches/information.component.tsx b/apps/frontend/src/components/launches/information.component.tsx new file mode 100644 index 00000000..38ed5f21 --- /dev/null +++ b/apps/frontend/src/components/launches/information.component.tsx @@ -0,0 +1,205 @@ +import React, { FC, Fragment, useMemo } from 'react'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useShallow } from 'zustand/react/shallow'; +import clsx from 'clsx'; +import Image from 'next/image'; +import { capitalize } from 'lodash'; + +const Valid: FC = () => { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <path + d="M6 7.33333L8 9.33333L14.6667 2.66667M10.6667 2H5.2C4.0799 2 3.51984 2 3.09202 2.21799C2.71569 2.40973 2.40973 2.71569 2.21799 3.09202C2 3.51984 2 4.07989 2 5.2V10.8C2 11.9201 2 12.4802 2.21799 12.908C2.40973 13.2843 2.71569 13.5903 3.09202 13.782C3.51984 14 4.07989 14 5.2 14H10.8C11.9201 14 12.4802 14 12.908 13.782C13.2843 13.5903 13.5903 13.2843 13.782 12.908C14 12.4802 14 11.9201 14 10.8V8" + stroke="#00EB75" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + ); +}; + +const Invalid: FC = () => { + return ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <g clip-path="url(#clip0_2482_97670)"> + <path + d="M8.00049 6.00015V8.66682M8.00049 11.3335H8.00715M7.07737 2.59464L1.59411 12.0657C1.28997 12.591 1.1379 12.8537 1.16038 13.0693C1.17998 13.2573 1.2785 13.4282 1.4314 13.5394C1.60671 13.6668 1.91022 13.6668 2.51723 13.6668H13.4837C14.0908 13.6668 14.3943 13.6668 14.5696 13.5394C14.7225 13.4282 14.821 13.2573 14.8406 13.0693C14.8631 12.8537 14.711 12.591 14.4069 12.0657L8.92361 2.59463C8.62056 2.07119 8.46904 1.80947 8.27135 1.72157C8.09892 1.64489 7.90206 1.64489 7.72962 1.72157C7.53193 1.80947 7.38041 2.07119 7.07737 2.59464Z" + stroke="white" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2482_97670"> + <rect width="16" height="16" fill="white" /> + </clipPath> + </defs> + </svg> + ); +}; +export const InformationComponent: FC<{ + chars: Record<string, number>; + totalChars: number; + totalAllowedChars: number; + isPicture: boolean; +}> = ({ totalChars, totalAllowedChars, chars, isPicture }) => { + const { isGlobal, selectedIntegrations, internal } = useLaunchStore( + useShallow((state) => ({ + isGlobal: state.current === 'global', + selectedIntegrations: state.selectedIntegrations, + internal: state.internal, + })) + ); + + const isInternal = useMemo(() => { + if (!isGlobal) { + return []; + } + return selectedIntegrations.map((p) => { + const findIt = internal.find( + (a) => a.integration.id === p.integration.id + ); + + return !!findIt; + }); + }, [isGlobal, internal, selectedIntegrations]); + + const isValid = useMemo(() => { + if (!isPicture && !totalChars) { + return false; + } + + if (totalChars > totalAllowedChars && !isGlobal) { + return false; + } + + if (totalChars <= totalAllowedChars && !isGlobal) { + return true; + } + + if ( + selectedIntegrations.some((p, index) => { + if (isInternal[index]) { + return false; + } + + return totalChars > (chars?.[p.integration.id] || 0); + }) + ) { + return false; + } + + return true; + }, [totalAllowedChars, totalChars, isInternal, isPicture]); + + return ( + <div + className={clsx( + 'group rounded-[6px] gap-[4px] h-[30px] px-[6px] flex justify-center items-center relative', + isValid ? 'border border-newColColor' : 'bg-[#FF3F3F]' + )} + > + {isValid ? <Valid /> : <Invalid />} + + {!isGlobal && ( + <div className="text-[10px] font-[600] flex justify-center items-center"> + {totalChars}/{totalAllowedChars} + </div> + )} + {((isGlobal && selectedIntegrations.length) || !isValid) && ( + <svg + className="group-hover:rotate-180" + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <path + d="M5.4563 6L10.5437 6C10.9494 6 11.1526 6.56798 10.8657 6.90016L8.32201 9.84556C8.14417 10.0515 7.85583 10.0515 7.67799 9.84556L5.13429 6.90016C4.84741 6.56798 5.05059 6 5.4563 6Z" + fill="currentColor" + /> + </svg> + )} + {((isGlobal && selectedIntegrations.length) || !isValid) && ( + <div + className={clsx( + 'z-[300] hidden rounded-[12px] bg-newBgColorInner group-hover:flex absolute end-0 bottom-[100%] mb-[5px] p-[12px] flex-col', + isValid ? 'border border-newColColor' : 'border border-[#FF3F3F]' + )} + > + {!isPicture && !totalChars && ( + <div + className={clsx( + 'text-sm text-[#FF3F3F] whitespace-nowrap', + isGlobal && selectedIntegrations.length && 'mb-[12px]' + )} + > + Your post should have at least + <br /> + one character or one image. + </div> + )} + {isGlobal && ( + <div className="grid grid-cols-[auto_auto_auto] text-[14px] font-[500] gap-[8px] items-center"> + {selectedIntegrations.map((p, index) => ( + <Fragment key={p.integration.id}> + <div> + <Image + src={`/icons/platforms/${p.integration.identifier}.png`} + alt={p.integration.name} + className="rounded-[4px] w-[16px] h-[16px] min-w-[16px] min-h-[16px]" + width={16} + height={16} + /> + </div> + <div + className={clsx( + 'whitespace-nowrap', + isInternal?.[index] + ? '' + : totalChars > (chars?.[p.integration.id] || 0) + ? 'text-[#FF3F3F]' + : '' + )} + > + {p.integration.name} ( + {capitalize(p.integration.identifier.split('-')[0])}): + </div> + <div + className={clsx( + 'whitespace-nowrap', + isInternal?.[index] + ? '' + : totalChars > (chars?.[p.integration.id] || 0) + ? 'text-[#FF3F3F]' + : '' + )} + > + {isInternal?.[index] + ? 'Internal Edit' + : `${totalChars}/${chars?.[p.integration.id] || 0}`} + </div> + </Fragment> + ))} + </div> + )} + </div> + )} + </div> + ); +}; diff --git a/apps/frontend/src/components/launches/repeat.component.tsx b/apps/frontend/src/components/launches/repeat.component.tsx index 883255bd..416426ab 100644 --- a/apps/frontend/src/components/launches/repeat.component.tsx +++ b/apps/frontend/src/components/launches/repeat.component.tsx @@ -1,42 +1,49 @@ -import { FC } from 'react'; +import { FC, useMemo, useState } from 'react'; import { Select } from '@gitroom/react/form/select'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useClickOutside } from '@mantine/hooks'; +import { isUSCitizen } from '@gitroom/frontend/components/launches/helpers/isuscitizen.utils'; +import clsx from 'clsx'; const list = [ { value: 1, - label: 'Every Day', + label: 'Day', }, { value: 2, - label: 'Every Two Days', + label: 'Two Days', }, { value: 3, - label: 'Every Three Days', + label: 'Three Days', }, { value: 4, - label: 'Every Four Days', + label: 'Four Days', }, { value: 5, - label: 'Every Five Days', + label: 'Five Days', }, { value: 6, - label: 'Every Six Days', + label: 'Six Days', }, { value: 7, - label: 'Every Week', + label: 'Week', }, { value: 14, - label: 'Every Two Weeks', + label: 'Two Weeks', }, { value: 30, - label: 'Every Month', + label: 'Month', + }, + { + value: null, + label: 'Cancel', }, ]; export const RepeatComponent: FC<{ @@ -45,21 +52,95 @@ export const RepeatComponent: FC<{ }> = (props) => { const { repeat } = props; const t = useT(); + const [isOpen, setIsOpen] = useState(false); + + const ref = useClickOutside(() => { + if (!isOpen) { + return; + } + setIsOpen(false); + }); + + const everyLabel = useMemo(() => { + if (!repeat) { + return ''; + } + return list.find((p) => p.value === repeat)?.label; + }, [repeat]); + return ( - <Select - disableForm={true} - label="" - hideErrors={true} - name="repeat" - value={repeat ? repeat : undefined} - onChange={(e) => props.onChange(Number(e.target.value))} + <div + ref={ref} + className={clsx( + 'border rounded-[8px] justify-center flex items-center relative h-[44px] text-[15px] font-[600] select-none', + isOpen ? 'border-[#612BD3]' : 'border-newTextColor/10', + )} > - <option>{t('repeat_post_every', 'Repeat Post Every...')}</option> - {list.map((item) => ( - <option key={item.value} value={item.value}> - {item.label} - </option> - ))} - </Select> + <div + onClick={() => setIsOpen(!isOpen)} + className="px-[16px] justify-center flex gap-[8px] items-center h-full select-none flex-1" + > + <div className="cursor-pointer"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <g clip-path="url(#clip0_2403_67385)"> + <path + d="M14.1667 1.66602L17.5 4.99935M17.5 4.99935L14.1667 8.33268M17.5 4.99935H6.5C5.09987 4.99935 4.3998 4.99935 3.86502 5.27183C3.39462 5.51152 3.01217 5.89397 2.77248 6.36437C2.5 6.89915 2.5 7.59922 2.5 8.99935V9.16602M2.5 14.9993H13.5C14.9001 14.9993 15.6002 14.9993 16.135 14.7269C16.6054 14.4872 16.9878 14.1047 17.2275 13.6343C17.5 13.0995 17.5 12.3995 17.5 10.9993V10.8327M2.5 14.9993L5.83333 18.3327M2.5 14.9993L5.83333 11.666" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2403_67385"> + <rect width="20" height="20" fill="currentColor" /> + </clipPath> + </defs> + </svg> + </div> + <div className="cursor-pointer"> + {repeat + ? `Repeat Post Every ${everyLabel}` + : t('repeat_post_every', 'Repeat Post Every...')} + </div> + <div className="cursor-pointer"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + className={isOpen ? 'rotate-180' : ''} + > + <path + d="M7.4563 8L12.5437 8C12.9494 8 13.1526 8.56798 12.8657 8.90016L10.322 11.8456C10.1442 12.0515 9.85583 12.0515 9.67799 11.8456L7.13429 8.90016C6.84741 8.56798 7.05059 8 7.4563 8Z" + fill="currentColor" + /> + </svg> + </div> + </div> + {isOpen && ( + <div className="z-[300] absolute left-0 bottom-[100%] w-[240px] bg-newBgColorInner p-[12px] menu-shadow -translate-y-[10px] flex flex-col"> + {list.map((p) => ( + <div + onClick={() => { + props.onChange(Number(p.value)); + setIsOpen(false); + }} + key={p.label} + className="h-[40px] py-[8px] px-[20px] -mx-[12px] hover:bg-newBgColor" + > + {p.label} + </div> + ))} + </div> + )} + </div> ); }; diff --git a/apps/frontend/src/components/launches/select.customer.tsx b/apps/frontend/src/components/launches/select.customer.tsx index 82022090..157e5365 100644 --- a/apps/frontend/src/components/launches/select.customer.tsx +++ b/apps/frontend/src/components/launches/select.customer.tsx @@ -1,43 +1,127 @@ -import { Select } from '@gitroom/react/form/select'; import { uniqBy } from 'lodash'; -import React, { FC, useMemo, useState } from 'react'; +import React, { FC, useCallback, useMemo, useRef, useState } from 'react'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import clsx from 'clsx'; +import { useClickOutside } from '@mantine/hooks'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useShallow } from 'zustand/react/shallow'; + export const SelectCustomer: FC<{ onChange: (value: string) => void; integrations: Integrations[]; customer?: string; }> = (props) => { const { onChange, integrations, customer: currentCustomer } = props; + const { setCurrent } = useLaunchStore( + useShallow((state) => ({ + setCurrent: state.setCurrent, + })) + ); + const toaster = useToaster(); const t = useT(); const [customer, setCustomer] = useState(currentCustomer || ''); + const [pos, setPos] = useState<any>({}); + const [open, setOpen] = useState(false); + const ref = useClickOutside(() => { + if (open) { + setOpen(false); + } + }); + + const openClose = useCallback(() => { + if (open) { + setOpen(false); + return; + } + + const { x, y, width, height } = ref.current?.getBoundingClientRect(); + setPos({ top: y + height, left: x }); + setOpen(true); + }, [open]); + const totalCustomers = useMemo(() => { return uniqBy(integrations, (i) => i?.customer?.id).length; }, [integrations]); if (totalCustomers <= 1) { return null; } + return ( - <Select - hideErrors={true} - label="" - name="customer" - value={customer} - onChange={(e) => { - setCustomer(e.target.value); - onChange(e.target.value); - }} - disableForm={true} - > - <option value="">{t('selected_customer', 'Selected Customer')}</option> - {uniqBy(integrations, (u) => u?.customer?.name) - .filter((f) => f.customer?.name) - .map((p) => ( - <option key={p.customer?.id} value={p.customer?.id}> - {t('customer', 'Customer:')} - {p.customer?.name} - </option> - ))} - </Select> + <div className="relative select-none z-[500]" ref={ref}> + <div + data-tooltip-id="tooltip" + data-tooltip-content="Select Customer" + onClick={openClose} + className={clsx( + 'relative z-[20] cursor-pointer h-[42px] rounded-[8px] pl-[16px] pr-[12px] gap-[8px] border flex items-center', + open ? 'border-[#612BD3]' : 'border-newColColor' + )} + > + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M16.6673 17.5C16.6673 16.337 16.6673 15.7555 16.5238 15.2824C16.2006 14.217 15.3669 13.3834 14.3016 13.0602C13.8284 12.9167 13.247 12.9167 12.084 12.9167H7.91732C6.75435 12.9167 6.17286 12.9167 5.6997 13.0602C4.63436 13.3834 3.80068 14.217 3.47752 15.2824C3.33398 15.7555 3.33398 16.337 3.33398 17.5M13.7507 6.25C13.7507 8.32107 12.0717 10 10.0007 10C7.92958 10 6.25065 8.32107 6.25065 6.25C6.25065 4.17893 7.92958 2.5 10.0007 2.5C12.0717 2.5 13.7507 4.17893 13.7507 6.25Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div> + <svg + className={clsx(open && 'rotate-180')} + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M12.5437 8L7.4563 8C7.05059 8 6.84741 8.56798 7.13429 8.90016L9.67799 11.8456C9.85583 12.0515 10.1442 12.0515 10.322 11.8456L12.8657 8.90016C13.1526 8.56798 12.9494 8 12.5437 8Z" + fill="currentColor" + /> + </svg> + </div> + </div> + {open && ( + <div + style={pos} + className="flex flex-col fixed pt-[12px] bg-newBgColorInner menu-shadow min-w-[250px]" + > + <div className="text-[14px] font-[600] px-[12px] mb-[5px]"> + Customers + </div> + {uniqBy(integrations, (u) => u?.customer?.name) + .filter((f) => f.customer?.name) + .map((p) => ( + <div + onClick={() => { + toaster.show( + t('customer_socials_selected', 'Customer socials selected'), + 'success' + ); + setCustomer(p.customer?.id); + onChange(p.customer?.id); + setOpen(false); + setCurrent('global') + }} + key={p.customer?.id} + className="p-[12px] hover:bg-newBgColor text-[14px] font-[500] h-[32px] flex items-center" + > + {p.customer?.name} + </div> + ))} + </div> + )} + </div> ); }; diff --git a/apps/frontend/src/components/launches/tags.component.tsx b/apps/frontend/src/components/launches/tags.component.tsx index b7da39e4..457233b5 100644 --- a/apps/frontend/src/components/launches/tags.component.tsx +++ b/apps/frontend/src/components/launches/tags.component.tsx @@ -8,6 +8,10 @@ import { ColorPicker } from '@gitroom/react/form/color.picker'; import { Button } from '@gitroom/react/form/button'; import { uniqBy } from 'lodash'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useClickOutside } from '@mantine/hooks'; +import clsx from 'clsx'; +import { useModals } from '@gitroom/frontend/components/layout/new-modal'; + export const TagsComponent: FC<{ name: string; label: string; @@ -18,6 +22,254 @@ export const TagsComponent: FC<{ name: string; }; }) => void; +}> = (props) => { + const fetch = useFetch(); + + const loadTags = useCallback(async () => { + return (await fetch('/posts/tags')).json(); + }, []); + + const { data, isLoading, mutate } = useSWR('load-tags', loadTags); + + if (isLoading) { + return null; + } + + return <TagsComponentInner {...props} allTags={data} mutate={mutate} />; +}; + +export const TagsComponentInner: FC<{ + name: string; + label: string; + initial: any[]; + allTags: any; + mutate: () => Promise<any>; + onChange: (event: { + target: { + value: any[]; + name: string; + }; + }) => void; +}> = ({ initial, onChange, name, mutate, allTags: data }) => { + const t = useT(); + const [isOpen, setIsOpen] = useState(false); + const [tagValue, setTagValue] = useState<any[]>( + (initial?.slice(0) || []).map((p: any) => { + return data?.tags.find((a: any) => a.name === p.value) || p; + }) + ); + const modals = useModals(); + + const ref = useClickOutside(() => { + if (!isOpen) { + return; + } + setIsOpen(false); + }); + + const addTag = useCallback(async () => { + const val: string | undefined = await new Promise((resolve) => { + modals.openModal({ + title: 'Add new tag', + children: (close) => ( + <ShowModal tag="" close={close} resolve={resolve} /> + ), + }); + }); + + const newValues = await mutate(); + + if (!val) { + return; + } + + const newTag = newValues.tags.find((p: any) => p.name === val); + if (newTag) { + const modify = [...tagValue, newTag]; + setTagValue(modify); + onChange({ + target: { + value: modify, + name, + }, + }); + } + }, []); + + return ( + <div + ref={ref} + className={clsx( + 'border rounded-[8px] justify-center flex items-center relative h-[44px] text-[15px] font-[600] select-none', + isOpen ? 'border-[#612BD3]' : 'border-newTextColor/10' + )} + > + <div + onClick={() => setIsOpen(!isOpen)} + className="px-[16px] justify-center flex gap-[8px] items-center h-full select-none flex-1" + > + <div className="cursor-pointer"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="17" + height="19" + viewBox="0 0 17 19" + fill="none" + > + <path + d="M15.75 8.25L9.42157 1.92157C8.98919 1.48919 8.773 1.273 8.52071 1.1184C8.29703 0.981328 8.05317 0.880317 7.79808 0.819075C7.51036 0.75 7.20462 0.75 6.59314 0.75L3.25 0.75M0.75 6.33333L0.75 7.97876C0.75 8.38641 0.75 8.59024 0.79605 8.78205C0.836878 8.95211 0.904218 9.11469 0.9956 9.26381C1.09867 9.432 1.2428 9.57613 1.53105 9.86438L8.03105 16.3644C8.69108 17.0244 9.02109 17.3544 9.40164 17.4781C9.73638 17.5868 10.097 17.5868 10.4317 17.4781C10.8122 17.3544 11.1423 17.0244 11.8023 16.3644L13.8644 14.3023C14.5244 13.6423 14.8544 13.3122 14.9781 12.9317C15.0868 12.597 15.0868 12.2364 14.9781 11.9016C14.8544 11.5211 14.5244 11.1911 13.8644 10.531L7.78105 4.44772C7.4928 4.15946 7.34867 4.01534 7.18048 3.91227C7.03135 3.82089 6.86878 3.75354 6.69872 3.71272C6.50691 3.66667 6.30308 3.66667 5.89543 3.66667H3.41667C2.48325 3.66667 2.01654 3.66667 1.66002 3.84832C1.34641 4.00811 1.09145 4.26308 0.931656 4.57668C0.75 4.9332 0.75 5.39991 0.75 6.33333Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="cursor-pointer flex gap-[4px]"> + {tagValue.length === 0 ? ( + 'Add New Tag' + ) : ( + <> + <div + className="h-full flex justify-center items-center px-[8px] rounded-[4px]" + style={{ backgroundColor: tagValue[0].color }} + > + <span className="mix-blend-difference text-[#fff]"> + {tagValue[0].name} + </span> + </div> + {tagValue.length > 1 ? <span>+{tagValue.length - 1}</span> : null} + </> + )} + </div> + <div className="cursor-pointer"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + className={isOpen ? 'rotate-180' : ''} + > + <path + d="M7.4563 8L12.5437 8C12.9494 8 13.1526 8.56798 12.8657 8.90016L10.322 11.8456C10.1442 12.0515 9.85583 12.0515 9.67799 11.8456L7.13429 8.90016C6.84741 8.56798 7.05059 8 7.4563 8Z" + fill="currentColor" + /> + </svg> + </div> + </div> + {isOpen && ( + <div className="z-[300] absolute left-0 bottom-[100%] w-[240px] bg-newBgColorInner p-[12px] menu-shadow -translate-y-[10px] flex flex-col"> + {(data?.tags || []).map((p: any) => ( + <div + onClick={() => { + const exists = !!tagValue.find((a) => a.id === p.id); + let modify = []; + if (exists) { + modify = tagValue.filter((a) => a.id !== p.id); + } else { + modify = [...tagValue, p]; + } + setTagValue(modify); + onChange({ + target: { + value: modify.map((p: any) => ({ + label: p.name, + value: p.name, + })), + name, + }, + }); + }} + key={p.name} + className="h-[40px] py-[8px] px-[20px] -mx-[12px] flex gap-[8px]" + > + <Check + onChange={() => {}} + value={!!tagValue.find((a) => a.id === p.id)} + /> + <div + className="h-full flex justify-center items-center px-[8px] rounded-[8px]" + style={{ backgroundColor: p.color }} + > + <span className="mix-blend-difference text-[#fff]"> + {p.name} + </span> + </div> + </div> + ))} + <div + onClick={addTag} + className="cursor-pointer gap-[8px] flex w-full h-[34px] rounded-[8px] mt-[12px] px-[16px] justify-center items-center bg-[#612BD3] text-white" + > + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <path + d="M8.00065 3.33301V12.6663M3.33398 7.99967H12.6673" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="text-[13px] font-[600]">Add New Tag</div> + </div> + </div> + )} + </div> + ); +}; + +const Check: FC<{ value: boolean; onChange: (value: boolean) => void }> = ({ + value, + onChange, +}) => { + return ( + <div + onClick={() => onChange(!value)} + className={clsx( + 'text-[10px] font-[500] text-center flex border border-btnSimple rounded-[6px] w-[20px] h-[20px] justify-center items-center', + value && 'bg-[#612BD3]' + )} + > + {value ? ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="11" + height="8" + viewBox="0 0 11 8" + fill="none" + > + <path + fillRule="evenodd" + clipRule="evenodd" + d="M10.7071 0.292893C11.0976 0.683417 11.0976 1.31658 10.7071 1.70711L4.70711 7.70711C4.31658 8.09763 3.68342 8.09763 3.29289 7.70711L0.292893 4.70711C-0.0976311 4.31658 -0.0976311 3.68342 0.292893 3.29289C0.683417 2.90237 1.31658 2.90237 1.70711 3.29289L4 5.58579L9.29289 0.292893C9.68342 -0.0976311 10.3166 -0.0976311 10.7071 0.292893Z" + fill="white" + /> + </svg> + ) : ( + '' + )} + </div> + ); +}; +export const TagsComponentA: FC<{ + name: string; + label: string; + initial: any[]; + onChange: (event: { + target: { + value: any[]; + name: string; + }; + }) => void; }> = (props) => { const { onChange, name, initial } = props; const fetch = useFetch(); @@ -228,53 +480,28 @@ const ShowModal: FC<{ }), }); resolve(tagName); + close(); }, [tagName, color, id]); return ( - <div className="bg-black/40 fixed start-0 top-0 w-full h-full z-[500]"> - <div className="relative w-[500px] mx-auto flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0"> - <TopTitle title={`Create a new tag`} /> - <button - className="outline-none absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - onClick={close} - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - - <div> - <Input - name="name" - disableForm={true} - label="Name" - value={tagName} - onChange={(e) => setTagName(e.target.value)} - /> - <ColorPicker - onChange={(e) => setColor(e.target.value)} - label="Tag Color" - name="color" - value={color} - enabled={true} - canBeCancelled={false} - /> - <Button onClick={save} className="mt-[16px]"> - {t('save', 'Save')} - </Button> - </div> - </div> + <div> + <Input + name="name" + disableForm={true} + label="Name" + value={tagName} + onChange={(e) => setTagName(e.target.value)} + /> + <ColorPicker + onChange={(e) => setColor(e.target.value)} + label="Tag Color" + name="color" + value={color} + enabled={true} + canBeCancelled={false} + /> + <Button onClick={save} className="mt-[16px]"> + {t('save', 'Save')} + </Button> </div> ); }; diff --git a/apps/frontend/src/components/launches/up.down.arrow.tsx b/apps/frontend/src/components/launches/up.down.arrow.tsx index b4ad786c..f88b29fd 100644 --- a/apps/frontend/src/components/launches/up.down.arrow.tsx +++ b/apps/frontend/src/components/launches/up.down.arrow.tsx @@ -7,17 +7,20 @@ const Arrow: FC<{ return ( <svg xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - viewBox="0 0 16 16" - fill="currentColor" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" style={{ - transform: flip ? 'rotate(180deg)' : '', + transform: !flip ? 'rotate(180deg)' : '', }} > <path - d="M13.354 6.35378L8.35403 11.3538C8.30759 11.4003 8.25245 11.4372 8.19175 11.4623C8.13105 11.4875 8.06599 11.5004 8.00028 11.5004C7.93457 11.5004 7.86951 11.4875 7.80881 11.4623C7.74811 11.4372 7.69296 11.4003 7.64653 11.3538L2.64653 6.35378C2.55271 6.25996 2.5 6.13272 2.5 6.00003C2.5 5.86735 2.55271 5.7401 2.64653 5.64628C2.74035 5.55246 2.8676 5.49976 3.00028 5.49976C3.13296 5.49976 3.26021 5.55246 3.35403 5.64628L8.00028 10.2932L12.6465 5.64628C12.693 5.59983 12.7481 5.56298 12.8088 5.53784C12.8695 5.5127 12.9346 5.49976 13.0003 5.49976C13.066 5.49976 13.131 5.5127 13.1917 5.53784C13.2524 5.56298 13.3076 5.59983 13.354 5.64628C13.4005 5.69274 13.4373 5.74789 13.4625 5.80859C13.4876 5.86928 13.5006 5.93434 13.5006 6.00003C13.5006 6.06573 13.4876 6.13079 13.4625 6.19148C13.4373 6.25218 13.4005 6.30733 13.354 6.35378Z" - fill="currentColor" + d="M15 12.5L10 7.5L5 12.5" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> ); @@ -35,14 +38,14 @@ export const UpDownArrow: FC<{ [] ); return ( - <div className="flex flex-col"> + <div className="flex flex-col gap-[8px] pt-[8px]"> <button onClick={changePosition('up')} className={clsx( - 'outline-none rounded-tl-[20px] rounded-tr-[20px] w-[24px] h-[24px] flex justify-center items-center', + 'outline-none w-[20px] h-[20px] flex justify-center items-center', isUp - ? 'bg-input hover:bg-seventh cursor-pointer' - : 'bg-customColor8 pointer-events-none text-textColor' + ? 'cursor-pointer' + : 'pointer-events-none text-textColor opacity-50' )} > <Arrow flip={true} /> @@ -50,10 +53,10 @@ export const UpDownArrow: FC<{ <button onClick={changePosition('down')} className={clsx( - 'outline-none rounded-bl-[20px] rounded-br-[20px] w-[24px] h-[24px] flex justify-center items-center', + 'outline-none rounded-bl-[20px] w-[20px] h-[20px] flex justify-center items-center', isDown - ? 'bg-input hover:bg-seventh cursor-pointer' - : 'bg-customColor8 pointer-events-none text-textColor' + ? 'cursor-pointer' + : 'pointer-events-none text-textColor opacity-50' )} > <Arrow flip={false} /> diff --git a/apps/frontend/src/components/layout/drop.files.tsx b/apps/frontend/src/components/layout/drop.files.tsx index e802e99e..746c1585 100644 --- a/apps/frontend/src/components/layout/drop.files.tsx +++ b/apps/frontend/src/components/layout/drop.files.tsx @@ -1,8 +1,10 @@ import { useDropzone } from 'react-dropzone'; import { FC, ReactNode } from 'react'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import clsx from 'clsx'; export const DropFiles: FC<{ children: ReactNode; + className?: string; onDrop: (files: File[]) => void; }> = (props) => { const t = useT(); @@ -11,7 +13,7 @@ export const DropFiles: FC<{ onDrop: props.onDrop, }); return ( - <div {...getRootProps()} className="relative"> + <div {...getRootProps()} className={clsx("relative", props.className)}> {isDragActive && ( <div className="absolute start-0 top-0 w-full h-full bg-black/90 flex items-center justify-center z-[200] animate-normalFadeIn"> {t('drag_n_drop_some_files_here', 'Drag n drop some files here')} diff --git a/apps/frontend/src/components/layout/new-modal.tsx b/apps/frontend/src/components/layout/new-modal.tsx index 9e807b9e..bcaaf61e 100644 --- a/apps/frontend/src/components/layout/new-modal.tsx +++ b/apps/frontend/src/components/layout/new-modal.tsx @@ -20,6 +20,7 @@ interface OpenModalInterface { title?: string; closeOnClickOutside?: boolean; removeLayout?: boolean; + fullScreen?: boolean; closeOnEscape?: boolean; withCloseButton?: boolean; askClose?: boolean; @@ -131,14 +132,25 @@ export const Component: FC<{ <div style={{ zIndex }} className={clsx( - 'fixed flex left-0 top-0 min-w-full min-h-full bg-popup transition-all animate-fadeIn overflow-y-auto pb-[50px] text-newTextColor', + !modal.fullScreen + ? 'pb-[50px] min-w-full min-h-full' + : 'w-full h-full', + 'fixed flex left-0 top-0 bg-popup transition-all animate-fadeIn overflow-y-auto text-newTextColor', !isLast && '!overflow-hidden' )} > - <div className="relative flex-1"> - <div className="absolute top-0 left-0 min-w-full min-h-full"> + <div className={clsx(modal.fullScreen && 'flex', 'relative flex-1')}> + <div + className={clsx( + modal.fullScreen + ? 'flex flex-1' + : 'absolute top-0 left-0 min-w-full min-h-full' + )} + > <div - className="mx-auto py-[48px]" + className={clsx( + modal.fullScreen ? 'w-full h-full flex-1' : 'mx-auto py-[48px]' + )} {...(modal.size && { style: { width: modal.size } })} > {typeof modal.children === 'function' @@ -156,17 +168,34 @@ export const Component: FC<{ <div onClick={closeModalFunction} style={{ zIndex }} - className="fixed flex left-0 top-0 min-w-full min-h-full bg-popup transition-all animate-fadeIn overflow-y-auto pb-[50px] text-newTextColor" + className={clsx( + 'fixed flex left-0 top-0 min-w-full min-h-full bg-popup transition-all animate-fadeIn overflow-y-auto text-newTextColor', + !modal.fullScreen && 'pb-[50px]' + )} > <div className="relative flex-1"> - <div className="absolute top-0 left-0 min-w-full min-h-full pt-[100px] pb-[100px]"> + <div + className={clsx( + 'absolute min-w-full', + !modal.fullScreen + ? 'min-h-full pt-[100px] pb-[100px]' + : 'h-screen', + modal.size && modal.height ? 'flex justify-center items-center' : 'top-0 left-0' + )} + > <div className={clsx( !modal.removeLayout && 'gap-[40px] p-[32px]', 'bg-newBgColorInner mx-auto flex flex-col w-fit rounded-[24px] relative', - modal.size ? '' : 'min-w-[600px]' + modal.size ? '' : 'min-w-[600px]', + modal.fullScreen && 'h-full', )} - {...(modal.size && { style: { width: modal.size } })} + {...((!!modal.size || !!modal.height) && { + style: { + ...(modal.size ? { width: modal.size } : {}), + ...(modal.height ? { height: modal.height } : {}), + }, + })} onClick={(e) => e.stopPropagation()} > <div className="flex items-center"> @@ -199,7 +228,7 @@ export const Component: FC<{ </div> ) : null} </div> - <div className="whitespace-pre-line">{RenderComponent}</div> + <div className={clsx("whitespace-pre-line", !!modal.height && !!modal.size && 'flex flex-1 flex-col')}>{RenderComponent}</div> </div> </div> </div> diff --git a/apps/frontend/src/components/layout/support.tsx b/apps/frontend/src/components/layout/support.tsx index 3dc543d7..77de166b 100644 --- a/apps/frontend/src/components/layout/support.tsx +++ b/apps/frontend/src/components/layout/support.tsx @@ -19,6 +19,7 @@ export const Support = () => { if (!discordUrl || !show) return null; return ( <div + id="support-discord" className="bg-customColor39 w-[194px] h-[58px] fixed end-[20px] bottom-[20px] z-[500] text-[16px] text-customColor40 rounded-[30px] !rounded-br-[0] cursor-pointer flex justify-center items-center gap-[10px]" onClick={() => window.open(discordUrl)} > diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index f4ecf897..010208a8 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -1,6 +1,7 @@ 'use client'; import React, { + ChangeEvent, ClipboardEvent, FC, Fragment, @@ -20,7 +21,10 @@ import EventEmitter from 'events'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import clsx from 'clsx'; import { VideoFrame } from '@gitroom/react/helpers/video.frame'; -import { MultipartFileUploader } from '@gitroom/frontend/components/media/new.uploader'; +import { + MultipartFileUploader, + useUppyUploader, +} from '@gitroom/frontend/components/media/new.uploader'; import dynamic from 'next/dynamic'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { AiImage } from '@gitroom/frontend/components/launches/ai.image'; @@ -37,6 +41,8 @@ import { import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { AiVideo } from '@gitroom/frontend/components/launches/ai.video'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; +import { Dashboard } from '@uppy/react'; +import { timer } from '@gitroom/helpers/utils/timer'; const Polonto = dynamic( () => import('@gitroom/frontend/components/launches/polonto') ); @@ -88,7 +94,7 @@ export const Pagination: FC<{ aria-current="page" onClick={() => setPage(page)} className={clsx( - 'cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border hover:bg-forth h-10 w-10 hover:text-white border-[#1F1F1F]', + 'cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border hover:bg-forth h-10 w-10 hover:text-white border-newBorder', current === page ? 'bg-forth !text-white' : 'text-textColor hover:text-white' @@ -163,96 +169,71 @@ export const MediaBox: FC<{ standalone?: boolean; type?: 'image' | 'video'; closeModal: () => void; -}> = (props) => { - const { setMedia, type, closeModal } = props; - const [mediaList, setListMedia] = useState<Media[]>([]); - const setActivateExitButton = useLaunchStore((e) => e.setActivateExitButton); - const fetch = useFetch(); - const mediaDirectory = useMediaDirectory(); +}> = ({ type, standalone, setMedia }) => { const [page, setPage] = useState(0); - const [pages, setPages] = useState(0); - const [selectedMedia, setSelectedMedia] = useState<Media[]>([]); - const ref = useRef<any>(null); - - useEffect(() => { - setActivateExitButton(false); - return () => { - setActivateExitButton(true); - }; - }, []); - + const fetch = useFetch(); + const modals = useModals(); const loadMedia = useCallback(async () => { return (await fetch(`/media?page=${page + 1}`)).json(); }, [page]); + const { data, mutate, isLoading } = useSWR(`get-media-${page}`, loadMedia); + const [selected, setSelected] = useState([]); + const t = useT(); + const uploaderRef = useRef<any>(null); + const mediaDirectory = useMediaDirectory(); - const setNewMedia = useCallback( - (media: Media) => () => { - if (props.standalone) { + const uppy = useUppyUploader({ + allowedFileTypes: + type == 'image' + ? 'image/*' + : type == 'video' + ? 'video/mp4' + : 'image/*,video/mp4', + onUploadSuccess: async (arr) => { + uppy.clear(); + await mutate(); + if (standalone) { return; } - setSelectedMedia( - selectedMedia.find((p) => p.id === media.id) - ? selectedMedia.filter((f) => f.id !== media.id) - : [ - ...selectedMedia.map((p) => ({ - ...p, - })), - { - ...media, - }, - ] - ); + setSelected((prevSelected) => { + return [...prevSelected, ...arr]; + }); }, - [selectedMedia] - ); + }); - const addNewMedia = useCallback( - (media: Media[]) => () => { - if (props.standalone) { + const addRemoveSelected = useCallback( + (media: any) => () => { + if (standalone) { return; } - setSelectedMedia((currentMedia) => [...currentMedia, ...media]); - // closeModal(); + const exists = selected.find((p: any) => p.id === media.id); + if (exists) { + setSelected(selected.filter((f: any) => f.id !== media.id)); + return; + } + setSelected([...selected, media]); }, - [selectedMedia] + [selected] ); + const addMedia = useCallback(async () => { - if (props.standalone) { + if (standalone) { return; } // @ts-ignore - setMedia(selectedMedia); - closeModal(); - }, [selectedMedia]); - const { data, mutate } = useSWR(`get-media-${page}`, loadMedia); + setMedia(selected); + modals.closeCurrent(); + }, [selected]); - const finishUpload = useCallback( - async (res: any) => { - const lastMedia = mediaList?.[0]?.id; - const newData = await mutate(); - const untilLastMedia = newData.results.findIndex( - (f: any) => f.id === lastMedia - ); - const onlyNewMedia = newData.results.slice( - 0, - untilLastMedia === -1 ? newData.results.length : untilLastMedia - ); - - if (props.standalone) { - return; - } - - addNewMedia(onlyNewMedia)(); - }, - [mutate, addNewMedia, mediaList, selectedMedia] - ); + const addToUpload = useCallback(async (e: ChangeEvent<HTMLInputElement>) => { + const files = Array.from(e.target.files).slice(0, 5); + for (const file of files) { + uppy.addFile(file); + } + }, []); const dragAndDrop = useCallback( async (event: ClipboardEvent<HTMLDivElement> | File[]) => { - if (!ref?.current?.setOptions) { - return; - } - // @ts-ignore const clipboardItems = event.map((p) => ({ kind: 'file', @@ -261,39 +242,26 @@ export const MediaBox: FC<{ if (!clipboardItems) { return; } - const files: File[] = []; + const files = []; // @ts-ignore for (const item of clipboardItems) { if (item.kind === 'file') { const file = item.getAsFile(); if (file) { - const isImage = file.type.startsWith('image/'); - const isVideo = file.type.startsWith('video/'); - if (isImage || isVideo) { - files.push(file); // Collect images or videos - } + files.push(file); } } } - if (files.length === 0) { - return; + + for (const file of files.slice(0, 5)) { + uppy.addFile(file); } - ref.current.setOptions({ - autoProceed: false, - }); - for (const file of files) { - ref.current.addFile(file); - await ref.current.upload(); - ref.current.clear(); - } - ref.current.setOptions({ - autoProceed: true, - }); }, - [mutate, addNewMedia, mediaList, selectedMedia] + [] ); - const removeItem = useCallback( + + const deleteImage = useCallback( (media: Media) => async (e: any) => { e.stopPropagation(); if ( @@ -314,212 +282,270 @@ export const MediaBox: FC<{ [mutate] ); - const refNew = useRef(null); - - useEffect(() => { - if (data?.pages) { - setPages(data.pages); - } - if (data?.results && data?.results?.length) { - setListMedia([...data.results]); - } - }, [data]); - - useEffect(() => { - refNew?.current?.scrollIntoView({ - behavior: 'smooth', - }); + const btn = useMemo(() => { + return ( + <button + onClick={() => uploaderRef?.current?.click()} + className="cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]" + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="14" + height="14" + viewBox="0 0 14 14" + fill="none" + > + <path + d="M6.58333 0.75V12.4167M0.75 6.58333H12.4167" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + <div>Upload</div> + </button> + ); }, []); - const t = useT(); - return ( - <div - {...(props.standalone - ? { - className: - 'bg-newBgColorInner p-[20px] flex flex-col gap-[15px] transition-all', - } - : { - ref: refNew, - className: - 'removeEditor fixed start-0 top-0 bg-primary/80 z-[300] w-full min-h-full p-4 md:p-[60px] animate-fade', - })} - > - <div - {...(props.standalone - ? {} - : { - className: - 'max-w-[1000px] w-full h-full bg-newBgColorInner border-tableBorder border-2 rounded-xl relative mx-auto', - })} - > - <DropFiles onDrop={dragAndDrop}> - <div className="pb-[20px] px-[20px] w-full h-full"> - <div className="flex flex-col"> - <div className="flex-1"> - {!props.standalone ? ( - <TopTitle title="Media Library" /> - ) : ( - <div className="h-[100px]" /> - )} - </div> - {!props.standalone ? ( - <button - onClick={closeModal} - className="outline-none z-[300] absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root bg-primary hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - ) : ( - <div /> - )} - - <div className="absolute flex justify-center mt-[55px] items-center pointer-events-none text-center h-[57px] w-full start-0 rounded-lg transition-all group text-sm font-semibold bg-transparent text-gray-800 hover:bg-gray-100 focus:text-primary-500"> - {t( - 'select_or_upload_pictures_maximum_5_at_a_time', - 'Select or upload pictures (maximum 5 at a time)' - )} - <br /> - {t( - 'you_can_also_drag_drop_pictures', - 'You can also drag & drop pictures' - )} - </div> - - {!!mediaList.length && ( - <> - <div className="flex absolute h-[57px] w-full start-0 top-0 rounded-lg transition-all group text-sm font-semibold bg-transparent text-gray-800 hover:bg-gray-100 focus:text-primary-500"> - <div className="relative flex flex-1 pe-[55px] gap-2 items-center justify-center"> - <div className="flex-1" /> - <MultipartFileUploader - uppRef={ref} - onUploadSuccess={finishUpload} - allowedFileTypes={ - type === 'video' - ? 'video/mp4' - : type === 'image' - ? 'image/*' - : 'image/*,video/mp4' - } - /> - </div> - </div> - </> - )} + <DropFiles className="flex flex-col flex-1" onDrop={dragAndDrop}> + <div className="flex flex-col flex-1"> + <div + className={clsx( + 'flex', + !isLoading && !data?.results?.length && 'hidden' + )} + > + {!isLoading && !!data?.results?.length && ( + <div className="flex-1 text-[14px] font-[600] whitespace-pre-line"> + Select or upload pictures (maximum 5 at a time).{'\n'} + You can also drag & drop pictures. </div> - <div - className={clsx( - 'flex flex-wrap gap-[10px] mt-[35px] pt-[20px]', - !!mediaList.length && - 'justify-center items-center text-textColor' - )} - > - {!mediaList.length ? ( - <div className="flex flex-col text-center items-center justify-center mx-auto"> - <div> - {t( - 'you_don_t_have_any_assets_yet', - "You don't have any assets yet." - )} - </div> - <div> - {t( - 'click_the_button_below_to_upload_one', - 'Click the button below to upload one' - )} - </div> - <div className="mt-[10px] justify-center items-center flex flex-col-reverse gap-[10px]"> - <MultipartFileUploader - onUploadSuccess={finishUpload} - allowedFileTypes={ - type === 'video' - ? 'video/mp4' - : type === 'image' - ? 'image/*' - : 'image/*,video/mp4' - } - /> - </div> + )} + <input + type="file" + ref={uploaderRef} + onChange={addToUpload} + className="hidden" + multiple={true} + /> + {!isLoading && !!data?.results?.length && btn} + </div> + <div className="w-full pointer-events-none relative mt-[5px] mb-[5px]"> + <div className="w-full h-[46px] overflow-hidden absolute left-0 bg-newBgColorInner uppyChange"> + <Dashboard + height={46} + uppy={uppy} + id={`uploader`} + showProgressDetails={true} + hideUploadButton={true} + hideRetryButton={true} + hidePauseResumeButton={true} + hideCancelButton={true} + hideProgressAfterFinish={true} + /> + </div> + <div className="w-full h-[46px] uppyChange" /> + </div> + <div + className={clsx( + 'flex-1 relative', + !isLoading && + !data?.results?.length && + 'bg-newTextColor/[0.02] rounded-[12px]' + )} + > + <div + className={clsx( + 'absolute -left-[3px] -top-[3px] withp3 h-full overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner', + !isLoading && + !data?.results?.length && + 'flex justify-center items-center gap-[20px] flex-col' + )} + > + {!isLoading && !data?.results?.length && ( + <> + <svg + width="192" + height="151" + viewBox="0 0 192 151" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M109.75 59.0141C104.489 59.0141 113.46 -5.73557 91.0289 1.57563C69.7021 8.5269 99.5229 59.0141 94.5119 59.0141C89.5009 59.0141 54.4775 56.107 52.1458 71.9377C49.5418 89.6178 95.4225 79.7216 96.7894 81.9895C98.1563 84.2573 78.775 111.109 91.0289 119.324C103.724 127.835 119.934 96.3491 122.711 96.3491C125.489 96.3491 139.845 147.93 151.514 133.684C160.997 122.106 138.391 96.3491 142.873 96.3491C147.355 96.3491 180.793 98.9658 186.076 81.9895C192.534 61.2424 134.828 76.0575 131.352 71.9377C127.876 67.818 159.167 34.7484 142.873 25.987C126.785 17.3361 115.012 59.0141 109.75 59.0141Z" + stroke="white" + strokeOpacity="0.08" + strokeWidth="2" + strokeLinecap="round" + /> + <rect + x="22.6328" + y="62.541" + width="49.2079" + height="49.2079" + rx="12.6792" + transform="rotate(-16.275 22.6328 62.541)" + fill="#232222" + /> + <path + d="M66.8573 81.5379L60.538 73.8505C59.3847 72.4421 58.0986 71.8279 56.9172 72.1076C55.7477 72.3838 54.8664 73.5134 54.4627 75.298L53.377 80.0552C53.1492 81.0592 52.6158 81.7748 51.8894 82.0519C51.1544 82.3446 50.2829 82.1692 49.4433 81.5678L49.0813 81.3089C47.9176 80.4896 46.7111 80.2818 45.6626 80.705C44.6142 81.1282 43.9074 82.1418 43.6491 83.5323L42.7814 88.278C42.4752 89.995 43.055 91.7139 44.3442 92.8742C45.6334 94.0345 47.406 94.4417 49.0739 93.9549L64.3851 89.4863C65.9931 89.017 67.2584 87.7753 67.7541 86.1722C68.2738 84.5621 67.924 82.8282 66.8573 81.5379Z" + fill="white" + fillOpacity="0.4" + /> + <path + d="M45.8412 76.6818C48.0811 76.0281 49.367 73.6823 48.7133 71.4423C48.0595 69.2024 45.7137 67.9165 43.4738 68.5702C41.2338 69.2239 39.9479 71.5697 40.6017 73.8097C41.2554 76.0497 43.6012 77.3355 45.8412 76.6818Z" + fill="white" + fillOpacity="0.4" + /> + <rect + x="64.8125" + y="70.6133" + width="66.3578" + height="66.3578" + rx="18.1132" + fill="#2C2B2B" + /> + <path + d="M80.1261 117.087L80.0882 117.125C79.5762 116.006 79.2538 114.735 79.1211 113.332C79.2538 114.716 79.6141 115.968 80.1261 117.087Z" + fill="white" + fillOpacity="0.4" + /> + <path + d="M92.3022 100.72C94.7948 100.72 96.8154 98.6991 96.8154 96.2065C96.8154 93.714 94.7948 91.6934 92.3022 91.6934C89.8097 91.6934 87.7891 93.714 87.7891 96.2065C87.7891 98.6991 89.8097 100.72 92.3022 100.72Z" + fill="white" + fillOpacity="0.4" + /> + <path + d="M105.936 84.8301H90.0448C83.1423 84.8301 79.0273 88.945 79.0273 95.8476V111.739C79.0273 113.805 79.3876 115.607 80.0893 117.124C81.7201 120.727 85.2093 122.756 90.0448 122.756H105.936C112.838 122.756 116.953 118.641 116.953 111.739V107.396V95.8476C116.953 88.945 112.838 84.8301 105.936 84.8301ZM113.862 104.741C112.383 103.471 109.994 103.471 108.515 104.741L100.626 111.511C99.147 112.781 96.7577 112.781 95.2786 111.511L94.6339 110.98C93.2875 109.804 91.1447 109.691 89.6276 110.715L82.5355 115.474C82.1183 114.412 81.8718 113.18 81.8718 111.739V95.8476C81.8718 90.5 84.6973 87.6745 90.0448 87.6745H105.936C111.283 87.6745 114.109 90.5 114.109 95.8476V104.95L113.862 104.741Z" + fill="white" + fillOpacity="0.4" + /> + </svg> + <div className="text-[20px] font-[600]"> + You don't have any media yet </div> - ) : ( - <> - {selectedMedia.length > 0 && ( - <div className="flex justify-center absolute top-[7px] text-white"> - <Button - onClick={props.standalone ? () => {} : addMedia} - className="!text-white" - > - <span className="!text-white"> - {t('add_selected_media', 'Add selected media')} - </span> - </Button> - </div> - )} - </> - )} - {mediaList - .filter((f) => { - if (type === 'video') { - return f.path.indexOf('mp4') > -1; - } else if (type === 'image') { - return f.path.indexOf('mp4') === -1; - } - return true; - }) - .map((media) => ( + <div className="whitespace-pre-line text-newTextColor/[0.6] text-center"> + Select or upload pictures (maximum 5 at a time). {'\n'} + You can also drag & drop pictures. + </div> + <div className="forceChange">{btn}</div> + </> + )} + {isLoading && ( + <> + {[...new Array(16)].map((_, i) => ( <div - key={media.id} className={clsx( - 'w-[120px] h-[120px] flex select-none relative cursor-pointer', - selectedMedia.find((p) => p.id === media.id) - ? 'border-4 border-forth' - : 'border-tableBorder border-2' + 'px-[3px] py-[3px] float-left rounded-[6px] cursor-pointer w8-max aspect-square' )} - onClick={props.standalone ? () => {} : setNewMedia(media)} + key={i} > - <div - onClick={removeItem(media)} - className="border border-red-400 !text-white flex justify-center items-center absolute w-[20px] z-[100] h-[20px] rounded-full bg-red-700 -top-[5px] -end-[5px]" - > - X - </div> - - {media.path.indexOf('mp4') > -1 ? ( - <VideoFrame url={mediaDirectory.set(media.path)} /> - ) : ( - <Image - width={120} - height={120} - className="w-full h-full object-cover" - src={mediaDirectory.set(media.path)} - alt="media" - /> - )} + <div className="w-full h-full bg-newSep rounded-[6px] animate-pulse" /> </div> ))} - </div> - {(pages || 0) > 1 && ( - <Pagination current={page} totalPages={pages} setPage={setPage} /> + </> + )} + {data?.results + ?.filter((f: any) => { + if (type === 'video') { + return f.path.indexOf('mp4') > -1; + } else if (type === 'image') { + return f.path.indexOf('mp4') === -1; + } + return true; + }) + .map((media: any) => ( + <div + className={clsx( + 'group px-[3px] py-[3px] float-left rounded-[6px] w8-max aspect-square', + !standalone && 'cursor-pointer' + )} + key={media.id} + > + <div + className={clsx( + 'w-full h-full rounded-[6px] border-[4px] relative', + !!selected.find((p) => p.id === media.id) + ? 'border-[#612BD3]' + : 'border-transparent' + )} + onClick={addRemoveSelected(media)} + > + {!!selected.find((p: any) => p.id === media.id) ? ( + <div className="flex justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]"> + {selected.findIndex((z: any) => z.id === media.id) + 1} + </div> + ) : ( + <svg + className="cursor-pointer hidden z-[100] group-hover:block absolute -top-[5px] -end-[5px]" + onClick={deleteImage(media)} + xmlns="http://www.w3.org/2000/svg" + width="18" + height="18" + viewBox="0 0 18 18" + fill="none" + > + <ellipse + cx="9.96484" + cy="9.10742" + rx="6" + ry="5.5" + fill="white" + /> + <path + d="M9 1.5C4.8675 1.5 1.5 4.8675 1.5 9C1.5 13.1325 4.8675 16.5 9 16.5C13.1325 16.5 16.5 13.1325 16.5 9C16.5 4.8675 13.1325 1.5 9 1.5ZM11.52 10.725C11.7375 10.9425 11.7375 11.3025 11.52 11.52C11.4075 11.6325 11.265 11.685 11.1225 11.685C10.98 11.685 10.8375 11.6325 10.725 11.52L9 9.795L7.275 11.52C7.1625 11.6325 7.02 11.685 6.8775 11.685C6.735 11.685 6.5925 11.6325 6.48 11.52C6.2625 11.3025 6.2625 10.9425 6.48 10.725L8.205 9L6.48 7.275C6.2625 7.0575 6.2625 6.6975 6.48 6.48C6.6975 6.2625 7.0575 6.2625 7.275 6.48L9 8.205L10.725 6.48C10.9425 6.2625 11.3025 6.2625 11.52 6.48C11.7375 6.6975 11.7375 7.0575 11.52 7.275L9.795 9L11.52 10.725Z" + fill="#FF3535" + /> + </svg> + )} + <div className="w-full h-full rounded-[6px] overflow-hidden"> + {media.path.indexOf('mp4') > -1 ? ( + <VideoFrame url={mediaDirectory.set(media.path)} /> + ) : ( + <img + width="100%" + height="100%" + className="w-full h-full object-cover" + src={mediaDirectory.set(media.path)} + alt="media" + /> + )} + </div> + </div> + </div> + ))} + </div> + </div> + {(data?.pages || 0) > 1 && ( + <Pagination + current={page} + totalPages={data?.pages} + setPage={setPage} + /> + )} + {!standalone && ( + <div className="flex justify-end mt-[32px] gap-[8px]"> + <button + onClick={() => modals.closeCurrent()} + className="cursor-pointer h-[52px] px-[20px] items-center justify-center border border-newTextColor/10 flex rounded-[10px]" + > + Cancel + </button> + {!isLoading && !!data?.results?.length && ( + <button + onClick={standalone ? () => {} : addMedia} + disabled={selected.length === 0} + className="cursor-pointer text-white disabled:opacity-80 disabled:cursor-not-allowed h-[52px] px-[20px] items-center justify-center bg-[#612BD3] flex rounded-[10px]" + > + {t('add_selected_media', 'Add selected media')} + </button> )} </div> - </DropFiles> + )} </div> - </div> + </DropFiles> ); }; export const MultiMediaComponent: FC<{ @@ -543,6 +569,8 @@ export const MultiMediaComponent: FC<{ error?: any; onOpen?: () => void; onClose?: () => void; + toolBar?: React.ReactNode; + information?: React.ReactNode; onChange: (event: { target: { name: string; @@ -557,8 +585,6 @@ export const MultiMediaComponent: FC<{ }) => void; }> = (props) => { const { - onOpen, - onClose, name, error, text, @@ -566,6 +592,8 @@ export const MultiMediaComponent: FC<{ value, allData, dummy, + toolBar, + information, } = props; const user = useUser(); const modals = useModals(); @@ -574,7 +602,7 @@ export const MultiMediaComponent: FC<{ setCurrentMedia(value); } }, [value]); - const [mediaModal, setMediaModal] = useState(false); + const [currentMedia, setCurrentMedia] = useState(value); const mediaDirectory = useMediaDirectory(); const changeMedia = useCallback( @@ -603,7 +631,12 @@ export const MultiMediaComponent: FC<{ ); const showModal = useCallback(() => { modals.openModal({ + title: 'Media Library', askClose: false, + closeOnEscape: true, + fullScreen: true, + size: 'calc(100% - 80px)', + height: 'calc(100% - 80px)', children: (close) => ( <MediaBox setMedia={changeMedia} closeModal={close} /> ), @@ -637,40 +670,12 @@ export const MultiMediaComponent: FC<{ } }, [changeMedia]); - const mediaSettings = useMediaSettings(); - const t = useT(); return ( <> - <div className="b1 flex flex-col gap-[8px] bg-bigStrip rounded-bl-[8px] select-none w-full"> - <div className="flex gap-[10px]"> - <Button - onClick={showModal} - className="ms-[10px] !px-[0] !h-[80px] w-[80px] rounded-[4px] mb-[10px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-newBgLineColor bg-newColColor" - > - <div className="flex flex-col gap-[5px] items-center"> - <div> - <svg - className="!text-primary" - xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" - fill="none" - > - <path - d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z" - fill="currentColor" - /> - </svg> - </div> - <div className="text-[12px] font-[500] !text-current"> - {t('insert_media', 'Insert Media')} - </div> - </div> - </Button> - + <div className="b1 flex flex-col gap-[8px] rounded-bl-[8px] select-none w-full"> + <div className="flex gap-[10px] px-[12px]"> {!!currentMedia && ( <ReactSortable list={currentMedia} @@ -684,10 +689,27 @@ export const MultiMediaComponent: FC<{ > {currentMedia.map((media, index) => ( <Fragment key={media.id}> - <div className="cursor-pointer rounded-[4px] w-[80px] h-[80px] border-2 border-tableBorder relative flex transition-all"> - <div className="dragging text-sm absolute pe-[1px] z-[10] pb-[3px] -start-[4px] -top-[4px] bg-blue-700 cursor-move rounded-full w-[15px] h-[15px] text-white flex justify-center items-center"> - :: - </div> + <div className="cursor-pointer rounded-[5px] w-[40px] h-[40px] border-2 border-tableBorder relative flex transition-all"> + <svg + className="z-[20] dragging absolute pe-[1px] pb-[3px] -start-[4px] -top-[4px] cursor-move" + xmlns="http://www.w3.org/2000/svg" + width="15" + height="15" + viewBox="0 0 15 15" + fill="none" + > + <ellipse + cx="8.23242" + cy="7.5" + rx="6" + ry="5.5" + fill="white" + /> + <path + d="M7.5 0C11.6421 0 15 3.35786 15 7.5C14.9998 11.642 11.642 15 7.5 15C3.35799 15 0.000197912 11.642 0 7.5C0 3.35786 3.35786 0 7.5 0ZM5.55566 8.38867C4.97286 8.38867 4.50026 8.86159 4.5 9.44434C4.5 10.0273 4.9727 10.5 5.55566 10.5C6.13858 10.4999 6.61133 10.0273 6.61133 9.44434C6.61107 8.86162 6.13842 8.38873 5.55566 8.38867ZM9.44434 8.38867C8.86158 8.38873 8.38893 8.86162 8.38867 9.44434C8.38867 10.0273 8.86142 10.4999 9.44434 10.5C10.0273 10.5 10.5 10.0273 10.5 9.44434C10.4997 8.86159 10.0271 8.38867 9.44434 8.38867ZM5.55566 9.38867C5.58614 9.38873 5.61107 9.41391 5.61133 9.44434C5.61133 9.47498 5.5863 9.49994 5.55566 9.5C5.52498 9.5 5.5 9.47502 5.5 9.44434C5.50026 9.41387 5.52514 9.38867 5.55566 9.38867ZM9.44434 9.38867C9.47486 9.38867 9.49974 9.41387 9.5 9.44434C9.5 9.47502 9.47502 9.5 9.44434 9.5C9.4137 9.49994 9.38867 9.47498 9.38867 9.44434C9.38893 9.41391 9.41386 9.38873 9.44434 9.38867ZM5.55566 4.5C4.97282 4.5 4.5002 4.97287 4.5 5.55566C4.50006 6.13858 4.97273 6.61133 5.55566 6.61133C6.13855 6.61127 6.61127 6.13855 6.61133 5.55566C6.61113 4.9729 6.13846 4.50006 5.55566 4.5ZM9.44434 4.5C8.86154 4.50006 8.38887 4.9729 8.38867 5.55566C8.38873 6.13855 8.86145 6.61127 9.44434 6.61133C10.0273 6.61133 10.4999 6.13858 10.5 5.55566C10.4998 4.97287 10.0272 4.5 9.44434 4.5ZM5.55566 5.5C5.58617 5.50006 5.61113 5.52519 5.61133 5.55566C5.61127 5.58626 5.58626 5.61127 5.55566 5.61133C5.52502 5.61133 5.50006 5.5863 5.5 5.55566C5.5002 5.52515 5.5251 5.5 5.55566 5.5ZM9.44434 5.5C9.4749 5.5 9.4998 5.52515 9.5 5.55566C9.49994 5.5863 9.47498 5.61133 9.44434 5.61133C9.41374 5.61127 9.38873 5.58626 9.38867 5.55566C9.38887 5.52519 9.41383 5.50006 9.44434 5.5Z" + fill="#618DFF" + /> + </svg> <div className="w-full h-full relative group"> <div @@ -699,7 +721,6 @@ export const MultiMediaComponent: FC<{ media={media as any} onClose={close} onSelect={(value: any) => { - console.log(value); onChange({ target: { name: 'upload', @@ -719,7 +740,7 @@ export const MultiMediaComponent: FC<{ ), }); }} - className="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[50%] bg-black/80 rounded-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-[100]" + className="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[50%] bg-black/80 rounded-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-[9]" > <svg width="40" @@ -744,12 +765,21 @@ export const MultiMediaComponent: FC<{ /> )} </div> - <div + + <svg onClick={clearMedia(index)} - className="rounded-full w-[15px] h-[15px] bg-red-800 text-white flex justify-center items-center absolute -end-[4px] -top-[4px]" + className="absolute -end-[4px] -top-[4px] z-[20] rounded-full bg-white" + xmlns="http://www.w3.org/2000/svg" + width="15" + height="15" + viewBox="0 0 15 15" + fill="none" > - x - </div> + <path + d="M7.5 0C3.3675 0 0 3.3675 0 7.5C0 11.6325 3.3675 15 7.5 15C11.6325 15 15 11.6325 15 7.5C15 3.3675 11.6325 0 7.5 0ZM10.02 9.225C10.2375 9.4425 10.2375 9.8025 10.02 10.02C9.9075 10.1325 9.765 10.185 9.6225 10.185C9.48 10.185 9.3375 10.1325 9.225 10.02L7.5 8.295L5.775 10.02C5.6625 10.1325 5.52 10.185 5.3775 10.185C5.235 10.185 5.0925 10.1325 4.98 10.02C4.7625 9.8025 4.7625 9.4425 4.98 9.225L6.705 7.5L4.98 5.775C4.7625 5.5575 4.7625 5.1975 4.98 4.98C5.1975 4.7625 5.5575 4.7625 5.775 4.98L7.5 6.705L9.225 4.98C9.4425 4.7625 9.8025 4.7625 10.02 4.98C10.2375 5.1975 10.2375 5.5575 10.02 5.775L8.295 7.5L10.02 9.225Z" + fill="#FF3535" + /> + </svg> </div> </Fragment> ))} @@ -757,32 +787,76 @@ export const MultiMediaComponent: FC<{ )} </div> {!dummy && ( - <div className="flex gap-[10px] bg-newBgLineColor w-full b1"> - <div className="flex py-[10px] b2"> - <Button + <div className="flex gap-[8px] px-[12px] border-t border-newColColor w-full b1 text-textColor"> + <div className="flex py-[10px] b2 items-center gap-[4px]"> + <div + onClick={showModal} + className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" + > + <div className="flex gap-[8px] items-center"> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <g clip-path="url(#clip0_2352_53043)"> + <path + d="M8.33333 1.99967H5.2C4.0799 1.99967 3.51984 1.99967 3.09202 2.21766C2.71569 2.40941 2.40973 2.71537 2.21799 3.09169C2 3.51952 2 4.07957 2 5.19967V10.7997C2 11.9198 2 12.4798 2.21799 12.9077C2.40973 13.284 2.71569 13.5899 3.09202 13.7817C3.51984 13.9997 4.07989 13.9997 5.2 13.9997H11.3333C11.9533 13.9997 12.2633 13.9997 12.5176 13.9315C13.2078 13.7466 13.7469 13.2075 13.9319 12.5173C14 12.263 14 11.953 14 11.333M12.6667 5.33301V1.33301M10.6667 3.33301H14.6667M7 5.66634C7 6.40272 6.40305 6.99967 5.66667 6.99967C4.93029 6.99967 4.33333 6.40272 4.33333 5.66634C4.33333 4.92996 4.93029 4.33301 5.66667 4.33301C6.40305 4.33301 7 4.92996 7 5.66634ZM9.99336 7.94511L4.3541 13.0717C4.03691 13.3601 3.87831 13.5042 3.86429 13.6291C3.85213 13.7374 3.89364 13.8448 3.97546 13.9167C4.06985 13.9997 4.28419 13.9997 4.71286 13.9997H10.9707C11.9301 13.9997 12.4098 13.9997 12.7866 13.8385C13.2596 13.6361 13.6365 13.2593 13.8388 12.7863C14 12.4095 14 11.9298 14 10.9703C14 10.6475 14 10.4861 13.9647 10.3358C13.9204 10.1469 13.8353 9.96991 13.7155 9.81727C13.6202 9.69581 13.4941 9.59497 13.242 9.39331L11.3772 7.90145C11.1249 7.69961 10.9988 7.5987 10.8599 7.56308C10.7374 7.53169 10.6086 7.53575 10.4884 7.5748C10.352 7.6191 10.2324 7.72777 9.99336 7.94511Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2352_53043"> + <rect width="16" height="16" fill="currentColor" /> + </clipPath> + </defs> + </svg> + </div> + <div className="text-[10px] font-[600] maxMedia:hidden block"> + {t('insert_media', 'Insert Media')} + </div> + </div> + </div> + <div onClick={designMedia} - className="ms-[10px] rounded-[4px] gap-[8px] !text-primary justify-center items-center w-[127px] flex border border-dashed border-newBgLineColor bg-newColColor" + className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" > <div className="flex gap-[5px] items-center"> <div> <svg xmlns="http://www.w3.org/2000/svg" - width="24" - height="24" - viewBox="0 0 24 24" + width="16" + height="16" + viewBox="0 0 16 16" fill="none" > - <path - d="M19.5 3H7.5C7.10218 3 6.72064 3.15804 6.43934 3.43934C6.15804 3.72064 6 4.10218 6 4.5V6H4.5C4.10218 6 3.72064 6.15804 3.43934 6.43934C3.15804 6.72064 3 7.10218 3 7.5V19.5C3 19.8978 3.15804 20.2794 3.43934 20.5607C3.72064 20.842 4.10218 21 4.5 21H16.5C16.8978 21 17.2794 20.842 17.5607 20.5607C17.842 20.2794 18 19.8978 18 19.5V18H19.5C19.8978 18 20.2794 17.842 20.5607 17.5607C20.842 17.2794 21 16.8978 21 16.5V4.5C21 4.10218 20.842 3.72064 20.5607 3.43934C20.2794 3.15804 19.8978 3 19.5 3ZM7.5 4.5H19.5V11.0044L17.9344 9.43875C17.6531 9.15766 17.2717 8.99976 16.8741 8.99976C16.4764 8.99976 16.095 9.15766 15.8137 9.43875L8.75344 16.5H7.5V4.5ZM16.5 19.5H4.5V7.5H6V16.5C6 16.8978 6.15804 17.2794 6.43934 17.5607C6.72064 17.842 7.10218 18 7.5 18H16.5V19.5ZM19.5 16.5H10.875L16.875 10.5L19.5 13.125V16.5ZM11.25 10.5C11.695 10.5 12.13 10.368 12.5 10.1208C12.87 9.87357 13.1584 9.52217 13.3287 9.11104C13.499 8.6999 13.5436 8.2475 13.4568 7.81105C13.37 7.37459 13.1557 6.97368 12.841 6.65901C12.5263 6.34434 12.1254 6.13005 11.689 6.04323C11.2525 5.95642 10.8001 6.00097 10.389 6.17127C9.97783 6.34157 9.62643 6.62996 9.37919 6.99997C9.13196 7.36998 9 7.80499 9 8.25C9 8.84674 9.23705 9.41903 9.65901 9.84099C10.081 10.2629 10.6533 10.5 11.25 10.5ZM11.25 7.5C11.3983 7.5 11.5433 7.54399 11.6667 7.6264C11.79 7.70881 11.8861 7.82594 11.9429 7.96299C11.9997 8.10003 12.0145 8.25083 11.9856 8.39632C11.9566 8.5418 11.8852 8.67544 11.7803 8.78033C11.6754 8.88522 11.5418 8.95665 11.3963 8.98559C11.2508 9.01453 11.1 8.99968 10.963 8.94291C10.8259 8.88614 10.7088 8.79001 10.6264 8.66668C10.544 8.54334 10.5 8.39834 10.5 8.25C10.5 8.05109 10.579 7.86032 10.7197 7.71967C10.8603 7.57902 11.0511 7.5 11.25 7.5Z" - fill="currentColor" - /> + <g clip-path="url(#clip0_2352_53048)"> + <path + d="M7.79167 1.99984H5.2C4.07989 1.99984 3.51984 1.99984 3.09202 2.21782C2.71569 2.40957 2.40973 2.71553 2.21799 3.09186C2 3.51968 2 4.07973 2 5.19984V10.7998C2 11.9199 2 12.48 2.21799 12.9078C2.40973 13.2841 2.71569 13.5901 3.09202 13.7818C3.51984 13.9998 4.07989 13.9998 5.2 13.9998H11.3333C11.9533 13.9998 12.2633 13.9998 12.5176 13.9317C13.2078 13.7468 13.7469 13.2077 13.9319 12.5175C14 12.2631 14 11.9532 14 11.3332M7 5.6665C7 6.40288 6.40305 6.99984 5.66667 6.99984C4.93029 6.99984 4.33333 6.40288 4.33333 5.6665C4.33333 4.93012 4.93029 4.33317 5.66667 4.33317C6.40305 4.33317 7 4.93012 7 5.6665ZM9.99336 7.94527L4.3541 13.0719C4.03691 13.3602 3.87831 13.5044 3.86429 13.6293C3.85213 13.7376 3.89364 13.8449 3.97546 13.9169C4.06985 13.9998 4.28419 13.9998 4.71286 13.9998H10.9707C11.9301 13.9998 12.4098 13.9998 12.7866 13.8387C13.2596 13.6363 13.6365 13.2595 13.8388 12.7864C14 12.4097 14 11.9299 14 10.9705C14 10.6477 14 10.4863 13.9647 10.3359C13.9204 10.147 13.8353 9.97007 13.7155 9.81743C13.6202 9.69597 13.4941 9.59514 13.242 9.39348L11.3772 7.90161C11.1249 7.69978 10.9988 7.59886 10.8599 7.56324C10.7374 7.53185 10.6086 7.53592 10.4884 7.57496C10.352 7.61926 10.2324 7.72794 9.99336 7.94527ZM15.0951 6.49981L13.0275 5.90908C12.9285 5.88079 12.879 5.86664 12.8328 5.84544C12.7918 5.82662 12.7528 5.80368 12.7164 5.77698C12.6755 5.74692 12.6391 5.71051 12.5663 5.6377L10.2617 3.33317C9.80143 2.87292 9.80144 2.1267 10.2617 1.66646C10.7219 1.20623 11.4681 1.20623 11.9284 1.66647L14.2329 3.97103C14.3058 4.04384 14.3422 4.08025 14.3722 4.12121C14.3989 4.15757 14.4219 4.19655 14.4407 4.23755C14.4619 4.28373 14.476 4.33323 14.5043 4.43224L15.0951 6.49981Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2352_53048"> + <rect width="16" height="16" fill="currentColor" /> + </clipPath> + </defs> </svg> </div> - <div className="text-[12px] font-[500] !text-current"> + <div className="text-[10px] font-[600] iconBreak:hidden block"> {t('design_media', 'Design Media')} </div> </div> - </Button> + </div> <ThirdPartyMedia allData={allData} onChange={changeMedia} /> @@ -793,6 +867,32 @@ export const MultiMediaComponent: FC<{ </> )} </div> + <div className="text-newColColor h-full flex items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="2" + height="17" + viewBox="0 0 2 17" + fill="none" + > + <path + d="M0.75 0.75V16" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + /> + </svg> + </div> + {!!toolBar && ( + <div className="flex py-[10px] b2 items-center gap-[4px]"> + {toolBar} + </div> + )} + {information && ( + <div className="flex-1 justify-end flex py-[10px] b2 items-center gap-[4px]"> + {information} + </div> + )} </div> )} </div> diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index febe58fe..be292f37 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -116,7 +116,8 @@ export function useUppyUploader(props: { uppy2.log(error.message, 'error'); uppy2.info(error.message, 'error', 5000); toast.show( - `File type "${fileType}" is not allowed. Allowed types: ${allowedFileTypes}` + `File type "${fileType}" is not allowed. Allowed types: ${allowedFileTypes}`, + 'warning' ); uppy2.removeFile(file.id); return reject(error); @@ -197,6 +198,7 @@ export function useUppyUploader(props: { }); }); uppy2.on('error', (result) => { + uppy2.clear(); setLocked(false); }); uppy2.on('complete', async (result) => { diff --git a/apps/frontend/src/components/new-launch/a.component.tsx b/apps/frontend/src/components/new-launch/a.component.tsx index 10e6802f..16f65ab2 100644 --- a/apps/frontend/src/components/new-launch/a.component.tsx +++ b/apps/frontend/src/components/new-launch/a.component.tsx @@ -24,27 +24,43 @@ export const AComponent: FC<{ // update link try { - editor?.chain()?.focus()?.extendMarkRange('link')?.setLink({ href: url })?.run(); - } catch (e) { - } + editor + ?.chain() + ?.focus() + ?.extendMarkRange('link') + ?.setLink({ href: url }) + ?.run(); + } catch (e) {} editor?.commands?.focus(); }; return ( <div + data-tooltip-id="tooltip" + data-tooltip-content="Link" onClick={mark} - className="select-none cursor-pointer w-[40px] p-[5px] text-center" + className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" > <svg - width="20" - height="20" - viewBox="0 0 26 26" - fill="none" xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" > - <path - d="M17.7079 8.29252C17.8008 8.38539 17.8746 8.49568 17.9249 8.61708C17.9752 8.73847 18.0011 8.8686 18.0011 9.00002C18.0011 9.13143 17.9752 9.26156 17.9249 9.38296C17.8746 9.50436 17.8008 9.61465 17.7079 9.70752L9.70786 17.7075C9.61495 17.8004 9.50465 17.8741 9.38325 17.9244C9.26186 17.9747 9.13175 18.0006 9.00036 18.0006C8.86896 18.0006 8.73885 17.9747 8.61746 17.9244C8.49607 17.8741 8.38577 17.8004 8.29286 17.7075C8.19995 17.6146 8.12625 17.5043 8.07596 17.3829C8.02568 17.2615 7.9998 17.1314 7.9998 17C7.9998 16.8686 8.02568 16.7385 8.07596 16.6171C8.12625 16.4957 8.19995 16.3854 8.29286 16.2925L16.2929 8.29252C16.3857 8.19954 16.496 8.12578 16.6174 8.07546C16.7388 8.02513 16.8689 7.99923 17.0004 7.99923C17.1318 7.99923 17.2619 8.02513 17.3833 8.07546C17.5047 8.12578 17.615 8.19954 17.7079 8.29252ZM23.9504 2.05002C23.3003 1.39993 22.5286 0.884251 21.6793 0.532423C20.83 0.180596 19.9197 -0.000488281 19.0004 -0.000488281C18.081 -0.000488281 17.1707 0.180596 16.3214 0.532423C15.4721 0.884251 14.7004 1.39993 14.0504 2.05002L10.2929 5.80627C10.1052 5.99391 9.9998 6.2484 9.9998 6.51377C9.9998 6.77913 10.1052 7.03363 10.2929 7.22127C10.4805 7.40891 10.735 7.51432 11.0004 7.51432C11.2657 7.51432 11.5202 7.40891 11.7079 7.22127L15.4654 3.47127C16.4065 2.55083 17.6726 2.03866 18.989 2.04591C20.3053 2.05316 21.5657 2.57924 22.4966 3.50999C23.4276 4.44074 23.9539 5.70105 23.9613 7.01742C23.9688 8.33379 23.4569 9.6 22.5366 10.5413L18.7779 14.2988C18.5902 14.4862 18.4847 14.7406 18.4846 15.0058C18.4845 15.2711 18.5898 15.5255 18.7772 15.7131C18.9647 15.9008 19.219 16.0063 19.4843 16.0064C19.7495 16.0065 20.004 15.9012 20.1916 15.7138L23.9504 11.95C24.6004 11.3 25.1161 10.5283 25.468 9.67897C25.8198 8.82964 26.0009 7.91933 26.0009 7.00002C26.0009 6.0807 25.8198 5.17039 25.468 4.32107C25.1161 3.47174 24.6004 2.70004 23.9504 2.05002ZM14.2929 18.7775L10.5354 22.535C10.073 23.0078 9.52136 23.3842 8.9125 23.6423C8.30365 23.9004 7.64963 24.0352 6.98832 24.0389C6.32702 24.0425 5.67156 23.9149 5.05989 23.6635C4.44823 23.4121 3.89252 23.0418 3.42494 22.5742C2.95736 22.1065 2.5872 21.5507 2.33589 20.939C2.08458 20.3273 1.95711 19.6718 1.96087 19.0105C1.96463 18.3492 2.09954 17.6952 2.35779 17.0864C2.61603 16.4776 2.99249 15.9261 3.46536 15.4638L7.22161 11.7075C7.40925 11.5199 7.51466 11.2654 7.51466 11C7.51466 10.7347 7.40925 10.4802 7.22161 10.2925C7.03397 10.1049 6.77947 9.99946 6.51411 9.99946C6.24874 9.99946 5.99425 10.1049 5.80661 10.2925L2.05036 14.05C0.737536 15.3628 0 17.1434 0 19C0 20.8566 0.737536 22.6372 2.05036 23.95C3.36318 25.2628 5.14375 26.0004 7.00036 26.0004C8.85697 26.0004 10.6375 25.2628 11.9504 23.95L15.7079 20.1913C15.8953 20.0036 16.0006 19.7492 16.0005 19.4839C16.0004 19.2187 15.8949 18.9644 15.7072 18.7769C15.5196 18.5894 15.2652 18.4842 14.9999 18.4843C14.7347 18.4844 14.4803 18.5899 14.2929 18.7775Z" - fill="currentColor" - /> + <g clip-path="url(#clip0_2452_193804)"> + <path + d="M6.6668 8.66599C6.9531 9.04875 7.31837 9.36545 7.73783 9.59462C8.1573 9.82379 8.62114 9.96007 9.0979 9.99422C9.57466 10.0284 10.0532 9.95957 10.501 9.79251C10.9489 9.62546 11.3555 9.36404 11.6935 9.02599L13.6935 7.02599C14.3007 6.39732 14.6366 5.55531 14.629 4.68132C14.6215 3.80733 14.2709 2.97129 13.6529 2.35326C13.0348 1.73524 12.1988 1.38467 11.3248 1.37708C10.4508 1.36948 9.60881 1.70547 8.98013 2.31266L7.83347 3.45266M9.33347 7.33266C9.04716 6.94991 8.68189 6.6332 8.26243 6.40403C7.84297 6.17486 7.37913 6.03858 6.90237 6.00444C6.4256 5.97029 5.94708 6.03908 5.49924 6.20614C5.0514 6.3732 4.64472 6.63461 4.3068 6.97266L2.3068 8.97266C1.69961 9.60133 1.36363 10.4433 1.37122 11.3173C1.37881 12.1913 1.72938 13.0274 2.3474 13.6454C2.96543 14.2634 3.80147 14.614 4.67546 14.6216C5.54945 14.6292 6.39146 14.2932 7.02013 13.686L8.16013 12.546" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_2452_193804"> + <rect width="16" height="16" fill="white" /> + </clipPath> + </defs> </svg> </div> ); diff --git a/apps/frontend/src/components/new-launch/add.edit.modal.tsx b/apps/frontend/src/components/new-launch/add.edit.modal.tsx index 1c01faae..961d5c43 100644 --- a/apps/frontend/src/components/new-launch/add.edit.modal.tsx +++ b/apps/frontend/src/components/new-launch/add.edit.modal.tsx @@ -128,7 +128,7 @@ export const AddEditModalInnerInner: FC<AddEditModalProps> = (props) => { internal: state.internal, setTags: state.setTags, setEditor: state.setEditor, - setRepeater: state.setRepeater + setRepeater: state.setRepeater, })) ); @@ -161,8 +161,7 @@ export const AddEditModalInnerInner: FC<AddEditModalProps> = (props) => { })) ); setCurrent(existingData.integration); - } - else { + } else { setEditor('normal'); } @@ -212,5 +211,12 @@ export const AddEditModalInnerInner: FC<AddEditModalProps> = (props) => { return null; } - return <ManageModal {...props} />; + return ( + <> + <style> + {`#support-discord {display: none !important;}`} + </style> + <ManageModal {...props} /> + </> + ); }; diff --git a/apps/frontend/src/components/new-launch/add.post.button.tsx b/apps/frontend/src/components/new-launch/add.post.button.tsx index 1a2ca5b7..c43c991e 100644 --- a/apps/frontend/src/components/new-launch/add.post.button.tsx +++ b/apps/frontend/src/components/new-launch/add.post.button.tsx @@ -13,22 +13,25 @@ export const AddPostButton: FC<{ const t = useT(); return ( - <div> - <Button + <div className="flex"> + <div onClick={onClick} - className="!h-[24px] rounded-[3px] flex gap-[4px] text-[12px] font-[500]" + className="select-none cursor-pointer h-[34px] rounded-[6px] flex bg-[#D82D7E] gap-[8px] justify-center items-center pl-[16px] pr-[20px] text-[13px] font-[600] mt-[12px]" > <div> <svg xmlns="http://www.w3.org/2000/svg" - width="14" - height="14" - viewBox="0 0 14 14" + width="16" + height="16" + viewBox="0 0 16 16" fill="none" > <path - d="M7 1.3125C5.87512 1.3125 4.7755 1.64607 3.8402 2.27102C2.90489 2.89597 2.17591 3.78423 1.74544 4.82349C1.31496 5.86274 1.20233 7.00631 1.42179 8.10958C1.64124 9.21284 2.18292 10.2263 2.97833 11.0217C3.77374 11.8171 4.78716 12.3588 5.89043 12.5782C6.99369 12.7977 8.13726 12.685 9.17651 12.2546C10.2158 11.8241 11.104 11.0951 11.729 10.1598C12.3539 9.2245 12.6875 8.12488 12.6875 7C12.6859 5.49207 12.0862 4.04636 11.0199 2.98009C9.95365 1.91382 8.50793 1.31409 7 1.3125ZM7 11.8125C6.04818 11.8125 5.11773 11.5303 4.32632 11.0014C3.53491 10.4726 2.91808 9.72103 2.55383 8.84166C2.18959 7.96229 2.09428 6.99466 2.27997 6.06113C2.46566 5.12759 2.92401 4.27009 3.59705 3.59705C4.27009 2.92401 5.1276 2.46566 6.06113 2.27997C6.99466 2.09428 7.9623 2.18958 8.84167 2.55383C9.72104 2.91808 10.4726 3.53491 11.0015 4.32632C11.5303 5.11773 11.8125 6.04818 11.8125 7C11.8111 8.27591 11.3036 9.49915 10.4014 10.4014C9.49915 11.3036 8.27591 11.8111 7 11.8125ZM9.625 7C9.625 7.11603 9.57891 7.22731 9.49686 7.30936C9.41481 7.39141 9.30353 7.4375 9.1875 7.4375H7.4375V9.1875C7.4375 9.30353 7.39141 9.41481 7.30936 9.49686C7.22731 9.57891 7.11603 9.625 7 9.625C6.88397 9.625 6.77269 9.57891 6.69064 9.49686C6.6086 9.41481 6.5625 9.30353 6.5625 9.1875V7.4375H4.8125C4.69647 7.4375 4.58519 7.39141 4.50314 7.30936C4.4211 7.22731 4.375 7.11603 4.375 7C4.375 6.88397 4.4211 6.77269 4.50314 6.69064C4.58519 6.60859 4.69647 6.5625 4.8125 6.5625H6.5625V4.8125C6.5625 4.69647 6.6086 4.58519 6.69064 4.50314C6.77269 4.42109 6.88397 4.375 7 4.375C7.11603 4.375 7.22731 4.42109 7.30936 4.50314C7.39141 4.58519 7.4375 4.69647 7.4375 4.8125V6.5625H9.1875C9.30353 6.5625 9.41481 6.60859 9.49686 6.69064C9.57891 6.77269 9.625 6.88397 9.625 7Z" - fill="white" + d="M8.00065 3.33301V12.6663M3.33398 7.99967H12.6673" + stroke="white" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> </div> @@ -41,7 +44,7 @@ export const AddPostButton: FC<{ : ['add_comment', 'Add comment']) )} </div> - </Button> + </div> </div> ); }; diff --git a/apps/frontend/src/components/new-launch/bold.text.tsx b/apps/frontend/src/components/new-launch/bold.text.tsx index d0478a81..76ef1d80 100644 --- a/apps/frontend/src/components/new-launch/bold.text.tsx +++ b/apps/frontend/src/components/new-launch/bold.text.tsx @@ -81,32 +81,25 @@ export const BoldText: FC<{ }; return ( <div + data-tooltip-id="tooltip" + data-tooltip-content="Bold Text" onClick={mark} - className="select-none cursor-pointer w-[40px] p-[5px] text-center" + className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" > <svg - width="25" - height="24" - viewBox="0 0 25 24" - fill="none" xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" > - <g clipPath="url(#clip0_31_12616)"> - <path - d="M14.7686 12.24C15.4192 12.3787 15.9419 12.704 16.3366 13.216C16.7312 13.7173 16.9286 14.2933 16.9286 14.944C16.9286 15.8827 16.5979 16.6293 15.9366 17.184C15.2859 17.728 14.3739 18 13.2006 18H7.96856V6.768H13.0246C14.1659 6.768 15.0566 7.02933 15.6966 7.552C16.3472 8.07467 16.6726 8.784 16.6726 9.68C16.6726 10.3413 16.4966 10.8907 16.1446 11.328C15.8032 11.7653 15.3446 12.0693 14.7686 12.24ZM10.7046 11.312H12.4966C12.9446 11.312 13.2859 11.216 13.5206 11.024C13.7659 10.8213 13.8886 10.528 13.8886 10.144C13.8886 9.76 13.7659 9.46667 13.5206 9.264C13.2859 9.06133 12.9446 8.96 12.4966 8.96H10.7046V11.312ZM12.7206 15.792C13.1792 15.792 13.5312 15.6907 13.7766 15.488C14.0326 15.2747 14.1606 14.9707 14.1606 14.576C14.1606 14.1813 14.0272 13.872 13.7606 13.648C13.5046 13.424 13.1472 13.312 12.6886 13.312H10.7046V15.792H12.7206Z" - fill="currentColor" - /> - </g> - <defs> - <clipPath id="clip0_31_12616"> - <rect - width="24" - height="24" - fill="white" - transform="translate(0.25)" - /> - </clipPath> - </defs> + <path + d="M4 8.00033H9.33333C10.8061 8.00033 12 6.80642 12 5.33366C12 3.8609 10.8061 2.66699 9.33333 2.66699H4V8.00033ZM4 8.00033H10C11.4728 8.00033 12.6667 9.19423 12.6667 10.667C12.6667 12.1398 11.4728 13.3337 10 13.3337H4V8.00033Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> </svg> </div> ); diff --git a/apps/frontend/src/components/new-launch/bullets.component.tsx b/apps/frontend/src/components/new-launch/bullets.component.tsx index 454162e1..24cb3344 100644 --- a/apps/frontend/src/components/new-launch/bullets.component.tsx +++ b/apps/frontend/src/components/new-launch/bullets.component.tsx @@ -11,19 +11,24 @@ export const Bullets: FC<{ }; return ( <div + data-tooltip-id="tooltip" + data-tooltip-content="Bullets" onClick={bullet} - className="select-none cursor-pointer w-[40px] p-[5px] text-center" + className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" > <svg - width="20" - height="14" - viewBox="0 0 26 20" - fill="none" xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" > <path - d="M6.5 2C6.5 1.60218 6.65804 1.22064 6.93934 0.93934C7.22064 0.658035 7.60218 0.5 8 0.5H24C24.3978 0.5 24.7794 0.658035 25.0607 0.93934C25.342 1.22064 25.5 1.60218 25.5 2C25.5 2.39782 25.342 2.77936 25.0607 3.06066C24.7794 3.34196 24.3978 3.5 24 3.5H8C7.60218 3.5 7.22064 3.34196 6.93934 3.06066C6.65804 2.77936 6.5 2.39782 6.5 2ZM24 8.5H8C7.60218 8.5 7.22064 8.65804 6.93934 8.93934C6.65804 9.22064 6.5 9.60218 6.5 10C6.5 10.3978 6.65804 10.7794 6.93934 11.0607C7.22064 11.342 7.60218 11.5 8 11.5H24C24.3978 11.5 24.7794 11.342 25.0607 11.0607C25.342 10.7794 25.5 10.3978 25.5 10C25.5 9.60218 25.342 9.22064 25.0607 8.93934C24.7794 8.65804 24.3978 8.5 24 8.5ZM24 16.5H8C7.60218 16.5 7.22064 16.658 6.93934 16.9393C6.65804 17.2206 6.5 17.6022 6.5 18C6.5 18.3978 6.65804 18.7794 6.93934 19.0607C7.22064 19.342 7.60218 19.5 8 19.5H24C24.3978 19.5 24.7794 19.342 25.0607 19.0607C25.342 18.7794 25.5 18.3978 25.5 18C25.5 17.6022 25.342 17.2206 25.0607 16.9393C24.7794 16.658 24.3978 16.5 24 16.5ZM2.5 8C2.10444 8 1.71776 8.1173 1.38886 8.33706C1.05996 8.55682 0.803617 8.86918 0.652242 9.23463C0.500867 9.60009 0.46126 10.0022 0.53843 10.3902C0.615601 10.7781 0.806082 11.1345 1.08579 11.4142C1.36549 11.6939 1.72186 11.8844 2.10982 11.9616C2.49778 12.0387 2.89992 11.9991 3.26537 11.8478C3.63082 11.6964 3.94318 11.44 4.16294 11.1111C4.3827 10.7822 4.5 10.3956 4.5 10C4.5 9.46957 4.28929 8.96086 3.91421 8.58579C3.53914 8.21071 3.03043 8 2.5 8ZM2.5 0C2.10444 0 1.71776 0.117298 1.38886 0.337061C1.05996 0.556824 0.803617 0.869181 0.652242 1.23463C0.500867 1.60009 0.46126 2.00222 0.53843 2.39018C0.615601 2.77814 0.806082 3.13451 1.08579 3.41421C1.36549 3.69392 1.72186 3.8844 2.10982 3.96157C2.49778 4.03874 2.89992 3.99913 3.26537 3.84776C3.63082 3.69638 3.94318 3.44004 4.16294 3.11114C4.3827 2.78224 4.5 2.39556 4.5 2C4.5 1.46957 4.28929 0.960859 3.91421 0.585786C3.53914 0.210714 3.03043 0 2.5 0ZM2.5 16C2.10444 16 1.71776 16.1173 1.38886 16.3371C1.05996 16.5568 0.803617 16.8692 0.652242 17.2346C0.500867 17.6001 0.46126 18.0022 0.53843 18.3902C0.615601 18.7781 0.806082 19.1345 1.08579 19.4142C1.36549 19.6939 1.72186 19.8844 2.10982 19.9616C2.49778 20.0387 2.89992 19.9991 3.26537 19.8478C3.63082 19.6964 3.94318 19.44 4.16294 19.1111C4.3827 18.7822 4.5 18.3956 4.5 18C4.5 17.4696 4.28929 16.9609 3.91421 16.5858C3.53914 16.2107 3.03043 16 2.5 16Z" - fill="currentColor" + d="M14 8.00065L6 8.00065M14 4.00065L6 4.00065M14 12.0007L6 12.0007M3.33333 8.00065C3.33333 8.36884 3.03486 8.66732 2.66667 8.66732C2.29848 8.66732 2 8.36884 2 8.00065C2 7.63246 2.29848 7.33398 2.66667 7.33398C3.03486 7.33398 3.33333 7.63246 3.33333 8.00065ZM3.33333 4.00065C3.33333 4.36884 3.03486 4.66732 2.66667 4.66732C2.29848 4.66732 2 4.36884 2 4.00065C2 3.63246 2.29848 3.33398 2.66667 3.33398C3.03486 3.33398 3.33333 3.63246 3.33333 4.00065ZM3.33333 12.0007C3.33333 12.3688 3.03486 12.6673 2.66667 12.6673C2.29848 12.6673 2 12.3688 2 12.0007C2 11.6325 2.29848 11.334 2.66667 11.334C3.03486 11.334 3.33333 11.6325 3.33333 12.0007Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> </div> diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 6d6c9b09..eb9e51a0 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -58,7 +58,8 @@ import Mention from '@tiptap/extension-mention'; import { suggestion } from '@gitroom/frontend/components/new-launch/mention.component'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { AComponent } from '@gitroom/frontend/components/new-launch/a.component'; -import { capitalize } from 'lodash'; +import { Placeholder } from '@tiptap/extensions'; +import { InformationComponent } from '@gitroom/frontend/components/launches/information.component'; const InterceptBoldShortcut = Extension.create({ name: 'preventBoldWithUnderline', @@ -91,7 +92,7 @@ const InterceptUnderlineShortcut = Extension.create({ export const EditorWrapper: FC<{ totalPosts: number; value: string; -}> = (props) => { +}> = () => { const { setGlobalValueText, setInternalValueText, @@ -278,6 +279,12 @@ export const EditorWrapper: FC<{ const addValue = useCallback( (index: number) => () => { + setTimeout(() => { + // scroll the the bottom + document.querySelector('#social-content').scrollTo({ + top: document.querySelector('#social-content').scrollHeight, + }); + }, 20); if (internal) { return addInternalValue(index, current, [ { @@ -326,30 +333,116 @@ export const EditorWrapper: FC<{ } return ( - <div className="relative flex flex-col gap-[20px]"> + <div + className={clsx( + 'relative flex-col gap-[20px] flex-1', + items.length === 1 && 'flex', + !canEdit && !isCreateSet && 'bg-newSettings rounded-[12px]' + )} + > + {isCreateSet && current !== 'global' && ( + <> + <div className="text-center absolute w-full h-full left-0 top-0 items-center justify-center flex z-[101] flex-col gap-[16px]"> + <div> + <div className="w-[54px] h-[54px] rounded-full absolute z-[101] flex justify-center items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="32" + height="32" + viewBox="0 0 32 32" + fill="none" + > + <path + d="M22.6673 13.3333V10.6667C22.6673 6.98477 19.6825 4 16.0007 4C12.3188 4 9.33398 6.98477 9.33398 10.6667V13.3333M16.0007 19.3333V22M11.734 28H20.2673C22.5075 28 23.6276 28 24.4833 27.564C25.2359 27.1805 25.8479 26.5686 26.2313 25.816C26.6673 24.9603 26.6673 23.8402 26.6673 21.6V19.7333C26.6673 17.4931 26.6673 16.373 26.2313 15.5174C25.8479 14.7647 25.2359 14.1528 24.4833 13.7693C23.6276 13.3333 22.5075 13.3333 20.2673 13.3333H11.734C9.49377 13.3333 8.37367 13.3333 7.51802 13.7693C6.76537 14.1528 6.15345 14.7647 5.76996 15.5174C5.33398 16.373 5.33398 17.4931 5.33398 19.7333V21.6C5.33398 23.8402 5.33398 24.9603 5.76996 25.816C6.15345 26.5686 6.76537 27.1805 7.51802 27.564C8.37367 28 9.49377 28 11.734 28Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> + </div> + <div className="text-[14px] font-[600] text-white"> + You can't edit networks when creating a set + </div> + </div> + <div className="absolute w-full h-full left-0 top-0 bg-newBackdrop opacity-60 z-[100] rounded-[12px]" /> + </> + )} + {!canEdit && !isCreateSet && ( + <> + <div + onClick={() => { + setLoaded(false); + addRemoveInternal(current); + }} + className="text-center absolute w-full h-full left-0 top-0 items-center justify-center flex z-[101] flex-col gap-[16px]" + > + <div> + <div className="w-[54px] h-[54px] rounded-full absolute z-[101] flex justify-center items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="32" + height="32" + viewBox="0 0 32 32" + fill="none" + > + <path + d="M22.6673 13.3333V10.6667C22.6673 6.98477 19.6825 4 16.0007 4C12.3188 4 9.33398 6.98477 9.33398 10.6667V13.3333M16.0007 19.3333V22M11.734 28H20.2673C22.5075 28 23.6276 28 24.4833 27.564C25.2359 27.1805 25.8479 26.5686 26.2313 25.816C26.6673 24.9603 26.6673 23.8402 26.6673 21.6V19.7333C26.6673 17.4931 26.6673 16.373 26.2313 15.5174C25.8479 14.7647 25.2359 14.1528 24.4833 13.7693C23.6276 13.3333 22.5075 13.3333 20.2673 13.3333H11.734C9.49377 13.3333 8.37367 13.3333 7.51802 13.7693C6.76537 14.1528 6.15345 14.7647 5.76996 15.5174C5.33398 16.373 5.33398 17.4931 5.33398 19.7333V21.6C5.33398 23.8402 5.33398 24.9603 5.76996 25.816C6.15345 26.5686 6.76537 27.1805 7.51802 27.564C8.37367 28 9.49377 28 11.734 28Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> + </div> + <div className="text-[14px] font-[600] text-white"> + Click this button to exit global editing + <br /> + and customize the post for this channel + </div> + <div> + <div className="text-white rounded-[8px] h-[44px] px-[20px] bg-[#D82D7E] cursor-pointer flex justify-center items-center"> + Edit content + </div> + </div> + </div> + <div className="absolute w-full h-full left-0 top-0 bg-newBackdrop opacity-60 z-[100] rounded-[12px]" /> + </> + )} {items.map((g, index) => ( - <div key={g.id} className="relative flex flex-col gap-[20px]"> - {!canEdit && !isCreateSet && ( - <div - onClick={() => { - if (index !== 0) { - return; - } - - setLoaded(false); - addRemoveInternal(current); - }} - className="select-none cursor-pointer absolute w-full h-full left-0 top-0 bg-red-600/10 z-[100]" - > - {index === 0 && ( - <div className="absolute left-[50%] top-[50%] bg-red-400 -translate-x-[50%] z-[101] -translate-y-[50%] border-dashed border p-[10px] border-black"> - Edit + <div + key={g.id} + className={clsx( + 'relative flex flex-col gap-[20px] flex-1 bg-newSettings', + index === 0 && 'rounded-t-[12px]', + index === items.length - 1 && 'rounded-b-[12px]', + !canEdit && !isCreateSet && 'blur-s' + )} + > + <div className="flex gap-[5px] flex-1 w-full"> + <div className="flex-1 flex w-full"> + {index > 0 && ( + <div className="flex justify-center pl-[12px] text-newSep"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="87" + viewBox="0 0 18 87" + fill="none" + > + <path + d="M0.75 0.75V79.75C0.75 83.0637 3.43629 85.75 6.75 85.75H16.75" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + /> + </svg> </div> )} - </div> - )} - <div className="flex gap-[5px]"> - <div className="flex-1"> <Editor editorType={editor} allValues={items} @@ -370,61 +463,80 @@ export const EditorWrapper: FC<{ chars={chars} childButton={ <> - {canEdit ? ( - <AddPostButton - num={index} - onClick={addValue(index)} - postComment={postComment} - /> - ) : ( - <div className="h-[25px]" /> - )} + {canEdit && items.length - 1 === index ? ( + <div className="flex items-center"> + <div className="flex-1"> + <AddPostButton + num={index} + onClick={addValue(index)} + postComment={postComment} + /> + </div> + {!!internal && !existingData?.integration && ( + <div + className="mt-[12px] flex gap-[20px] items-center cursor-pointer select-none" + onClick={goBackToGlobal} + > + <div className="flex gap-[6px] items-center"> + <div className="w-[8px] h-[8px] rounded-full bg-[#FC69FF]" /> + <div className="text-[14px] font-[600]"> + Editing a Specific Network + </div> + </div> + <div className="flex gap-[6px] items-center"> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <path + d="M1.33398 6.66667C1.33398 6.66667 2.67064 4.84548 3.75654 3.75883C4.84244 2.67218 6.34305 2 8.00065 2C11.3144 2 14.0007 4.68629 14.0007 8C14.0007 11.3137 11.3144 14 8.00065 14C5.26526 14 2.95739 12.1695 2.23516 9.66667M1.33398 6.66667V2.66667M1.33398 6.66667H5.33398" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="text-[13px] font-[600]"> + Back to global + </div> + </div> + </div> + )} + </div> + ) : null} </> } /> </div> - <div className="flex flex-col items-center gap-[10px]"> + <div className="flex flex-col items-center gap-[10px] pe-[12px]"> <UpDownArrow isUp={index !== 0} isDown={index !== items.length - 1} onChange={changeOrder(index)} /> - {index === 0 && - current !== 'global' && - canEdit && - !existingData.integration && ( - <svg - onClick={goBackToGlobal} - className="cursor-pointer" - data-tooltip-id="tooltip" - data-tooltip-content="Go back to global mode" - width="20" - height="20" - viewBox="0 0 32 32" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M16 3C13.4288 3 10.9154 3.76244 8.77759 5.1909C6.63975 6.61935 4.97351 8.64968 3.98957 11.0251C3.00563 13.4006 2.74819 16.0144 3.2498 18.5362C3.75141 21.0579 4.98953 23.3743 6.80762 25.1924C8.6257 27.0105 10.9421 28.2486 13.4638 28.7502C15.9856 29.2518 18.5995 28.9944 20.9749 28.0104C23.3503 27.0265 25.3807 25.3603 26.8091 23.2224C28.2376 21.0846 29 18.5712 29 16C28.9964 12.5533 27.6256 9.24882 25.1884 6.81163C22.7512 4.37445 19.4467 3.00364 16 3ZM12.7038 21H19.2963C18.625 23.2925 17.5 25.3587 16 26.9862C14.5 25.3587 13.375 23.2925 12.7038 21ZM12.25 19C11.9183 17.0138 11.9183 14.9862 12.25 13H19.75C20.0817 14.9862 20.0817 17.0138 19.75 19H12.25ZM5.00001 16C4.99914 14.9855 5.13923 13.9759 5.41626 13H10.2238C9.92542 14.9889 9.92542 17.0111 10.2238 19H5.41626C5.13923 18.0241 4.99914 17.0145 5.00001 16ZM19.2963 11H12.7038C13.375 8.7075 14.5 6.64125 16 5.01375C17.5 6.64125 18.625 8.7075 19.2963 11ZM21.7763 13H26.5838C27.1388 14.9615 27.1388 17.0385 26.5838 19H21.7763C22.0746 17.0111 22.0746 14.9889 21.7763 13ZM25.7963 11H21.3675C20.8572 8.99189 20.0001 7.0883 18.835 5.375C20.3236 5.77503 21.7119 6.48215 22.9108 7.45091C24.1097 8.41967 25.0926 9.62861 25.7963 11ZM13.165 5.375C11.9999 7.0883 11.1428 8.99189 10.6325 11H6.20376C6.90741 9.62861 7.89029 8.41967 9.08918 7.45091C10.2881 6.48215 11.6764 5.77503 13.165 5.375ZM6.20376 21H10.6325C11.1428 23.0081 11.9999 24.9117 13.165 26.625C11.6764 26.225 10.2881 25.5178 9.08918 24.5491C7.89029 23.5803 6.90741 22.3714 6.20376 21ZM18.835 26.625C20.0001 24.9117 20.8572 23.0081 21.3675 21H25.7963C25.0926 22.3714 24.1097 23.5803 22.9108 24.5491C21.7119 25.5178 20.3236 26.225 18.835 26.625Z" - fill="#ef4444" - /> - </svg> - )} {items.length > 1 && ( <svg onClick={deletePost(index)} - className="cursor-pointer" + xmlns="http://www.w3.org/2000/svg" data-tooltip-id="tooltip" data-tooltip-content="Delete Post" - xmlns="http://www.w3.org/2000/svg" + className="cursor-pointer" width="20" height="20" - viewBox="0 0 14 14" - fill="currentColor" + viewBox="0 0 20 20" + fill="none" > <path - d="M11.8125 2.625H9.625V2.1875C9.625 1.8394 9.48672 1.50556 9.24058 1.25942C8.99444 1.01328 8.6606 0.875 8.3125 0.875H5.6875C5.3394 0.875 5.00556 1.01328 4.75942 1.25942C4.51328 1.50556 4.375 1.8394 4.375 2.1875V2.625H2.1875C2.07147 2.625 1.96019 2.67109 1.87814 2.75314C1.79609 2.83519 1.75 2.94647 1.75 3.0625C1.75 3.17853 1.79609 3.28981 1.87814 3.37186C1.96019 3.45391 2.07147 3.5 2.1875 3.5H2.625V11.375C2.625 11.6071 2.71719 11.8296 2.88128 11.9937C3.04538 12.1578 3.26794 12.25 3.5 12.25H10.5C10.7321 12.25 10.9546 12.1578 11.1187 11.9937C11.2828 11.8296 11.375 11.6071 11.375 11.375V3.5H11.8125C11.9285 3.5 12.0398 3.45391 12.1219 3.37186C12.2039 3.28981 12.25 3.17853 12.25 3.0625C12.25 2.94647 12.2039 2.83519 12.1219 2.75314C12.0398 2.67109 11.9285 2.625 11.8125 2.625ZM5.25 2.1875C5.25 2.07147 5.29609 1.96019 5.37814 1.87814C5.46019 1.79609 5.57147 1.75 5.6875 1.75H8.3125C8.42853 1.75 8.53981 1.79609 8.62186 1.87814C8.70391 1.96019 8.75 2.07147 8.75 2.1875V2.625H5.25V2.1875ZM10.5 11.375H3.5V3.5H10.5V11.375ZM6.125 5.6875V9.1875C6.125 9.30353 6.07891 9.41481 5.99686 9.49686C5.91481 9.57891 5.80353 9.625 5.6875 9.625C5.57147 9.625 5.46019 9.57891 5.37814 9.49686C5.29609 9.41481 5.25 9.30353 5.25 9.1875V5.6875C5.25 5.57147 5.29609 5.46019 5.37814 5.37814C5.46019 5.29609 5.57147 5.25 5.6875 5.25C5.80353 5.25 5.91481 5.29609 5.99686 5.37814C6.07891 5.46019 6.125 5.57147 6.125 5.6875ZM8.75 5.6875V9.1875C8.75 9.30353 8.70391 9.41481 8.62186 9.49686C8.53981 9.57891 8.42853 9.625 8.3125 9.625C8.19647 9.625 8.08519 9.57891 8.00314 9.49686C7.92109 9.41481 7.875 9.30353 7.875 9.1875V5.6875C7.875 5.57147 7.92109 5.46019 8.00314 5.37814C8.08519 5.29609 8.19647 5.25 8.3125 5.25C8.42853 5.25 8.53981 5.29609 8.62186 5.37814C8.70391 5.46019 8.75 5.57147 8.75 5.6875Z" - fill="#ef4444" + d="M7.5 2.5L12.5 2.5M2.5 5L17.5 5M15.8333 5L15.2489 13.7661C15.1612 15.0813 15.1174 15.7389 14.8333 16.2375C14.5833 16.6765 14.206 17.0294 13.7514 17.2497C13.235 17.5 12.5759 17.5 11.2578 17.5H8.74221C7.42409 17.5 6.76503 17.5 6.24861 17.2497C5.79396 17.0294 5.41674 16.6765 5.16665 16.2375C4.88259 15.7389 4.83875 15.0813 4.75107 13.7661L4.16667 5M8.33333 8.75V12.9167M11.6667 8.75L11.6667 12.9167" + stroke="#FF3F3F" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> )} @@ -528,64 +640,47 @@ export const Editor: FC<{ [props.value, id] ); + const [loadedEditor, setLoadedEditor] = useState(editorType); + const [showEditor, setShowEditor] = useState(true); + useEffect(() => { + if (editorType === loadedEditor) { + return; + } + setLoadedEditor(editorType); + setShowEditor(false); + }, [editorType]); + + useEffect(() => { + if (showEditor) { + return; + } + setTimeout(() => { + setShowEditor(true); + }, 20); + }, [showEditor]); + + if (!showEditor) { + return null; + } + return ( - <div className="flex flex-col gap-[20px]"> - <div className="relative bg-bigStrip" id={id}> - <div className="flex gap-[5px] bg-newBgLineColor border-b border-t border-customColor3 justify-center items-center p-[5px]"> - <SignatureBox editor={editorRef?.current?.editor} /> - <UText - editor={editorRef?.current?.editor} - currentValue={props.value!} - /> - <BoldText - editor={editorRef?.current?.editor} - currentValue={props.value!} - /> - {(editorType === 'markdown' || editorType === 'html') && - identifier !== 'telegram' && ( - <> - <AComponent - editor={editorRef?.current?.editor} - currentValue={props.value!} - /> - <Bullets - editor={editorRef?.current?.editor} - currentValue={props.value!} - /> - <HeadingComponent - editor={editorRef?.current?.editor} - currentValue={props.value!} - /> - </> - )} - <div - className="select-none cursor-pointer w-[40px] p-[5px] text-center" - onClick={() => setEmojiPickerOpen(!emojiPickerOpen)} - > - {'\uD83D\uDE00'} - </div> - <div className="relative"> - <div className="absolute z-[200] top-[35px] -start-[50px]"> - <EmojiPicker - theme={(localStorage.getItem('mode') as Theme) || Theme.DARK} - onEmojiClick={(e) => { - addText(e.emoji); - setEmojiPickerOpen(false); - }} - open={emojiPickerOpen} - /> - </div> - </div> - </div> - <div className="relative cursor-text"> - {validateChars && - props.value.length === 0 && - pictures?.length === 0 && ( - <div className="px-3 text-sm bg-red-600 !text-white mb-[4px]"> - Your post should have at least one character or one image. - </div> - )} - <div {...getRootProps()}> + <div className="flex flex-col gap-[20px] flex-1"> + <div + className={clsx( + 'relative flex-1 px-[12px] pt-[12px] pb-[12px] flex flex-col', + num > 0 && '!rounded-bs-[0]' + )} + id={id} + > + <div className="relative cursor-text flex flex-1 flex-col"> + {/*{validateChars &&*/} + {/* props.value.length === 0 &&*/} + {/* pictures?.length === 0 && (*/} + {/* <div className="px-3 text-sm bg-red-600 !text-white mb-[4px]">*/} + {/* Your post should have at least one character or one image.*/} + {/* </div>*/} + {/* )}*/} + <div {...getRootProps()} className="flex flex-1 flex-col"> <div className={clsx( 'absolute left-0 top-0 w-full h-full bg-black/70 z-[300] transition-all items-center justify-center flex text-white text-sm', @@ -594,7 +689,7 @@ export const Editor: FC<{ > Drop your files here to upload </div> - <div className="px-[10px] pt-[10px]"> + <div className="px-[10px] pt-[10px] bg-newBgColorInner rounded-t-[6px] relative z-[99]"> <OnlyEditor value={props.value} editorType={editorType} @@ -603,38 +698,40 @@ export const Editor: FC<{ ref={editorRef} /> </div> - <div - className="w-full h-[46px] overflow-hidden absolute left-0" + className="bg-newBgColorInner flex-1" onClick={() => { if (editorRef?.current?.editor?.isFocused) { return; } editorRef?.current?.editor?.commands?.focus('end'); }} - > - <Dashboard - height={46} - uppy={uppy} - id={`prog-${num}`} - showProgressDetails={true} - hideUploadButton={true} - hideRetryButton={true} - hidePauseResumeButton={true} - hideCancelButton={true} - hideProgressAfterFinish={true} - /> + /> + <div className="w-full pointer-events-none"> + <div className="w-full h-[46px] overflow-hidden absolute left-0 bg-newBgColorInner uppyChange"> + <Dashboard + height={46} + uppy={uppy} + id={`prog-${num}`} + showProgressDetails={true} + hideUploadButton={true} + hideRetryButton={true} + hidePauseResumeButton={true} + hideCancelButton={true} + hideProgressAfterFinish={true} + /> + </div> </div> - <div className="w-full h-[46px] pointer-events-none" /> <div - className="flex bg-newBgLineColor" + className="w-full h-[46px] bg-newBgColorInner cursor-text" onClick={() => { if (editorRef?.current?.editor?.isFocused) { return; } editorRef?.current?.editor?.commands?.focus('end'); }} - > + /> + <div className="flex bg-newBgColorInner rounded-b-[6px] cursor-default"> {setImages && ( <MultiMediaComponent allData={allValues} @@ -644,6 +741,112 @@ export const Editor: FC<{ value={props.pictures} dummy={dummy} name="image" + information={ + <InformationComponent + isPicture={pictures?.length > 0} + chars={chars} + totalChars={valueWithoutHtml.length} + totalAllowedChars={props.totalChars} + /> + } + toolBar={ + <div className="flex gap-[5px]"> + <SignatureBox editor={editorRef?.current?.editor} /> + <UText + editor={editorRef?.current?.editor} + currentValue={props.value!} + /> + <BoldText + editor={editorRef?.current?.editor} + currentValue={props.value!} + /> + {(editorType === 'markdown' || editorType === 'html') && + identifier !== 'telegram' && ( + <> + <AComponent + editor={editorRef?.current?.editor} + currentValue={props.value!} + /> + <Bullets + editor={editorRef?.current?.editor} + currentValue={props.value!} + /> + <HeadingComponent + editor={editorRef?.current?.editor} + currentValue={props.value!} + /> + </> + )} + <div + data-tooltip-id="tooltip" + data-tooltip-content="Insert Emoji" + className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" + onClick={() => setEmojiPickerOpen(!emojiPickerOpen)} + > + <svg + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M7.97917 14.6663C11.6611 14.6663 14.6458 11.6816 14.6458 7.99967C14.6458 4.31778 11.6611 1.33301 7.97917 1.33301C4.29727 1.33301 1.3125 4.31778 1.3125 7.99967C1.3125 11.6816 4.29727 14.6663 7.97917 14.6663Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M4.80664 10C5.50664 11.0067 6.67997 11.6667 7.99997 11.6667C9.31997 11.6667 10.4866 11.0067 11.1933 10" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M4.66602 6.16699C5.33268 6.83366 6.41935 6.83366 7.09268 6.16699" + stroke="currentColor" + strokeWidth="1.2" + strokeMiterlimit="10" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M8.90625 6.16699C9.57292 6.83366 10.6596 6.83366 11.3329 6.16699" + stroke="currentColor" + strokeWidth="1.2" + strokeMiterlimit="10" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="relative"> + <div + className={clsx( + 'absolute z-[500] -start-[50px]', + num === 0 && allValues?.length > 1 + ? 'top-[35px]' + : 'bottom-[35px]' + )} + > + <EmojiPicker + height={400} + theme={ + (localStorage.getItem('mode') as Theme) || + Theme.DARK + } + onEmojiClick={(e) => { + addText(e.emoji); + setEmojiPickerOpen(false); + }} + open={emojiPickerOpen} + /> + </div> + </div> + </div> + } onChange={(value) => { setImages(value.target.value); }} @@ -652,52 +855,52 @@ export const Editor: FC<{ /> )} </div> + <div>{childButton}</div> </div> </div> </div> - <div className="flex"> - <div className="flex-1">{childButton}</div> - <div className="bottom-10px end-[25px]"> - {(props?.totalChars || 0) > 0 ? ( - <div - className={clsx( - 'text-end text-sm mt-1', - valueWithoutHtml.length > props.totalChars && '!text-red-500' - )} - > - {valueWithoutHtml.length}/{props.totalChars} - </div> - ) : ( - <div - className={clsx( - 'text-end text-sm mt-1 grid grid-cols-[max-content_max-content] gap-x-[5px]' - )} - > - {selectedIntegration?.map((p) => ( - <Fragment key={p.integration.id}> - <div - className={ - valueWithoutHtml.length > chars?.[p.integration.id] && - '!text-red-500' - } - > - {p.integration.name} ({capitalize(p.integration.identifier)} - ): - </div> - <div - className={ - valueWithoutHtml.length > chars?.[p.integration.id] && - '!text-red-500' - } - > - {valueWithoutHtml.length}/{chars?.[p.integration.id]} - </div> - </Fragment> - ))} - </div> - )} - </div> - </div> + {/*<div className="flex">*/} + {/* <div className="bottom-10px end-[25px]">*/} + {/* {(props?.totalChars || 0) > 0 ? (*/} + {/* <div*/} + {/* className={clsx(*/} + {/* 'text-end text-sm mt-1',*/} + {/* valueWithoutHtml.length > props.totalChars && '!text-red-500'*/} + {/* )}*/} + {/* >*/} + {/* {valueWithoutHtml.length}/{props.totalChars}*/} + {/* </div>*/} + {/* ) : (*/} + {/* <div*/} + {/* className={clsx(*/} + {/* 'text-end text-sm mt-1 grid grid-cols-[max-content_max-content] gap-x-[5px]'*/} + {/* )}*/} + {/* >*/} + {/* {selectedIntegration?.map((p) => (*/} + {/* <Fragment key={p.integration.id}>*/} + {/* <div*/} + {/* className={*/} + {/* valueWithoutHtml.length > chars?.[p.integration.id] &&*/} + {/* '!text-red-500'*/} + {/* }*/} + {/* >*/} + {/* {p.integration.name} ({capitalize(p.integration.identifier)}*/} + {/* ):*/} + {/* </div>*/} + {/* <div*/} + {/* className={*/} + {/* valueWithoutHtml.length > chars?.[p.integration.id] &&*/} + {/* '!text-red-500'*/} + {/* }*/} + {/* >*/} + {/* {valueWithoutHtml.length}/{chars?.[p.integration.id]}*/} + {/* </div>*/} + {/* </Fragment>*/} + {/* ))}*/} + {/* </div>*/} + {/* )}*/} + {/* </div>*/} + {/*</div>*/} </div> ); }; @@ -712,6 +915,7 @@ export const OnlyEditor = forwardRef< } >(({ editorType, value, onChange, paste }, ref) => { const fetch = useFetch(); + const { internal } = useLaunchStore( useShallow((state) => ({ internal: state.internal.find((p) => p.integration.id === state.current), @@ -759,6 +963,10 @@ export const OnlyEditor = forwardRef< InterceptUnderlineShortcut, BulletList, ListItem, + Placeholder.configure({ + placeholder: 'Write something …', + emptyEditorClass: 'is-editor-empty', + }), ...(editorType === 'html' || editorType === 'markdown' ? [ Link.configure({ diff --git a/apps/frontend/src/components/new-launch/heading.component.tsx b/apps/frontend/src/components/new-launch/heading.component.tsx index 26985fe0..99ba129c 100644 --- a/apps/frontend/src/components/new-launch/heading.component.tsx +++ b/apps/frontend/src/components/new-launch/heading.component.tsx @@ -13,20 +13,27 @@ export const HeadingComponent: FC<{ }; return ( - <div className="select-none cursor-pointer w-[40px] p-[5px] text-center relative group"> + <div className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center group relative"> <svg - width="20" - height="16" - viewBox="0 0 24 24" - fill="none" xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" > <path - d="M22 0H2C1.46957 0 0.960859 0.210714 0.585786 0.585786C0.210714 0.960859 0 1.46957 0 2V22C0 22.5304 0.210714 23.0391 0.585786 23.4142C0.960859 23.7893 1.46957 24 2 24H22C22.5304 24 23.0391 23.7893 23.4142 23.4142C23.7893 23.0391 24 22.5304 24 22V2C24 1.46957 23.7893 0.960859 23.4142 0.585786C23.0391 0.210714 22.5304 0 22 0ZM19 18C19 18.2652 18.8946 18.5196 18.7071 18.7071C18.5196 18.8946 18.2652 19 18 19C17.7348 19 17.4804 18.8946 17.2929 18.7071C17.1054 18.5196 17 18.2652 17 18V13H7V18C7 18.2652 6.89464 18.5196 6.70711 18.7071C6.51957 18.8946 6.26522 19 6 19C5.73478 19 5.48043 18.8946 5.29289 18.7071C5.10536 18.5196 5 18.2652 5 18V6C5 5.73478 5.10536 5.48043 5.29289 5.29289C5.48043 5.10536 5.73478 5 6 5C6.26522 5 6.51957 5.10536 6.70711 5.29289C6.89464 5.48043 7 5.73478 7 6V11H17V6C17 5.73478 17.1054 5.48043 17.2929 5.29289C17.4804 5.10536 17.7348 5 18 5C18.2652 5 18.5196 5.10536 18.7071 5.29289C18.8946 5.48043 19 5.73478 19 6V18Z" - fill="currentColor" + d="M3.9974 2.66602V13.3327M11.9974 2.66602V13.3327M5.33073 2.66602H2.66406M11.9974 7.99935L3.9974 7.99935M5.33073 13.3327H2.66406M13.3307 13.3327H10.6641M13.3307 2.66602H10.6641" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> - <div className="flex p-[10px] gap-[5px] -left-[50%] opacity-0 pointer-events-none group-hover:pointer-events-auto group-hover:opacity-100 bg-customColor55 border border-customColor3 z-[100] absolute transition-all"> + <div + data-tooltip-id="tooltip" + data-tooltip-content="Title" + className="flex p-[10px] gap-[5px] -left-[50%] rounded-[6px] bottom-[100%] opacity-0 pointer-events-none group-hover:pointer-events-auto group-hover:opacity-100 bg-newColColor border border-newColColor z-[100] absolute transition-all" + > <div onClick={setHeading(1)}> <svg width="20" diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 993bc528..30e9972b 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { FC, useCallback, useRef, useState } from 'react'; +import React, { FC, useCallback, useMemo, useRef, useState } from 'react'; import { AddEditModalProps } from '@gitroom/frontend/components/new-launch/add.edit.modal'; import clsx from 'clsx'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -43,6 +43,577 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const [loading, setLoading] = useState(false); const toaster = useToaster(); const modal = useModals(); + const [showSettings, setShowSettings] = useState(false); + + const { addEditSets, mutate, customClose, dummy } = props; + + const { + selectedIntegrations, + hide, + date, + setDate, + repeater, + setRepeater, + tags, + setTags, + integrations, + setSelectedIntegrations, + locked, + current, + activateExitButton, + } = useLaunchStore( + useShallow((state) => ({ + hide: state.hide, + date: state.date, + setDate: state.setDate, + current: state.current, + repeater: state.repeater, + setRepeater: state.setRepeater, + tags: state.tags, + setTags: state.setTags, + selectedIntegrations: state.selectedIntegrations, + integrations: state.integrations, + setSelectedIntegrations: state.setSelectedIntegrations, + locked: state.locked, + activateExitButton: state.activateExitButton, + })) + ); + + const currentIntegrationText = useMemo(() => { + if (current === 'global') { + return ''; + } + + const currentIntegration = integrations.find((p) => p.id === current)!; + + return `${currentIntegration.name} (${capitalize( + currentIntegration.identifier.split('-').shift() + )})`; + }, [current]); + + const changeCustomer = useCallback( + (customer: string) => { + const neededIntegrations = integrations.filter( + (p) => p?.customer?.id === customer + ); + setSelectedIntegrations( + neededIntegrations.map((p) => ({ + settings: {}, + selectedIntegrations: p, + })) + ); + }, + [integrations] + ); + + const askClose = useCallback(async () => { + if (!activateExitButton || dummy) { + return; + } + + if ( + await deleteDialog( + t( + 'are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost', + 'Are you sure you want to close this modal? (all data will be lost)' + ), + t('yes_close_it', 'Yes, close it!') + ) + ) { + if (customClose) { + customClose(); + return; + } + modal.closeAll(); + } + }, [activateExitButton, dummy]); + + const deletePost = useCallback(async () => { + setLoading(true); + if ( + !(await deleteDialog( + 'Are you sure you want to delete this post?', + 'Yes, delete it!' + )) + ) { + setLoading(false); + return; + } + await fetch(`/posts/${existingData.group}`, { + method: 'DELETE', + }); + mutate(); + modal.closeAll(); + return; + }, [existingData, mutate, modal]); + + const schedule = useCallback( + (type: 'draft' | 'now' | 'schedule') => async () => { + setLoading(true); + const checkAllValid = await ref.current.checkAllValid(); + if (type !== 'draft') { + const notEnoughChars = checkAllValid.filter((p: any) => { + return p.values.some((a: any) => { + return ( + countCharacters( + stripHtmlValidation('normal', a.content, true), + p?.integration?.identifier || '' + ) === 0 && a.media?.length === 0 + ); + }); + }); + + for (const item of notEnoughChars) { + toaster.show( + '' + + item.integration.name + + ' Your post should have at least one character or one image.', + 'warning' + ); + setLoading(false); + item.preview(); + return; + } + + for (const item of checkAllValid) { + if (item.valid === false) { + toaster.show('Please fix your settings', 'warning'); + item.fix(); + setLoading(false); + setShowSettings(true); + return; + } + + if (item.errors !== true) { + toaster.show( + `${capitalize(item.integration.identifier.split('-')[0])} (${ + item.integration.name + }): ${item.errors}`, + 'warning' + ); + item.preview(); + setLoading(false); + setShowSettings(true); + return; + } + } + + const sliceNeeded = checkAllValid.filter((p: any) => { + return p.values.some((a: any) => { + const strip = stripHtmlValidation('normal', a.content, true); + const weightedLength = countCharacters( + strip, + p?.integration?.identifier || '' + ); + const totalCharacters = + weightedLength > strip.length ? weightedLength : strip.length; + + return totalCharacters > (p.maximumCharacters || 1000000); + }); + }); + + for (const item of sliceNeeded) { + toaster.show( + `${item?.integration?.name} (${item?.integration?.identifier}) post is too long, please fix it`, + 'warning' + ); + item.preview(); + setLoading(false); + return; + } + } + + const shortLinkUrl = dummy + ? { ask: false } + : await ( + await fetch('/posts/should-shortlink', { + method: 'POST', + body: JSON.stringify({ + messages: checkAllValid.flatMap((p: any) => + p.values.flatMap((a: any) => a.content) + ), + }), + }) + ).json(); + + const shortLink = !shortLinkUrl.ask + ? false + : await deleteDialog( + 'Do you want to shortlink the URLs? it will let you get statistics over clicks', + 'Yes, shortlink it!' + ); + + const group = existingData.group || makeId(10); + const data = { + type, + ...(repeater ? { inter: repeater } : {}), + tags, + shortLink, + date: date.utc().format('YYYY-MM-DDTHH:mm:ss'), + posts: checkAllValid.map((post: any) => ({ + integration: { + id: post.integration.id, + }, + group, + settings: { ...(post.settings || {}) }, + value: post.values.map((value: any) => ({ + ...(value.id ? { id: value.id } : {}), + content: value.content, + image: + (value?.media || []).map( + ({ id, path, alt, thumbnail, thumbnailTimestamp }: any) => ({ + id, + path, + alt, + thumbnail, + thumbnailTimestamp, + }) + ) || [], + })), + })), + }; + + if (dummy) { + modal.openModal({ + title: '', + children: <DummyCodeComponent code={data} />, + classNames: { + modal: 'w-[100%] bg-transparent text-textColor', + }, + size: '100%', + withCloseButton: false, + closeOnEscape: true, + closeOnClickOutside: true, + }); + + setLoading(false); + } + + if (!dummy) { + addEditSets + ? addEditSets(data) + : await fetch('/posts', { + method: 'POST', + body: JSON.stringify(data), + }); + + if (!addEditSets) { + mutate(); + toaster.show( + !existingData.integration + ? 'Added successfully' + : 'Updated successfully' + ); + } + if (customClose) { + setTimeout(() => { + customClose(); + }, 2000); + } + + if (!addEditSets) { + modal.closeAll(); + } + } + }, + [ref, repeater, tags, date, addEditSets, dummy] + ); + + return ( + <div className="w-full h-full flex-1 p-[40px] flex relative"> + <div className="flex flex-1 bg-newBgColorInner rounded-[20px] flex-col"> + <div className="flex-1 flex"> + <div className="flex flex-col flex-1 border-r border-newBorder"> + <div className="bg-newBgColor h-[65px] rounded-tl-[20px] flex items-center px-[20px] text-[20px] font-[600]"> + Create Post + </div> + <div className="flex-1 flex flex-col gap-[16px]"> + <div className="flex-1 relative"> + <div + id="social-content" + className="gap-[32px] flex flex-col pr-[8px] pt-[20px] pl-[20px] absolute top-0 left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" + > + <div className="flex w-full"> + <div className="flex flex-1"> + <PicksSocialsComponent toolTip={true} /> + </div> + <div> + {!dummy && ( + <SelectCustomer + onChange={changeCustomer} + integrations={integrations} + /> + )} + </div> + </div> + <div className="flex flex-1 gap-[6px] flex-col"> + <div>{!existingData.integration && <SelectCurrent />}</div> + <div className="flex-1 flex"> + {!hide && <EditorWrapper totalPosts={1} value="" />} + </div> + <div + id="social-empty" + className={clsx( + 'pb-[16px]', + current !== 'global' && 'hidden' + )} + /> + </div> + </div> + </div> + <div + id="wrapper-settings" + className={clsx( + 'pb-[20px] px-[20px] select-none', + current === 'global' && 'hidden' + )} + > + <div className="bg-newSettings flex flex-col rounded-[12px] gap-[12px]"> + <div + onClick={() => setShowSettings(!showSettings)} + className={clsx( + 'bg-[#612BD3] rounded-[12px] flex items-center gap-[8px] cursor-pointer p-[12px]', + showSettings ? '!rounded-b-none' : '' + )} + > + <div className="flex"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M7.82888 16.1429L8.31591 17.2383C8.4607 17.5644 8.69698 17.8414 8.9961 18.0358C9.29522 18.2303 9.64434 18.3337 10.0011 18.3337C10.3579 18.3337 10.707 18.2303 11.0061 18.0358C11.3052 17.8414 11.5415 17.5644 11.6863 17.2383L12.1733 16.1429C12.3467 15.7542 12.6383 15.4302 13.0067 15.217C13.3773 15.0032 13.8061 14.9121 14.2317 14.9568L15.4233 15.0837C15.778 15.1212 16.136 15.055 16.4539 14.8931C16.7717 14.7312 17.0358 14.4806 17.2141 14.1716C17.3925 13.8628 17.4776 13.5089 17.4588 13.1527C17.4401 12.7966 17.3184 12.4535 17.1085 12.1651L16.403 11.1957C16.1517 10.8479 16.0175 10.4293 16.0196 10.0003C16.0195 9.57248 16.155 9.15562 16.4067 8.80959L17.1122 7.84014C17.3221 7.55179 17.4438 7.20872 17.4625 6.85255C17.4813 6.49639 17.3962 6.14244 17.2178 5.83366C17.0395 5.52469 16.7754 5.27407 16.4576 5.11218C16.1397 4.9503 15.7817 4.8841 15.427 4.92162L14.2354 5.04847C13.8098 5.09317 13.381 5.00209 13.0104 4.78829C12.6413 4.57387 12.3496 4.24812 12.177 3.85773L11.6863 2.76236C11.5415 2.4363 11.3052 2.15925 11.0061 1.96482C10.707 1.77039 10.3579 1.66693 10.0011 1.66699C9.64434 1.66693 9.29522 1.77039 8.9961 1.96482C8.69698 2.15925 8.4607 2.4363 8.31591 2.76236L7.82888 3.85773C7.65632 4.24812 7.3646 4.57387 6.99554 4.78829C6.62489 5.00209 6.1961 5.09317 5.77054 5.04847L4.57517 4.92162C4.22045 4.8841 3.86246 4.9503 3.5446 5.11218C3.22675 5.27407 2.96269 5.52469 2.78443 5.83366C2.60595 6.14244 2.52092 6.49639 2.53965 6.85255C2.55839 7.20872 2.68009 7.55179 2.88999 7.84014L3.59554 8.80959C3.84716 9.15562 3.98266 9.57248 3.98258 10.0003C3.98266 10.4282 3.84716 10.845 3.59554 11.1911L2.88999 12.1605C2.68009 12.4489 2.55839 12.7919 2.53965 13.1481C2.52092 13.5043 2.60595 13.8582 2.78443 14.167C2.96286 14.4758 3.22696 14.7263 3.54476 14.8882C3.86257 15.05 4.22047 15.1163 4.57517 15.079L5.76684 14.9522C6.1924 14.9075 6.62119 14.9986 6.99184 15.2124C7.36228 15.4262 7.65535 15.752 7.82888 16.1429Z" + stroke="white" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M9.99961 12.5003C11.3803 12.5003 12.4996 11.381 12.4996 10.0003C12.4996 8.61961 11.3803 7.50033 9.99961 7.50033C8.6189 7.50033 7.49961 8.61961 7.49961 10.0003C7.49961 11.381 8.6189 12.5003 9.99961 12.5003Z" + stroke="white" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="flex-1 text-[14px] font-[600] text-white"> + {currentIntegrationText} Settings + </div> + <div> + <svg + className={clsx( + showSettings && 'rotate-180', + 'transition-transform' + )} + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M5 7.5L10 12.5L15 7.5" + stroke="white" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + <div + id="social-settings" + className={clsx( + !showSettings && 'hidden', + 'px-[12px] pb-[12px] text-[14px] text-textColor font-[500] max-h-[400px] overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner' + )} + /> + <style> + {`#social-settings [data-id="${current}"] {display: block !important;}`} + </style> + </div> + </div> + </div> + </div> + <div className="w-[580px] flex flex-col"> + <div className="bg-newBgColor h-[65px] rounded-tr-[20px] flex items-center px-[20px] text-[20px] font-[600]"> + <div className="flex-1">Post Preview</div> + <div className="cursor-pointer"> + <svg + onClick={askClose} + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M16 4L4 16M4 4L16 16" + stroke="#A3A3A3" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + <div className="flex-1 relative"> + <div className="absolute top-0 p-[20px] left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner"> + <ShowAllProviders ref={ref} /> + </div> + </div> + </div> + </div> + <div className="select-none h-[84px] py-[20px] border-t border-newBorder flex items-center"> + <div className="flex-1 flex pl-[20px] gap-[8px]"> + {!dummy && ( + <TagsComponent + name="tags" + label={t('tags', 'Tags')} + initial={tags} + onChange={(e) => { + setTags(e.target.value); + }} + /> + )} + + {!dummy && ( + <RepeatComponent repeat={repeater} onChange={setRepeater} /> + )} + </div> + <div className="pr-[20px] flex items-center justify-end gap-[8px]"> + {existingData?.integration && ( + <button onClick={deletePost} className="cursor-pointer flex text-[#FF3F3F] gap-[8px] items-center text-[15px] font-[600]"> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M7.5 2.5H12.5M2.5 5H17.5M15.8333 5L15.2489 13.7661C15.1612 15.0813 15.1174 15.7389 14.8333 16.2375C14.5833 16.6765 14.206 17.0294 13.7514 17.2497C13.235 17.5 12.5759 17.5 11.2578 17.5H8.74221C7.42409 17.5 6.76503 17.5 6.24861 17.2497C5.79396 17.0294 5.41674 16.6765 5.16665 16.2375C4.88259 15.7389 4.83875 15.0813 4.75107 13.7661L4.16667 5M8.33333 8.75V12.9167M11.6667 8.75V12.9167" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div>Delete Post</div> + </button> + )} + <DatePicker onChange={setDate} date={date} /> + {!addEditSets && ( + <button + disabled={ + selectedIntegrations.length === 0 || loading || locked + } + onClick={schedule('draft')} + className="cursor-pointer disabled:cursor-not-allowed px-[20px] h-[44px] bg-btnSimple justify-center items-center flex rounded-[8px] text-[15px] font-[600]" + > + Save as Draft + </button> + )} + {addEditSets && ( + <button + className="text-white text-[15px] font-[600] min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] pl-[20px] pr-[16px]" + disabled={ + selectedIntegrations.length === 0 || loading || locked + } + onClick={schedule('draft')} + > + Save Set + </button> + )} + {!addEditSets && ( + <div className="group cursor-pointer relative"> + <button + disabled={ + selectedIntegrations.length === 0 || loading || locked + } + onClick={schedule('schedule')} + className="text-white min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] pl-[20px] pr-[16px]" + > + <div className="text-[15px] font-[600]"> + {selectedIntegrations.length === 0 + ? 'Check the circles above' + : dummy + ? 'Create output' + : !existingData?.integration + ? t('add_to_calendar', 'Add to calendar') + : existingData?.posts?.[0]?.state === 'DRAFT' + ? t('schedule', 'Schedule') + : t('update', 'Update')} + </div> + <div className="flex justify-center items-center h-[20px] w-[20px] pt-[4px] arrow-change"> + <svg + className="group-hover:rotate-180 transition-transform" + xmlns="http://www.w3.org/2000/svg" + width="6" + height="4" + viewBox="0 0 6 4" + fill="none" + > + <path + d="M0.456301 9.69291e-07L5.5437 7.97823e-08C5.94941 8.84616e-09 6.15259 0.567978 5.86571 0.90016L3.32201 3.84556C3.14417 4.05148 2.85583 4.05148 2.67799 3.84556L0.134293 0.900162C-0.152585 0.56798 0.0505934 1.04023e-06 0.456301 9.69291e-07Z" + fill="white" + /> + </svg> + </div> + </button> + + <button + onClick={schedule('now')} + disabled={ + selectedIntegrations.length === 0 || loading || locked + } + className="disabled:cursor-not-allowed disabled:opacity-80 hidden group-hover:flex absolute bottom-[100%] -left-[12px] p-[12px] w-[206px] bg-newBgColorInner" + > + <div className="text-white rounded-[8px] bg-[#D82D7E] h-[44px] w-full flex justify-center items-center post-now"> + Post Now + </div> + </button> + </div> + )} + </div> + </div> + </div> + <CopilotPopup + hitEscapeToClose={false} + clickOutsideToClose={true} + instructions={` +You are an assistant that help the user to schedule their social media posts, +Here are the things you can do: +- Add a new comment / post to the list of posts +- Delete a comment / post from the list of posts +- Add content to the comment / post +- Activate or deactivate the comment / post + +Post content can be added using the addPostContentFor{num} function. +After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} function. +`} + labels={{ + title: 'Your Assistant', + initial: 'Hi! I can help you to refine your social media posts.', + }} + /> + </div> + ); +}; +export const ManageModalA: FC<AddEditModalProps> = (props) => { + const t = useT(); + const fetch = useFetch(); + const ref = useRef(null); + const existingData = useExistingData(); + const [loading, setLoading] = useState(false); + const toaster = useToaster(); + const modal = useModals(); const { addEditSets, mutate, customClose, dummy } = props; @@ -490,25 +1061,6 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { <ShowAllProviders ref={ref} /> </div> </div> - <CopilotPopup - hitEscapeToClose={false} - clickOutsideToClose={true} - instructions={` -You are an assistant that help the user to schedule their social media posts, -Here are the things you can do: -- Add a new comment / post to the list of posts -- Delete a comment / post from the list of posts -- Add content to the comment / post -- Activate or deactivate the comment / post - -Post content can be added using the addPostContentFor{num} function. -After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} function. -`} - labels={{ - title: 'Your Assistant', - initial: 'Hi! 👋 How can I assist you today?', - }} - /> </div> </> ); diff --git a/apps/frontend/src/components/new-launch/picks.socials.component.tsx b/apps/frontend/src/components/new-launch/picks.socials.component.tsx index c966a177..6451ea66 100644 --- a/apps/frontend/src/components/new-launch/picks.socials.component.tsx +++ b/apps/frontend/src/components/new-launch/picks.socials.component.tsx @@ -6,6 +6,7 @@ import Image from 'next/image'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; import { useExistingData } from '@gitroom/frontend/components/launches/helpers/use.existing.data'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ toolTip, @@ -26,13 +27,13 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ })) ); + return ( <div className={clsx('flex', locked && 'opacity-50 pointer-events-none')}> - <div className="flex"> - <div className="innerComponent"> - <div className="grid grid-cols-13 gap-[10px]"> - {integrations - .filter((f) => { + <div className="flex flex-1"> + <div className="innerComponent flex-1 flex"> + <div className="flex flex-wrap gap-[12px] flex-1"> + {integrations.filter((f) => { if (exising.integration) { return f.id === exising.integration; } @@ -55,34 +56,41 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ addOrRemoveSelectedIntegration(integration, {}); }} className={clsx( - 'cursor-pointer relative w-[34px] h-[34px] rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500', + 'cursor-pointer border-[1.5px] relative rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500', selectedIntegrations.findIndex( (p) => p.integration.id === integration.id ) === -1 - ? 'opacity-40' - : '' + ? 'grayscale border-transparent' + : 'border-[#622FF6]' )} > <Image src={integration.picture || '/no-picture.jpg'} - className="rounded-full" + className={clsx( + 'rounded-full transition-all min-w-[42px] border-[1.5px] min-h-[42px]', + selectedIntegrations.findIndex( + (p) => p.integration.id === integration.id + ) === -1 + ? 'border-transparent' + : 'border-[#000]' + )} alt={integration.identifier} - width={32} - height={32} + width={42} + height={42} /> {integration.identifier === 'youtube' ? ( <img src="/icons/platforms/youtube.svg" - className="absolute z-10 -bottom-[5px] -end-[5px]" - width={20} + className="absolute z-10 -bottom-[5px] -end-[5px] min-w-[16px]" + width={16} /> ) : ( <Image src={`/icons/platforms/${integration.identifier}.png`} - className="rounded-full absolute z-10 -bottom-[5px] -end-[5px] border border-fifth" + className="rounded-[4px] absolute z-10 bottom-0 -end-[5px] min-w-[16px] min-h-[16px]" alt={integration.identifier} - width={20} - height={20} + width={16} + height={16} /> )} </div> diff --git a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx index eb888169..82bf421e 100644 --- a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx @@ -22,6 +22,7 @@ import useSWR from 'swr'; import { InternalChannels } from '@gitroom/frontend/components/launches/internal.channels'; import { capitalize } from 'lodash'; import clsx from 'clsx'; +import { createPortal } from 'react-dom'; class Empty { @IsOptional() @@ -255,40 +256,6 @@ export const withProvider = function <T extends object>(params: { > <FormProvider {...form}> <div className={current ? '' : 'hidden'}> - <div className="flex gap-[4px] mb-[20px] p-[4px] border border-newTableBorder rounded-[8px]"> - <div className="flex-1 flex"> - <div - onClick={() => setTab(0)} - className={clsx( - 'cursor-pointer rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px]', - tab !== 0 && !!SettingsComponent - ? '' - : 'text-textItemFocused bg-boxFocused' - )} - > - {t('preview', 'Preview')} - </div> - </div> - {current && - (!!SettingsComponent || !!data?.internalPlugs?.length) && ( - <div className="flex-1 flex"> - <div - onClick={() => setTab(1)} - className={clsx( - 'cursor-pointer rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px]', - tab !== 1 ? '' : 'text-textItemFocused bg-boxFocused' - )} - > - {t('settings', 'Settings')} ( - {capitalize( - selectedIntegration.integration.identifier.split('-')[0] - )} - ) - </div> - </div> - )} - </div> - {current && (tab === 0 || (!SettingsComponent && !data?.internalPlugs?.length)) && @@ -331,14 +298,22 @@ export const withProvider = function <T extends object>(params: { } /> ))} - {(SettingsComponent || !!data?.internalPlugs?.length) && ( - <div className={tab === 1 ? '' : 'hidden'}> - <SettingsComponent /> - {!!data?.internalPlugs?.length && !dummy && ( - <InternalChannels plugs={data?.internalPlugs} /> - )} - </div> - )} + {(SettingsComponent || !!data?.internalPlugs?.length) && + createPortal( + <div data-id={props.id} className="hidden"> + <SettingsComponent /> + {!!data?.internalPlugs?.length && !dummy && ( + <InternalChannels plugs={data?.internalPlugs} /> + )} + </div>, + document.querySelector('#social-settings') || document.createElement('div') + )} + {current && + !SettingsComponent && + createPortal( + <style>{`#wrapper-settings {display: none !important;} #social-empty {display: block !important;}`}</style>, + document.querySelector('#social-settings')! + )} </div> </FormProvider> </IntegrationContext.Provider> diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 187a9657..6afe1533 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -192,13 +192,6 @@ export const ShowAllProviders = forwardRef((props, ref) => { })), }} > - <div className="flex gap-[4px] mb-[20px]"> - <div className="flex-1 flex p-[4px] border border-newTableBorder rounded-[8px]"> - <div className="rounded-[4px] flex-1 overflow-hidden whitespace-nowrap text-center pt-[6px] pb-[5px] text-textItemFocused bg-boxFocused"> - {t('preview', 'Preview')} - </div> - </div> - </div> {global?.[0]?.content?.length === 0 ? ( <div> {t( diff --git a/apps/frontend/src/components/new-launch/select.current.tsx b/apps/frontend/src/components/new-launch/select.current.tsx index 8222939c..53350bbe 100644 --- a/apps/frontend/src/components/new-launch/select.current.tsx +++ b/apps/frontend/src/components/new-launch/select.current.tsx @@ -1,12 +1,6 @@ 'use client'; -import { - FC, - RefObject, - useEffect, - useRef, - useState, -} from 'react'; +import { FC, RefObject, useEffect, useRef, useState } from 'react'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import clsx from 'clsx'; import Image from 'next/image'; @@ -72,11 +66,11 @@ export const SelectCurrent: FC = () => { return ( <> - <div className="left-0 absolute w-full z-[100] px-[24px]"> + <div className="select-none left-0 absolute w-full z-[100] px-[20px]"> <div ref={contentRef} className={clsx( - 'flex gap-[3px] w-full overflow-x-auto scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary', + 'flex gap-[6px] w-full overflow-x-auto scrollbar scrollbar-thumb-tableBorder scrollbar-track-secondary', locked && 'opacity-50 pointer-events-none' )} > @@ -85,19 +79,27 @@ export const SelectCurrent: FC = () => { setHide(true); setCurrent('global'); }} - className="cursor-pointer flex gap-[8px] items-center bg-newBgLineColor p-[10px] rounded-tl-[4px] rounded-tr-[4px]" + className={clsx( + 'cursor-pointer flex gap-[8px] rounded-[8px] w-[40px] h-[40px] justify-center items-center bg-newBgLineColor', + current !== 'global' + ? 'text-[#A3A3A3]' + : 'border border-[#FC69FF] text-[#FC69FF]' + )} > - <div className={clsx(current !== 'global' ? 'opacity-40' : '')}> + <div> <svg + xmlns="http://www.w3.org/2000/svg" width="20" height="20" - viewBox="0 0 32 32" + viewBox="0 0 20 20" fill="none" - xmlns="http://www.w3.org/2000/svg" > <path - d="M16 3C13.4288 3 10.9154 3.76244 8.77759 5.1909C6.63975 6.61935 4.97351 8.64968 3.98957 11.0251C3.00563 13.4006 2.74819 16.0144 3.2498 18.5362C3.75141 21.0579 4.98953 23.3743 6.80762 25.1924C8.6257 27.0105 10.9421 28.2486 13.4638 28.7502C15.9856 29.2518 18.5995 28.9944 20.9749 28.0104C23.3503 27.0265 25.3807 25.3603 26.8091 23.2224C28.2376 21.0846 29 18.5712 29 16C28.9964 12.5533 27.6256 9.24882 25.1884 6.81163C22.7512 4.37445 19.4467 3.00364 16 3ZM12.7038 21H19.2963C18.625 23.2925 17.5 25.3587 16 26.9862C14.5 25.3587 13.375 23.2925 12.7038 21ZM12.25 19C11.9183 17.0138 11.9183 14.9862 12.25 13H19.75C20.0817 14.9862 20.0817 17.0138 19.75 19H12.25ZM5.00001 16C4.99914 14.9855 5.13923 13.9759 5.41626 13H10.2238C9.92542 14.9889 9.92542 17.0111 10.2238 19H5.41626C5.13923 18.0241 4.99914 17.0145 5.00001 16ZM19.2963 11H12.7038C13.375 8.7075 14.5 6.64125 16 5.01375C17.5 6.64125 18.625 8.7075 19.2963 11ZM21.7763 13H26.5838C27.1388 14.9615 27.1388 17.0385 26.5838 19H21.7763C22.0746 17.0111 22.0746 14.9889 21.7763 13ZM25.7963 11H21.3675C20.8572 8.99189 20.0001 7.0883 18.835 5.375C20.3236 5.77503 21.7119 6.48215 22.9108 7.45091C24.1097 8.41967 25.0926 9.62861 25.7963 11ZM13.165 5.375C11.9999 7.0883 11.1428 8.99189 10.6325 11H6.20376C6.90741 9.62861 7.89029 8.41967 9.08918 7.45091C10.2881 6.48215 11.6764 5.77503 13.165 5.375ZM6.20376 21H10.6325C11.1428 23.0081 11.9999 24.9117 13.165 26.625C11.6764 26.225 10.2881 25.5178 9.08918 24.5491C7.89029 23.5803 6.90741 22.3714 6.20376 21ZM18.835 26.625C20.0001 24.9117 20.8572 23.0081 21.3675 21H25.7963C25.0926 22.3714 24.1097 23.5803 22.9108 24.5491C21.7119 25.5178 20.3236 26.225 18.835 26.625Z" - fill="currentColor" + d="M2.56267 6.23601L6.13604 8.78837C6.32197 8.92118 6.41494 8.98759 6.51225 9.00289C6.59786 9.01635 6.68554 9.00278 6.76309 8.96407C6.85121 8.92008 6.91976 8.82868 7.05686 8.64588L7.81194 7.63909C7.85071 7.5874 7.8701 7.56155 7.89288 7.53925C7.91311 7.51945 7.93531 7.50177 7.95913 7.48647C7.98595 7.46924 8.01547 7.45612 8.07452 7.42988L11.2983 5.99707C11.432 5.93767 11.4988 5.90798 11.5492 5.8616C11.5938 5.82057 11.6288 5.77033 11.652 5.71436C11.6782 5.65108 11.6831 5.57812 11.6928 5.4322L11.9288 1.8915M11.2493 11.2503L13.4294 12.1846C13.6823 12.293 13.8088 12.3472 13.8757 12.4372C13.9345 12.5162 13.9634 12.6135 13.9573 12.7117C13.9504 12.8237 13.8741 12.9382 13.7214 13.1672L12.6973 14.7035C12.6249 14.812 12.5887 14.8663 12.5409 14.9056C12.4986 14.9403 12.4498 14.9664 12.3974 14.9824C12.3382 15.0003 12.273 15.0003 12.1426 15.0003H10.4799C10.3071 15.0003 10.2207 15.0003 10.1472 14.9714C10.0822 14.9459 10.0248 14.9045 9.98003 14.851C9.92936 14.7904 9.90204 14.7084 9.8474 14.5445L9.25334 12.7623C9.22111 12.6656 9.205 12.6173 9.20076 12.5681C9.19699 12.5246 9.20011 12.4807 9.21 12.4381C9.22114 12.3901 9.24393 12.3445 9.28951 12.2533L9.74077 11.3508C9.83246 11.1674 9.8783 11.0758 9.94891 11.0188C10.0111 10.9687 10.0865 10.9375 10.166 10.9289C10.2561 10.9193 10.3534 10.9517 10.5479 11.0165L11.2493 11.2503ZM18.3327 10.0003C18.3327 14.6027 14.6017 18.3337 9.99935 18.3337C5.39698 18.3337 1.66602 14.6027 1.66602 10.0003C1.66602 5.39795 5.39698 1.66699 9.99935 1.66699C14.6017 1.66699 18.3327 5.39795 18.3327 10.0003Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" /> </svg> </div> @@ -109,34 +111,39 @@ export const SelectCurrent: FC = () => { setCurrent(integration.id); }} key={integration.id} - className="cursor-pointer flex gap-[8px] items-center bg-newBgLineColor p-[10px] rounded-tl-[4px] rounded-tr-[4px]" + className={clsx( + 'border cursor-pointer relative flex gap-[8px] w-[40px] h-[40px] rounded-[8px] items-center bg-newBgLineColor justify-center', + current === integration.id + ? 'border-[#FC69FF] text-[#FC69FF]' + : 'border-transparent' + )} > + <IsGlobal id={integration.id} /> <div className={clsx( - 'relative w-[20px] h-[20px] rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500', - current !== integration.id ? 'opacity-40' : '' + 'relative w-full h-full rounded-full flex justify-center items-center filter transition-all duration-500' )} > <Image src={integration.picture || '/no-picture.jpg'} - className="rounded-full" + className="rounded-full min-w-[26px]" alt={integration.identifier} - width={20} - height={20} + width={26} + height={26} /> {integration.identifier === 'youtube' ? ( <img src="/icons/platforms/youtube.svg" - className="absolute z-10 -bottom-[5px] -end-[5px]" - width={20} + className="absolute z-10 bottom-[2px] end-[2px] min-w-[12px]" + width={12} /> ) : ( <Image src={`/icons/platforms/${integration.identifier}.png`} - className="rounded-full absolute z-10 -bottom-[5px] -end-[5px] border border-fifth" + className="min-w-[12px] min-h-[12px] rounded-[3px] absolute z-10 bottom-[6px] end-[6px]" alt={integration.identifier} - width={15} - height={15} + width={12} + height={12} /> )} </div> @@ -148,3 +155,24 @@ export const SelectCurrent: FC = () => { </> ); }; + +export const IsGlobal: FC<{ id: string }> = ({ id }) => { + const { isInternal } = + useLaunchStore( + useShallow((state) => ({ + isInternal: !!state.internal.find(p => p.integration.id === id), + })) + ); + + if (!isInternal) { + return null; + } + + return ( + <div + data-tooltip-id="tooltip" + data-tooltip-content="No longer in global mode" + className="w-[8px] h-[8px] bg-[#FC69FF] -top-[1px] -end-[3px] absolute rounded-full" + /> + ); +}; diff --git a/apps/frontend/src/components/new-launch/u.text.tsx b/apps/frontend/src/components/new-launch/u.text.tsx index 3c0315a0..a03d392b 100644 --- a/apps/frontend/src/components/new-launch/u.text.tsx +++ b/apps/frontend/src/components/new-launch/u.text.tsx @@ -81,36 +81,25 @@ export const UText: FC<{ }; return ( <div + data-tooltip-id="tooltip" + data-tooltip-content="Underline" onClick={mark} - className="select-none cursor-pointer w-[40px] p-[5px] text-center" + className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" > <svg - width="25" - height="24" - viewBox="0 0 25 24" - fill="none" xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" > - <g clipPath="url(#clip0_31_12620)"> - <path - d="M10.4119 6.47V12.77C10.4119 13.4 10.5669 13.885 10.8769 14.225C11.1869 14.565 11.6419 14.735 12.2419 14.735C12.8419 14.735 13.3019 14.565 13.6219 14.225C13.9419 13.885 14.1019 13.4 14.1019 12.77V6.47H16.6669V12.755C16.6669 13.695 16.4669 14.49 16.0669 15.14C15.6669 15.79 15.1269 16.28 14.4469 16.61C13.7769 16.94 13.0269 17.105 12.1969 17.105C11.3669 17.105 10.6219 16.945 9.96191 16.625C9.31191 16.295 8.79691 15.805 8.41691 15.155C8.03691 14.495 7.84691 13.695 7.84691 12.755V6.47H10.4119Z" - fill="currentColor" - /> - <path - d="M6.96191 18.5H17.5369V19.25H6.96191V18.5Z" - fill="currentColor" - /> - </g> - <defs> - <clipPath id="clip0_31_12620"> - <rect - width="24" - height="24" - fill="white" - transform="translate(0.25)" - /> - </clipPath> - </defs> + <path + d="M11.9993 2.66699V7.33366C11.9993 9.5428 10.2085 11.3337 7.99935 11.3337C5.79021 11.3337 3.99935 9.5428 3.99935 7.33366V2.66699M2.66602 14.0003H13.3327" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> </svg> </div> ); diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index d34db3aa..dcfc7d37 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -35,7 +35,6 @@ import { TopMenu } from '@gitroom/frontend/components/layout/top.menu'; import { LanguageComponent } from '@gitroom/frontend/components/layout/language.component'; import { ChromeExtensionComponent } from '@gitroom/frontend/components/layout/chrome.extension.component'; import NotificationComponent from '@gitroom/frontend/components/notifications/notification.component'; -import { BillingAfter } from '@gitroom/frontend/components/new-layout/billing.after'; import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector'; import { PreConditionComponent } from '@gitroom/frontend/components/layout/pre-condition.component'; import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/sentry.feedback.component'; diff --git a/apps/frontend/src/components/sets/sets.tsx b/apps/frontend/src/components/sets/sets.tsx index df07d7e2..a37ae809 100644 --- a/apps/frontend/src/components/sets/sets.tsx +++ b/apps/frontend/src/components/sets/sets.tsx @@ -94,10 +94,13 @@ export const Sets: FC = () => { (params?: { id?: string; name?: string; content?: string }) => () => { modal.openModal({ closeOnClickOutside: false, + removeLayout: true, closeOnEscape: false, withCloseButton: false, - removeLayout: true, - askClose: true, + fullScreen: true, + classNames: { + modal: 'w-[100%] max-w-[1400px] text-textColor', + }, id: 'add-edit-modal', children: ( <AddEditModal diff --git a/apps/frontend/src/components/signature.tsx b/apps/frontend/src/components/signature.tsx index d1ee4b0f..fbacf237 100644 --- a/apps/frontend/src/components/signature.tsx +++ b/apps/frontend/src/components/signature.tsx @@ -13,6 +13,7 @@ export const SignatureBox: FC<{ const addSignature = useCallback(() => { modals.openModal({ title: 'Add Signature', + withCloseButton: true, children: (close) => ( <SignatureModal appendSignature={appendValue} close={close} /> ), @@ -23,18 +24,36 @@ export const SignatureBox: FC<{ <> <div onClick={addSignature} - className="select-none cursor-pointer w-[40px] p-[5px] text-center" + data-tooltip-id="tooltip" + data-tooltip-content="Add Signature" + className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" > <svg - width="25" - viewBox="0 0 30 28" - fill="none" xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" > - <path - d="M28.0007 18.5H7.79447C8.06947 17.9475 8.34572 17.3825 8.61822 16.8087C8.64822 16.7475 8.67697 16.6837 8.70572 16.6262C10.4007 16.4262 12.3032 15.2187 14.3957 13.0137C15.062 14.5212 16.1545 16.1225 17.852 16.4437C18.5082 16.5687 19.5382 16.5425 20.7145 15.7175C21.1274 15.4227 21.5079 15.085 21.8495 14.71C22.8907 15.6625 24.7345 16.5 28.0007 16.5C28.3985 16.5 28.7801 16.3419 29.0614 16.0606C29.3427 15.7793 29.5007 15.3978 29.5007 15C29.5007 14.6021 29.3427 14.2206 29.0614 13.9393C28.7801 13.658 28.3985 13.5 28.0007 13.5C23.9182 13.5 23.5245 12.0487 23.5007 11.925C23.4832 11.5853 23.3508 11.2617 23.1252 11.0072C22.8996 10.7527 22.5942 10.5824 22.2591 10.5243C21.924 10.4661 21.5791 10.5236 21.2809 10.6873C20.9828 10.8509 20.7491 11.1111 20.6182 11.425C19.447 13.1662 18.6682 13.55 18.4107 13.5C17.797 13.3837 16.8545 11.5375 16.4707 9.70122C16.4097 9.40441 16.2602 9.13304 16.0419 8.92284C15.8237 8.71265 15.5468 8.5735 15.2479 8.5237C14.949 8.47391 14.6421 8.51581 14.3674 8.64389C14.0928 8.77197 13.8634 8.98022 13.7095 9.24122C12.4595 10.7887 11.3895 11.8575 10.4995 12.5537C13.1632 5.90497 12.6432 3.48872 11.9332 2.23247C11.3082 1.12372 10.1832 0.509973 8.76322 0.503723H8.72322C6.47322 0.519973 4.66947 2.58622 3.88197 6.03747C3.45072 7.92872 3.38197 10.0225 3.69947 11.78C4.02947 13.6162 4.74072 14.9887 5.77572 15.8075C5.32947 16.73 4.87572 17.6362 4.43197 18.5H2.00072C1.6029 18.5 1.22137 18.658 0.940062 18.9393C0.658758 19.2206 0.500722 19.6021 0.500722 20C0.500722 20.3978 0.658758 20.7793 0.940062 21.0606C1.22137 21.3419 1.6029 21.5 2.00072 21.5H2.83572C1.62572 23.7087 0.730722 25.2 0.710722 25.2262C0.60917 25.395 0.541869 25.5822 0.512665 25.7771C0.483461 25.9719 0.492925 26.1706 0.540517 26.3618C0.588109 26.5529 0.672896 26.7329 0.790035 26.8913C0.907175 27.0497 1.05437 27.1835 1.22322 27.285C1.45751 27.4272 1.72666 27.5016 2.00072 27.5C2.25956 27.5002 2.51405 27.4334 2.73944 27.3061C2.96483 27.1789 3.15346 26.9955 3.28697 26.7737C3.36447 26.6425 4.65322 24.5 6.25072 21.5H28.0007C28.3985 21.5 28.7801 21.3419 29.0614 21.0606C29.3427 20.7793 29.5007 20.3978 29.5007 20C29.5007 19.6021 29.3427 19.2206 29.0614 18.9393C28.7801 18.658 28.3985 18.5 28.0007 18.5ZM23.5007 12C23.5007 11.9775 23.5007 11.955 23.5007 11.9325C23.5026 11.9549 23.5026 11.9775 23.5007 12ZM6.80572 6.70122C7.22197 4.87497 8.05572 3.49997 8.75072 3.49997C9.20947 3.49997 9.28197 3.62497 9.32572 3.70497C9.50447 4.02247 10.1445 5.84122 7.14822 12.805C6.9073 12.3137 6.73968 11.7898 6.65072 11.25C6.40827 9.73793 6.46091 8.19325 6.80572 6.70122Z" - fill="currentColor" - /> + <g clipPath="url(#clip0_2352_53073)"> + <path + d="M1.61528 13.4634C4.98807 11.1708 6.12853 2.72516 2.42576 2.54001C-0.271332 2.40515 1.52719 6.99029 4.04454 11.4405C4.43518 12.1311 5.08312 12.0221 5.35096 11.8873C6.23825 11.4405 6.49355 9.29898 6.95238 8.8935C7.41122 8.48802 7.98909 8.45521 8.49293 9.16322C9.12361 10.0494 9.65463 9.91856 10.0456 9.70264C10.6103 9.39078 11.3197 8.22463 12.1949 9.16322C12.7765 9.78692 12.5068 10.4612 14.9173 10.1915" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + /> + <path + d="M7.16602 12.917H13.8327" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + /> + </g> + <defs> + <clipPath id="clip0_2352_53073"> + <rect width="16" height="16" fill="white" /> + </clipPath> + </defs> </svg> </div> </> @@ -45,11 +64,5 @@ export const SignatureModal: FC<{ appendSignature: (sign: string) => void; }> = (props) => { const { appendSignature } = props; - return ( - <div className="bg-black/40 fixed start-0 top-0 w-full h-full z-[500]"> - <div className="relative w-[900px] mx-auto flex gap-[20px] flex-col flex-1 rounded-[4px] pt-0"> - <SignaturesComponent appendSignature={appendSignature} /> - </div> - </div> - ); + return <SignaturesComponent appendSignature={appendSignature} />; }; diff --git a/apps/frontend/src/components/third-parties/third-party.media.tsx b/apps/frontend/src/components/third-parties/third-party.media.tsx index 2729607c..59917fec 100644 --- a/apps/frontend/src/components/third-parties/third-party.media.tsx +++ b/apps/frontend/src/components/third-parties/third-party.media.tsx @@ -182,9 +182,9 @@ export const ThirdPartyMedia: FC<{ return ( <> <div className="relative group"> - <Button + <div className={clsx( - 'relative ms-[10px] !px-[10px] rounded-[4px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-newBgLineColor bg-newColColor' + 'cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]' )} onClick={() => { modals.openModal({ @@ -204,8 +204,8 @@ export const ThirdPartyMedia: FC<{ <div className={clsx('flex gap-[5px] items-center')}> <div> <svg - width="24" - height="24" + width="16" + height="16" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg" @@ -216,11 +216,11 @@ export const ThirdPartyMedia: FC<{ /> </svg> </div> - <div className="text-[12px] font-[500] !text-current"> + <div className="text-[10px] font-[600] iconBreak:hidden block"> {t('integrations', 'Integrations')} </div> </div> - </Button> + </div> </div> </> ); diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index e5c3f7e9..15e6a72b 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -74,6 +74,9 @@ module.exports = { modalCustom: 'var(--color-modalCustom)', newBgColor: 'var(--new-bgColor)', + newBackdrop: 'var(--new-back-drop)', + newSep: 'var(--new-sep)', + newBorder: 'var(--new-border)', newBgColorInner: 'var(--new-bgColorInner)', newBgLineColor: 'var(--new-bgLineColor)', textItemFocused: 'var(--new-textItemFocused)', @@ -91,6 +94,7 @@ module.exports = { newTableText: 'var(--new-table-text)', newTableTextFocused: 'var(--new-table-text-focused)', newColColor: 'var(--new-col-color)', + newSettings: 'var(--new-settings)', menuDots: 'var(--new-menu-dots)', menuDotsHover: 'var(--new-menu-hover)', bigStrip: 'var(--new-big-strips)', @@ -223,6 +227,12 @@ module.exports = { }, }), screens: { + iconBreak: { + raw: '(max-width: 1560px)', + }, + maxMedia: { + raw: '(max-width: 1400px)', + }, custom: { raw: '(max-height: 800px)', }, diff --git a/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts b/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts index 572d1dfd..4c1d3e0b 100644 --- a/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts @@ -78,10 +78,7 @@ export class MediaRepository { }, }, }; - const pages = - pageNum === 0 - ? Math.ceil((await this._media.model.media.count(query)) / 28) - : 0; + const pages = Math.ceil((await this._media.model.media.count(query)) / 18); const results = await this._media.model.media.findMany({ where: { organizationId: org, @@ -98,8 +95,8 @@ export class MediaRepository { alt: true, thumbnailTimestamp: true, }, - skip: pageNum * 28, - take: 28, + skip: pageNum * 18, + take: 18, }); return { From f5dff1e6550d69786f30c342c1681eb2f0f9e765 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 25 Dec 2025 19:02:52 +0700 Subject: [PATCH 054/340] feat: svg change --- .../launches/helpers/date.picker.tsx | 17 +- .../launches/helpers/top.title.component.tsx | 29 +- .../components/launches/repeat.component.tsx | 38 +- .../components/launches/select.customer.tsx | 31 +- .../components/launches/tags.component.tsx | 66 +- .../src/components/launches/up.down.arrow.tsx | 19 +- .../src/components/media/media.component.tsx | 254 +---- .../src/components/new-launch/editor.tsx | 128 +-- .../components/new-launch/manage.modal.tsx | 558 +---------- .../components/new-launch/select.current.tsx | 17 +- .../src/components/ui/icons/index.tsx | 878 ++++++++++++++++++ apps/frontend/tailwind.config.js | 1 + 12 files changed, 954 insertions(+), 1082 deletions(-) create mode 100644 apps/frontend/src/components/ui/icons/index.tsx diff --git a/apps/frontend/src/components/launches/helpers/date.picker.tsx b/apps/frontend/src/components/launches/helpers/date.picker.tsx index 2abebc7d..585f8e91 100644 --- a/apps/frontend/src/components/launches/helpers/date.picker.tsx +++ b/apps/frontend/src/components/launches/helpers/date.picker.tsx @@ -6,6 +6,7 @@ import { Button } from '@gitroom/react/form/button'; import { isUSCitizen } from './isuscitizen.utils'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; +import { CalendarIcon } from '@gitroom/frontend/components/ui/icons'; export const DatePicker: FC<{ date: dayjs.Dayjs; onChange: (day: dayjs.Dayjs) => void; @@ -39,21 +40,7 @@ export const DatePicker: FC<{ ref={ref} > <div className="cursor-pointer"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="17" - height="19" - viewBox="0 0 17 19" - fill="none" - > - <path - d="M15.75 7.41667H0.75M11.5833 0.75V4.08333M4.91667 0.75V4.08333M4.75 17.4167H11.75C13.1501 17.4167 13.8502 17.4167 14.385 17.1442C14.8554 16.9045 15.2378 16.522 15.4775 16.0516C15.75 15.5169 15.75 14.8168 15.75 13.4167V6.41667C15.75 5.01654 15.75 4.31647 15.4775 3.78169C15.2378 3.31129 14.8554 2.92883 14.385 2.68915C13.8502 2.41667 13.1501 2.41667 11.75 2.41667H4.75C3.34987 2.41667 2.6498 2.41667 2.11502 2.68915C1.64462 2.92883 1.26217 3.31129 1.02248 3.78169C0.75 4.31647 0.75 5.01654 0.75 6.41667V13.4167C0.75 14.8168 0.75 15.5169 1.02248 16.0516C1.26217 16.522 1.64462 16.9045 2.11502 17.1442C2.6498 17.4167 3.34987 17.4167 4.75 17.4167Z" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <CalendarIcon /> </div> <div className="cursor-pointer"> {date.format(isUSCitizen() ? 'MM/DD/YYYY hh:mm A' : 'DD/MM/YYYY HH:mm')} diff --git a/apps/frontend/src/components/launches/helpers/top.title.component.tsx b/apps/frontend/src/components/launches/helpers/top.title.component.tsx index 2a44f958..48624e60 100644 --- a/apps/frontend/src/components/launches/helpers/top.title.component.tsx +++ b/apps/frontend/src/components/launches/helpers/top.title.component.tsx @@ -1,6 +1,7 @@ import { FC, ReactNode } from 'react'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import clsx from 'clsx'; +import { ExpandIcon, CollapseIcon } from '@gitroom/frontend/components/ui/icons'; export const TopTitle: FC<{ title: string; @@ -44,33 +45,9 @@ export const TopTitle: FC<{ {shouldExpend !== undefined && ( <div className="cursor-pointer"> {!shouldExpend ? ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="25" - onClick={expend} - viewBox="0 0 24 25" - fill="none" - > - <path - d="M20.25 5V9.5C20.25 9.69891 20.171 9.88968 20.0303 10.0303C19.8897 10.171 19.6989 10.25 19.5 10.25C19.3011 10.25 19.1103 10.171 18.9697 10.0303C18.829 9.88968 18.75 9.69891 18.75 9.5V6.81031L14.0306 11.5306C13.8899 11.6714 13.699 11.7504 13.5 11.7504C13.301 11.7504 13.1101 11.6714 12.9694 11.5306C12.8286 11.3899 12.7496 11.199 12.7496 11C12.7496 10.801 12.8286 10.6101 12.9694 10.4694L17.6897 5.75H15C14.8011 5.75 14.6103 5.67098 14.4697 5.53033C14.329 5.38968 14.25 5.19891 14.25 5C14.25 4.80109 14.329 4.61032 14.4697 4.46967C14.6103 4.32902 14.8011 4.25 15 4.25H19.5C19.6989 4.25 19.8897 4.32902 20.0303 4.46967C20.171 4.61032 20.25 4.80109 20.25 5ZM9.96937 13.4694L5.25 18.1897V15.5C5.25 15.3011 5.17098 15.1103 5.03033 14.9697C4.88968 14.829 4.69891 14.75 4.5 14.75C4.30109 14.75 4.11032 14.829 3.96967 14.9697C3.82902 15.1103 3.75 15.3011 3.75 15.5V20C3.75 20.1989 3.82902 20.3897 3.96967 20.5303C4.11032 20.671 4.30109 20.75 4.5 20.75H9C9.19891 20.75 9.38968 20.671 9.53033 20.5303C9.67098 20.3897 9.75 20.1989 9.75 20C9.75 19.8011 9.67098 19.6103 9.53033 19.4697C9.38968 19.329 9.19891 19.25 9 19.25H6.31031L11.0306 14.5306C11.1714 14.3899 11.2504 14.199 11.2504 14C11.2504 13.801 11.1714 13.6101 11.0306 13.4694C10.8899 13.3286 10.699 13.2496 10.5 13.2496C10.301 13.2496 10.1101 13.3286 9.96937 13.4694Z" - fill="white" - /> - </svg> + <ExpandIcon onClick={expend} className="text-white" /> ) : ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="24" - height="25" - onClick={collapse} - viewBox="0 0 24 25" - fill="none" - > - <path - d="M13.5004 10.2499V6.49993C13.5004 6.30102 13.5794 6.11025 13.7201 5.9696C13.8607 5.82895 14.0515 5.74993 14.2504 5.74993C14.4493 5.74993 14.6401 5.82895 14.7807 5.9696C14.9214 6.11025 15.0004 6.30102 15.0004 6.49993V8.43962L18.9698 4.4693C19.1105 4.32857 19.3014 4.24951 19.5004 4.24951C19.6994 4.24951 19.8903 4.32857 20.031 4.4693C20.1718 4.61003 20.2508 4.80091 20.2508 4.99993C20.2508 5.19895 20.1718 5.38982 20.031 5.53055L16.0607 9.49993H18.0004C18.1993 9.49993 18.3901 9.57895 18.5307 9.7196C18.6714 9.86025 18.7504 10.051 18.7504 10.2499C18.7504 10.4488 18.6714 10.6396 18.5307 10.7803C18.3901 10.9209 18.1993 10.9999 18.0004 10.9999H14.2504C14.0515 10.9999 13.8607 10.9209 13.7201 10.7803C13.5794 10.6396 13.5004 10.4488 13.5004 10.2499ZM9.75042 13.9999H6.00042C5.8015 13.9999 5.61074 14.0789 5.47009 14.2196C5.32943 14.3603 5.25042 14.551 5.25042 14.7499C5.25042 14.9488 5.32943 15.1396 5.47009 15.2803C5.61074 15.4209 5.8015 15.4999 6.00042 15.4999H7.9401L3.96979 19.4693C3.82906 19.61 3.75 19.8009 3.75 19.9999C3.75 20.199 3.82906 20.3898 3.96979 20.5306C4.11052 20.6713 4.30139 20.7503 4.50042 20.7503C4.69944 20.7503 4.89031 20.6713 5.03104 20.5306L9.00042 16.5602V18.4999C9.00042 18.6988 9.07943 18.8896 9.22009 19.0303C9.36074 19.1709 9.5515 19.2499 9.75042 19.2499C9.94933 19.2499 10.1401 19.1709 10.2807 19.0303C10.4214 18.8896 10.5004 18.6988 10.5004 18.4999V14.7499C10.5004 14.551 10.4214 14.3603 10.2807 14.2196C10.1401 14.0789 9.94933 13.9999 9.75042 13.9999ZM16.0607 15.4999H18.0004C18.1993 15.4999 18.3901 15.4209 18.5307 15.2803C18.6714 15.1396 18.7504 14.9488 18.7504 14.7499C18.7504 14.551 18.6714 14.3603 18.5307 14.2196C18.3901 14.0789 18.1993 13.9999 18.0004 13.9999H14.2504C14.0515 13.9999 13.8607 14.0789 13.7201 14.2196C13.5794 14.3603 13.5004 14.551 13.5004 14.7499V18.4999C13.5004 18.6988 13.5794 18.8896 13.7201 19.0303C13.8607 19.1709 14.0515 19.2499 14.2504 19.2499C14.4493 19.2499 14.6401 19.1709 14.7807 19.0303C14.9214 18.8896 15.0004 18.6988 15.0004 18.4999V16.5602L18.9698 20.5306C19.0395 20.6002 19.1222 20.6555 19.2132 20.6932C19.3043 20.7309 19.4019 20.7503 19.5004 20.7503C19.599 20.7503 19.6965 20.7309 19.7876 20.6932C19.8786 20.6555 19.9614 20.6002 20.031 20.5306C20.1007 20.4609 20.156 20.3781 20.1937 20.2871C20.2314 20.1961 20.2508 20.0985 20.2508 19.9999C20.2508 19.9014 20.2314 19.8038 20.1937 19.7128C20.156 19.6217 20.1007 19.539 20.031 19.4693L16.0607 15.4999ZM9.75042 5.74993C9.5515 5.74993 9.36074 5.82895 9.22009 5.9696C9.07943 6.11025 9.00042 6.30102 9.00042 6.49993V8.43962L5.03104 4.4693C4.89031 4.32857 4.69944 4.24951 4.50042 4.24951C4.30139 4.24951 4.11052 4.32857 3.96979 4.4693C3.82906 4.61003 3.75 4.80091 3.75 4.99993C3.75 5.19895 3.82906 5.38982 3.96979 5.53055L7.9401 9.49993H6.00042C5.8015 9.49993 5.61074 9.57895 5.47009 9.7196C5.32943 9.86025 5.25042 10.051 5.25042 10.2499C5.25042 10.4488 5.32943 10.6396 5.47009 10.7803C5.61074 10.9209 5.8015 10.9999 6.00042 10.9999H9.75042C9.94933 10.9999 10.1401 10.9209 10.2807 10.7803C10.4214 10.6396 10.5004 10.4488 10.5004 10.2499V6.49993C10.5004 6.30102 10.4214 6.11025 10.2807 5.9696C10.1401 5.82895 9.94933 5.74993 9.75042 5.74993Z" - fill="white" - /> - </svg> + <CollapseIcon onClick={collapse} className="text-white" /> )} </div> )} diff --git a/apps/frontend/src/components/launches/repeat.component.tsx b/apps/frontend/src/components/launches/repeat.component.tsx index 416426ab..9bc6e140 100644 --- a/apps/frontend/src/components/launches/repeat.component.tsx +++ b/apps/frontend/src/components/launches/repeat.component.tsx @@ -4,6 +4,7 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useClickOutside } from '@mantine/hooks'; import { isUSCitizen } from '@gitroom/frontend/components/launches/helpers/isuscitizen.utils'; import clsx from 'clsx'; +import { RepeatIcon, DropdownArrowIcon } from '@gitroom/frontend/components/ui/icons'; const list = [ { value: 1, @@ -81,28 +82,7 @@ export const RepeatComponent: FC<{ className="px-[16px] justify-center flex gap-[8px] items-center h-full select-none flex-1" > <div className="cursor-pointer"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <g clip-path="url(#clip0_2403_67385)"> - <path - d="M14.1667 1.66602L17.5 4.99935M17.5 4.99935L14.1667 8.33268M17.5 4.99935H6.5C5.09987 4.99935 4.3998 4.99935 3.86502 5.27183C3.39462 5.51152 3.01217 5.89397 2.77248 6.36437C2.5 6.89915 2.5 7.59922 2.5 8.99935V9.16602M2.5 14.9993H13.5C14.9001 14.9993 15.6002 14.9993 16.135 14.7269C16.6054 14.4872 16.9878 14.1047 17.2275 13.6343C17.5 13.0995 17.5 12.3995 17.5 10.9993V10.8327M2.5 14.9993L5.83333 18.3327M2.5 14.9993L5.83333 11.666" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </g> - <defs> - <clipPath id="clip0_2403_67385"> - <rect width="20" height="20" fill="currentColor" /> - </clipPath> - </defs> - </svg> + <RepeatIcon /> </div> <div className="cursor-pointer"> {repeat @@ -110,19 +90,7 @@ export const RepeatComponent: FC<{ : t('repeat_post_every', 'Repeat Post Every...')} </div> <div className="cursor-pointer"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - className={isOpen ? 'rotate-180' : ''} - > - <path - d="M7.4563 8L12.5437 8C12.9494 8 13.1526 8.56798 12.8657 8.90016L10.322 11.8456C10.1442 12.0515 9.85583 12.0515 9.67799 11.8456L7.13429 8.90016C6.84741 8.56798 7.05059 8 7.4563 8Z" - fill="currentColor" - /> - </svg> + <DropdownArrowIcon rotated={isOpen} /> </div> </div> {isOpen && ( diff --git a/apps/frontend/src/components/launches/select.customer.tsx b/apps/frontend/src/components/launches/select.customer.tsx index 157e5365..f1ed526f 100644 --- a/apps/frontend/src/components/launches/select.customer.tsx +++ b/apps/frontend/src/components/launches/select.customer.tsx @@ -7,6 +7,7 @@ import { useClickOutside } from '@mantine/hooks'; import { useToaster } from '@gitroom/react/toaster/toaster'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; +import { UserIcon, DropdownArrowIcon } from '@gitroom/frontend/components/ui/icons'; export const SelectCustomer: FC<{ onChange: (value: string) => void; @@ -60,36 +61,10 @@ export const SelectCustomer: FC<{ )} > <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M16.6673 17.5C16.6673 16.337 16.6673 15.7555 16.5238 15.2824C16.2006 14.217 15.3669 13.3834 14.3016 13.0602C13.8284 12.9167 13.247 12.9167 12.084 12.9167H7.91732C6.75435 12.9167 6.17286 12.9167 5.6997 13.0602C4.63436 13.3834 3.80068 14.217 3.47752 15.2824C3.33398 15.7555 3.33398 16.337 3.33398 17.5M13.7507 6.25C13.7507 8.32107 12.0717 10 10.0007 10C7.92958 10 6.25065 8.32107 6.25065 6.25C6.25065 4.17893 7.92958 2.5 10.0007 2.5C12.0717 2.5 13.7507 4.17893 13.7507 6.25Z" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <UserIcon /> </div> <div> - <svg - className={clsx(open && 'rotate-180')} - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M12.5437 8L7.4563 8C7.05059 8 6.84741 8.56798 7.13429 8.90016L9.67799 11.8456C9.85583 12.0515 10.1442 12.0515 10.322 11.8456L12.8657 8.90016C13.1526 8.56798 12.9494 8 12.5437 8Z" - fill="currentColor" - /> - </svg> + <DropdownArrowIcon rotated={open} /> </div> </div> {open && ( diff --git a/apps/frontend/src/components/launches/tags.component.tsx b/apps/frontend/src/components/launches/tags.component.tsx index 457233b5..0679c4ea 100644 --- a/apps/frontend/src/components/launches/tags.component.tsx +++ b/apps/frontend/src/components/launches/tags.component.tsx @@ -11,6 +11,7 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useClickOutside } from '@mantine/hooks'; import clsx from 'clsx'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; +import { TagIcon, DropdownArrowIcon, PlusIcon, CheckmarkIcon } from '@gitroom/frontend/components/ui/icons'; export const TagsComponent: FC<{ name: string; @@ -109,21 +110,7 @@ export const TagsComponentInner: FC<{ className="px-[16px] justify-center flex gap-[8px] items-center h-full select-none flex-1" > <div className="cursor-pointer"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="17" - height="19" - viewBox="0 0 17 19" - fill="none" - > - <path - d="M15.75 8.25L9.42157 1.92157C8.98919 1.48919 8.773 1.273 8.52071 1.1184C8.29703 0.981328 8.05317 0.880317 7.79808 0.819075C7.51036 0.75 7.20462 0.75 6.59314 0.75L3.25 0.75M0.75 6.33333L0.75 7.97876C0.75 8.38641 0.75 8.59024 0.79605 8.78205C0.836878 8.95211 0.904218 9.11469 0.9956 9.26381C1.09867 9.432 1.2428 9.57613 1.53105 9.86438L8.03105 16.3644C8.69108 17.0244 9.02109 17.3544 9.40164 17.4781C9.73638 17.5868 10.097 17.5868 10.4317 17.4781C10.8122 17.3544 11.1423 17.0244 11.8023 16.3644L13.8644 14.3023C14.5244 13.6423 14.8544 13.3122 14.9781 12.9317C15.0868 12.597 15.0868 12.2364 14.9781 11.9016C14.8544 11.5211 14.5244 11.1911 13.8644 10.531L7.78105 4.44772C7.4928 4.15946 7.34867 4.01534 7.18048 3.91227C7.03135 3.82089 6.86878 3.75354 6.69872 3.71272C6.50691 3.66667 6.30308 3.66667 5.89543 3.66667H3.41667C2.48325 3.66667 2.01654 3.66667 1.66002 3.84832C1.34641 4.00811 1.09145 4.26308 0.931656 4.57668C0.75 4.9332 0.75 5.39991 0.75 6.33333Z" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <TagIcon /> </div> <div className="cursor-pointer flex gap-[4px]"> {tagValue.length === 0 ? ( @@ -143,19 +130,7 @@ export const TagsComponentInner: FC<{ )} </div> <div className="cursor-pointer"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - className={isOpen ? 'rotate-180' : ''} - > - <path - d="M7.4563 8L12.5437 8C12.9494 8 13.1526 8.56798 12.8657 8.90016L10.322 11.8456C10.1442 12.0515 9.85583 12.0515 9.67799 11.8456L7.13429 8.90016C6.84741 8.56798 7.05059 8 7.4563 8Z" - fill="currentColor" - /> - </svg> + <DropdownArrowIcon rotated={isOpen} /> </div> </div> {isOpen && ( @@ -203,21 +178,7 @@ export const TagsComponentInner: FC<{ className="cursor-pointer gap-[8px] flex w-full h-[34px] rounded-[8px] mt-[12px] px-[16px] justify-center items-center bg-[#612BD3] text-white" > <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - viewBox="0 0 16 16" - fill="none" - > - <path - d="M8.00065 3.33301V12.6663M3.33398 7.99967H12.6673" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <PlusIcon /> </div> <div className="text-[13px] font-[600]">Add New Tag</div> </div> @@ -239,24 +200,7 @@ const Check: FC<{ value: boolean; onChange: (value: boolean) => void }> = ({ value && 'bg-[#612BD3]' )} > - {value ? ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="11" - height="8" - viewBox="0 0 11 8" - fill="none" - > - <path - fillRule="evenodd" - clipRule="evenodd" - d="M10.7071 0.292893C11.0976 0.683417 11.0976 1.31658 10.7071 1.70711L4.70711 7.70711C4.31658 8.09763 3.68342 8.09763 3.29289 7.70711L0.292893 4.70711C-0.0976311 4.31658 -0.0976311 3.68342 0.292893 3.29289C0.683417 2.90237 1.31658 2.90237 1.70711 3.29289L4 5.58579L9.29289 0.292893C9.68342 -0.0976311 10.3166 -0.0976311 10.7071 0.292893Z" - fill="white" - /> - </svg> - ) : ( - '' - )} + {value ? <CheckmarkIcon className="text-white" /> : ''} </div> ); }; diff --git a/apps/frontend/src/components/launches/up.down.arrow.tsx b/apps/frontend/src/components/launches/up.down.arrow.tsx index f88b29fd..04a756c6 100644 --- a/apps/frontend/src/components/launches/up.down.arrow.tsx +++ b/apps/frontend/src/components/launches/up.down.arrow.tsx @@ -1,28 +1,17 @@ import { FC, useCallback } from 'react'; import clsx from 'clsx'; +import { ChevronUpIcon } from '@gitroom/frontend/components/ui/icons'; + const Arrow: FC<{ flip: boolean; }> = (props) => { const { flip } = props; return ( - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" + <ChevronUpIcon style={{ transform: !flip ? 'rotate(180deg)' : '', }} - > - <path - d="M15 12.5L10 7.5L5 12.5" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + /> ); }; export const UpDownArrow: FC<{ diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 010208a8..8c810dbc 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -18,17 +18,14 @@ import { Media } from '@prisma/client'; import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import EventEmitter from 'events'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import clsx from 'clsx'; import { VideoFrame } from '@gitroom/react/helpers/video.frame'; import { - MultipartFileUploader, useUppyUploader, } from '@gitroom/frontend/components/media/new.uploader'; import dynamic from 'next/dynamic'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { AiImage } from '@gitroom/frontend/components/launches/ai.image'; -import Image from 'next/image'; import { DropFiles } from '@gitroom/frontend/components/layout/drop.files'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -36,13 +33,23 @@ import { ThirdPartyMedia } from '@gitroom/frontend/components/third-parties/thir import { ReactSortable } from 'react-sortablejs'; import { MediaComponentInner, - useMediaSettings, } from '@gitroom/frontend/components/launches/helpers/media.settings.component'; -import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { AiVideo } from '@gitroom/frontend/components/launches/ai.video'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; import { Dashboard } from '@uppy/react'; -import { timer } from '@gitroom/helpers/utils/timer'; +import { + ChevronLeftIcon, + ChevronRightIcon, + PlusIcon, + DeleteCircleIcon, + CloseCircleIcon, + DragHandleIcon, + MediaSettingsIcon, + InsertMediaIcon, + DesignMediaIcon, + VerticalDividerIcon, + NoMediaIcon, +} from '@gitroom/frontend/components/ui/icons'; const Polonto = dynamic( () => import('@gitroom/frontend/components/launches/polonto') ); @@ -71,20 +78,7 @@ export const Pagination: FC<{ aria-label="Go to previous page" onClick={() => setPage(current - 1)} > - <svg - xmlns="http://www.w3.org/2000/svg" - width={24} - height={24} - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth={2} - strokeLinecap="round" - strokeLinejoin="round" - className="lucide lucide-chevron-left h-4 w-4" - > - <path d="m15 18-6-6 6-6" /> - </svg> + <ChevronLeftIcon className="lucide lucide-chevron-left h-4 w-4" /> <span>{t('previous', 'Previous')}</span> </div> </li> @@ -115,20 +109,7 @@ export const Pagination: FC<{ onClick={() => setPage(current + 1)} > <span>{t('next', 'Next')}</span> - <svg - xmlns="http://www.w3.org/2000/svg" - width={24} - height={24} - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth={2} - strokeLinecap="round" - strokeLinejoin="round" - className="lucide lucide-chevron-right h-4 w-4" - > - <path d="m9 18 6-6-6-6" /> - </svg> + <ChevronRightIcon className="lucide lucide-chevron-right h-4 w-4" /> </a> </li> </ul> @@ -288,21 +269,7 @@ export const MediaBox: FC<{ onClick={() => uploaderRef?.current?.click()} className="cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]" > - <svg - xmlns="http://www.w3.org/2000/svg" - width="14" - height="14" - viewBox="0 0 14 14" - fill="none" - > - <path - d="M6.58333 0.75V12.4167M0.75 6.58333H12.4167" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <PlusIcon size={14} /> <div>Upload</div> </button> ); @@ -366,63 +333,7 @@ export const MediaBox: FC<{ > {!isLoading && !data?.results?.length && ( <> - <svg - width="192" - height="151" - viewBox="0 0 192 151" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M109.75 59.0141C104.489 59.0141 113.46 -5.73557 91.0289 1.57563C69.7021 8.5269 99.5229 59.0141 94.5119 59.0141C89.5009 59.0141 54.4775 56.107 52.1458 71.9377C49.5418 89.6178 95.4225 79.7216 96.7894 81.9895C98.1563 84.2573 78.775 111.109 91.0289 119.324C103.724 127.835 119.934 96.3491 122.711 96.3491C125.489 96.3491 139.845 147.93 151.514 133.684C160.997 122.106 138.391 96.3491 142.873 96.3491C147.355 96.3491 180.793 98.9658 186.076 81.9895C192.534 61.2424 134.828 76.0575 131.352 71.9377C127.876 67.818 159.167 34.7484 142.873 25.987C126.785 17.3361 115.012 59.0141 109.75 59.0141Z" - stroke="white" - strokeOpacity="0.08" - strokeWidth="2" - strokeLinecap="round" - /> - <rect - x="22.6328" - y="62.541" - width="49.2079" - height="49.2079" - rx="12.6792" - transform="rotate(-16.275 22.6328 62.541)" - fill="#232222" - /> - <path - d="M66.8573 81.5379L60.538 73.8505C59.3847 72.4421 58.0986 71.8279 56.9172 72.1076C55.7477 72.3838 54.8664 73.5134 54.4627 75.298L53.377 80.0552C53.1492 81.0592 52.6158 81.7748 51.8894 82.0519C51.1544 82.3446 50.2829 82.1692 49.4433 81.5678L49.0813 81.3089C47.9176 80.4896 46.7111 80.2818 45.6626 80.705C44.6142 81.1282 43.9074 82.1418 43.6491 83.5323L42.7814 88.278C42.4752 89.995 43.055 91.7139 44.3442 92.8742C45.6334 94.0345 47.406 94.4417 49.0739 93.9549L64.3851 89.4863C65.9931 89.017 67.2584 87.7753 67.7541 86.1722C68.2738 84.5621 67.924 82.8282 66.8573 81.5379Z" - fill="white" - fillOpacity="0.4" - /> - <path - d="M45.8412 76.6818C48.0811 76.0281 49.367 73.6823 48.7133 71.4423C48.0595 69.2024 45.7137 67.9165 43.4738 68.5702C41.2338 69.2239 39.9479 71.5697 40.6017 73.8097C41.2554 76.0497 43.6012 77.3355 45.8412 76.6818Z" - fill="white" - fillOpacity="0.4" - /> - <rect - x="64.8125" - y="70.6133" - width="66.3578" - height="66.3578" - rx="18.1132" - fill="#2C2B2B" - /> - <path - d="M80.1261 117.087L80.0882 117.125C79.5762 116.006 79.2538 114.735 79.1211 113.332C79.2538 114.716 79.6141 115.968 80.1261 117.087Z" - fill="white" - fillOpacity="0.4" - /> - <path - d="M92.3022 100.72C94.7948 100.72 96.8154 98.6991 96.8154 96.2065C96.8154 93.714 94.7948 91.6934 92.3022 91.6934C89.8097 91.6934 87.7891 93.714 87.7891 96.2065C87.7891 98.6991 89.8097 100.72 92.3022 100.72Z" - fill="white" - fillOpacity="0.4" - /> - <path - d="M105.936 84.8301H90.0448C83.1423 84.8301 79.0273 88.945 79.0273 95.8476V111.739C79.0273 113.805 79.3876 115.607 80.0893 117.124C81.7201 120.727 85.2093 122.756 90.0448 122.756H105.936C112.838 122.756 116.953 118.641 116.953 111.739V107.396V95.8476C116.953 88.945 112.838 84.8301 105.936 84.8301ZM113.862 104.741C112.383 103.471 109.994 103.471 108.515 104.741L100.626 111.511C99.147 112.781 96.7577 112.781 95.2786 111.511L94.6339 110.98C93.2875 109.804 91.1447 109.691 89.6276 110.715L82.5355 115.474C82.1183 114.412 81.8718 113.18 81.8718 111.739V95.8476C81.8718 90.5 84.6973 87.6745 90.0448 87.6745H105.936C111.283 87.6745 114.109 90.5 114.109 95.8476V104.95L113.862 104.741Z" - fill="white" - fillOpacity="0.4" - /> - </svg> + <NoMediaIcon /> <div className="text-[20px] font-[600]"> You don't have any media yet </div> @@ -478,27 +389,10 @@ export const MediaBox: FC<{ {selected.findIndex((z: any) => z.id === media.id) + 1} </div> ) : ( - <svg + <DeleteCircleIcon className="cursor-pointer hidden z-[100] group-hover:block absolute -top-[5px] -end-[5px]" onClick={deleteImage(media)} - xmlns="http://www.w3.org/2000/svg" - width="18" - height="18" - viewBox="0 0 18 18" - fill="none" - > - <ellipse - cx="9.96484" - cy="9.10742" - rx="6" - ry="5.5" - fill="white" - /> - <path - d="M9 1.5C4.8675 1.5 1.5 4.8675 1.5 9C1.5 13.1325 4.8675 16.5 9 16.5C13.1325 16.5 16.5 13.1325 16.5 9C16.5 4.8675 13.1325 1.5 9 1.5ZM11.52 10.725C11.7375 10.9425 11.7375 11.3025 11.52 11.52C11.4075 11.6325 11.265 11.685 11.1225 11.685C10.98 11.685 10.8375 11.6325 10.725 11.52L9 9.795L7.275 11.52C7.1625 11.6325 7.02 11.685 6.8775 11.685C6.735 11.685 6.5925 11.6325 6.48 11.52C6.2625 11.3025 6.2625 10.9425 6.48 10.725L8.205 9L6.48 7.275C6.2625 7.0575 6.2625 6.6975 6.48 6.48C6.6975 6.2625 7.0575 6.2625 7.275 6.48L9 8.205L10.725 6.48C10.9425 6.2625 11.3025 6.2625 11.52 6.48C11.7375 6.6975 11.7375 7.0575 11.52 7.275L9.795 9L11.52 10.725Z" - fill="#FF3535" - /> - </svg> + /> )} <div className="w-full h-full rounded-[6px] overflow-hidden"> {media.path.indexOf('mp4') > -1 ? ( @@ -690,26 +584,7 @@ export const MultiMediaComponent: FC<{ {currentMedia.map((media, index) => ( <Fragment key={media.id}> <div className="cursor-pointer rounded-[5px] w-[40px] h-[40px] border-2 border-tableBorder relative flex transition-all"> - <svg - className="z-[20] dragging absolute pe-[1px] pb-[3px] -start-[4px] -top-[4px] cursor-move" - xmlns="http://www.w3.org/2000/svg" - width="15" - height="15" - viewBox="0 0 15 15" - fill="none" - > - <ellipse - cx="8.23242" - cy="7.5" - rx="6" - ry="5.5" - fill="white" - /> - <path - d="M7.5 0C11.6421 0 15 3.35786 15 7.5C14.9998 11.642 11.642 15 7.5 15C3.35799 15 0.000197912 11.642 0 7.5C0 3.35786 3.35786 0 7.5 0ZM5.55566 8.38867C4.97286 8.38867 4.50026 8.86159 4.5 9.44434C4.5 10.0273 4.9727 10.5 5.55566 10.5C6.13858 10.4999 6.61133 10.0273 6.61133 9.44434C6.61107 8.86162 6.13842 8.38873 5.55566 8.38867ZM9.44434 8.38867C8.86158 8.38873 8.38893 8.86162 8.38867 9.44434C8.38867 10.0273 8.86142 10.4999 9.44434 10.5C10.0273 10.5 10.5 10.0273 10.5 9.44434C10.4997 8.86159 10.0271 8.38867 9.44434 8.38867ZM5.55566 9.38867C5.58614 9.38873 5.61107 9.41391 5.61133 9.44434C5.61133 9.47498 5.5863 9.49994 5.55566 9.5C5.52498 9.5 5.5 9.47502 5.5 9.44434C5.50026 9.41387 5.52514 9.38867 5.55566 9.38867ZM9.44434 9.38867C9.47486 9.38867 9.49974 9.41387 9.5 9.44434C9.5 9.47502 9.47502 9.5 9.44434 9.5C9.4137 9.49994 9.38867 9.47498 9.38867 9.44434C9.38893 9.41391 9.41386 9.38873 9.44434 9.38867ZM5.55566 4.5C4.97282 4.5 4.5002 4.97287 4.5 5.55566C4.50006 6.13858 4.97273 6.61133 5.55566 6.61133C6.13855 6.61127 6.61127 6.13855 6.61133 5.55566C6.61113 4.9729 6.13846 4.50006 5.55566 4.5ZM9.44434 4.5C8.86154 4.50006 8.38887 4.9729 8.38867 5.55566C8.38873 6.13855 8.86145 6.61127 9.44434 6.61133C10.0273 6.61133 10.4999 6.13858 10.5 5.55566C10.4998 4.97287 10.0272 4.5 9.44434 4.5ZM5.55566 5.5C5.58617 5.50006 5.61113 5.52519 5.61133 5.55566C5.61127 5.58626 5.58626 5.61127 5.55566 5.61133C5.52502 5.61133 5.50006 5.5863 5.5 5.55566C5.5002 5.52515 5.5251 5.5 5.55566 5.5ZM9.44434 5.5C9.4749 5.5 9.4998 5.52515 9.5 5.55566C9.49994 5.5863 9.47498 5.61133 9.44434 5.61133C9.41374 5.61127 9.38873 5.58626 9.38867 5.55566C9.38887 5.52519 9.41383 5.50006 9.44434 5.5Z" - fill="#618DFF" - /> - </svg> + <DragHandleIcon className="z-[20] dragging absolute pe-[1px] pb-[3px] -start-[4px] -top-[4px] cursor-move" /> <div className="w-full h-full relative group"> <div @@ -742,19 +617,7 @@ export const MultiMediaComponent: FC<{ }} className="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[50%] bg-black/80 rounded-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-[9]" > - <svg - width="40" - height="40" - viewBox="0 0 40 40" - fill="none" - xmlns="http://www.w3.org/2000/svg" - className="cursor-pointer relative z-[200]" - > - <path - d="M19.9987 15.5C19.1087 15.5 18.2387 15.7639 17.4986 16.2584C16.7586 16.7528 16.1818 17.4556 15.8413 18.2779C15.5007 19.1002 15.4115 20.005 15.5852 20.8779C15.7588 21.7508 16.1874 22.5526 16.8167 23.182C17.4461 23.8113 18.2479 24.2399 19.1208 24.4135C19.9937 24.5871 20.8985 24.498 21.7208 24.1574C22.5431 23.8168 23.2459 23.2401 23.7403 22.5C24.2348 21.76 24.4987 20.89 24.4987 20C24.4975 18.8069 24.023 17.663 23.1793 16.8194C22.3357 15.9757 21.1918 15.5012 19.9987 15.5ZM19.9987 23C19.4054 23 18.8254 22.824 18.332 22.4944C17.8387 22.1647 17.4541 21.6962 17.2271 21.148C17 20.5999 16.9406 19.9967 17.0564 19.4147C17.1721 18.8328 17.4578 18.2982 17.8774 17.8787C18.297 17.4591 18.8315 17.1734 19.4134 17.0576C19.9954 16.9419 20.5986 17.0013 21.1468 17.2283C21.6949 17.4554 22.1635 17.8399 22.4931 18.3333C22.8228 18.8266 22.9987 19.4066 22.9987 20C22.9987 20.7956 22.6826 21.5587 22.12 22.1213C21.5574 22.6839 20.7944 23 19.9987 23ZM30.3056 18.0509C30.2847 17.9453 30.2413 17.8454 30.1784 17.7581C30.1155 17.6707 30.0345 17.5979 29.9409 17.5447L27.1443 15.9509L27.1331 12.799C27.1327 12.6905 27.1089 12.5833 27.063 12.4849C27.0172 12.3865 26.9506 12.2992 26.8678 12.229C25.8533 11.3709 24.6851 10.7134 23.4253 10.2912C23.3261 10.2577 23.2209 10.2452 23.1166 10.2547C23.0123 10.2643 22.9111 10.2955 22.8197 10.3465L19.9987 11.9234L17.175 10.3437C17.0834 10.2924 16.9821 10.2609 16.8776 10.2513C16.7732 10.2416 16.6678 10.2539 16.5684 10.2875C15.3095 10.7127 14.1426 11.3728 13.1297 12.2328C13.0469 12.3028 12.9804 12.39 12.9346 12.4882C12.8888 12.5865 12.8648 12.6935 12.8643 12.8019L12.8503 15.9565L10.0537 17.5503C9.96015 17.6036 9.87916 17.6763 9.81623 17.7637C9.7533 17.8511 9.70992 17.9509 9.68903 18.0565C9.43309 19.3427 9.43309 20.6667 9.68903 21.9528C9.70992 22.0584 9.7533 22.1583 9.81623 22.2456C9.87916 22.333 9.96015 22.4058 10.0537 22.459L12.8503 24.0528L12.8615 27.2047C12.8619 27.3132 12.8858 27.4204 12.9316 27.5188C12.9774 27.6172 13.044 27.7045 13.1268 27.7747C14.1413 28.6328 15.3095 29.2904 16.5693 29.7125C16.6686 29.7461 16.7737 29.7585 16.878 29.749C16.9823 29.7394 17.0835 29.7082 17.175 29.6572L19.9987 28.0765L22.8225 29.6562C22.9342 29.7185 23.0602 29.7508 23.1881 29.75C23.27 29.75 23.3514 29.7367 23.429 29.7106C24.6878 29.286 25.8547 28.6265 26.8678 27.7672C26.9505 27.6971 27.017 27.61 27.0628 27.5117C27.1087 27.4135 27.1326 27.3065 27.1331 27.1981L27.1472 24.0434L29.9437 22.4497C30.0373 22.3964 30.1183 22.3236 30.1812 22.2363C30.2441 22.1489 30.2875 22.049 30.3084 21.9434C30.5629 20.6583 30.562 19.3357 30.3056 18.0509ZM28.8993 21.3237L26.2209 22.8472C26.1035 22.9139 26.0064 23.0111 25.9397 23.1284C25.8853 23.2222 25.8281 23.3215 25.77 23.4153C25.6956 23.5335 25.6559 23.6703 25.6556 23.81L25.6415 26.8334C24.9216 27.3988 24.1195 27.8509 23.2631 28.174L20.5612 26.6684C20.449 26.6064 20.3228 26.5741 20.1947 26.5747H20.1768C20.0634 26.5747 19.949 26.5747 19.8356 26.5747C19.7014 26.5713 19.5688 26.6037 19.4512 26.6684L16.7475 28.1778C15.8892 27.8571 15.0849 27.4072 14.3625 26.8437L14.3522 23.825C14.3517 23.685 14.3121 23.548 14.2378 23.4294C14.1797 23.3356 14.1225 23.2419 14.069 23.1425C14.0028 23.0233 13.9056 22.9242 13.7878 22.8556L11.1065 21.3284C10.9678 20.4507 10.9678 19.5567 11.1065 18.679L13.7803 17.1528C13.8976 17.0861 13.9948 16.9889 14.0615 16.8715C14.1159 16.7778 14.1731 16.6784 14.2312 16.5847C14.3056 16.4664 14.3453 16.3297 14.3456 16.19L14.3597 13.1665C15.0796 12.6012 15.8816 12.1491 16.7381 11.8259L19.4362 13.3315C19.5536 13.3966 19.6864 13.429 19.8206 13.4253C19.934 13.4253 20.0484 13.4253 20.1618 13.4253C20.296 13.4286 20.4287 13.3963 20.5462 13.3315L23.25 11.8222C24.1082 12.1429 24.9125 12.5927 25.635 13.1562L25.6453 16.175C25.6457 16.3149 25.6854 16.452 25.7597 16.5706C25.8178 16.6644 25.875 16.7581 25.9284 16.8575C25.9947 16.9767 26.0918 17.0758 26.2097 17.1444L28.8909 18.6715C29.0315 19.5499 29.0331 20.4449 28.8956 21.3237H28.8993Z" - fill="currentColor" - /> - </svg> + <MediaSettingsIcon className="cursor-pointer relative z-[200]" /> </div> {media?.path?.indexOf('mp4') > -1 ? ( <VideoFrame url={mediaDirectory.set(media?.path)} /> @@ -766,20 +629,10 @@ export const MultiMediaComponent: FC<{ )} </div> - <svg + <CloseCircleIcon onClick={clearMedia(index)} className="absolute -end-[4px] -top-[4px] z-[20] rounded-full bg-white" - xmlns="http://www.w3.org/2000/svg" - width="15" - height="15" - viewBox="0 0 15 15" - fill="none" - > - <path - d="M7.5 0C3.3675 0 0 3.3675 0 7.5C0 11.6325 3.3675 15 7.5 15C11.6325 15 15 11.6325 15 7.5C15 3.3675 11.6325 0 7.5 0ZM10.02 9.225C10.2375 9.4425 10.2375 9.8025 10.02 10.02C9.9075 10.1325 9.765 10.185 9.6225 10.185C9.48 10.185 9.3375 10.1325 9.225 10.02L7.5 8.295L5.775 10.02C5.6625 10.1325 5.52 10.185 5.3775 10.185C5.235 10.185 5.0925 10.1325 4.98 10.02C4.7625 9.8025 4.7625 9.4425 4.98 9.225L6.705 7.5L4.98 5.775C4.7625 5.5575 4.7625 5.1975 4.98 4.98C5.1975 4.7625 5.5575 4.7625 5.775 4.98L7.5 6.705L9.225 4.98C9.4425 4.7625 9.8025 4.7625 10.02 4.98C10.2375 5.1975 10.2375 5.5575 10.02 5.775L8.295 7.5L10.02 9.225Z" - fill="#FF3535" - /> - </svg> + /> </div> </Fragment> ))} @@ -795,28 +648,7 @@ export const MultiMediaComponent: FC<{ > <div className="flex gap-[8px] items-center"> <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - viewBox="0 0 16 16" - fill="none" - > - <g clip-path="url(#clip0_2352_53043)"> - <path - d="M8.33333 1.99967H5.2C4.0799 1.99967 3.51984 1.99967 3.09202 2.21766C2.71569 2.40941 2.40973 2.71537 2.21799 3.09169C2 3.51952 2 4.07957 2 5.19967V10.7997C2 11.9198 2 12.4798 2.21799 12.9077C2.40973 13.284 2.71569 13.5899 3.09202 13.7817C3.51984 13.9997 4.07989 13.9997 5.2 13.9997H11.3333C11.9533 13.9997 12.2633 13.9997 12.5176 13.9315C13.2078 13.7466 13.7469 13.2075 13.9319 12.5173C14 12.263 14 11.953 14 11.333M12.6667 5.33301V1.33301M10.6667 3.33301H14.6667M7 5.66634C7 6.40272 6.40305 6.99967 5.66667 6.99967C4.93029 6.99967 4.33333 6.40272 4.33333 5.66634C4.33333 4.92996 4.93029 4.33301 5.66667 4.33301C6.40305 4.33301 7 4.92996 7 5.66634ZM9.99336 7.94511L4.3541 13.0717C4.03691 13.3601 3.87831 13.5042 3.86429 13.6291C3.85213 13.7374 3.89364 13.8448 3.97546 13.9167C4.06985 13.9997 4.28419 13.9997 4.71286 13.9997H10.9707C11.9301 13.9997 12.4098 13.9997 12.7866 13.8385C13.2596 13.6361 13.6365 13.2593 13.8388 12.7863C14 12.4095 14 11.9298 14 10.9703C14 10.6475 14 10.4861 13.9647 10.3358C13.9204 10.1469 13.8353 9.96991 13.7155 9.81727C13.6202 9.69581 13.4941 9.59497 13.242 9.39331L11.3772 7.90145C11.1249 7.69961 10.9988 7.5987 10.8599 7.56308C10.7374 7.53169 10.6086 7.53575 10.4884 7.5748C10.352 7.6191 10.2324 7.72777 9.99336 7.94511Z" - stroke="currentColor" - strokeWidth="1.2" - strokeLinecap="round" - strokeLinejoin="round" - /> - </g> - <defs> - <clipPath id="clip0_2352_53043"> - <rect width="16" height="16" fill="currentColor" /> - </clipPath> - </defs> - </svg> + <InsertMediaIcon /> </div> <div className="text-[10px] font-[600] maxMedia:hidden block"> {t('insert_media', 'Insert Media')} @@ -829,28 +661,7 @@ export const MultiMediaComponent: FC<{ > <div className="flex gap-[5px] items-center"> <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - viewBox="0 0 16 16" - fill="none" - > - <g clip-path="url(#clip0_2352_53048)"> - <path - d="M7.79167 1.99984H5.2C4.07989 1.99984 3.51984 1.99984 3.09202 2.21782C2.71569 2.40957 2.40973 2.71553 2.21799 3.09186C2 3.51968 2 4.07973 2 5.19984V10.7998C2 11.9199 2 12.48 2.21799 12.9078C2.40973 13.2841 2.71569 13.5901 3.09202 13.7818C3.51984 13.9998 4.07989 13.9998 5.2 13.9998H11.3333C11.9533 13.9998 12.2633 13.9998 12.5176 13.9317C13.2078 13.7468 13.7469 13.2077 13.9319 12.5175C14 12.2631 14 11.9532 14 11.3332M7 5.6665C7 6.40288 6.40305 6.99984 5.66667 6.99984C4.93029 6.99984 4.33333 6.40288 4.33333 5.6665C4.33333 4.93012 4.93029 4.33317 5.66667 4.33317C6.40305 4.33317 7 4.93012 7 5.6665ZM9.99336 7.94527L4.3541 13.0719C4.03691 13.3602 3.87831 13.5044 3.86429 13.6293C3.85213 13.7376 3.89364 13.8449 3.97546 13.9169C4.06985 13.9998 4.28419 13.9998 4.71286 13.9998H10.9707C11.9301 13.9998 12.4098 13.9998 12.7866 13.8387C13.2596 13.6363 13.6365 13.2595 13.8388 12.7864C14 12.4097 14 11.9299 14 10.9705C14 10.6477 14 10.4863 13.9647 10.3359C13.9204 10.147 13.8353 9.97007 13.7155 9.81743C13.6202 9.69597 13.4941 9.59514 13.242 9.39348L11.3772 7.90161C11.1249 7.69978 10.9988 7.59886 10.8599 7.56324C10.7374 7.53185 10.6086 7.53592 10.4884 7.57496C10.352 7.61926 10.2324 7.72794 9.99336 7.94527ZM15.0951 6.49981L13.0275 5.90908C12.9285 5.88079 12.879 5.86664 12.8328 5.84544C12.7918 5.82662 12.7528 5.80368 12.7164 5.77698C12.6755 5.74692 12.6391 5.71051 12.5663 5.6377L10.2617 3.33317C9.80143 2.87292 9.80144 2.1267 10.2617 1.66646C10.7219 1.20623 11.4681 1.20623 11.9284 1.66647L14.2329 3.97103C14.3058 4.04384 14.3422 4.08025 14.3722 4.12121C14.3989 4.15757 14.4219 4.19655 14.4407 4.23755C14.4619 4.28373 14.476 4.33323 14.5043 4.43224L15.0951 6.49981Z" - stroke="currentColor" - strokeWidth="1.2" - strokeLinecap="round" - strokeLinejoin="round" - /> - </g> - <defs> - <clipPath id="clip0_2352_53048"> - <rect width="16" height="16" fill="currentColor" /> - </clipPath> - </defs> - </svg> + <DesignMediaIcon /> </div> <div className="text-[10px] font-[600] iconBreak:hidden block"> {t('design_media', 'Design Media')} @@ -868,20 +679,7 @@ export const MultiMediaComponent: FC<{ )} </div> <div className="text-newColColor h-full flex items-center"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="2" - height="17" - viewBox="0 0 2 17" - fill="none" - > - <path - d="M0.75 0.75V16" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - /> - </svg> + <VerticalDividerIcon /> </div> {!!toolBar && ( <div className="flex py-[10px] b2 items-center gap-[4px]"> diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index eb9e51a0..a28284f6 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -60,6 +60,13 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { AComponent } from '@gitroom/frontend/components/new-launch/a.component'; import { Placeholder } from '@tiptap/extensions'; import { InformationComponent } from '@gitroom/frontend/components/launches/information.component'; +import { + LockIcon, + ConnectionLineIcon, + ResetIcon, + TrashIcon, + EmojiIcon, +} from '@gitroom/frontend/components/ui/icons'; const InterceptBoldShortcut = Extension.create({ name: 'preventBoldWithUnderline', @@ -345,21 +352,7 @@ export const EditorWrapper: FC<{ <div className="text-center absolute w-full h-full left-0 top-0 items-center justify-center flex z-[101] flex-col gap-[16px]"> <div> <div className="w-[54px] h-[54px] rounded-full absolute z-[101] flex justify-center items-center"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="32" - height="32" - viewBox="0 0 32 32" - fill="none" - > - <path - d="M22.6673 13.3333V10.6667C22.6673 6.98477 19.6825 4 16.0007 4C12.3188 4 9.33398 6.98477 9.33398 10.6667V13.3333M16.0007 19.3333V22M11.734 28H20.2673C22.5075 28 23.6276 28 24.4833 27.564C25.2359 27.1805 25.8479 26.5686 26.2313 25.816C26.6673 24.9603 26.6673 23.8402 26.6673 21.6V19.7333C26.6673 17.4931 26.6673 16.373 26.2313 15.5174C25.8479 14.7647 25.2359 14.1528 24.4833 13.7693C23.6276 13.3333 22.5075 13.3333 20.2673 13.3333H11.734C9.49377 13.3333 8.37367 13.3333 7.51802 13.7693C6.76537 14.1528 6.15345 14.7647 5.76996 15.5174C5.33398 16.373 5.33398 17.4931 5.33398 19.7333V21.6C5.33398 23.8402 5.33398 24.9603 5.76996 25.816C6.15345 26.5686 6.76537 27.1805 7.51802 27.564C8.37367 28 9.49377 28 11.734 28Z" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <LockIcon /> </div> <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> </div> @@ -381,21 +374,7 @@ export const EditorWrapper: FC<{ > <div> <div className="w-[54px] h-[54px] rounded-full absolute z-[101] flex justify-center items-center"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="32" - height="32" - viewBox="0 0 32 32" - fill="none" - > - <path - d="M22.6673 13.3333V10.6667C22.6673 6.98477 19.6825 4 16.0007 4C12.3188 4 9.33398 6.98477 9.33398 10.6667V13.3333M16.0007 19.3333V22M11.734 28H20.2673C22.5075 28 23.6276 28 24.4833 27.564C25.2359 27.1805 25.8479 26.5686 26.2313 25.816C26.6673 24.9603 26.6673 23.8402 26.6673 21.6V19.7333C26.6673 17.4931 26.6673 16.373 26.2313 15.5174C25.8479 14.7647 25.2359 14.1528 24.4833 13.7693C23.6276 13.3333 22.5075 13.3333 20.2673 13.3333H11.734C9.49377 13.3333 8.37367 13.3333 7.51802 13.7693C6.76537 14.1528 6.15345 14.7647 5.76996 15.5174C5.33398 16.373 5.33398 17.4931 5.33398 19.7333V21.6C5.33398 23.8402 5.33398 24.9603 5.76996 25.816C6.15345 26.5686 6.76537 27.1805 7.51802 27.564C8.37367 28 9.49377 28 11.734 28Z" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <LockIcon /> </div> <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> </div> @@ -427,20 +406,7 @@ export const EditorWrapper: FC<{ <div className="flex-1 flex w-full"> {index > 0 && ( <div className="flex justify-center pl-[12px] text-newSep"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="18" - height="87" - viewBox="0 0 18 87" - fill="none" - > - <path - d="M0.75 0.75V79.75C0.75 83.0637 3.43629 85.75 6.75 85.75H16.75" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - /> - </svg> + <ConnectionLineIcon /> </div> )} <Editor @@ -485,21 +451,7 @@ export const EditorWrapper: FC<{ </div> <div className="flex gap-[6px] items-center"> <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - viewBox="0 0 16 16" - fill="none" - > - <path - d="M1.33398 6.66667C1.33398 6.66667 2.67064 4.84548 3.75654 3.75883C4.84244 2.67218 6.34305 2 8.00065 2C11.3144 2 14.0007 4.68629 14.0007 8C14.0007 11.3137 11.3144 14 8.00065 14C5.26526 14 2.95739 12.1695 2.23516 9.66667M1.33398 6.66667V2.66667M1.33398 6.66667H5.33398" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <ResetIcon /> </div> <div className="text-[13px] font-[600]"> Back to global @@ -520,25 +472,12 @@ export const EditorWrapper: FC<{ onChange={changeOrder(index)} /> {items.length > 1 && ( - <svg + <TrashIcon onClick={deletePost(index)} - xmlns="http://www.w3.org/2000/svg" data-tooltip-id="tooltip" data-tooltip-content="Delete Post" - className="cursor-pointer" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M7.5 2.5L12.5 2.5M2.5 5L17.5 5M15.8333 5L15.2489 13.7661C15.1612 15.0813 15.1174 15.7389 14.8333 16.2375C14.5833 16.6765 14.206 17.0294 13.7514 17.2497C13.235 17.5 12.5759 17.5 11.2578 17.5H8.74221C7.42409 17.5 6.76503 17.5 6.24861 17.2497C5.79396 17.0294 5.41674 16.6765 5.16665 16.2375C4.88259 15.7389 4.83875 15.0813 4.75107 13.7661L4.16667 5M8.33333 8.75V12.9167M11.6667 8.75L11.6667 12.9167" - stroke="#FF3F3F" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + className="cursor-pointer text-[#FF3F3F]" + /> )} </div> </div> @@ -783,44 +722,7 @@ export const Editor: FC<{ className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" onClick={() => setEmojiPickerOpen(!emojiPickerOpen)} > - <svg - width="16" - height="16" - viewBox="0 0 16 16" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M7.97917 14.6663C11.6611 14.6663 14.6458 11.6816 14.6458 7.99967C14.6458 4.31778 11.6611 1.33301 7.97917 1.33301C4.29727 1.33301 1.3125 4.31778 1.3125 7.99967C1.3125 11.6816 4.29727 14.6663 7.97917 14.6663Z" - stroke="currentColor" - strokeWidth="1.2" - strokeLinecap="round" - strokeLinejoin="round" - /> - <path - d="M4.80664 10C5.50664 11.0067 6.67997 11.6667 7.99997 11.6667C9.31997 11.6667 10.4866 11.0067 11.1933 10" - stroke="currentColor" - strokeWidth="1.2" - strokeLinecap="round" - strokeLinejoin="round" - /> - <path - d="M4.66602 6.16699C5.33268 6.83366 6.41935 6.83366 7.09268 6.16699" - stroke="currentColor" - strokeWidth="1.2" - strokeMiterlimit="10" - strokeLinecap="round" - strokeLinejoin="round" - /> - <path - d="M8.90625 6.16699C9.57292 6.83366 10.6596 6.83366 11.3329 6.16699" - stroke="currentColor" - strokeWidth="1.2" - strokeMiterlimit="10" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <EmojiIcon /> </div> <div className="relative"> <div diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 30e9972b..8bbf5ccf 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -14,7 +14,6 @@ import { DatePicker } from '@gitroom/frontend/components/launches/helpers/date.p import { useShallow } from 'zustand/react/shallow'; import { RepeatComponent } from '@gitroom/frontend/components/launches/repeat.component'; import { TagsComponent } from '@gitroom/frontend/components/launches/tags.component'; -import { Button } from '@gitroom/react/form/button'; import { useToaster } from '@gitroom/react/toaster/toaster'; import { weightedLength } from '@gitroom/helpers/utils/count.length'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; @@ -22,11 +21,17 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; import { capitalize } from 'lodash'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer'; import { CopilotPopup } from '@copilotkit/react-ui'; import { DummyCodeComponent } from '@gitroom/frontend/components/new-launch/dummy.code.component'; import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { + SettingsIcon, + ChevronDownIcon, + CloseIcon, + TrashIcon, + DropdownArrowSmallIcon, +} from '@gitroom/frontend/components/ui/icons'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -377,52 +382,13 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { )} > <div className="flex"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M7.82888 16.1429L8.31591 17.2383C8.4607 17.5644 8.69698 17.8414 8.9961 18.0358C9.29522 18.2303 9.64434 18.3337 10.0011 18.3337C10.3579 18.3337 10.707 18.2303 11.0061 18.0358C11.3052 17.8414 11.5415 17.5644 11.6863 17.2383L12.1733 16.1429C12.3467 15.7542 12.6383 15.4302 13.0067 15.217C13.3773 15.0032 13.8061 14.9121 14.2317 14.9568L15.4233 15.0837C15.778 15.1212 16.136 15.055 16.4539 14.8931C16.7717 14.7312 17.0358 14.4806 17.2141 14.1716C17.3925 13.8628 17.4776 13.5089 17.4588 13.1527C17.4401 12.7966 17.3184 12.4535 17.1085 12.1651L16.403 11.1957C16.1517 10.8479 16.0175 10.4293 16.0196 10.0003C16.0195 9.57248 16.155 9.15562 16.4067 8.80959L17.1122 7.84014C17.3221 7.55179 17.4438 7.20872 17.4625 6.85255C17.4813 6.49639 17.3962 6.14244 17.2178 5.83366C17.0395 5.52469 16.7754 5.27407 16.4576 5.11218C16.1397 4.9503 15.7817 4.8841 15.427 4.92162L14.2354 5.04847C13.8098 5.09317 13.381 5.00209 13.0104 4.78829C12.6413 4.57387 12.3496 4.24812 12.177 3.85773L11.6863 2.76236C11.5415 2.4363 11.3052 2.15925 11.0061 1.96482C10.707 1.77039 10.3579 1.66693 10.0011 1.66699C9.64434 1.66693 9.29522 1.77039 8.9961 1.96482C8.69698 2.15925 8.4607 2.4363 8.31591 2.76236L7.82888 3.85773C7.65632 4.24812 7.3646 4.57387 6.99554 4.78829C6.62489 5.00209 6.1961 5.09317 5.77054 5.04847L4.57517 4.92162C4.22045 4.8841 3.86246 4.9503 3.5446 5.11218C3.22675 5.27407 2.96269 5.52469 2.78443 5.83366C2.60595 6.14244 2.52092 6.49639 2.53965 6.85255C2.55839 7.20872 2.68009 7.55179 2.88999 7.84014L3.59554 8.80959C3.84716 9.15562 3.98266 9.57248 3.98258 10.0003C3.98266 10.4282 3.84716 10.845 3.59554 11.1911L2.88999 12.1605C2.68009 12.4489 2.55839 12.7919 2.53965 13.1481C2.52092 13.5043 2.60595 13.8582 2.78443 14.167C2.96286 14.4758 3.22696 14.7263 3.54476 14.8882C3.86257 15.05 4.22047 15.1163 4.57517 15.079L5.76684 14.9522C6.1924 14.9075 6.62119 14.9986 6.99184 15.2124C7.36228 15.4262 7.65535 15.752 7.82888 16.1429Z" - stroke="white" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - <path - d="M9.99961 12.5003C11.3803 12.5003 12.4996 11.381 12.4996 10.0003C12.4996 8.61961 11.3803 7.50033 9.99961 7.50033C8.6189 7.50033 7.49961 8.61961 7.49961 10.0003C7.49961 11.381 8.6189 12.5003 9.99961 12.5003Z" - stroke="white" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <SettingsIcon className="text-white" /> </div> <div className="flex-1 text-[14px] font-[600] text-white"> {currentIntegrationText} Settings </div> <div> - <svg - className={clsx( - showSettings && 'rotate-180', - 'transition-transform' - )} - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M5 7.5L10 12.5L15 7.5" - stroke="white" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <ChevronDownIcon rotated={showSettings} className="text-white" /> </div> </div> <div @@ -443,22 +409,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { <div className="bg-newBgColor h-[65px] rounded-tr-[20px] flex items-center px-[20px] text-[20px] font-[600]"> <div className="flex-1">Post Preview</div> <div className="cursor-pointer"> - <svg - onClick={askClose} - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M16 4L4 16M4 4L16 16" - stroke="#A3A3A3" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <CloseIcon onClick={askClose} className="text-[#A3A3A3]" /> </div> </div> <div className="flex-1 relative"> @@ -489,21 +440,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { {existingData?.integration && ( <button onClick={deletePost} className="cursor-pointer flex text-[#FF3F3F] gap-[8px] items-center text-[15px] font-[600]"> <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M7.5 2.5H12.5M2.5 5H17.5M15.8333 5L15.2489 13.7661C15.1612 15.0813 15.1174 15.7389 14.8333 16.2375C14.5833 16.6765 14.206 17.0294 13.7514 17.2497C13.235 17.5 12.5759 17.5 11.2578 17.5H8.74221C7.42409 17.5 6.76503 17.5 6.24861 17.2497C5.79396 17.0294 5.41674 16.6765 5.16665 16.2375C4.88259 15.7389 4.83875 15.0813 4.75107 13.7661L4.16667 5M8.33333 8.75V12.9167M11.6667 8.75V12.9167" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <TrashIcon /> </div> <div>Delete Post</div> </button> @@ -552,19 +489,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { : t('update', 'Update')} </div> <div className="flex justify-center items-center h-[20px] w-[20px] pt-[4px] arrow-change"> - <svg - className="group-hover:rotate-180 transition-transform" - xmlns="http://www.w3.org/2000/svg" - width="6" - height="4" - viewBox="0 0 6 4" - fill="none" - > - <path - d="M0.456301 9.69291e-07L5.5437 7.97823e-08C5.94941 8.84616e-09 6.15259 0.567978 5.86571 0.90016L3.32201 3.84556C3.14417 4.05148 2.85583 4.05148 2.67799 3.84556L0.134293 0.900162C-0.152585 0.56798 0.0505934 1.04023e-06 0.456301 9.69291e-07Z" - fill="white" - /> - </svg> + <DropdownArrowSmallIcon className="group-hover:rotate-180 text-white" /> </div> </button> @@ -606,462 +531,3 @@ After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} f </div> ); }; -export const ManageModalA: FC<AddEditModalProps> = (props) => { - const t = useT(); - const fetch = useFetch(); - const ref = useRef(null); - const existingData = useExistingData(); - const [loading, setLoading] = useState(false); - const toaster = useToaster(); - const modal = useModals(); - - const { addEditSets, mutate, customClose, dummy } = props; - - const { - selectedIntegrations, - hide, - date, - setDate, - repeater, - setRepeater, - tags, - setTags, - integrations, - setSelectedIntegrations, - locked, - activateExitButton, - } = useLaunchStore( - useShallow((state) => ({ - hide: state.hide, - date: state.date, - setDate: state.setDate, - repeater: state.repeater, - setRepeater: state.setRepeater, - tags: state.tags, - setTags: state.setTags, - selectedIntegrations: state.selectedIntegrations, - integrations: state.integrations, - setSelectedIntegrations: state.setSelectedIntegrations, - locked: state.locked, - activateExitButton: state.activateExitButton, - })) - ); - - const deletePost = useCallback(async () => { - setLoading(true); - if ( - !(await deleteDialog( - 'Are you sure you want to delete this post?', - 'Yes, delete it!' - )) - ) { - setLoading(false); - return; - } - await fetch(`/posts/${existingData.group}`, { - method: 'DELETE', - }); - mutate(); - modal.closeAll(); - return; - }, [existingData, mutate, modal]); - - const askClose = useCallback(async () => { - if (!activateExitButton || dummy) { - return; - } - - if ( - await deleteDialog( - t( - 'are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost', - 'Are you sure you want to close this modal? (all data will be lost)' - ), - t('yes_close_it', 'Yes, close it!') - ) - ) { - if (customClose) { - customClose(); - return; - } - modal.closeAll(); - } - }, [activateExitButton, dummy]); - - const changeCustomer = useCallback( - (customer: string) => { - const neededIntegrations = integrations.filter( - (p) => p?.customer?.id === customer - ); - setSelectedIntegrations( - neededIntegrations.map((p) => ({ - settings: {}, - selectedIntegrations: p, - })) - ); - }, - [integrations] - ); - - const schedule = useCallback( - (type: 'draft' | 'now' | 'schedule') => async () => { - setLoading(true); - const checkAllValid = await ref.current.checkAllValid(); - if (type !== 'draft') { - const notEnoughChars = checkAllValid.filter((p: any) => { - return p.values.some((a: any) => { - return ( - countCharacters( - stripHtmlValidation('normal', a.content, true), - p?.integration?.identifier || '' - ) === 0 && a.media?.length === 0 - ); - }); - }); - - for (const item of notEnoughChars) { - toaster.show( - '' + - item.integration.name + - ' Your post should have at least one character or one image.', - 'warning' - ); - setLoading(false); - item.preview(); - return; - } - - for (const item of checkAllValid) { - if (item.valid === false) { - toaster.show('Some fields are not valid', 'warning'); - item.fix(); - setLoading(false); - return; - } - - if (item.errors !== true) { - toaster.show( - `${capitalize(item.integration.identifier.split('-')[0])} (${ - item.integration.name - }): ${item.errors}`, - 'warning' - ); - item.preview(); - setLoading(false); - return; - } - } - - const sliceNeeded = checkAllValid.filter((p: any) => { - return p.values.some((a: any) => { - const strip = stripHtmlValidation('normal', a.content, true); - const weightedLength = countCharacters( - strip, - p?.integration?.identifier || '' - ); - const totalCharacters = - weightedLength > strip.length ? weightedLength : strip.length; - - return totalCharacters > (p.maximumCharacters || 1000000); - }); - }); - - for (const item of sliceNeeded) { - toaster.show( - `${item?.integration?.name} (${item?.integration?.identifier}) post is too long, please fix it`, - 'warning' - ); - item.preview(); - setLoading(false); - return; - } - } - - const shortLinkUrl = dummy - ? { ask: false } - : await ( - await fetch('/posts/should-shortlink', { - method: 'POST', - body: JSON.stringify({ - messages: checkAllValid.flatMap((p: any) => - p.values.flatMap((a: any) => a.content) - ), - }), - }) - ).json(); - - const shortLink = !shortLinkUrl.ask - ? false - : await deleteDialog( - 'Do you want to shortlink the URLs? it will let you get statistics over clicks', - 'Yes, shortlink it!' - ); - - const group = existingData.group || makeId(10); - const data = { - type, - ...(repeater ? { inter: repeater } : {}), - tags, - shortLink, - date: date.utc().format('YYYY-MM-DDTHH:mm:ss'), - posts: checkAllValid.map((post: any) => ({ - integration: { - id: post.integration.id, - }, - group, - settings: { ...(post.settings || {}) }, - value: post.values.map((value: any) => ({ - ...(value.id ? { id: value.id } : {}), - content: value.content, - image: - (value?.media || []).map( - ({ id, path, alt, thumbnail, thumbnailTimestamp }: any) => ({ - id, - path, - alt, - thumbnail, - thumbnailTimestamp, - }) - ) || [], - })), - })), - }; - - if (dummy) { - modal.openModal({ - title: '', - children: <DummyCodeComponent code={data} />, - classNames: { - modal: 'w-[100%] bg-transparent text-textColor', - }, - size: '100%', - withCloseButton: false, - closeOnEscape: true, - closeOnClickOutside: true, - }); - - setLoading(false); - } - - if (!dummy) { - addEditSets - ? addEditSets(data) - : await fetch('/posts', { - method: 'POST', - body: JSON.stringify(data), - }); - - if (!addEditSets) { - mutate(); - toaster.show( - !existingData.integration - ? 'Added successfully' - : 'Updated successfully' - ); - } - if (customClose) { - setTimeout(() => { - customClose(); - }, 2000); - } - - if (!addEditSets) { - modal.closeAll(); - } - } - }, - [ref, repeater, tags, date, addEditSets, dummy] - ); - - return ( - <> - <div - className={clsx( - 'flex flex-col md:flex-row bg-newBgLineColor gap-[1px] rounded-[24px] trz' - )} - > - <div - className={clsx( - 'flex flex-1 flex-col gap-[16px] transition-all duration-700 whitespace-nowrap bg-newBgColorInner rounded-s-[24px]' - )} - > - <div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] p-[24px] pt-0"> - <TopTitle - extraClass="h-[75px]" - titleSize="text-[24px]" - title={ - dummy - ? 'Generate an API request' - : existingData.integration - ? t('update_post', 'Update Existing Post') - : t('create_new_post', 'Create Post') - } - > - <div className="flex items-center"> - {!dummy && ( - <RepeatComponent repeat={repeater} onChange={setRepeater} /> - )} - <DatePicker onChange={setDate} date={date} /> - </div> - </TopTitle> - - <PicksSocialsComponent toolTip={true} /> - <div> - {!existingData.integration && <SelectCurrent />} - <div className="flex gap-[4px]"> - <div className="flex-1 editor text-textColor gap-[10px] flex-col flex"> - {!hide && <EditorWrapper totalPosts={1} value="" />} - </div> - </div> - </div> - </div> - <div className="relative min-h-[68px] flex flex-col rounded-[4px]"> - <div className="gap-[10px] relative flex flex-col justify-center items-center min-h-full px-[24px]"> - <div - id="add-edit-post-dialog-buttons" - className="flex flex-row flex-wrap w-full h-full gap-[10px] justify-end items-center" - > - <div className="flex justify-center items-center gap-[5px] h-full"> - {!!existingData.integration && ( - <Button - onClick={deletePost} - className="rounded-[4px] border-2 border-red-400 text-red-400" - secondary={true} - disabled={loading || locked} - > - {t('delete_post', 'Delete Post')} - </Button> - )} - - {!addEditSets && !dummy && ( - <Button - onClick={schedule('draft')} - className="rounded-[4px] border-2 border-customColor21" - secondary={true} - disabled={ - selectedIntegrations.length === 0 || loading || locked - } - > - {t('save_as_draft', 'Save as draft')} - </Button> - )} - - {addEditSets && ( - <Button - className="rounded-[4px] relative group" - disabled={ - selectedIntegrations.length === 0 || loading || locked - } - onClick={schedule('draft')} - > - Save Set - </Button> - )} - {!addEditSets && ( - <Button - className="rounded-[4px] relative group" - disabled={ - selectedIntegrations.length === 0 || loading || locked - } - > - <div className="flex justify-center items-center gap-[5px] h-full"> - <div - className="h-full flex items-center text-white" - onClick={schedule('schedule')} - > - {selectedIntegrations.length === 0 - ? t( - 'select_channels_from_circles', - 'Select channels from the circles above' - ) - : dummy - ? 'Create output' - : !existingData?.integration - ? t('add_to_calendar', 'Add to calendar') - : existingData?.posts?.[0]?.state === 'DRAFT' - ? t('schedule', 'Schedule') - : t('update', 'Update')} - </div> - {!dummy && ( - <div className="h-full flex items-center"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="18" - height="18" - viewBox="0 0 18 18" - fill="none" - > - <path - d="M15.0233 7.14804L9.39828 12.773C9.34604 12.8253 9.284 12.8668 9.21572 12.8951C9.14743 12.9234 9.07423 12.938 9.00031 12.938C8.92639 12.938 8.8532 12.9234 8.78491 12.8951C8.71662 12.8668 8.65458 12.8253 8.60234 12.773L2.97734 7.14804C2.8718 7.04249 2.8125 6.89934 2.8125 6.75007C2.8125 6.6008 2.8718 6.45765 2.97734 6.3521C3.08289 6.24655 3.22605 6.18726 3.37531 6.18726C3.52458 6.18726 3.66773 6.24655 3.77328 6.3521L9.00031 11.5798L14.2273 6.3521C14.2796 6.29984 14.3417 6.25838 14.4099 6.2301C14.4782 6.20181 14.5514 6.18726 14.6253 6.18726C14.6992 6.18726 14.7724 6.20181 14.8407 6.2301C14.909 6.25838 14.971 6.29984 15.0233 6.3521C15.0755 6.40436 15.117 6.46641 15.1453 6.53469C15.1736 6.60297 15.1881 6.67616 15.1881 6.75007C15.1881 6.82398 15.1736 6.89716 15.1453 6.96545C15.117 7.03373 15.0755 7.09578 15.0233 7.14804Z" - fill="white" - /> - </svg> - <div - onClick={schedule('now')} - className={clsx( - 'hidden group-hover:flex hover:flex flex-col justify-center absolute start-0 top-[100%] w-full h-[40px] bg-customColor22 border border-tableBorder', - (locked || loading) && - 'cursor-not-allowed pointer-events-none opacity-50' - )} - > - {t('post_now', 'Post now')} - </div> - </div> - )} - </div> - </Button> - )} - </div> - </div> - </div> - </div> - </div> - <div - className={clsx( - 'px-[24px] flex-grow rounded-e-[24px] w-[650px] max-w-[650px] min-w-[650px] flex gap-[20px] flex-col rounded-[4px] bg-newBgColorInner border-newBgLineColor flex-1 transition-all duration-700' - )} - > - <div> - <TopTitle title="" removeTitle={true} extraClass="h-[75px]"> - <div className="flex flex-1 gap-[10px]"> - <div> - {!dummy && ( - <TagsComponent - name="tags" - label={t('tags', 'Tags')} - initial={tags} - onChange={(e) => setTags(e.target.value)} - /> - )} - </div> - {!dummy && ( - <SelectCustomer - onChange={changeCustomer} - integrations={integrations} - /> - )} - </div> - <svg - onClick={askClose} - width="10" - height="11" - viewBox="0 0 10 11" - fill="none" - xmlns="http://www.w3.org/2000/svg" - className="cursor-pointer" - > - <path - d="M9.85403 9.64628C9.90048 9.69274 9.93733 9.74789 9.96247 9.80859C9.98762 9.86928 10.0006 9.93434 10.0006 10C10.0006 10.0657 9.98762 10.1308 9.96247 10.1915C9.93733 10.2522 9.90048 10.3073 9.85403 10.3538C9.80757 10.4002 9.75242 10.4371 9.69173 10.4622C9.63103 10.4874 9.56598 10.5003 9.50028 10.5003C9.43458 10.5003 9.36953 10.4874 9.30883 10.4622C9.24813 10.4371 9.19298 10.4002 9.14653 10.3538L5.00028 6.20691L0.854028 10.3538C0.760208 10.4476 0.63296 10.5003 0.500278 10.5003C0.367596 10.5003 0.240348 10.4476 0.146528 10.3538C0.0527077 10.26 2.61548e-09 10.1327 0 10C-2.61548e-09 9.86735 0.0527077 9.7401 0.146528 9.64628L4.2934 5.50003L0.146528 1.35378C0.0527077 1.25996 0 1.13272 0 1.00003C0 0.867352 0.0527077 0.740104 0.146528 0.646284C0.240348 0.552464 0.367596 0.499756 0.500278 0.499756C0.63296 0.499756 0.760208 0.552464 0.854028 0.646284L5.00028 4.79316L9.14653 0.646284C9.24035 0.552464 9.3676 0.499756 9.50028 0.499756C9.63296 0.499756 9.76021 0.552464 9.85403 0.646284C9.94785 0.740104 10.0006 0.867352 10.0006 1.00003C10.0006 1.13272 9.94785 1.25996 9.85403 1.35378L5.70715 5.50003L9.85403 9.64628Z" - fill="currentColor" - /> - </svg> - </TopTitle> - </div> - <div className="flex-1 flex flex-col pt-0 pb-[24px]"> - <ShowAllProviders ref={ref} /> - </div> - </div> - </div> - </> - ); -}; diff --git a/apps/frontend/src/components/new-launch/select.current.tsx b/apps/frontend/src/components/new-launch/select.current.tsx index 53350bbe..4f0d9c49 100644 --- a/apps/frontend/src/components/new-launch/select.current.tsx +++ b/apps/frontend/src/components/new-launch/select.current.tsx @@ -5,6 +5,7 @@ import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import clsx from 'clsx'; import Image from 'next/image'; import { useShallow } from 'zustand/react/shallow'; +import { GlobalIcon } from '@gitroom/frontend/components/ui/icons'; export function useHasScroll(ref: RefObject<HTMLElement>): boolean { const [hasHorizontalScroll, setHasHorizontalScroll] = useState(false); @@ -87,21 +88,7 @@ export const SelectCurrent: FC = () => { )} > <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="20" - height="20" - viewBox="0 0 20 20" - fill="none" - > - <path - d="M2.56267 6.23601L6.13604 8.78837C6.32197 8.92118 6.41494 8.98759 6.51225 9.00289C6.59786 9.01635 6.68554 9.00278 6.76309 8.96407C6.85121 8.92008 6.91976 8.82868 7.05686 8.64588L7.81194 7.63909C7.85071 7.5874 7.8701 7.56155 7.89288 7.53925C7.91311 7.51945 7.93531 7.50177 7.95913 7.48647C7.98595 7.46924 8.01547 7.45612 8.07452 7.42988L11.2983 5.99707C11.432 5.93767 11.4988 5.90798 11.5492 5.8616C11.5938 5.82057 11.6288 5.77033 11.652 5.71436C11.6782 5.65108 11.6831 5.57812 11.6928 5.4322L11.9288 1.8915M11.2493 11.2503L13.4294 12.1846C13.6823 12.293 13.8088 12.3472 13.8757 12.4372C13.9345 12.5162 13.9634 12.6135 13.9573 12.7117C13.9504 12.8237 13.8741 12.9382 13.7214 13.1672L12.6973 14.7035C12.6249 14.812 12.5887 14.8663 12.5409 14.9056C12.4986 14.9403 12.4498 14.9664 12.3974 14.9824C12.3382 15.0003 12.273 15.0003 12.1426 15.0003H10.4799C10.3071 15.0003 10.2207 15.0003 10.1472 14.9714C10.0822 14.9459 10.0248 14.9045 9.98003 14.851C9.92936 14.7904 9.90204 14.7084 9.8474 14.5445L9.25334 12.7623C9.22111 12.6656 9.205 12.6173 9.20076 12.5681C9.19699 12.5246 9.20011 12.4807 9.21 12.4381C9.22114 12.3901 9.24393 12.3445 9.28951 12.2533L9.74077 11.3508C9.83246 11.1674 9.8783 11.0758 9.94891 11.0188C10.0111 10.9687 10.0865 10.9375 10.166 10.9289C10.2561 10.9193 10.3534 10.9517 10.5479 11.0165L11.2493 11.2503ZM18.3327 10.0003C18.3327 14.6027 14.6017 18.3337 9.99935 18.3337C5.39698 18.3337 1.66602 14.6027 1.66602 10.0003C1.66602 5.39795 5.39698 1.66699 9.99935 1.66699C14.6017 1.66699 18.3327 5.39795 18.3327 10.0003Z" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> + <GlobalIcon /> </div> </div> {selectedIntegrations.map(({ integration }) => ( diff --git a/apps/frontend/src/components/ui/icons/index.tsx b/apps/frontend/src/components/ui/icons/index.tsx new file mode 100644 index 00000000..78758ad4 --- /dev/null +++ b/apps/frontend/src/components/ui/icons/index.tsx @@ -0,0 +1,878 @@ +import React, { FC, SVGProps, useEffect } from 'react'; +import clsx from 'clsx'; +import useCookie from 'react-use-cookie'; +import { modeEmitter } from '@gitroom/frontend/components/layout/mode.component'; + +export type IconProps = SVGProps<SVGSVGElement> & { + size?: number; +}; + +// Settings/Gear Icon +export const SettingsIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <path + d="M7.82888 16.1429L8.31591 17.2383C8.4607 17.5644 8.69698 17.8414 8.9961 18.0358C9.29522 18.2303 9.64434 18.3337 10.0011 18.3337C10.3579 18.3337 10.707 18.2303 11.0061 18.0358C11.3052 17.8414 11.5415 17.5644 11.6863 17.2383L12.1733 16.1429C12.3467 15.7542 12.6383 15.4302 13.0067 15.217C13.3773 15.0032 13.8061 14.9121 14.2317 14.9568L15.4233 15.0837C15.778 15.1212 16.136 15.055 16.4539 14.8931C16.7717 14.7312 17.0358 14.4806 17.2141 14.1716C17.3925 13.8628 17.4776 13.5089 17.4588 13.1527C17.4401 12.7966 17.3184 12.4535 17.1085 12.1651L16.403 11.1957C16.1517 10.8479 16.0175 10.4293 16.0196 10.0003C16.0195 9.57248 16.155 9.15562 16.4067 8.80959L17.1122 7.84014C17.3221 7.55179 17.4438 7.20872 17.4625 6.85255C17.4813 6.49639 17.3962 6.14244 17.2178 5.83366C17.0395 5.52469 16.7754 5.27407 16.4576 5.11218C16.1397 4.9503 15.7817 4.8841 15.427 4.92162L14.2354 5.04847C13.8098 5.09317 13.381 5.00209 13.0104 4.78829C12.6413 4.57387 12.3496 4.24812 12.177 3.85773L11.6863 2.76236C11.5415 2.4363 11.3052 2.15925 11.0061 1.96482C10.707 1.77039 10.3579 1.66693 10.0011 1.66699C9.64434 1.66693 9.29522 1.77039 8.9961 1.96482C8.69698 2.15925 8.4607 2.4363 8.31591 2.76236L7.82888 3.85773C7.65632 4.24812 7.3646 4.57387 6.99554 4.78829C6.62489 5.00209 6.1961 5.09317 5.77054 5.04847L4.57517 4.92162C4.22045 4.8841 3.86246 4.9503 3.5446 5.11218C3.22675 5.27407 2.96269 5.52469 2.78443 5.83366C2.60595 6.14244 2.52092 6.49639 2.53965 6.85255C2.55839 7.20872 2.68009 7.55179 2.88999 7.84014L3.59554 8.80959C3.84716 9.15562 3.98266 9.57248 3.98258 10.0003C3.98266 10.4282 3.84716 10.845 3.59554 11.1911L2.88999 12.1605C2.68009 12.4489 2.55839 12.7919 2.53965 13.1481C2.52092 13.5043 2.60595 13.8582 2.78443 14.167C2.96286 14.4758 3.22696 14.7263 3.54476 14.8882C3.86257 15.05 4.22047 15.1163 4.57517 15.079L5.76684 14.9522C6.1924 14.9075 6.62119 14.9986 6.99184 15.2124C7.36228 15.4262 7.65535 15.752 7.82888 16.1429Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M9.99961 12.5003C11.3803 12.5003 12.4996 11.381 12.4996 10.0003C12.4996 8.61961 11.3803 7.50033 9.99961 7.50033C8.6189 7.50033 7.49961 8.61961 7.49961 10.0003C7.49961 11.381 8.6189 12.5003 9.99961 12.5003Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Chevron Down Icon (rotatable) +export const ChevronDownIcon: FC<IconProps & { rotated?: boolean }> = ({ + size = 20, + className, + rotated, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={clsx(rotated && 'rotate-180', 'transition-transform', className)} + {...props} + > + <path + d="M5 7.5L10 12.5L15 7.5" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Chevron Up Icon +export const ChevronUpIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <path + d="M15 12.5L10 7.5L5 12.5" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Close/X Icon +export const CloseIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <path + d="M16 4L4 16M4 4L16 16" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Small Close Icon (10x11 variant) +export const CloseIconSmall: FC<IconProps> = ({ + size = 10, + className, + ...props +}) => ( + <svg + width={size} + height={size + 1} + viewBox="0 0 10 11" + fill="none" + xmlns="http://www.w3.org/2000/svg" + className={className} + {...props} + > + <path + d="M9.85403 9.64628C9.90048 9.69274 9.93733 9.74789 9.96247 9.80859C9.98762 9.86928 10.0006 9.93434 10.0006 10C10.0006 10.0657 9.98762 10.1308 9.96247 10.1915C9.93733 10.2522 9.90048 10.3073 9.85403 10.3538C9.80757 10.4002 9.75242 10.4371 9.69173 10.4622C9.63103 10.4874 9.56598 10.5003 9.50028 10.5003C9.43458 10.5003 9.36953 10.4874 9.30883 10.4622C9.24813 10.4371 9.19298 10.4002 9.14653 10.3538L5.00028 6.20691L0.854028 10.3538C0.760208 10.4476 0.63296 10.5003 0.500278 10.5003C0.367596 10.5003 0.240348 10.4476 0.146528 10.3538C0.0527077 10.26 2.61548e-09 10.1327 0 10C-2.61548e-09 9.86735 0.0527077 9.7401 0.146528 9.64628L4.2934 5.50003L0.146528 1.35378C0.0527077 1.25996 0 1.13272 0 1.00003C0 0.867352 0.0527077 0.740104 0.146528 0.646284C0.240348 0.552464 0.367596 0.499756 0.500278 0.499756C0.63296 0.499756 0.760208 0.552464 0.854028 0.646284L5.00028 4.79316L9.14653 0.646284C9.24035 0.552464 9.3676 0.499756 9.50028 0.499756C9.63296 0.499756 9.76021 0.552464 9.85403 0.646284C9.94785 0.740104 10.0006 0.867352 10.0006 1.00003C10.0006 1.13272 9.94785 1.25996 9.85403 1.35378L5.70715 5.50003L9.85403 9.64628Z" + fill="currentColor" + /> + </svg> +); + +// Trash/Delete Icon +export const TrashIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <path + d="M7.5 2.5H12.5M2.5 5H17.5M15.8333 5L15.2489 13.7661C15.1612 15.0813 15.1174 15.7389 14.8333 16.2375C14.5833 16.6765 14.206 17.0294 13.7514 17.2497C13.235 17.5 12.5759 17.5 11.2578 17.5H8.74221C7.42409 17.5 6.76503 17.5 6.24861 17.2497C5.79396 17.0294 5.41674 16.6765 5.16665 16.2375C4.88259 15.7389 4.83875 15.0813 4.75107 13.7661L4.16667 5M8.33333 8.75V12.9167M11.6667 8.75V12.9167" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Dropdown Arrow (filled triangle) +export const DropdownArrowIcon: FC<IconProps & { rotated?: boolean }> = ({ + size = 20, + className, + rotated, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={clsx(rotated && 'rotate-180', 'transition-transform', className)} + {...props} + > + <path + d="M7.4563 8L12.5437 8C12.9494 8 13.1526 8.56798 12.8657 8.90016L10.322 11.8456C10.1442 12.0515 9.85583 12.0515 9.67799 11.8456L7.13429 8.90016C6.84741 8.56798 7.05059 8 7.4563 8Z" + fill="currentColor" + /> + </svg> +); + +// Small Dropdown Arrow (6x4) +export const DropdownArrowSmallIcon: FC<IconProps & { rotated?: boolean }> = ({ + className, + rotated, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="6" + height="4" + viewBox="0 0 6 4" + fill="none" + className={clsx(rotated && 'rotate-180', 'transition-transform', className)} + {...props} + > + <path + d="M0.456301 9.69291e-07L5.5437 7.97823e-08C5.94941 8.84616e-09 6.15259 0.567978 5.86571 0.90016L3.32201 3.84556C3.14417 4.05148 2.85583 4.05148 2.67799 3.84556L0.134293 0.900162C-0.152585 0.56798 0.0505934 1.04023e-06 0.456301 9.69291e-07Z" + fill="currentColor" + /> + </svg> +); + +// Calendar Icon +export const CalendarIcon: FC<IconProps> = ({ className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="17" + height="19" + viewBox="0 0 17 19" + fill="none" + className={className} + {...props} + > + <path + d="M15.75 7.41667H0.75M11.5833 0.75V4.08333M4.91667 0.75V4.08333M4.75 17.4167H11.75C13.1501 17.4167 13.8502 17.4167 14.385 17.1442C14.8554 16.9045 15.2378 16.522 15.4775 16.0516C15.75 15.5169 15.75 14.8168 15.75 13.4167V6.41667C15.75 5.01654 15.75 4.31647 15.4775 3.78169C15.2378 3.31129 14.8554 2.92883 14.385 2.68915C13.8502 2.41667 13.1501 2.41667 11.75 2.41667H4.75C3.34987 2.41667 2.6498 2.41667 2.11502 2.68915C1.64462 2.92883 1.26217 3.31129 1.02248 3.78169C0.75 4.31647 0.75 5.01654 0.75 6.41667V13.4167C0.75 14.8168 0.75 15.5169 1.02248 16.0516C1.26217 16.522 1.64462 16.9045 2.11502 17.1442C2.6498 17.4167 3.34987 17.4167 4.75 17.4167Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Repeat/Cycle Icon +export const RepeatIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <g clipPath="url(#clip0_repeat)"> + <path + d="M14.1667 1.66602L17.5 4.99935M17.5 4.99935L14.1667 8.33268M17.5 4.99935H6.5C5.09987 4.99935 4.3998 4.99935 3.86502 5.27183C3.39462 5.51152 3.01217 5.89397 2.77248 6.36437C2.5 6.89915 2.5 7.59922 2.5 8.99935V9.16602M2.5 14.9993H13.5C14.9001 14.9993 15.6002 14.9993 16.135 14.7269C16.6054 14.4872 16.9878 14.1047 17.2275 13.6343C17.5 13.0995 17.5 12.3995 17.5 10.9993V10.8327M2.5 14.9993L5.83333 18.3327M2.5 14.9993L5.83333 11.666" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_repeat"> + <rect width="20" height="20" fill="currentColor" /> + </clipPath> + </defs> + </svg> +); + +// Tag Icon +export const TagIcon: FC<IconProps> = ({ className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="17" + height="19" + viewBox="0 0 17 19" + fill="none" + className={className} + {...props} + > + <path + d="M15.75 8.25L9.42157 1.92157C8.98919 1.48919 8.773 1.273 8.52071 1.1184C8.29703 0.981328 8.05317 0.880317 7.79808 0.819075C7.51036 0.75 7.20462 0.75 6.59314 0.75L3.25 0.75M0.75 6.33333L0.75 7.97876C0.75 8.38641 0.75 8.59024 0.79605 8.78205C0.836878 8.95211 0.904218 9.11469 0.9956 9.26381C1.09867 9.432 1.2428 9.57613 1.53105 9.86438L8.03105 16.3644C8.69108 17.0244 9.02109 17.3544 9.40164 17.4781C9.73638 17.5868 10.097 17.5868 10.4317 17.4781C10.8122 17.3544 11.1423 17.0244 11.8023 16.3644L13.8644 14.3023C14.5244 13.6423 14.8544 13.3122 14.9781 12.9317C15.0868 12.597 15.0868 12.2364 14.9781 11.9016C14.8544 11.5211 14.5244 11.1911 13.8644 10.531L7.78105 4.44772C7.4928 4.15946 7.34867 4.01534 7.18048 3.91227C7.03135 3.82089 6.86878 3.75354 6.69872 3.71272C6.50691 3.66667 6.30308 3.66667 5.89543 3.66667H3.41667C2.48325 3.66667 2.01654 3.66667 1.66002 3.84832C1.34641 4.00811 1.09145 4.26308 0.931656 4.57668C0.75 4.9332 0.75 5.39991 0.75 6.33333Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Plus Icon +export const PlusIcon: FC<IconProps> = ({ size = 16, className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 16 16" + fill="none" + className={className} + {...props} + > + <path + d="M8.00065 3.33301V12.6663M3.33398 7.99967H12.6673" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Checkmark Icon +export const CheckmarkIcon: FC<IconProps> = ({ className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="11" + height="8" + viewBox="0 0 11 8" + fill="none" + className={className} + {...props} + > + <path + fillRule="evenodd" + clipRule="evenodd" + d="M10.7071 0.292893C11.0976 0.683417 11.0976 1.31658 10.7071 1.70711L4.70711 7.70711C4.31658 8.09763 3.68342 8.09763 3.29289 7.70711L0.292893 4.70711C-0.0976311 4.31658 -0.0976311 3.68342 0.292893 3.29289C0.683417 2.90237 1.31658 2.90237 1.70711 3.29289L4 5.58579L9.29289 0.292893C9.68342 -0.0976311 10.3166 -0.0976311 10.7071 0.292893Z" + fill="currentColor" + /> + </svg> +); + +// Global/Planet Icon +export const GlobalIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <path + d="M2.56267 6.23601L6.13604 8.78837C6.32197 8.92118 6.41494 8.98759 6.51225 9.00289C6.59786 9.01635 6.68554 9.00278 6.76309 8.96407C6.85121 8.92008 6.91976 8.82868 7.05686 8.64588L7.81194 7.63909C7.85071 7.5874 7.8701 7.56155 7.89288 7.53925C7.91311 7.51945 7.93531 7.50177 7.95913 7.48647C7.98595 7.46924 8.01547 7.45612 8.07452 7.42988L11.2983 5.99707C11.432 5.93767 11.4988 5.90798 11.5492 5.8616C11.5938 5.82057 11.6288 5.77033 11.652 5.71436C11.6782 5.65108 11.6831 5.57812 11.6928 5.4322L11.9288 1.8915M11.2493 11.2503L13.4294 12.1846C13.6823 12.293 13.8088 12.3472 13.8757 12.4372C13.9345 12.5162 13.9634 12.6135 13.9573 12.7117C13.9504 12.8237 13.8741 12.9382 13.7214 13.1672L12.6973 14.7035C12.6249 14.812 12.5887 14.8663 12.5409 14.9056C12.4986 14.9403 12.4498 14.9664 12.3974 14.9824C12.3382 15.0003 12.273 15.0003 12.1426 15.0003H10.4799C10.3071 15.0003 10.2207 15.0003 10.1472 14.9714C10.0822 14.9459 10.0248 14.9045 9.98003 14.851C9.92936 14.7904 9.90204 14.7084 9.8474 14.5445L9.25334 12.7623C9.22111 12.6656 9.205 12.6173 9.20076 12.5681C9.19699 12.5246 9.20011 12.4807 9.21 12.4381C9.22114 12.3901 9.24393 12.3445 9.28951 12.2533L9.74077 11.3508C9.83246 11.1674 9.8783 11.0758 9.94891 11.0188C10.0111 10.9687 10.0865 10.9375 10.166 10.9289C10.2561 10.9193 10.3534 10.9517 10.5479 11.0165L11.2493 11.2503ZM18.3327 10.0003C18.3327 14.6027 14.6017 18.3337 9.99935 18.3337C5.39698 18.3337 1.66602 14.6027 1.66602 10.0003C1.66602 5.39795 5.39698 1.66699 9.99935 1.66699C14.6017 1.66699 18.3327 5.39795 18.3327 10.0003Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// User Icon +export const UserIcon: FC<IconProps> = ({ size = 20, className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 20 20" + fill="none" + className={className} + {...props} + > + <path + d="M16.6673 17.5C16.6673 16.337 16.6673 15.7555 16.5238 15.2824C16.2006 14.217 15.3669 13.3834 14.3016 13.0602C13.8284 12.9167 13.247 12.9167 12.084 12.9167H7.91732C6.75435 12.9167 6.17286 12.9167 5.6997 13.0602C4.63436 13.3834 3.80068 14.217 3.47752 15.2824C3.33398 15.7555 3.33398 16.337 3.33398 17.5M13.7507 6.25C13.7507 8.32107 12.0717 10 10.0007 10C7.92958 10 6.25065 8.32107 6.25065 6.25C6.25065 4.17893 7.92958 2.5 10.0007 2.5C12.0717 2.5 13.7507 4.17893 13.7507 6.25Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Expand Icon +export const ExpandIcon: FC<IconProps> = ({ + size = 24, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size + 1} + viewBox="0 0 24 25" + fill="none" + className={className} + {...props} + > + <path + d="M20.25 5V9.5C20.25 9.69891 20.171 9.88968 20.0303 10.0303C19.8897 10.171 19.6989 10.25 19.5 10.25C19.3011 10.25 19.1103 10.171 18.9697 10.0303C18.829 9.88968 18.75 9.69891 18.75 9.5V6.81031L14.0306 11.5306C13.8899 11.6714 13.699 11.7504 13.5 11.7504C13.301 11.7504 13.1101 11.6714 12.9694 11.5306C12.8286 11.3899 12.7496 11.199 12.7496 11C12.7496 10.801 12.8286 10.6101 12.9694 10.4694L17.6897 5.75H15C14.8011 5.75 14.6103 5.67098 14.4697 5.53033C14.329 5.38968 14.25 5.19891 14.25 5C14.25 4.80109 14.329 4.61032 14.4697 4.46967C14.6103 4.32902 14.8011 4.25 15 4.25H19.5C19.6989 4.25 19.8897 4.32902 20.0303 4.46967C20.171 4.61032 20.25 4.80109 20.25 5ZM9.96937 13.4694L5.25 18.1897V15.5C5.25 15.3011 5.17098 15.1103 5.03033 14.9697C4.88968 14.829 4.69891 14.75 4.5 14.75C4.30109 14.75 4.11032 14.829 3.96967 14.9697C3.82902 15.1103 3.75 15.3011 3.75 15.5V20C3.75 20.1989 3.82902 20.3897 3.96967 20.5303C4.11032 20.671 4.30109 20.75 4.5 20.75H9C9.19891 20.75 9.38968 20.671 9.53033 20.5303C9.67098 20.3897 9.75 20.1989 9.75 20C9.75 19.8011 9.67098 19.6103 9.53033 19.4697C9.38968 19.329 9.19891 19.25 9 19.25H6.31031L11.0306 14.5306C11.1714 14.3899 11.2504 14.199 11.2504 14C11.2504 13.801 11.1714 13.6101 11.0306 13.4694C10.8899 13.3286 10.699 13.2496 10.5 13.2496C10.301 13.2496 10.1101 13.3286 9.96937 13.4694Z" + fill="currentColor" + /> + </svg> +); + +// Collapse Icon +export const CollapseIcon: FC<IconProps> = ({ + size = 24, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size + 1} + viewBox="0 0 24 25" + fill="none" + className={className} + {...props} + > + <path + d="M13.5004 10.2499V6.49993C13.5004 6.30102 13.5794 6.11025 13.7201 5.9696C13.8607 5.82895 14.0515 5.74993 14.2504 5.74993C14.4493 5.74993 14.6401 5.82895 14.7807 5.9696C14.9214 6.11025 15.0004 6.30102 15.0004 6.49993V8.43962L18.9698 4.4693C19.1105 4.32857 19.3014 4.24951 19.5004 4.24951C19.6994 4.24951 19.8903 4.32857 20.031 4.4693C20.1718 4.61003 20.2508 4.80091 20.2508 4.99993C20.2508 5.19895 20.1718 5.38982 20.031 5.53055L16.0607 9.49993H18.0004C18.1993 9.49993 18.3901 9.57895 18.5307 9.7196C18.6714 9.86025 18.7504 10.051 18.7504 10.2499C18.7504 10.4488 18.6714 10.6396 18.5307 10.7803C18.3901 10.9209 18.1993 10.9999 18.0004 10.9999H14.2504C14.0515 10.9999 13.8607 10.9209 13.7201 10.7803C13.5794 10.6396 13.5004 10.4488 13.5004 10.2499ZM9.75042 13.9999H6.00042C5.8015 13.9999 5.61074 14.0789 5.47009 14.2196C5.32943 14.3603 5.25042 14.551 5.25042 14.7499C5.25042 14.9488 5.32943 15.1396 5.47009 15.2803C5.61074 15.4209 5.8015 15.4999 6.00042 15.4999H7.9401L3.96979 19.4693C3.82906 19.61 3.75 19.8009 3.75 19.9999C3.75 20.199 3.82906 20.3898 3.96979 20.5306C4.11052 20.6713 4.30139 20.7503 4.50042 20.7503C4.69944 20.7503 4.89031 20.6713 5.03104 20.5306L9.00042 16.5602V18.4999C9.00042 18.6988 9.07943 18.8896 9.22009 19.0303C9.36074 19.1709 9.5515 19.2499 9.75042 19.2499C9.94933 19.2499 10.1401 19.1709 10.2807 19.0303C10.4214 18.8896 10.5004 18.6988 10.5004 18.4999V14.7499C10.5004 14.551 10.4214 14.3603 10.2807 14.2196C10.1401 14.0789 9.94933 13.9999 9.75042 13.9999ZM16.0607 15.4999H18.0004C18.1993 15.4999 18.3901 15.4209 18.5307 15.2803C18.6714 15.1396 18.7504 14.9488 18.7504 14.7499C18.7504 14.551 18.6714 14.3603 18.5307 14.2196C18.3901 14.0789 18.1993 13.9999 18.0004 13.9999H14.2504C14.0515 13.9999 13.8607 14.0789 13.7201 14.2196C13.5794 14.3603 13.5004 14.551 13.5004 14.7499V18.4999C13.5004 18.6988 13.5794 18.8896 13.7201 19.0303C13.8607 19.1709 14.0515 19.2499 14.2504 19.2499C14.4493 19.2499 14.6401 19.1709 14.7807 19.0303C14.9214 18.8896 15.0004 18.6988 15.0004 18.4999V16.5602L18.9698 20.5306C19.0395 20.6002 19.1222 20.6555 19.2132 20.6932C19.3043 20.7309 19.4019 20.7503 19.5004 20.7503C19.599 20.7503 19.6965 20.7309 19.7876 20.6932C19.8786 20.6555 19.9614 20.6002 20.031 20.5306C20.1007 20.4609 20.156 20.3781 20.1937 20.2871C20.2314 20.1961 20.2508 20.0985 20.2508 19.9999C20.2508 19.9014 20.2314 19.8038 20.1937 19.7128C20.156 19.6217 20.1007 19.539 20.031 19.4693L16.0607 15.4999ZM9.75042 5.74993C9.5515 5.74993 9.36074 5.82895 9.22009 5.9696C9.07943 6.11025 9.00042 6.30102 9.00042 6.49993V8.43962L5.03104 4.4693C4.89031 4.32857 4.69944 4.24951 4.50042 4.24951C4.30139 4.24951 4.11052 4.32857 3.96979 4.4693C3.82906 4.61003 3.75 4.80091 3.75 4.99993C3.75 5.19895 3.82906 5.38982 3.96979 5.53055L7.9401 9.49993H6.00042C5.8015 9.49993 5.61074 9.57895 5.47009 9.7196C5.32943 9.86025 5.25042 10.051 5.25042 10.2499C5.25042 10.4488 5.32943 10.6396 5.47009 10.7803C5.61074 10.9209 5.8015 10.9999 6.00042 10.9999H9.75042C9.94933 10.9999 10.1401 10.9209 10.2807 10.7803C10.4214 10.6396 10.5004 10.4488 10.5004 10.2499V6.49993C10.5004 6.30102 10.4214 6.11025 10.2807 5.9696C10.1401 5.82895 9.94933 5.74993 9.75042 5.74993Z" + fill="currentColor" + /> + </svg> +); + +// Lock Icon +export const LockIcon: FC<IconProps> = ({ size = 32, className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 32 32" + fill="none" + className={className} + {...props} + > + <path + d="M22.6673 13.3333V10.6667C22.6673 6.98477 19.6825 4 16.0007 4C12.3188 4 9.33398 6.98477 9.33398 10.6667V13.3333M16.0007 19.3333V22M11.734 28H20.2673C22.5075 28 23.6276 28 24.4833 27.564C25.2359 27.1805 25.8479 26.5686 26.2313 25.816C26.6673 24.9603 26.6673 23.8402 26.6673 21.6V19.7333C26.6673 17.4931 26.6673 16.373 26.2313 15.5174C25.8479 14.7647 25.2359 14.1528 24.4833 13.7693C23.6276 13.3333 22.5075 13.3333 20.2673 13.3333H11.734C9.49377 13.3333 8.37367 13.3333 7.51802 13.7693C6.76537 14.1528 6.15345 14.7647 5.76996 15.5174C5.33398 16.373 5.33398 17.4931 5.33398 19.7333V21.6C5.33398 23.8402 5.33398 24.9603 5.76996 25.816C6.15345 26.5686 6.76537 27.1805 7.51802 27.564C8.37367 28 9.49377 28 11.734 28Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Connection Line Icon (for thread/comment indication) +export const ConnectionLineIcon: FC<IconProps> = ({ className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="87" + viewBox="0 0 18 87" + fill="none" + className={className} + {...props} + > + <path + d="M0.75 0.75V79.75C0.75 83.0637 3.43629 85.75 6.75 85.75H16.75" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + /> + </svg> +); + +// Reset/Back to Global Icon +export const ResetIcon: FC<IconProps> = ({ + size = 16, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 16 16" + fill="none" + className={className} + {...props} + > + <path + d="M1.33398 6.66667C1.33398 6.66667 2.67064 4.84548 3.75654 3.75883C4.84244 2.67218 6.34305 2 8.00065 2C11.3144 2 14.0007 4.68629 14.0007 8C14.0007 11.3137 11.3144 14 8.00065 14C5.26526 14 2.95739 12.1695 2.23516 9.66667M1.33398 6.66667V2.66667M1.33398 6.66667H5.33398" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Emoji Icon +export const EmojiIcon: FC<IconProps> = ({ + size = 16, + className, + ...props +}) => ( + <svg + width={size} + height={size} + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" + className={className} + {...props} + > + <path + d="M7.97917 14.6663C11.6611 14.6663 14.6458 11.6816 14.6458 7.99967C14.6458 4.31778 11.6611 1.33301 7.97917 1.33301C4.29727 1.33301 1.3125 4.31778 1.3125 7.99967C1.3125 11.6816 4.29727 14.6663 7.97917 14.6663Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M4.80664 10C5.50664 11.0067 6.67997 11.6667 7.99997 11.6667C9.31997 11.6667 10.4866 11.0067 11.1933 10" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M4.66602 6.16699C5.33268 6.83366 6.41935 6.83366 7.09268 6.16699" + stroke="currentColor" + strokeWidth="1.2" + strokeMiterlimit="10" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M8.90625 6.16699C9.57292 6.83366 10.6596 6.83366 11.3329 6.16699" + stroke="currentColor" + strokeWidth="1.2" + strokeMiterlimit="10" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> +); + +// Chevron Left Icon +export const ChevronLeftIcon: FC<IconProps> = ({ + size = 24, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth={2} + strokeLinecap="round" + strokeLinejoin="round" + className={className} + {...props} + > + <path d="m15 18-6-6 6-6" /> + </svg> +); + +// Chevron Right Icon +export const ChevronRightIcon: FC<IconProps> = ({ + size = 24, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth={2} + strokeLinecap="round" + strokeLinejoin="round" + className={className} + {...props} + > + <path d="m9 18 6-6-6-6" /> + </svg> +); + +// Delete Circle Icon (for media delete) +export const DeleteCircleIcon: FC<IconProps> = ({ + size = 18, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 18 18" + fill="none" + className={className} + {...props} + > + <ellipse cx="9.96484" cy="9.10742" rx="6" ry="5.5" fill="white" /> + <path + d="M9 1.5C4.8675 1.5 1.5 4.8675 1.5 9C1.5 13.1325 4.8675 16.5 9 16.5C13.1325 16.5 16.5 13.1325 16.5 9C16.5 4.8675 13.1325 1.5 9 1.5ZM11.52 10.725C11.7375 10.9425 11.7375 11.3025 11.52 11.52C11.4075 11.6325 11.265 11.685 11.1225 11.685C10.98 11.685 10.8375 11.6325 10.725 11.52L9 9.795L7.275 11.52C7.1625 11.6325 7.02 11.685 6.8775 11.685C6.735 11.685 6.5925 11.6325 6.48 11.52C6.2625 11.3025 6.2625 10.9425 6.48 10.725L8.205 9L6.48 7.275C6.2625 7.0575 6.2625 6.6975 6.48 6.48C6.6975 6.2625 7.0575 6.2625 7.275 6.48L9 8.205L10.725 6.48C10.9425 6.2625 11.3025 6.2625 11.52 6.48C11.7375 6.6975 11.7375 7.0575 11.52 7.275L9.795 9L11.52 10.725Z" + fill="#FF3535" + /> + </svg> +); + +// Close Circle Icon (smaller, for clearing media) +export const CloseCircleIcon: FC<IconProps> = ({ + size = 15, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 15 15" + fill="none" + className={className} + {...props} + > + <path + d="M7.5 0C3.3675 0 0 3.3675 0 7.5C0 11.6325 3.3675 15 7.5 15C11.6325 15 15 11.6325 15 7.5C15 3.3675 11.6325 0 7.5 0ZM10.02 9.225C10.2375 9.4425 10.2375 9.8025 10.02 10.02C9.9075 10.1325 9.765 10.185 9.6225 10.185C9.48 10.185 9.3375 10.1325 9.225 10.02L7.5 8.295L5.775 10.02C5.6625 10.1325 5.52 10.185 5.3775 10.185C5.235 10.185 5.0925 10.1325 4.98 10.02C4.7625 9.8025 4.7625 9.4425 4.98 9.225L6.705 7.5L4.98 5.775C4.7625 5.5575 4.7625 5.1975 4.98 4.98C5.1975 4.7625 5.5575 4.7625 5.775 4.98L7.5 6.705L9.225 4.98C9.4425 4.7625 9.8025 4.7625 10.02 4.98C10.2375 5.1975 10.2375 5.5575 10.02 5.775L8.295 7.5L10.02 9.225Z" + fill="#FF3535" + /> + </svg> +); + +// Drag Handle Icon +export const DragHandleIcon: FC<IconProps> = ({ + size = 15, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 15 15" + fill="none" + className={className} + {...props} + > + <ellipse cx="8.23242" cy="7.5" rx="6" ry="5.5" fill="white" /> + <path + d="M7.5 0C11.6421 0 15 3.35786 15 7.5C14.9998 11.642 11.642 15 7.5 15C3.35799 15 0.000197912 11.642 0 7.5C0 3.35786 3.35786 0 7.5 0ZM5.55566 8.38867C4.97286 8.38867 4.50026 8.86159 4.5 9.44434C4.5 10.0273 4.9727 10.5 5.55566 10.5C6.13858 10.4999 6.61133 10.0273 6.61133 9.44434C6.61107 8.86162 6.13842 8.38873 5.55566 8.38867ZM9.44434 8.38867C8.86158 8.38873 8.38893 8.86162 8.38867 9.44434C8.38867 10.0273 8.86142 10.4999 9.44434 10.5C10.0273 10.5 10.5 10.0273 10.5 9.44434C10.4997 8.86159 10.0271 8.38867 9.44434 8.38867ZM5.55566 9.38867C5.58614 9.38873 5.61107 9.41391 5.61133 9.44434C5.61133 9.47498 5.5863 9.49994 5.55566 9.5C5.52498 9.5 5.5 9.47502 5.5 9.44434C5.50026 9.41387 5.52514 9.38867 5.55566 9.38867ZM9.44434 9.38867C9.47486 9.38867 9.49974 9.41387 9.5 9.44434C9.5 9.47502 9.47502 9.5 9.44434 9.5C9.4137 9.49994 9.38867 9.47498 9.38867 9.44434C9.38893 9.41391 9.41386 9.38873 9.44434 9.38867ZM5.55566 4.5C4.97282 4.5 4.5002 4.97287 4.5 5.55566C4.50006 6.13858 4.97273 6.61133 5.55566 6.61133C6.13855 6.61127 6.61127 6.13855 6.61133 5.55566C6.61113 4.9729 6.13846 4.50006 5.55566 4.5ZM9.44434 4.5C8.86154 4.50006 8.38887 4.9729 8.38867 5.55566C8.38873 6.13855 8.86145 6.61127 9.44434 6.61133C10.0273 6.61133 10.4999 6.13858 10.5 5.55566C10.4998 4.97287 10.0272 4.5 9.44434 4.5ZM5.55566 5.5C5.58617 5.50006 5.61113 5.52519 5.61133 5.55566C5.61127 5.58626 5.58626 5.61127 5.55566 5.61133C5.52502 5.61133 5.50006 5.5863 5.5 5.55566C5.5002 5.52515 5.5251 5.5 5.55566 5.5ZM9.44434 5.5C9.4749 5.5 9.4998 5.52515 9.5 5.55566C9.49994 5.5863 9.47498 5.61133 9.44434 5.61133C9.41374 5.61127 9.38873 5.58626 9.38867 5.55566C9.38887 5.52519 9.41383 5.50006 9.44434 5.5Z" + fill="#618DFF" + /> + </svg> +); + +// Media Settings Icon (gear for media) +export const MediaSettingsIcon: FC<IconProps> = ({ + size = 40, + className, + ...props +}) => ( + <svg + width={size} + height={size} + viewBox="0 0 40 40" + fill="none" + xmlns="http://www.w3.org/2000/svg" + className={className} + {...props} + > + <path + d="M19.9987 15.5C19.1087 15.5 18.2387 15.7639 17.4986 16.2584C16.7586 16.7528 16.1818 17.4556 15.8413 18.2779C15.5007 19.1002 15.4115 20.005 15.5852 20.8779C15.7588 21.7508 16.1874 22.5526 16.8167 23.182C17.4461 23.8113 18.2479 24.2399 19.1208 24.4135C19.9937 24.5871 20.8985 24.498 21.7208 24.1574C22.5431 23.8168 23.2459 23.2401 23.7403 22.5C24.2348 21.76 24.4987 20.89 24.4987 20C24.4975 18.8069 24.023 17.663 23.1793 16.8194C22.3357 15.9757 21.1918 15.5012 19.9987 15.5ZM19.9987 23C19.4054 23 18.8254 22.824 18.332 22.4944C17.8387 22.1647 17.4541 21.6962 17.2271 21.148C17 20.5999 16.9406 19.9967 17.0564 19.4147C17.1721 18.8328 17.4578 18.2982 17.8774 17.8787C18.297 17.4591 18.8315 17.1734 19.4134 17.0576C19.9954 16.9419 20.5986 17.0013 21.1468 17.2283C21.6949 17.4554 22.1635 17.8399 22.4931 18.3333C22.8228 18.8266 22.9987 19.4066 22.9987 20C22.9987 20.7956 22.6826 21.5587 22.12 22.1213C21.5574 22.6839 20.7944 23 19.9987 23ZM30.3056 18.0509C30.2847 17.9453 30.2413 17.8454 30.1784 17.7581C30.1155 17.6707 30.0345 17.5979 29.9409 17.5447L27.1443 15.9509L27.1331 12.799C27.1327 12.6905 27.1089 12.5833 27.063 12.4849C27.0172 12.3865 26.9506 12.2992 26.8678 12.229C25.8533 11.3709 24.6851 10.7134 23.4253 10.2912C23.3261 10.2577 23.2209 10.2452 23.1166 10.2547C23.0123 10.2643 22.9111 10.2955 22.8197 10.3465L19.9987 11.9234L17.175 10.3437C17.0834 10.2924 16.9821 10.2609 16.8776 10.2513C16.7732 10.2416 16.6678 10.2539 16.5684 10.2875C15.3095 10.7127 14.1426 11.3728 13.1297 12.2328C13.0469 12.3028 12.9804 12.39 12.9346 12.4882C12.8888 12.5865 12.8648 12.6935 12.8643 12.8019L12.8503 15.9565L10.0537 17.5503C9.96015 17.6036 9.87916 17.6763 9.81623 17.7637C9.7533 17.8511 9.70992 17.9509 9.68903 18.0565C9.43309 19.3427 9.43309 20.6667 9.68903 21.9528C9.70992 22.0584 9.7533 22.1583 9.81623 22.2456C9.87916 22.333 9.96015 22.4058 10.0537 22.459L12.8503 24.0528L12.8615 27.2047C12.8619 27.3132 12.8858 27.4204 12.9316 27.5188C12.9774 27.6172 13.044 27.7045 13.1268 27.7747C14.1413 28.6328 15.3095 29.2904 16.5693 29.7125C16.6686 29.7461 16.7737 29.7585 16.878 29.749C16.9823 29.7394 17.0835 29.7082 17.175 29.6572L19.9987 28.0765L22.8225 29.6562C22.9342 29.7185 23.0602 29.7508 23.1881 29.75C23.27 29.75 23.3514 29.7367 23.429 29.7106C24.6878 29.286 25.8547 28.6265 26.8678 27.7672C26.9505 27.6971 27.017 27.61 27.0628 27.5117C27.1087 27.4135 27.1326 27.3065 27.1331 27.1981L27.1472 24.0434L29.9437 22.4497C30.0373 22.3964 30.1183 22.3236 30.1812 22.2363C30.2441 22.1489 30.2875 22.049 30.3084 21.9434C30.5629 20.6583 30.562 19.3357 30.3056 18.0509ZM28.8993 21.3237L26.2209 22.8472C26.1035 22.9139 26.0064 23.0111 25.9397 23.1284C25.8853 23.2222 25.8281 23.3215 25.77 23.4153C25.6956 23.5335 25.6559 23.6703 25.6556 23.81L25.6415 26.8334C24.9216 27.3988 24.1195 27.8509 23.2631 28.174L20.5612 26.6684C20.449 26.6064 20.3228 26.5741 20.1947 26.5747H20.1768C20.0634 26.5747 19.949 26.5747 19.8356 26.5747C19.7014 26.5713 19.5688 26.6037 19.4512 26.6684L16.7475 28.1778C15.8892 27.8571 15.0849 27.4072 14.3625 26.8437L14.3522 23.825C14.3517 23.685 14.3121 23.548 14.2378 23.4294C14.1797 23.3356 14.1225 23.2419 14.069 23.1425C14.0028 23.0233 13.9056 22.9242 13.7878 22.8556L11.1065 21.3284C10.9678 20.4507 10.9678 19.5567 11.1065 18.679L13.7803 17.1528C13.8976 17.0861 13.9948 16.9889 14.0615 16.8715C14.1159 16.7778 14.1731 16.6784 14.2312 16.5847C14.3056 16.4664 14.3453 16.3297 14.3456 16.19L14.3597 13.1665C15.0796 12.6012 15.8816 12.1491 16.7381 11.8259L19.4362 13.3315C19.5536 13.3966 19.6864 13.429 19.8206 13.4253C19.934 13.4253 20.0484 13.4253 20.1618 13.4253C20.296 13.4286 20.4287 13.3963 20.5462 13.3315L23.25 11.8222C24.1082 12.1429 24.9125 12.5927 25.635 13.1562L25.6453 16.175C25.6457 16.3149 25.6854 16.452 25.7597 16.5706C25.8178 16.6644 25.875 16.7581 25.9284 16.8575C25.9947 16.9767 26.0918 17.0758 26.2097 17.1444L28.8909 18.6715C29.0315 19.5499 29.0331 20.4449 28.8956 21.3237H28.8993Z" + fill="currentColor" + /> + </svg> +); + +// Insert Media Icon +export const InsertMediaIcon: FC<IconProps> = ({ + size = 16, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 16 16" + fill="none" + className={className} + {...props} + > + <g clipPath="url(#clip0_insertmedia)"> + <path + d="M8.33333 1.99967H5.2C4.0799 1.99967 3.51984 1.99967 3.09202 2.21766C2.71569 2.40941 2.40973 2.71537 2.21799 3.09169C2 3.51952 2 4.07957 2 5.19967V10.7997C2 11.9198 2 12.4798 2.21799 12.9077C2.40973 13.284 2.71569 13.5899 3.09202 13.7817C3.51984 13.9997 4.07989 13.9997 5.2 13.9997H11.3333C11.9533 13.9997 12.2633 13.9997 12.5176 13.9315C13.2078 13.7466 13.7469 13.2075 13.9319 12.5173C14 12.263 14 11.953 14 11.333M12.6667 5.33301V1.33301M10.6667 3.33301H14.6667M7 5.66634C7 6.40272 6.40305 6.99967 5.66667 6.99967C4.93029 6.99967 4.33333 6.40272 4.33333 5.66634C4.33333 4.92996 4.93029 4.33301 5.66667 4.33301C6.40305 4.33301 7 4.92996 7 5.66634ZM9.99336 7.94511L4.3541 13.0717C4.03691 13.3601 3.87831 13.5042 3.86429 13.6291C3.85213 13.7374 3.89364 13.8448 3.97546 13.9167C4.06985 13.9997 4.28419 13.9997 4.71286 13.9997H10.9707C11.9301 13.9997 12.4098 13.9997 12.7866 13.8385C13.2596 13.6361 13.6365 13.2593 13.8388 12.7863C14 12.4095 14 11.9298 14 10.9703C14 10.6475 14 10.4861 13.9647 10.3358C13.9204 10.1469 13.8353 9.96991 13.7155 9.81727C13.6202 9.69581 13.4941 9.59497 13.242 9.39331L11.3772 7.90145C11.1249 7.69961 10.9988 7.5987 10.8599 7.56308C10.7374 7.53169 10.6086 7.53575 10.4884 7.5748C10.352 7.6191 10.2324 7.72777 9.99336 7.94511Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_insertmedia"> + <rect width="16" height="16" fill="currentColor" /> + </clipPath> + </defs> + </svg> +); + +// Design Media Icon (pencil/edit) +export const DesignMediaIcon: FC<IconProps> = ({ + size = 16, + className, + ...props +}) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width={size} + height={size} + viewBox="0 0 16 16" + fill="none" + className={className} + {...props} + > + <g clipPath="url(#clip0_designmedia)"> + <path + d="M7.79167 1.99984H5.2C4.07989 1.99984 3.51984 1.99984 3.09202 2.21782C2.71569 2.40957 2.40973 2.71553 2.21799 3.09186C2 3.51968 2 4.07973 2 5.19984V10.7998C2 11.9199 2 12.48 2.21799 12.9078C2.40973 13.2841 2.71569 13.5901 3.09202 13.7818C3.51984 13.9998 4.07989 13.9998 5.2 13.9998H11.3333C11.9533 13.9998 12.2633 13.9998 12.5176 13.9317C13.2078 13.7468 13.7469 13.2077 13.9319 12.5175C14 12.2631 14 11.9532 14 11.3332M7 5.6665C7 6.40288 6.40305 6.99984 5.66667 6.99984C4.93029 6.99984 4.33333 6.40288 4.33333 5.6665C4.33333 4.93012 4.93029 4.33317 5.66667 4.33317C6.40305 4.33317 7 4.93012 7 5.6665ZM9.99336 7.94527L4.3541 13.0719C4.03691 13.3602 3.87831 13.5044 3.86429 13.6293C3.85213 13.7376 3.89364 13.8449 3.97546 13.9169C4.06985 13.9998 4.28419 13.9998 4.71286 13.9998H10.9707C11.9301 13.9998 12.4098 13.9998 12.7866 13.8387C13.2596 13.6363 13.6365 13.2595 13.8388 12.7864C14 12.4097 14 11.9299 14 10.9705C14 10.6477 14 10.4863 13.9647 10.3359C13.9204 10.147 13.8353 9.97007 13.7155 9.81743C13.6202 9.69597 13.4941 9.59514 13.242 9.39348L11.3772 7.90161C11.1249 7.69978 10.9988 7.59886 10.8599 7.56324C10.7374 7.53185 10.6086 7.53592 10.4884 7.57496C10.352 7.61926 10.2324 7.72794 9.99336 7.94527ZM15.0951 6.49981L13.0275 5.90908C12.9285 5.88079 12.879 5.86664 12.8328 5.84544C12.7918 5.82662 12.7528 5.80368 12.7164 5.77698C12.6755 5.74692 12.6391 5.71051 12.5663 5.6377L10.2617 3.33317C9.80143 2.87292 9.80144 2.1267 10.2617 1.66646C10.7219 1.20623 11.4681 1.20623 11.9284 1.66647L14.2329 3.97103C14.3058 4.04384 14.3422 4.08025 14.3722 4.12121C14.3989 4.15757 14.4219 4.19655 14.4407 4.23755C14.4619 4.28373 14.476 4.33323 14.5043 4.43224L15.0951 6.49981Z" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <defs> + <clipPath id="clip0_designmedia"> + <rect width="16" height="16" fill="currentColor" /> + </clipPath> + </defs> + </svg> +); + +// Vertical Divider Icon +export const VerticalDividerIcon: FC<IconProps> = ({ className, ...props }) => ( + <svg + xmlns="http://www.w3.org/2000/svg" + width="2" + height="17" + viewBox="0 0 2 17" + fill="none" + className={className} + {...props} + > + <path + d="M0.75 0.75V16" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + /> + </svg> +); + +export const NoMediaIcon: FC = () => { + const [mode, setMode] = useCookie('mode', 'dark'); + + useEffect(() => { + modeEmitter.on('mode', (value) => { + setMode(value); + }); + + return () => { + modeEmitter.removeAllListeners(); + }; + }, []); + + return ( + <> + {mode === 'light' ? ( + <svg + width="192" + height="151" + viewBox="0 0 192 151" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M109.75 59.0141C104.489 59.0141 113.46 -5.73557 91.0289 1.57563C69.7021 8.5269 99.5229 59.0141 94.5119 59.0141C89.5009 59.0141 54.4775 56.107 52.1458 71.9377C49.5418 89.6178 95.4225 79.7216 96.7894 81.9895C98.1563 84.2573 78.775 111.109 91.0289 119.324C103.724 127.835 119.934 96.3491 122.711 96.3491C125.489 96.3491 139.845 147.93 151.514 133.684C160.997 122.106 138.391 96.3491 142.873 96.3491C147.355 96.3491 180.793 98.9658 186.076 81.9895C192.534 61.2424 134.828 76.0575 131.352 71.9377C127.876 67.818 159.167 34.7484 142.873 25.987C126.785 17.3361 115.012 59.0141 109.75 59.0141Z" + stroke="#DACBFB" + stroke-opacity="0.4" + stroke-width="2" + stroke-linecap="round" + /> + <rect + x="22.6328" + y="62.543" + width="49.2079" + height="49.2079" + rx="12.6792" + transform="rotate(-16.275 22.6328 62.543)" + fill="#E7DDFE" + /> + <path + d="M66.8612 81.5418L60.5419 73.8544C59.3886 72.4461 58.1025 71.8318 56.9211 72.1115C55.7516 72.3877 54.8703 73.5173 54.4666 75.3019L53.3809 80.0591C53.1531 81.0631 52.6197 81.7787 51.8933 82.0558C51.1583 82.3485 50.2868 82.1731 49.4472 81.5718L49.0852 81.3128C47.9215 80.4935 46.715 80.2857 45.6666 80.7089C44.6181 81.1321 43.9113 82.1457 43.653 83.5362L42.7853 88.2819C42.4791 89.9989 43.0589 91.7178 44.3481 92.8781C45.6373 94.0384 47.4099 94.4456 49.0778 93.9588L64.3891 89.4902C65.997 89.0209 67.2623 87.7792 67.758 86.1761C68.2777 84.566 67.9279 82.8321 66.8612 81.5418Z" + fill="white" + /> + <path + d="M45.8451 76.6857C48.085 76.032 49.3709 73.6862 48.7172 71.4462C48.0634 69.2063 45.7176 67.9204 43.4777 68.5741C41.2377 69.2279 39.9518 71.5737 40.6056 73.8136C41.2593 76.0536 43.6051 77.3395 45.8451 76.6857Z" + fill="white" + /> + <rect + x="64.8105" + y="70.6133" + width="66.3578" + height="66.3578" + rx="18.1132" + fill="#DACBFB" + /> + <path + d="M80.1222 117.087L80.0843 117.125C79.5723 116.006 79.2499 114.735 79.1172 113.332C79.2499 114.716 79.6102 115.968 80.1222 117.087Z" + fill="white" + /> + <path + d="M92.2983 100.718C94.7909 100.718 96.8115 98.6972 96.8115 96.2046C96.8115 93.712 94.7909 91.6914 92.2983 91.6914C89.8058 91.6914 87.7852 93.712 87.7852 96.2046C87.7852 98.6972 89.8058 100.718 92.2983 100.718Z" + fill="white" + /> + <path + d="M105.932 84.8281H90.0409C83.1384 84.8281 79.0234 88.9431 79.0234 95.8456V111.737C79.0234 113.804 79.3837 115.605 80.0854 117.122C81.7162 120.725 85.2054 122.754 90.0409 122.754H105.932C112.834 122.754 116.949 118.639 116.949 111.737V107.394V95.8456C116.949 88.9431 112.834 84.8281 105.932 84.8281ZM113.858 104.739C112.379 103.469 109.99 103.469 108.511 104.739L100.622 111.509C99.1431 112.78 96.7538 112.78 95.2747 111.509L94.63 110.978C93.2836 109.802 91.1408 109.689 89.6237 110.713L82.5316 115.472C82.1144 114.41 81.8679 113.178 81.8679 111.737V95.8456C81.8679 90.4981 84.6934 87.6726 90.0409 87.6726H105.932C111.279 87.6726 114.105 90.4981 114.105 95.8456V104.948L113.858 104.739Z" + fill="white" + /> + </svg> + ) : ( + <svg + width="192" + height="151" + viewBox="0 0 192 151" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M109.75 59.0141C104.489 59.0141 113.46 -5.73557 91.0289 1.57563C69.7021 8.5269 99.5229 59.0141 94.5119 59.0141C89.5009 59.0141 54.4775 56.107 52.1458 71.9377C49.5418 89.6178 95.4225 79.7216 96.7894 81.9895C98.1563 84.2573 78.775 111.109 91.0289 119.324C103.724 127.835 119.934 96.3491 122.711 96.3491C125.489 96.3491 139.845 147.93 151.514 133.684C160.997 122.106 138.391 96.3491 142.873 96.3491C147.355 96.3491 180.793 98.9658 186.076 81.9895C192.534 61.2424 134.828 76.0575 131.352 71.9377C127.876 67.818 159.167 34.7484 142.873 25.987C126.785 17.3361 115.012 59.0141 109.75 59.0141Z" + stroke="white" + strokeOpacity="0.08" + strokeWidth="2" + strokeLinecap="round" + /> + <rect + x="22.6328" + y="62.541" + width="49.2079" + height="49.2079" + rx="12.6792" + transform="rotate(-16.275 22.6328 62.541)" + fill="#232222" + /> + <path + d="M66.8573 81.5379L60.538 73.8505C59.3847 72.4421 58.0986 71.8279 56.9172 72.1076C55.7477 72.3838 54.8664 73.5134 54.4627 75.298L53.377 80.0552C53.1492 81.0592 52.6158 81.7748 51.8894 82.0519C51.1544 82.3446 50.2829 82.1692 49.4433 81.5678L49.0813 81.3089C47.9176 80.4896 46.7111 80.2818 45.6626 80.705C44.6142 81.1282 43.9074 82.1418 43.6491 83.5323L42.7814 88.278C42.4752 89.995 43.055 91.7139 44.3442 92.8742C45.6334 94.0345 47.406 94.4417 49.0739 93.9549L64.3851 89.4863C65.9931 89.017 67.2584 87.7753 67.7541 86.1722C68.2738 84.5621 67.924 82.8282 66.8573 81.5379Z" + fill="white" + fillOpacity="0.4" + /> + <path + d="M45.8412 76.6818C48.0811 76.0281 49.367 73.6823 48.7133 71.4423C48.0595 69.2024 45.7137 67.9165 43.4738 68.5702C41.2338 69.2239 39.9479 71.5697 40.6017 73.8097C41.2554 76.0497 43.6012 77.3355 45.8412 76.6818Z" + fill="white" + fillOpacity="0.4" + /> + <rect + x="64.8125" + y="70.6133" + width="66.3578" + height="66.3578" + rx="18.1132" + fill="#2C2B2B" + /> + <path + d="M80.1261 117.087L80.0882 117.125C79.5762 116.006 79.2538 114.735 79.1211 113.332C79.2538 114.716 79.6141 115.968 80.1261 117.087Z" + fill="white" + fillOpacity="0.4" + /> + <path + d="M92.3022 100.72C94.7948 100.72 96.8154 98.6991 96.8154 96.2065C96.8154 93.714 94.7948 91.6934 92.3022 91.6934C89.8097 91.6934 87.7891 93.714 87.7891 96.2065C87.7891 98.6991 89.8097 100.72 92.3022 100.72Z" + fill="white" + fillOpacity="0.4" + /> + <path + d="M105.936 84.8301H90.0448C83.1423 84.8301 79.0273 88.945 79.0273 95.8476V111.739C79.0273 113.805 79.3876 115.607 80.0893 117.124C81.7201 120.727 85.2093 122.756 90.0448 122.756H105.936C112.838 122.756 116.953 118.641 116.953 111.739V107.396V95.8476C116.953 88.945 112.838 84.8301 105.936 84.8301ZM113.862 104.741C112.383 103.471 109.994 103.471 108.515 104.741L100.626 111.511C99.147 112.781 96.7577 112.781 95.2786 111.511L94.6339 110.98C93.2875 109.804 91.1447 109.691 89.6276 110.715L82.5355 115.474C82.1183 114.412 81.8718 113.18 81.8718 111.739V95.8476C81.8718 90.5 84.6973 87.6745 90.0448 87.6745H105.936C111.283 87.6745 114.109 90.5 114.109 95.8476V104.95L113.862 104.741Z" + fill="white" + fillOpacity="0.4" + /> + </svg> + )} + </> + ); +}; diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index 15e6a72b..0fd050db 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -1,5 +1,6 @@ const { join } = require('path'); module.exports = { + darkMode: 'class', content: ['./src/**/*.{ts,tsx,html}', '../../libraries/**/*.{ts,tsx,html}'], theme: { extend: { From 74b84b5eebef370cbd423ffcfe8852dfec09cd63 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 25 Dec 2025 21:38:08 +0700 Subject: [PATCH 055/340] feat: fix information display --- apps/frontend/src/components/launches/information.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/information.component.tsx b/apps/frontend/src/components/launches/information.component.tsx index 38ed5f21..dfcf6d8a 100644 --- a/apps/frontend/src/components/launches/information.component.tsx +++ b/apps/frontend/src/components/launches/information.component.tsx @@ -104,7 +104,7 @@ export const InformationComponent: FC<{ } return true; - }, [totalAllowedChars, totalChars, isInternal, isPicture]); + }, [totalAllowedChars, totalChars, isInternal, isPicture, chars]); return ( <div From d4f18d10301ed1fbafca0815f5b10fc46ca42542 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 26 Dec 2025 08:09:16 +0700 Subject: [PATCH 056/340] HOT FIX --- .../src/components/launches/calendar.tsx | 8 ++--- .../launches/generator/generator.tsx | 7 ++-- .../src/components/launches/menu/menu.tsx | 34 +++++++++++++------ .../src/components/launches/new.post.tsx | 7 ++-- apps/frontend/src/components/sets/sets.tsx | 3 +- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 1edb19da..67b176bd 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -548,16 +548,16 @@ export const CalendarColumn: FC<{ if (set === 'exit') return; modal.openModal({ + id: 'add-edit-modal', closeOnClickOutside: false, + removeLayout: true, closeOnEscape: false, withCloseButton: false, - removeLayout: true, - fullScreen: true, askClose: true, + fullScreen: true, classNames: { - modal: 'fixed left-0 top-0 w-full h-full', + modal: 'w-[100%] max-w-[1400px] text-textColor', }, - id: 'add-edit-modal', children: ( <AddEditModal allIntegrations={integrations.map((p) => ({ diff --git a/apps/frontend/src/components/launches/generator/generator.tsx b/apps/frontend/src/components/launches/generator/generator.tsx index 6669a514..67ac5f8c 100644 --- a/apps/frontend/src/components/launches/generator/generator.tsx +++ b/apps/frontend/src/components/launches/generator/generator.tsx @@ -150,15 +150,16 @@ const FirstStep: FC = (props) => { }); setShowStep(''); modal.openModal({ + id: 'add-edit-modal', closeOnClickOutside: false, + removeLayout: true, closeOnEscape: false, withCloseButton: false, - removeLayout: true, askClose: true, + fullScreen: true, classNames: { - modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', + modal: 'w-[100%] max-w-[1400px] text-textColor', }, - id: 'add-edit-modal', children: ( <AddEditModal allIntegrations={integrations.map((p) => ({ diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index bca334d1..b46fbb8a 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -1,7 +1,13 @@ 'use client'; import React, { - FC, MouseEventHandler, useCallback, useLayoutEffect, useMemo, useRef, useState + FC, + MouseEventHandler, + useCallback, + useLayoutEffect, + useMemo, + useRef, + useState, } from 'react'; import { useClickOutside } from '@mantine/hooks'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; @@ -56,7 +62,7 @@ export const Menu: FC<{ const { integrations, reloadCalendarView } = useCalendar(); const toast = useToaster(); const modal = useModals(); - const [show, setShow] = useState<false | {x: number, y: number}>(false); + const [show, setShow] = useState<false | { x: number; y: number }>(false); const menuRef = useRef<HTMLDivElement>(null); const ref = useClickOutside<HTMLDivElement>(() => { setShow(false); @@ -69,13 +75,16 @@ export const Menu: FC<{ const menuRect = menuRef.current.getBoundingClientRect(); const viewportHeight = window.innerHeight; const padding = 10; - + // Check if menu overflows bottom of viewport if (menuRect.bottom > viewportHeight - padding) { - const newY = Math.max(padding, viewportHeight - menuRect.height - padding); + const newY = Math.max( + padding, + viewportHeight - menuRect.height - padding + ); // Only update if position actually changed significantly to avoid infinite loop if (Math.abs(show.y - newY) > 1) { - setShow(prev => prev ? { ...prev, y: newY } : false); + setShow((prev) => (prev ? { ...prev, y: newY } : false)); } } } @@ -88,7 +97,11 @@ export const Menu: FC<{ e.stopPropagation(); // @ts-ignore const boundBox = showRef?.current?.getBoundingClientRect(); - setShow(show ? false : { x: boundBox?.left, y: boundBox?.top + boundBox?.height }); + setShow( + show + ? false + : { x: boundBox?.left, y: boundBox?.top + boundBox?.height } + ); }, [show] ); @@ -184,15 +197,16 @@ export const Menu: FC<{ ).json(); modal.openModal({ + id: 'add-edit-modal', closeOnClickOutside: false, + removeLayout: true, closeOnEscape: false, withCloseButton: false, - removeLayout: true, askClose: true, + fullScreen: true, classNames: { - modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', + modal: 'w-[100%] max-w-[1400px] text-textColor', }, - id: 'add-edit-modal', children: ( <AddEditModal allIntegrations={integrations.map((p) => ({ @@ -329,7 +343,7 @@ export const Menu: FC<{ <div ref={menuRef} onClick={(e) => e.stopPropagation()} - style={{left: show.x, top: show.y}} + style={{ left: show.x, top: show.y }} className={`fixed p-[12px] bg-newBgColorInner shadow-menu flex flex-col gap-[16px] z-[100] rounded-[8px] border border-tableBorder text-nowrap`} > {canDisable && !findIntegration?.refreshNeeded && ( diff --git a/apps/frontend/src/components/launches/new.post.tsx b/apps/frontend/src/components/launches/new.post.tsx index 2bd3b35b..d011290c 100644 --- a/apps/frontend/src/components/launches/new.post.tsx +++ b/apps/frontend/src/components/launches/new.post.tsx @@ -48,15 +48,16 @@ export const NewPost = () => { if (set === 'exit') return; modal.openModal({ + id: 'add-edit-modal', closeOnClickOutside: false, + removeLayout: true, closeOnEscape: false, withCloseButton: false, - removeLayout: true, askClose: true, + fullScreen: true, classNames: { - modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', + modal: 'w-[100%] max-w-[1400px] text-textColor', }, - id: 'add-edit-modal', children: ( <AddEditModal allIntegrations={integrations.map((p) => ({ diff --git a/apps/frontend/src/components/sets/sets.tsx b/apps/frontend/src/components/sets/sets.tsx index a37ae809..1116b55a 100644 --- a/apps/frontend/src/components/sets/sets.tsx +++ b/apps/frontend/src/components/sets/sets.tsx @@ -93,15 +93,16 @@ export const Sets: FC = () => { const addSet = useCallback( (params?: { id?: string; name?: string; content?: string }) => () => { modal.openModal({ + id: 'add-edit-modal', closeOnClickOutside: false, removeLayout: true, closeOnEscape: false, withCloseButton: false, + askClose: true, fullScreen: true, classNames: { modal: 'w-[100%] max-w-[1400px] text-textColor', }, - id: 'add-edit-modal', children: ( <AddEditModal allIntegrations={integrations.map((p: any) => ({ From 2497bd139d4441fd816e08824f5660ceee8f2b8a Mon Sep 17 00:00:00 2001 From: fer-r <fer_r_a@outlook.es> Date: Fri, 26 Dec 2025 03:10:49 +0100 Subject: [PATCH 057/340] feat: add WebP image upload support - Added image/webp to the list of allowed MIME types in the file uploader preprocessor - Added .webp extension validation in ValidUrlExtension validator - Updated error message to include .webp in the list of valid extensions Closes #1055 --- apps/frontend/src/components/media/new.uploader.tsx | 2 +- libraries/helpers/src/utils/valid.url.path.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index be292f37..f5d5e2b5 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -88,7 +88,7 @@ export function useUppyUploader(props: { // Expand generic types to specific ones const expandedTypes = allowedTypes.flatMap((type) => { if (type === 'image/*') { - return ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']; + return ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp']; } if (type === 'video/*') { return ['video/mp4', 'video/mpeg']; diff --git a/libraries/helpers/src/utils/valid.url.path.ts b/libraries/helpers/src/utils/valid.url.path.ts index fbcc0069..a9021ea5 100644 --- a/libraries/helpers/src/utils/valid.url.path.ts +++ b/libraries/helpers/src/utils/valid.url.path.ts @@ -12,6 +12,7 @@ export class ValidUrlExtension implements ValidatorConstraintInterface { !!text?.split?.('?')?.[0].endsWith('.jpg') || !!text?.split?.('?')?.[0].endsWith('.jpeg') || !!text?.split?.('?')?.[0].endsWith('.gif') || + !!text?.split?.('?')?.[0].endsWith('.webp') || !!text?.split?.('?')?.[0].endsWith('.mp4') ); } @@ -19,7 +20,7 @@ export class ValidUrlExtension implements ValidatorConstraintInterface { defaultMessage(args: ValidationArguments) { // here you can provide default error message if validation failed return ( - 'File must have a valid extension: .png, .jpg, .jpeg, .gif, or .mp4' + 'File must have a valid extension: .png, .jpg, .jpeg, .gif, .webp, or .mp4' ); } } From 1da332eec19e3dadffbe5fb10baf317fc2906e26 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 26 Dec 2025 11:21:12 +0700 Subject: [PATCH 058/340] feat: sentry errors --- .../src/components/launches/calendar.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 67b176bd..2f72c92c 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -110,10 +110,10 @@ export const DayView = () => { const options = useMemo(() => { const createdPosts = posts.map((post) => ({ integration: [integrations.find((i) => i.id === post.integration.id)!], - image: post.integration.picture, - identifier: post.integration.providerIdentifier, - id: post.integration.id, - name: post.integration.name, + image: post?.integration?.picture || '', + identifier: post?.integration?.providerIdentifier || '', + id: post?.integration?.id || '', + name: post?.integration?.name || '', time: dayjs .utc(post.publishDate) .diff(dayjs.utc(post.publishDate).startOf('day'), 'minute'), @@ -126,11 +126,11 @@ export const DayView = () => { ...integrations.flatMap((p) => p.time.flatMap((t) => ({ integration: p, - identifier: p.identifier, - name: p.name, - id: p.id, - image: p.picture, - time: t.time, + identifier: p?.identifier, + name: p?.name, + id: p?.id, + image: p?.picture, + time: t?.time, })) ), ], From 70d07249d380a81d3757860dbc6740f650bcb090 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 26 Dec 2025 14:11:24 +0700 Subject: [PATCH 059/340] faet: remove comments --- .../src/components/new-launch/editor.tsx | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index a28284f6..d14e3d63 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -612,13 +612,6 @@ export const Editor: FC<{ id={id} > <div className="relative cursor-text flex flex-1 flex-col"> - {/*{validateChars &&*/} - {/* props.value.length === 0 &&*/} - {/* pictures?.length === 0 && (*/} - {/* <div className="px-3 text-sm bg-red-600 !text-white mb-[4px]">*/} - {/* Your post should have at least one character or one image.*/} - {/* </div>*/} - {/* )}*/} <div {...getRootProps()} className="flex flex-1 flex-col"> <div className={clsx( @@ -761,48 +754,6 @@ export const Editor: FC<{ </div> </div> </div> - {/*<div className="flex">*/} - {/* <div className="bottom-10px end-[25px]">*/} - {/* {(props?.totalChars || 0) > 0 ? (*/} - {/* <div*/} - {/* className={clsx(*/} - {/* 'text-end text-sm mt-1',*/} - {/* valueWithoutHtml.length > props.totalChars && '!text-red-500'*/} - {/* )}*/} - {/* >*/} - {/* {valueWithoutHtml.length}/{props.totalChars}*/} - {/* </div>*/} - {/* ) : (*/} - {/* <div*/} - {/* className={clsx(*/} - {/* 'text-end text-sm mt-1 grid grid-cols-[max-content_max-content] gap-x-[5px]'*/} - {/* )}*/} - {/* >*/} - {/* {selectedIntegration?.map((p) => (*/} - {/* <Fragment key={p.integration.id}>*/} - {/* <div*/} - {/* className={*/} - {/* valueWithoutHtml.length > chars?.[p.integration.id] &&*/} - {/* '!text-red-500'*/} - {/* }*/} - {/* >*/} - {/* {p.integration.name} ({capitalize(p.integration.identifier)}*/} - {/* ):*/} - {/* </div>*/} - {/* <div*/} - {/* className={*/} - {/* valueWithoutHtml.length > chars?.[p.integration.id] &&*/} - {/* '!text-red-500'*/} - {/* }*/} - {/* >*/} - {/* {valueWithoutHtml.length}/{chars?.[p.integration.id]}*/} - {/* </div>*/} - {/* </Fragment>*/} - {/* ))}*/} - {/* </div>*/} - {/* )}*/} - {/* </div>*/} - {/*</div>*/} </div> ); }; From 1c61e76c9f54aa297c7742882e01032204fc9234 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 26 Dec 2025 17:44:10 +0700 Subject: [PATCH 060/340] fix: better block view --- .../src/components/launches/information.component.tsx | 4 ++-- apps/frontend/src/components/new-launch/editor.tsx | 5 +++-- .../new-launch/providers/instagram/instagram.preview.tsx | 0 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx diff --git a/apps/frontend/src/components/launches/information.component.tsx b/apps/frontend/src/components/launches/information.component.tsx index dfcf6d8a..07b50e5e 100644 --- a/apps/frontend/src/components/launches/information.component.tsx +++ b/apps/frontend/src/components/launches/information.component.tsx @@ -116,13 +116,13 @@ export const InformationComponent: FC<{ {isValid ? <Valid /> : <Invalid />} {!isGlobal && ( - <div className="text-[10px] font-[600] flex justify-center items-center"> + <div className={clsx("text-[10px] font-[600] flex justify-center items-center", !isValid && 'text-white')}> {totalChars}/{totalAllowedChars} </div> )} {((isGlobal && selectedIntegrations.length) || !isValid) && ( <svg - className="group-hover:rotate-180" + className={clsx('group-hover:rotate-180', !isValid && 'text-white')} xmlns="http://www.w3.org/2000/svg" width="16" height="16" diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index d14e3d63..9a07161a 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -343,7 +343,7 @@ export const EditorWrapper: FC<{ <div className={clsx( 'relative flex-col gap-[20px] flex-1', - items.length === 1 && 'flex', + (items.length === 1 || !canEdit) && 'flex', !canEdit && !isCreateSet && 'bg-newSettings rounded-[12px]' )} > @@ -399,7 +399,8 @@ export const EditorWrapper: FC<{ 'relative flex flex-col gap-[20px] flex-1 bg-newSettings', index === 0 && 'rounded-t-[12px]', index === items.length - 1 && 'rounded-b-[12px]', - !canEdit && !isCreateSet && 'blur-s' + !canEdit && !isCreateSet && 'blur-s', + !canEdit && index > 0 && 'hidden' )} > <div className="flex gap-[5px] flex-1 w-full"> diff --git a/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx b/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx new file mode 100644 index 00000000..e69de29b From f98ae083abb75b21cb134e846674e399c266b87e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 26 Dec 2025 18:14:32 +0700 Subject: [PATCH 061/340] feat: fix monthly yearly --- .../src/components/billing/first.billing.component.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index 928d2f4a..dde72dea 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -221,7 +221,9 @@ export const FirstBillingComponent = () => { ] } </span>{' '} - {t('billing_per_month', '/ month')} + {period === 'MONTHLY' + ? t('billing_per_month', '/ month') + : t('billing_per_year', '/ year')} </div> </div> ), From 6f889d42c80216a453f04aab8756e1691a59a62e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 26 Dec 2025 19:43:55 +0700 Subject: [PATCH 062/340] feat: responsive payment --- .../components/billing/embedded.billing.tsx | 56 ++++++-- .../src/components/billing/faq.component.tsx | 2 +- .../billing/first.billing.component.tsx | 135 +++++++++--------- .../new-layout/layout.component.tsx | 2 +- .../components/ui/check.icon.component.tsx | 4 +- apps/frontend/tailwind.config.js | 6 + i18n.lock | 2 +- .../translation/locales/ar/translation.json | 2 +- .../translation/locales/bn/translation.json | 2 +- .../translation/locales/de/translation.json | 2 +- .../translation/locales/en/translation.json | 2 +- .../translation/locales/es/translation.json | 2 +- .../translation/locales/fr/translation.json | 2 +- .../translation/locales/he/translation.json | 2 +- .../translation/locales/it/translation.json | 2 +- .../translation/locales/ja/translation.json | 2 +- .../translation/locales/ko/translation.json | 2 +- .../translation/locales/pt/translation.json | 2 +- .../translation/locales/ru/translation.json | 2 +- .../translation/locales/tr/translation.json | 2 +- .../translation/locales/vi/translation.json | 2 +- .../translation/locales/zh/translation.json | 2 +- 22 files changed, 139 insertions(+), 98 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index ef46aa4d..713c57e8 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -53,7 +53,7 @@ export const EmbeddedBilling: FC<{ } return ( - <div className="flex flex-col w-full pt-[24px] billing-form flex-1"> + <div className="flex flex-col w-full pt-[48px] billing-form flex-1 tablet:pt-0"> <CheckoutProvider stripe={stripe} options={{ @@ -124,20 +124,41 @@ const StripeInputs = () => { return ( <> <div> - <h4 className="mb-[8px] text-[24px]"> - {checkout.type === 'loading' ? '' : t('billing_billing_address', 'Billing Address')} + <h4 className="mb-[32px] text-[24px] font-[700]"> + {checkout.type === 'loading' + ? '' + : t('billing_billing_address', 'Billing Address')} </h4> <BillingAddressElement /> </div> <div> - <h4 className="mt-[20px] mb-[8px] text-[24px]"> + <h4 className="mt-[40px] mb-[32px] text-[24px] font-[700]"> {checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')} </h4> <PaymentElement id="payment-element" options={{ layout: 'tabs' }} /> {checkout.type === 'loading' ? null : ( - <div className="mt-[24px] flex gap-[10px]"> - <div>{t('billing_powered_by_stripe', 'Secure payments processed by Stripe')}</div> - <Image src="/stripe.svg" alt="Stripe" width={20} height={20} /> + <div className="mt-[24px] text-[16px] font-[600] flex gap-[4px] items-center"> + <div> + {t( + 'billing_powered_by_stripe', + 'Secure payments processed by' + )} + </div> + <svg + className="mt-[4px]" + xmlns="http://www.w3.org/2000/svg" + width="47" + height="20" + viewBox="0 0 47 20" + fill="none" + > + <path + fill-rule="evenodd" + clip-rule="evenodd" + d="M45.9725 11.0075H39.7596C39.906 12.4952 40.9929 12.9731 42.2262 12.9731C43.4904 12.9731 44.5079 12.6879 45.3481 12.2408V14.8C44.2819 15.4135 43.0618 15.7078 41.8331 15.6479C38.7421 15.6479 36.5683 13.7208 36.5683 9.88208C36.5683 6.65229 38.4106 4.08542 41.4246 4.08542C44.4463 4.08542 46.0187 6.61375 46.0187 9.86667C46.0187 10.175 45.9879 10.8379 45.9725 11.0075ZM41.4092 6.67542C40.6152 6.67542 39.7365 7.23812 39.7365 8.66417H43.0125C43.0125 7.23812 42.1877 6.67542 41.4092 6.67542ZM31.5656 15.6479C30.4556 15.6479 29.7773 15.1854 29.3302 14.8462L29.3148 18.4152L26.139 19.0858V4.29354H29.0373L29.099 5.07979C29.7712 4.44215 30.6622 4.0863 31.5887 4.08542C33.8242 4.08542 35.9208 6.08958 35.9208 9.78958C35.9208 13.821 33.8396 15.6479 31.5656 15.6479ZM30.8333 6.89896C30.101 6.89896 29.6462 7.16104 29.3148 7.52333L29.3302 12.2408C29.6385 12.58 30.0856 12.8421 30.8333 12.8421C32.005 12.8421 32.7912 11.5702 32.7912 9.85896C32.7912 8.20167 31.9896 6.89896 30.8333 6.89896ZM21.7683 4.29354H24.9519V15.4244H21.7683V4.29354ZM21.7683 0.670625L24.9519 0V2.59L21.7683 3.26833V0.678333V0.670625ZM18.4383 7.87792V15.4244H15.2625V4.29354H18.1146L18.2071 5.23396C18.9779 3.86958 20.5735 4.14708 20.9975 4.29354V7.215C20.5967 7.08396 19.2323 6.88354 18.4383 7.87792ZM11.8477 11.5162C11.8477 13.3894 13.8519 12.8112 14.2527 12.6417V15.2317C13.8287 15.4629 13.0656 15.6479 12.025 15.6479C11.5913 15.6606 11.1595 15.5849 10.756 15.4254C10.3525 15.2659 9.98559 15.026 9.6777 14.7203C9.36981 14.4146 9.12733 14.0494 8.96502 13.647C8.8027 13.2446 8.72395 12.8134 8.73354 12.3796L8.74125 2.22771L11.84 1.56479V4.29354H14.2604V7.01458H11.8477V11.524V11.5162ZM8.06292 12.0558C8.06292 14.3452 6.28229 15.6479 3.64604 15.6479C2.46306 15.647 1.29288 15.403 0.208125 14.931V11.9017C1.27188 12.4798 2.59771 12.9115 3.64604 12.9115C4.35521 12.9115 4.82542 12.7265 4.82542 12.1406C4.82542 10.6144 0 11.1848 0 7.66979C0 5.42667 1.7575 4.08542 4.33208 4.08542C5.38042 4.08542 6.42875 4.23958 7.48479 4.66354V7.65438C6.50888 7.14076 5.42694 6.86103 4.32438 6.83729C3.66146 6.83729 3.21438 7.03 3.21438 7.53104C3.21438 8.95708 8.06292 8.27875 8.06292 12.0635V12.0558Z" + fill="#635BFF" + /> + </svg> </div> )} </div> @@ -153,29 +174,38 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { } return ( - <div className="animate-fadeIn h-[92px] fixed bottom-0 w-full px-[12px] pb-[12px] left-0 bg-newBgColor z-[100]"> - <div className="w-full h-full border-t border-newColColor bg-newBgColorInner px-[80px] flex gap-[32px] justify-end items-center font-[400] text-[14px] text-[#A3A3A3]"> + <div className="animate-fadeIn h-[92px] mobile:h-auto fixed bottom-0 w-full px-[12px] pb-[12px] left-0 bg-newBgColor z-[100]"> + <div className="w-full h-full border-t border-newColColor bg-newBgColorInner px-[80px] tablet:px-[33px] mobile:!px-[16px] flex mobile:flex-col gap-[32px] mobile:gap-[16px] justify-end items-center font-[400] text-[14px] text-[#A3A3A3] mobile:py-[16px]"> {checkout.checkout.recurring?.trial?.trialEnd ? ( <div> {t('billing_your_7_day_trial_is', 'Your 7-day trial is')}{' '} - <span className="text-textColor font-[600]">{t('billing_100_percent_free', '100% free')}</span> {t('billing_ending', 'ending')}{' '} + <span className="text-textColor font-[600]"> + {t('billing_100_percent_free', '100% free')} + </span>{' '} + {t('billing_ending', 'ending')}{' '} + <br className="hidden mobile:block" /> <span className="text-textColor font-[600]"> {dayjs( checkout.checkout.recurring?.trial?.trialEnd * 1000 ).format('MMMM D, YYYY')}{' '} —{' '} </span> - <span className="text-textColor font-[600]">{t('billing_cancel_anytime_short', 'Cancel anytime.')}</span> + <span className="text-textColor font-[600]"> + {t('billing_cancel_anytime_short', 'Cancel anytime.')} + </span> </div> ) : null} <div> <Button - className="h-[42px] rounded-[10px]" + className="h-[42px] rounded-[10px] mobile:w-full" type="submit" loading={loading} > {checkout.checkout.recurring?.trial?.trialEnd - ? t('billing_pay_0_start_trial', 'Pay $0 Today - Start your free trial!') + ? t( + 'billing_pay_0_start_trial', + 'Pay $0 Today - Start your free trial!' + ) : t('billing_pay_now', 'Pay Now')} </Button> </div> diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index c0150181..b5eac193 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -134,7 +134,7 @@ export const FAQComponent: FC = () => { const list = useFaqList(); return ( <div> - <h3 className="text-[24px] mt-[81px] mb-[40px]"> + <h3 className="text-[24px] mt-[48px] mb-[40px] tablet:mt-[80px]"> {t('frequently_asked_questions', 'Frequently Asked Questions')} </h3> <div className="gap-[24px] flex-col flex select-none"> diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index dde72dea..a1bc4913 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -86,9 +86,57 @@ export const FirstBillingComponent = () => { [] ); + const JoinOver = () => { + return ( + <> + <div className="text-[46px] font-[600] leading-[110%] tablet:text-[36px] mobile:!text-[30px] whitespace-pre-line text-balance"> + {t('billing_join_over', 'Join Over')}{' '} + <span className="text-[#FC69FF]"> + {t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')} + </span>{' '} + {t('billing_who_use', 'who use')}{' '} + {t( + 'billing_postiz_grow_social', + 'Postiz To Grow Their Social Presence' + )} + </div> + + {!!user?.allowTrial && ( + <div className="flex mt-[32px] mb-[10px] gap-[15px] tablet:mt-[32px] tablet:mb-[32px] text-[16px] font-[500] mobile:flex-col"> + <div className="flex gap-[8px]"> + <div> + <CheckIconComponent /> + </div> + <div>{t('billing_no_risk_trial', '100% No-Risk Free Trial')}</div> + </div> + <div className="flex-1 flex gap-[8px] justify-center mobile:justify-start"> + <div> + <CheckIconComponent /> + </div> + <div> + {t( + 'billing_pay_nothing_7_days', + 'Pay NOTHING for the first 7-days' + )} + </div> + </div> + <div className="flex gap-[8px]"> + <div> + <CheckIconComponent /> + </div> + <div> + {t('billing_cancel_anytime', 'Cancel anytime, hassle-free')} + </div> + </div> + </div> + )} + </> + ); + }; + return ( - <div className="blurMe flex flex-1 flex-col bg-newBgColorInner pb-[60px]"> - <div className="h-[92px] px-[80px] py-[20px] flex border-b border-newColColor"> + <div className="blurMe flex flex-1 flex-col bg-newBgColorInner pb-[60px] mobile:pb-[100px]"> + <div className="h-[92px] px-[80px] tablet:px-[32px] mobile:!px-[16px] py-[20px] flex border-b border-newColColor"> <div className="flex-1 flex items-center text-textColor"> <LogoTextComponent /> </div> @@ -106,53 +154,9 @@ export const FirstBillingComponent = () => { </div> </div> </div> - <div className="flex px-[80px] flex-1"> - <div className="flex-1 py-[40px] flex flex-col pe-[40px]"> - <div className="text-[36px] font-[600] leading-[110%] whitespace-pre-line"> - {t('billing_join_over', 'Join Over')}{' '} - <span className="text-[#FC69FF]"> - {t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')} - </span>{' '} - {t('billing_who_use', 'who use')} - {'\n'} - {t( - 'billing_postiz_grow_social', - 'Postiz To Grow Their Social Presence' - )} - </div> - - {!!user?.allowTrial && ( - <div className="flex mt-[34px] mb-[10px]"> - <div className="flex gap-[8px]"> - <div> - <CheckIconComponent /> - </div> - <div> - {t('billing_no_risk_trial', '100% No-Risk Free Trial')} - </div> - </div> - <div className="flex-1 flex gap-[8px] justify-center"> - <div> - <CheckIconComponent /> - </div> - <div> - {t( - 'billing_pay_nothing_7_days', - 'Pay NOTHING for the first 7-days' - )} - </div> - </div> - <div className="flex gap-[8px]"> - <div> - <CheckIconComponent /> - </div> - <div> - {t('billing_cancel_anytime', 'Cancel anytime, hassle-free')} - </div> - </div> - </div> - )} - + <div className="flex px-[80px] tablet:px-[32px] mobile:!px-[16px] flex-1 flex-row tablet:flex-none tablet:flex-col-reverse"> + <div className="flex-1 py-[40px] tablet:pt-[80px] flex flex-col pe-[40px] tablet:pe-0"> + <div className="block tablet:hidden"><JoinOver /></div> {!isLoading && data && stripe ? ( <> <EmbeddedBilling stripe={stripe} secret={data.client_secret} /> @@ -162,16 +166,17 @@ export const FirstBillingComponent = () => { <LoadingComponent /> )} </div> - <div className="flex flex-col ps-[40px] border-l border-newColColor py-[40px]"> + <div className="flex flex-col ps-[40px] tablet:!ps-[0] border-l border-newColColor py-[40px] mobile:!pt-[24px] tablet:border-none tablet:pb-0"> <div className="top-[20px] sticky"> - <div className="flex mb-[24px]"> + <div className="hidden tablet:block"><JoinOver /></div> + <div className="flex mb-[24px] mobile:flex-col"> <div className="flex-1 text-[24px] font-[700]"> {t('billing_choose_plan', 'Choose a Plan')} </div> - <div className="h-[44px] px-[6px] flex items-center justify-center gap-[12px] border border-newColColor rounded-[12px] select-none"> + <div className="h-[44px] px-[6px] mobile:px-0 flex items-center justify-center mobile:justify-start gap-[12px] border border-newColColor rounded-[12px] select-none"> <div className={clsx( - 'h-[32px] rounded-[6px] text-[16px] px-[12px] flex justify-center items-center', + 'h-[32px] mobile:flex-1 rounded-[6px] text-[16px] px-[12px] flex justify-center items-center', period === 'MONTHLY' ? 'bg-boxFocused text-textItemFocused' : 'cursor-pointer' @@ -182,7 +187,7 @@ export const FirstBillingComponent = () => { </div> <div className={clsx( - 'gap-[10px] h-[32px] rounded-[6px] text-[16px] px-[12px] flex justify-center items-center', + 'gap-[10px] h-[32px] mobile:flex-1 rounded-[6px] text-[16px] px-[12px] flex justify-center items-center', period === 'YEARLY' ? 'bg-boxFocused text-textItemFocused' : 'cursor-pointer' @@ -190,30 +195,30 @@ export const FirstBillingComponent = () => { onClick={() => setPeriod('YEARLY')} > <div>{t('billing_yearly', 'Yearly')}</div> - <div className="bg-[#AA0FA4] text-[white] px-[8px] rounded-[4px]"> + <div className="bg-[#AA0FA4] text-[white] px-[8px] rounded-[4px] mobile:hidden"> {t('billing_20_percent_off', '20% Off')} </div> </div> </div> </div> - <div className="grid grid-cols-2 gap-[8px]"> + <div className="grid grid-cols-2 gap-[8px] mobile:!grid-cols-2 tablet:grid-cols-4"> {price.map( ([key, value]) => ( <div onClick={() => setTier(key)} key={key} className={clsx( - 'cursor-pointer select-none w-[266px] h-[138px] p-[24px] rounded-[20px] flex flex-col', + 'cursor-pointer select-none w-[266px] h-[138px] tablet:w-full tablet:h-[124px] p-[24px] tablet:p-[15px] rounded-[20px] flex flex-col', key === tier - ? 'bg-[linear-gradient(138deg,#4C27E1_9.56%,#2F007B_76.16%)] text-white' - : 'border border-newColColor' + ? 'border-[1.5px] border-[#618DFF]' + : 'border-[1.5px] border-newColColor' )} > - <div className="text-[20px] font-[500]"> + <div className="text-[20px] mobile:text-[18px] font-[500]"> {capitalize(key)} </div> - <div className="text-[24px] font-[400]"> - <span className="text-[44px] font-[600]"> + <div className="text-[24px] mobile:text-[18px] font-[400]"> + <span className="text-[44px] mobile:text-[30px] font-[600]"> $ { value[ @@ -230,7 +235,7 @@ export const FirstBillingComponent = () => { [] )} </div> - <div className="flex flex-col mt-[54px] gap-[24px]"> + <div className="flex flex-col mt-[54px] gap-[24px] tablet:mt-[40px]"> <div className="text-[24px] font-[700]"> {t('billing_features', 'Features')} </div> @@ -321,7 +326,7 @@ export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => { }; return ( - <div className="grid grid-cols-2 gap-y-[8px] gap-x-[32px]"> + <div className="grid grid-cols-2 mobile:grid-cols-1 gap-y-[8px] gap-x-[32px]"> {features.map((feature) => ( <div key={feature.key} className="flex items-center gap-[8px]"> <div> @@ -334,7 +339,7 @@ export const BillingFeatures: FC<{ tier: string }> = ({ tier }) => { > <path d="M11.825 0H4.84167C1.80833 0 0 1.80833 0 4.84167V11.8167C0 14.8583 1.80833 16.6667 4.84167 16.6667H11.8167C14.85 16.6667 16.6583 14.8583 16.6583 11.825V4.84167C16.6667 1.80833 14.8583 0 11.825 0ZM12.3167 6.41667L7.59167 11.1417C7.475 11.2583 7.31667 11.325 7.15 11.325C6.98333 11.325 6.825 11.2583 6.70833 11.1417L4.35 8.78333C4.10833 8.54167 4.10833 8.14167 4.35 7.9C4.59167 7.65833 4.99167 7.65833 5.23333 7.9L7.15 9.81667L11.4333 5.53333C11.675 5.29167 12.075 5.29167 12.3167 5.53333C12.5583 5.775 12.5583 6.16667 12.3167 6.41667Z" - fill="white" + fill="currentColor" /> </svg> </div> diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index dcfc7d37..8272e535 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -41,7 +41,7 @@ import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/se import { FirstBillingComponent } from '@gitroom/frontend/components/billing/first.billing.component'; const jakartaSans = Plus_Jakarta_Sans({ - weight: ['600', '500'], + weight: ['600', '500', '700'], style: ['normal', 'italic'], subsets: ['latin'], }); diff --git a/apps/frontend/src/components/ui/check.icon.component.tsx b/apps/frontend/src/components/ui/check.icon.component.tsx index 069d0bc4..fe259a03 100644 --- a/apps/frontend/src/components/ui/check.icon.component.tsx +++ b/apps/frontend/src/components/ui/check.icon.component.tsx @@ -9,7 +9,7 @@ export const CheckIconComponent = () => { > <path d="M13.2742 1.76687C5.72114 -3.89752 -3.90021 12.5553 2.93125 18.9592C4.68315 20.6017 6.79417 20.7975 9.0813 21.1959C10.475 21.4379 12.6902 21.8312 14.113 21.4755C25.5332 18.6198 20.3442 1.20854 10.339 1.20854" - stroke="#28ff12" + stroke="#00FF00" strokeWidth="1.2" strokeMiterlimit="10" strokeLinecap="round" @@ -17,7 +17,7 @@ export const CheckIconComponent = () => { /> <path d="M6.00586 11.2722C7.2516 12.5171 8.10319 14.104 9.22067 15.4652C9.75676 16.1175 10.0671 15.3361 10.1988 15.0462C11.4308 12.3359 14.0548 11.1902 16.0692 9.17578" - stroke="#28ff12" + stroke="#00FF00" strokeWidth="1.2" strokeMiterlimit="10" strokeLinecap="round" diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index 0fd050db..e4baea66 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -228,6 +228,12 @@ module.exports = { }, }), screens: { + mobile: { + raw: '(max-width: 1025px)', + }, + tablet: { + raw: '(max-width: 1300px)', + }, iconBreak: { raw: '(max-width: 1560px)', }, diff --git a/i18n.lock b/i18n.lock index e4f03d7f..5eb7039d 100644 --- a/i18n.lock +++ b/i18n.lock @@ -532,7 +532,7 @@ checksums: billing_ai_videos_per_month: c786199d8dc9bded54fab8f92c350b19 billing_billing_address: 48980a775bfc7292b0c4f9b74b4d352e billing_payment: 95142d3fd8b6a6f551aba771842e9c11 - billing_powered_by_stripe: c7c2723003ae04f17ae0c5bee195aeb6 + billing_powered_by_stripe: f2767a22c2cd93d4e8fe5c5d6ee05ad1 billing_your_7_day_trial_is: 4b59fb559f5fd520668974e909e3479c billing_100_percent_free: 6616fd6ee152264c06dd2537f6347e66 billing_ending: f66133a14aa7d86ea2453df97de12cd5 diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index a060b06d..ff17cde6 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "فيديوهات بالذكاء الاصطناعي شهريًا", "billing_billing_address": "عنوان الفاتورة", "billing_payment": "الدفع", - "billing_powered_by_stripe": "مدفوعات آمنة تتم معالجتها عبر سترايب", + "billing_powered_by_stripe": "مدفوعات آمنة تتم معالجتها بواسطة", "billing_your_7_day_trial_is": "فترة التجربة المجانية لمدة 7 أيام الخاصة بك هي", "billing_100_percent_free": "مجانية 100%", "billing_ending": "تنتهي", diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index 78430231..5bde1d21 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "প্রতি মাসে এআই ভিডিও", "billing_billing_address": "বিলিং ঠিকানা", "billing_payment": "পেমেন্ট", - "billing_powered_by_stripe": "নিরাপদ পেমেন্ট Stripe দ্বারা প্রক্রিয়াজাত করা হয়", + "billing_powered_by_stripe": "নিরাপদ পেমেন্ট প্রক্রিয়াকরণ করেছে", "billing_your_7_day_trial_is": "আপনার ৭ দিনের ট্রায়াল", "billing_100_percent_free": "১০০% ফ্রি", "billing_ending": "শেষ হচ্ছে", diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index 162a6e38..ba42367d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "KI-Videos pro Monat", "billing_billing_address": "Rechnungsadresse", "billing_payment": "Zahlung", - "billing_powered_by_stripe": "Sichere Zahlungen, abgewickelt von Stripe", + "billing_powered_by_stripe": "Sichere Zahlungen abgewickelt von", "billing_your_7_day_trial_is": "Ihre 7-tägige Testphase ist", "billing_100_percent_free": "100% kostenlos", "billing_ending": "endet", diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index c6aedaf5..f7822cac 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "AI Videos per month", "billing_billing_address": "Billing Address", "billing_payment": "Payment", - "billing_powered_by_stripe": "Secure payments processed by Stripe", + "billing_powered_by_stripe": "Secure payments processed by", "billing_your_7_day_trial_is": "Your 7-day trial is", "billing_100_percent_free": "100% free", "billing_ending": "ending", diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index e07584ad..88a130b6 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Videos de IA por mes", "billing_billing_address": "Dirección de facturación", "billing_payment": "Pago", - "billing_powered_by_stripe": "Pagos seguros procesados por Stripe", + "billing_powered_by_stripe": "Pagos seguros procesados por", "billing_your_7_day_trial_is": "Tu prueba de 7 días es", "billing_100_percent_free": "100% gratis", "billing_ending": "finaliza", diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index ca1d5e3f..80d40742 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Vidéos IA par mois", "billing_billing_address": "Adresse de facturation", "billing_payment": "Paiement", - "billing_powered_by_stripe": "Paiements sécurisés traités par Stripe", + "billing_powered_by_stripe": "Paiements sécurisés traités par", "billing_your_7_day_trial_is": "Votre essai de 7 jours est", "billing_100_percent_free": "100% gratuit", "billing_ending": "se termine", diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index 2d59ee8a..c6728b11 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "סרטוני בינה מלאכותית בחודש", "billing_billing_address": "כתובת לחיוב", "billing_payment": "תשלום", - "billing_powered_by_stripe": "תשלומים מאובטחים מעובדים על ידי Stripe", + "billing_powered_by_stripe": "תשלומים מאובטחים מעובדים על ידי", "billing_your_7_day_trial_is": "תקופת הניסיון שלך ל-7 ימים היא", "billing_100_percent_free": "100% חינם", "billing_ending": "מסתיימת", diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index 4e71853d..d56bd2be 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Video AI al mese", "billing_billing_address": "Indirizzo di fatturazione", "billing_payment": "Pagamento", - "billing_powered_by_stripe": "Pagamenti sicuri elaborati da Stripe", + "billing_powered_by_stripe": "Pagamenti sicuri elaborati da", "billing_your_7_day_trial_is": "La tua prova di 7 giorni è", "billing_100_percent_free": "100% gratuita", "billing_ending": "in scadenza", diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index a19dc3a0..1a66090a 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "月あたりのAI動画数", "billing_billing_address": "請求先住所", "billing_payment": "支払い", - "billing_powered_by_stripe": "Stripeによって処理される安全な支払い", + "billing_powered_by_stripe": "安全な支払いはによって処理されています", "billing_your_7_day_trial_is": "7日間の無料トライアルは", "billing_100_percent_free": "完全に無料", "billing_ending": "終了します", diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index 63572a0f..9fb6557d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "월별 AI 비디오", "billing_billing_address": "청구 주소", "billing_payment": "결제", - "billing_powered_by_stripe": "Stripe를 통한 안전한 결제가 처리됩니다", + "billing_powered_by_stripe": "안전한 결제는 Stripe에서 처리됩니다", "billing_your_7_day_trial_is": "7일 무료 체험이", "billing_100_percent_free": "100% 무료", "billing_ending": "종료됩니다", diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index 7d298126..c0807c34 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Vídeos de IA por mês", "billing_billing_address": "Endereço de cobrança", "billing_payment": "Pagamento", - "billing_powered_by_stripe": "Pagamentos seguros processados pela Stripe", + "billing_powered_by_stripe": "Pagamentos seguros processados por", "billing_your_7_day_trial_is": "Seu teste de 7 dias é", "billing_100_percent_free": "100% gratuito", "billing_ending": "terminando", diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index ccbda6bc..82343202 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "ИИ-видео в месяц", "billing_billing_address": "Платёжный адрес", "billing_payment": "Платёж", - "billing_powered_by_stripe": "Безопасные платежи обрабатываются через Stripe", + "billing_powered_by_stripe": "Безопасные платежи обрабатываются с помощью", "billing_your_7_day_trial_is": "Ваш 7-дневный пробный период", "billing_100_percent_free": "100% бесплатно", "billing_ending": "заканчивается", diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index 95fb3617..2c47213e 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Aylık Yapay Zeka Videoları", "billing_billing_address": "Fatura Adresi", "billing_payment": "Ödeme", - "billing_powered_by_stripe": "Güvenli ödemeler Stripe tarafından işlenir", + "billing_powered_by_stripe": "Güvenli ödemeler tarafından işlenir", "billing_your_7_day_trial_is": "7 günlük deneme süreniz", "billing_100_percent_free": "%100 ücretsiz", "billing_ending": "sona eriyor", diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 74088c49..02005756 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "Video AI mỗi tháng", "billing_billing_address": "Địa chỉ thanh toán", "billing_payment": "Thanh toán", - "billing_powered_by_stripe": "Thanh toán an toàn được xử lý bởi Stripe", + "billing_powered_by_stripe": "Thanh toán an toàn được xử lý bởi", "billing_your_7_day_trial_is": "Dùng thử 7 ngày của bạn là", "billing_100_percent_free": "Miễn phí 100%", "billing_ending": "kết thúc", diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index 5f22272a..805ec576 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -528,7 +528,7 @@ "billing_ai_videos_per_month": "每月AI视频数", "billing_billing_address": "账单地址", "billing_payment": "付款", - "billing_powered_by_stripe": "安全支付由Stripe处理", + "billing_powered_by_stripe": "安全支付由 Stripe 处理", "billing_your_7_day_trial_is": "您的7天试用期", "billing_100_percent_free": "100%免费", "billing_ending": "即将结束", From 90da9d4aff753aebb0ffe7dd0ca4c809ed9c9f70 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 27 Dec 2025 10:24:11 +0700 Subject: [PATCH 063/340] fix: pagination --- .../src/components/media/media.component.tsx | 103 +++++++++++++----- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 8c810dbc..52a2cfa5 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -20,9 +20,7 @@ import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.v import EventEmitter from 'events'; import clsx from 'clsx'; import { VideoFrame } from '@gitroom/react/helpers/video.frame'; -import { - useUppyUploader, -} from '@gitroom/frontend/components/media/new.uploader'; +import { useUppyUploader } from '@gitroom/frontend/components/media/new.uploader'; import dynamic from 'next/dynamic'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { AiImage } from '@gitroom/frontend/components/launches/ai.image'; @@ -31,9 +29,7 @@ import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { ThirdPartyMedia } from '@gitroom/frontend/components/third-parties/third-party.media'; import { ReactSortable } from 'react-sortablejs'; -import { - MediaComponentInner, -} from '@gitroom/frontend/components/launches/helpers/media.settings.component'; +import { MediaComponentInner } from '@gitroom/frontend/components/launches/helpers/media.settings.component'; import { AiVideo } from '@gitroom/frontend/components/launches/ai.video'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; import { Dashboard } from '@uppy/react'; @@ -62,14 +58,59 @@ export const Pagination: FC<{ const t = useT(); const { current, totalPages, setPage } = props; - const totalPagesList = useMemo(() => { - return Array.from( - { - length: totalPages, - }, - (_, i) => i - ); - }, [totalPages]); + + const paginationItems = useMemo(() => { + // Convert to 1-based for algorithm (current is 0-based) + const c = current + 1; + const m = totalPages; + + // If total pages <= 10, show all pages + if (m <= 10) { + return Array.from({ length: m }, (_, i) => i + 1); + } + + const delta = 3; + const left = c - delta; + const right = c + delta + 1; + const range: number[] = []; + const rangeWithDots: (number | '...')[] = []; + let l: number | undefined; + + // Build the range of pages to show + for (let i = 1; i <= m; i++) { + if (i === 1 || i === m || (i >= left && i < right)) { + range.push(i); + } + } + + // Add dots where there are gaps + for (const i of range) { + if (l !== undefined) { + if (i - l === 2) { + rangeWithDots.push(l + 1); + } else if (i - l !== 1) { + rangeWithDots.push('...'); + } + } + rangeWithDots.push(i); + l = i; + } + + // Limit to maximum 10 items by trimming pages near edges if needed + while (rangeWithDots.length > 10) { + const currentIndex = rangeWithDots.findIndex((item) => item === c); + if (currentIndex !== -1 && currentIndex > rangeWithDots.length / 2) { + // Current is in second half, remove one item from start side + rangeWithDots.splice(2, 1); + } else { + // Current is in first half, remove one item from end side + rangeWithDots.splice(-3, 1); + } + } + + return rangeWithDots; + }, [current, totalPages]); + return ( <ul className="flex flex-row items-center gap-1 justify-center mt-[15px]"> <li className={clsx(current === 0 && 'opacity-20 pointer-events-none')}> @@ -82,20 +123,26 @@ export const Pagination: FC<{ <span>{t('previous', 'Previous')}</span> </div> </li> - {totalPagesList.map((page) => ( - <li key={page} className=""> - <div - aria-current="page" - onClick={() => setPage(page)} - className={clsx( - 'cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border hover:bg-forth h-10 w-10 hover:text-white border-newBorder', - current === page - ? 'bg-forth !text-white' - : 'text-textColor hover:text-white' - )} - > - {page + 1} - </div> + {paginationItems.map((item, index) => ( + <li key={index}> + {item === '...' ? ( + <span className="inline-flex items-center justify-center h-10 w-10 text-textColor select-none"> + ... + </span> + ) : ( + <div + aria-current="page" + onClick={() => setPage(item - 1)} + className={clsx( + 'cursor-pointer inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 border hover:bg-forth h-10 w-10 hover:text-white border-newBorder', + current === item - 1 + ? 'bg-forth !text-white' + : 'text-textColor hover:text-white' + )} + > + {item} + </div> + )} </li> ))} <li From 0d134c0a6f6228d514a624d8f688fb1f4d12a652 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 27 Dec 2025 23:20:07 +0700 Subject: [PATCH 064/340] feat: editor bug fixes, some previews --- apps/frontend/public/no-video-youtube.png | Bin 0 -> 78014 bytes apps/frontend/src/app/colors.scss | 37 +- .../launches/general.preview.component.tsx | 2 +- .../helpers/pick.platform.component.tsx | 2 +- .../launches/launches.component.tsx | 4 +- .../src/components/media/media.component.tsx | 131 +++-- .../src/components/new-launch/editor.tsx | 75 +-- .../components/new-launch/manage.modal.tsx | 45 +- .../new-launch/picks.socials.component.tsx | 2 +- .../providers/facebook/facebook.preview.tsx | 338 ++++++++++++ .../providers/facebook/facebook.provider.tsx | 3 +- .../providers/high.order.provider.tsx | 15 +- .../instagram/instagram.collaborators.tsx | 4 +- .../providers/instagram/instagram.preview.tsx | 220 ++++++++ .../providers/linkedin/linkedin.preview.tsx | 513 ++++++++++++++++++ .../providers/linkedin/linkedin.provider.tsx | 3 +- .../providers/pinterest/pinterest.preview.tsx | 160 ++++++ .../pinterest/pinterest.provider.tsx | 8 +- .../providers/show.all.providers.tsx | 4 +- .../providers/tiktok/tiktok.preview.tsx | 183 +++++++ .../providers/tiktok/tiktok.provider.tsx | 76 +-- .../providers/youtube/youtube.preview.tsx | 149 +++++ .../providers/youtube/youtube.provider.tsx | 7 +- .../src/components/new-launch/store.ts | 7 + .../third-parties/slider.component.tsx | 71 +++ .../src/components/ui/is.scroll.hook.tsx | 21 + apps/frontend/tailwind.config.js | 14 + .../src/helpers/video.or.image.tsx | 9 +- 28 files changed, 1909 insertions(+), 194 deletions(-) create mode 100644 apps/frontend/public/no-video-youtube.png create mode 100644 apps/frontend/src/components/new-launch/providers/facebook/facebook.preview.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/linkedin/linkedin.preview.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/pinterest/pinterest.preview.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/tiktok/tiktok.preview.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/youtube/youtube.preview.tsx create mode 100644 apps/frontend/src/components/third-parties/slider.component.tsx create mode 100644 apps/frontend/src/components/ui/is.scroll.hook.tsx diff --git a/apps/frontend/public/no-video-youtube.png b/apps/frontend/public/no-video-youtube.png new file mode 100644 index 0000000000000000000000000000000000000000..5aa7597dd466aa162ecc23e08a1ab2eb4b44670b GIT binary patch literal 78014 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≥;Q+$6v+<E&{<xu!<DcRHFhjh-K!DD-vZyKck z*gpOCdszPbPcZ*#h4tZH@^>d`r8-Gbm1$M$n3=!=#{$_xNCH)2Fs@TOQ(nNB*kz{+ zPlq>KDZX%Ip#%3~B!j@cNBe~a&oYG1PXAgJFctvrwvF-Gb7_DBx6ee`tLNlP-Y=-+ zO+3m?0&8%5`LfbQq(Dc+ph<?1L*1NqCtfDlJw}Z~SD7zo=&Mo1UzREpQmjf}_vXFQ z;?QML?w$2P097Vq7(&y$y6!A{c7YwgM9Q2(<g=~6HLl-h0+R>4jopvOB(0lZ&tb&H zEi?p{ozW4dYKC8DZm7amvoc_9@)u@d+4G}@f|2M5g&e5i2210^UT)W`>y42J8(*Z^ zshX0MJT}1FcVwL*>Ku@TL!4MEEejls7TA7{YhWXzT2(<uMznCx1YRCdo{QQLJh+y( zB#Oo)+!>8s@Zkxi-yLG_Sq_yw_J%f=t7{<zOZc#8R{~7?c}UQ)N59r;WMO?St2Qr^ z`+ud|+}aqp7lHhcI6>Nj1FXhLueGoj|3Bp2$)WInr1%#}lrn#59v%T+w#T)_@*n^C zJGg!NOcwiztIw^crYlb{F_Rk~VZNF`>*na=9rg9m;IBlUQx><T(eZC-<x3e(RVXd; zu5D0Uz=fgcH9?oL@Hco~7PZxiu}oJ`?`~jv_Efxjj@|Q@UljcZYJPV$zZqb1<z=zT z@H%!y-w}XRmCpV-_m%S!2c}H>>HK%uGPaG)=RE}kGACEgsCYN+T~#(>F!^P)`O6ez zt>a<d;--;%WcK{PK*q{X_wL@X#eWI*5Qa`}<loGJ(Gmrwh@ES|N_N$hQa5;d?R>j8 zwfm!#Aq6wml+w5M`AM<pF%yHJw|~~Lx%V2z7Ws41OHX@BrxZrAbV?pGqju#3{f@90 z^TJ?tQjS7flc0UaS;-3rsPyiNhKJZ(Sn6&`@%*}}wt*d8E9O;H56$?x#dq$ittWG_ zs--t-O!9MkT7<u`7^xYoP*^SqddRzzL*c&*Vd(0%#o+%x|9`+2&l{BgyQVO2r0(|P z%Neepw}<`yFL3|OH*ov>O46`M2z*`94Dcx{q}6QQ)#JeGUGW!V=JY(z-xo8vjJeXJ z$EfB}dO1;7cCxrOrN7MV#J6$YJpuUgIaH3-b0Zu({|C!G&d@euMwdl05zRNow3?4` zbLd6_^kb@_WV#dkDc$=M?-Wbg4FkH*^A@RkhSa1_x>^k;J({<~k5n%e9NsaU!{q)R zUdXAB&vBfQmAn=MgtGS{R2^l?$H*|cdEZb2l--_Mpxb0<uEsf0BAt87b=)H%SKYg( z@g9pdR+I(JV3IQW^4|iuF-Gd<`x4m}U>P(>;l;|8WIhamWoFC*QOosmX3c6|DR}+= z`m<-Nyh-F*>>jR7q0dafy}6dTU`~-3dVtpcS<F(KHaDYf4hD3{t=iGR+x}*eM{3pz zhJqHzTu)M|Q<(lm9U4XvCl0T07f2><Hphs0BXnxtuc1N32f7x0WO69{pDgWRZBYL8 z|KT}2{KtQUo4Y$$zWE;NcPE%{Z{Yfd;PKDjHKY0&TwgWs|J#3r>n9Cjo{c>W%G?LZ ze?CbSJ<s(8>py=Y_3V{xUo<Xe3nVLU^uB(wrlEbw?8}NESp))2uG$_{EPSq#cJ~zO z7f%7%yT5uO$C4vZ|H6NGLgHnREmXCJ`y5;NtCAKle>c}lcd5fZ?yZ2a^o_7pCWUnY zB1=K56sB{C@^nS5RrfNy?x;f#1Nb~AxPTy#0ZK+q&1L@VrI>+kP;AEUmly^|CKH0( zebCmzx}_jhb`CNkpBx{(O|M@Q2^`PlJFN+!Khs9rX_dZi&=z~r(VkCZTgEjb^h$x= zk<ZbAGeZXZc=(SMG__F-G$sg^R38Dg@RZL%AUvr1s5ftItnF6t(^bvj)3m%b(idCM zz&ICGQz>DBUs^n>rPo99_rhLwLmS~vmHaBVV$zx!>Dl+Mh?_}nMEUp0@1Wu<WlS-J z(NG=)buZ5lsbN4+=To3LDKa$6sJOa<xvg*Ob=4}PP4Q3Fvsyl2C*&iM_pb0?7eo9M zgueztplfk^ym>B+_HuL51?aj#<@b+p{h~qT-@Sy#|Mow?%^$yPaYoL^UgQfTOO51W z&-N8ksBl7eE43sqP=<4>A(NU~M6gLa<83+B$x>sq4tv#wp}}~yEd=wEHl_yQ^J_s6 zIHnKK3j%$>Uo06}(M6XHnY_5u`Ib9EyPt)Egrqo$qIN;B^-VD;Qj*j2`Z44<lPR?A z+0%Cd?HuwNwS2KETq~bz1T<%)2OHjv2Y~QUvr^040%u6)*34RE&YA9MT6|2Vc*zKk z^1qPP%%>+Oi>}*?bJMCTYEz`5?2Rq&Muo@Y$>v;;0Lh-yt8++rkW}@s6gMPvEG1<p zIJ%HuU#vv{(5qhhdyL$9xXjW7OSJxj@ae};%g-w^Z`#kBb39t;ysacpZY53F*AH?e zs&1~|6nFnuUl`C7#RSEGGDDUM45$`By*My6Ai9Aq27o^|$;R#0259N=h75?3=b9fk z&79+gMeOx2W~2dqzz)fWE$?07BYyhP7_N_p^%kz$;=rwT=bI~7A0O1S__}$}|KI-^ z9=`o&xcPVg0`OONu)g{huD*Q<^mtOz%K8W%J(F_j!s5dy_~8Xkz*3#uGWNK1lmUgw zJQG`>+Hp8)%I#O{O<VYG?w~%oX`cPF_IHE$u5YBJt$%0b6!gn2UO!ln$v;zgvGI0} z$=lzfmxz~ien?I;gQRT3sEAnncpyqK`aG_!;d^EXC(qusI60pKVBlbMrQKCwPW#J+ zg>d(}sDly&!EM-X7Zqq7ZLpz9L<BHBciBVirJD)!T+}GmB!(q#XWPOE*y|qH$1#qP znd1th8!plQp4HE%lfKW;@w#e}Bq4{@vum5|5r|9*n_q$OQN4PdOAVK)gYVabmilUi zeBwU;JnNhRN#T93439U+eriY^qj!o{pdy5EXC^J90O`5Qk8V=p{xV0sFu1?HSQjPa zO7AH^B+QsG5oNd2LsAp0fcBYxUxJM?DGJjTC@po&DUzovSh!8`sR}_8aTxd^Zy`Se zr2lCvdlsCUf`8f6?YG~<)$^xKkuPv{^91UvdszRw{{ZVh`~hzN=I<4~Y|rcU)2490 zZZBRn=)dg)FJD5rzmJlt6bqh}c6G0fzd*?y)4XN-7Ack)Im?<~kBT@nYro!I+v{gg zu4Ym6yyv`H4z829K6v5lQ|uP6nzFx7l?0Plo(+In>yF{+^G3tUmY3~~3IxcoGe9@Q za63Lq9?YoNonLTngn)WrP$Hc$AX=~{NOts|0}Xzw0j!bFS%?A8mqeKhTy>alu*dzm z^V(5ZO+hkw%~<emL=c^uiV@=ny(R9^XAX*l7mP~p9FuOm=q>8W7&RTYD_&Lv+~AJC z20rKO+K~}g<}AgE^2D^K+5^S%_lq2?-&|4pa=X>@XH73?1pGWJoSUEe^c#}HCO(sE zpWo?s8dRAo=}n<|Le0R*UU~Qr`XG+yF;rk*up6*3CM6ilD=Su_Q8u#lQjCo~6*ihN z$pc}o4ylpi4Y0gNh@@-sD^~hbm8zq4Zf*=`Hw(|{fcbxj95MW#D7pYIQltdF`4&z$ z`gA{D5#0U$E4cbU{ZZ2G>Pee!_!p%G0$_diW%Ih9KzVuJV*Y=I`h8RU%vx=+{OYTD zd!|owl@h<wxeb)*TG1Bk)1m|zu6NJ7%)-QzcC~cV;QIE%jc&@5tG0->ZLpW~6;Fm? z@wE==WFNN3jDwb?9hI5ugkFu#lw|V0qUs)bk(chR*v96Zpm|YtM7ePquaz_`N=lVh zs~%?_6k-2eJhf6uXkl7-Y(0%V_BU545SNS$fK{_2&3BfjNM5HNQ*|@n&-sb$J=tO8 zPvL(kGElOW0}!!niCx>6oN=OK;6)yEg(rhyU6I(hR@tYg*%c##auVV+86mGqR5_l< ze*S8tQ?isT&^FbXBq+CNmg5!V22V_cJwck`@bCd3p=A)rM>hggje1HR9~Zz(3qv4c zTZYH0&cWowST2-R;H;bCI~B9Iv`ZhC_nl#G&IPmW(Gi=1Rw+Y6h5AfRQRiVm!9brQ z|ETr{ux}8SFU9#oWyEvY3v#J}TKa+FbhNRxF(Ed(rb!o18M7VF+JSm%t=w-mXeEii zg(c^9-JIUnP*OTQcC-Kvxc_x>DEyx&s+Ze?_Usn!|ModN`RiW;ebM0k8_x0O2l7!C z*O|`u<k%ZOxaE2K`QjPieS`VG`=-VFo7dma0pN$3c`Vh4R!w0uv8onF0$Qw#OWv}V z!>M-6vcGMfeEXqmErqK}zUUXa_Id*R?X1=-{NqvfF1i|tH)daMPqjO9xYPFXDt+iM zc<wh~(*$YR5|7vy9&d-Lj?iib0t)lEVt*dGGDmCD17O4pa0LPe%b&ZmJq{o7<?NAC zzm{5QfWKjg^XKc;QETzVwR_W4a~+#LV}30P-iL>&sK_{;@b+Tdy7)#>9qJ2yF;vAB zSCnYweT{(0imK;*>oK*izR>C7x5w_a-1A~}3LuDJneU+2l+7wr^QC9DRLfk5dI{E! zxR}|)ZJ>p;hkhS^zN~R=tKH)COeFl`4(4Zf+S<4%0TZ$zWY;QlP&T0`xqRDdu$FhM z74*YmY>&y#jcxQxVoyo79DN)94p4v2ibMZs$eS5a;@{Xex|B|;gns|XAT38Hc)b%F zqd8PPv*>+VULUs{3jZgH`#kJXU;Xd@rYX^rR@b{no80DYuUhNd7bzFxnp;e?!20y5 zlIdPN!tx*g5$11SL4AA)OY`E(mtVH{`xAB7tc@q1oEHG@QD3EeZ-eYP1xf0MYv~Nn zrJo#jbxwNyxt6N;Cwcs_s0|7f)U%D8?!H%e`o8dCS}GE3Ile+8GO1^R;B8k+G}O`H z@DzIrJWom4co7;r&vuXc`<TYC1FxdhDyP1gIa&D{ozPe#4leMig$rs)X_|FCo7rwW z(TGd{b;AVG9OZG^ykpufEGd3j^`1zG*3$za8J<;hzZJ?NiP?jf#5EtzEfRr~EBB~> zglRx=a4>!#kDnFBQsZ-s5$HEWdkk0_A@=*?^)dAHR)O(7DwoiuIwe32wx3=+6H*&# zQ;hb#E@SE(hcXQ5u|E~o-_8SKQlz|6to@vB?1deQ5w_{O_wbg7sR%p21=IfW7JAr5 zjW>oDxb&`=DwevJnmGUfb=(Jg$)IAH0&GB3G>`uJN}rXsPD&|g`cTUt{qk}s{GTX- zq)GRT_NDpuqz@v;VokxIl*;8->)9hHOI;A=8q~S8SR{X0Z<=@Bl>2Ixs7kY9@z*ba zTI^pGIa|M`*s6U)BP=Vr$ZKi2kD29T{~}}$ViCfUEPB9%ts9*L6Rtui>te}~)*S_^ z*sVF{7>lKpQ3e>`M=|c|6mdmC``NqXdgH81YB8RLp%Cs7(OCJ8x<(D9=qYJ=gmJe@ zGGdl7D}z$3IkF76!k;Met~T6j2HaH2OZ|U+!3HrBne@4|d9bEE)vk;C_=5GOqdLIj zQJ*acc{Xgf8fuuF>a9o32}p*-6xX5A3Y3tRi=2_i<~iL$W>pI|_G52vo|IMYK(8BX z0v(i4IPy(FLmw<NAz)_e_n(c%MId}KL#m|NeYf7e+~#&<UZAC27JKz~x8l)DsGJ=M zy;=G!8mhvVKc}+KW09_`P-`*|J+aa6KN!N1_Jt0u%iq?lD5r(SQ*ZZLU+cVJ6zuV5 zZI*kc)hhc+h1L|xOCk+MtQcQphd@b{)#tMP#bl&%4GY&cf7EqVXW*Bw4*DU<q43|8 zh*DYAdd*e7UbD;j^3VxkbBn#N505Pde+6`V+v4%YsHc)hW*wSaC0Cy=9QX1WKW34u zZ<>G}i+Q~@=Dw>y|I6jv>l>l(QPhKh?tO$IC6p;yP>5zt4>N_WJz_hn-Xq|{h)hXm z{usUH{!!)(h?QZ>XBZcw^w@NtgKDu;VXkAYc4yaPy%mHLu=cKukS;$L;1MsF6o!p( z#bWbQdJO`<Dym({keyF<MHmrfcMmfZU%jL&4x33Foe?ljX{-(rgVcd{4doOLPfOIa zR!hBGmFy~?TZvyU$m0)@qdI~E2*PHmevqc9$3T9vxGgBWovUVge0c}cd<z~v3f1iM zgaCNPSFzc`?U6_6hrpdtY`4q||9)@I(StHzMCZpeV|WL}!gTM^-pC;td2aog0dM8K z$~_UJf$N*_y?I=k1JzB-LsSlh|4xL4XX>Y@)yV0s3WL@50CUbh$Mt8fp1~wMPH{?x zg}kKC@WCdok>9+Ns&PCe%0c%OeG$e!H|L)Ac_?6b4}AaRX&3~&aO#(YPgpq`j4z6` zng@t>j}}lvV}b0wiW07g?1sI%oS!e8-eFNKv5hL=ys*sxyJ!YxBt9z5IrN@c?6{nC zY|2hc#J&#g(ztfp>1>Woi+3-b)C;o#u;;9VzD1Ro8wQq5A%7Y{RTZ_~8*f;{X?F-c zBcTZEUkQ8pUWx%oeXPBik;3(oAz(3l_HidOemrDSCQApCTW*bbEE6FJd;w#_JCi7~ z^o(Qm-K%%CthWNK)wTM<HCr~I&y%zrPS0?$3<Ld2D0`oNz`!rnQ^{$dc?|ipYC(Fz zZzvZl;`={`A=&N8elEXPN>nZpVsUz&@D%*}V)=nnnVZAZ48fBdu0Lf}{&eI}`0qd* zo<;oWO4pUW7s26jlg#2@ziaXQo9_Xh-U%}oZl6fJUSLJ5wioGj@EHj=#lc0Obj0+< zPB7!(`G<moh<k4#@hvMpu*^Vilujo7&{xd^SE5&1{Hj8z41+JcFh?MK`;5?;ho8=z z3;-BE(-Q~`y;NQJb+Nq!Ez@W-<ZZ|aj402WC70V)?)Ae^x$unR72doWU0Q`s&*A;i z!Q$n`6nDQt5Y_bnY8t0S?F&R>8<|E+KSWW;Al`j#A)SfY+u_B7#h5*1j5N1f3je4E z)mj36m3lsP_SJAx1*X?l4bu0HZ&G;?U{iYbhfDUyJq?p5S{eAaqWpEfip9v$hZNrX zsE8>%ej$~5fXZX%LyF!zv{K@b83h>87-LuN?UalL=T#K&uw-q9vX^@%Ora?|a8&E# zt=qFOt=fo*(NQtfPLC3L3i9k{EQi8>SMsj3EFWi8FN9N#;Pe3X{*^xZ_wDQYm%ngc zSX1~nLRO*Q{<>MXPizGM{art&r<y%6wvc9BP~00#TVxeGlJ~X>c54)1d@?LDpkBNh zc^d0w7~fHJ@uf=d#D<fmnQ7bNI-D^h2e}Tym^tAO1q|tbK9b2$15XDO#Z-G-JlM^{ zv{5`K3c(jq5K<D?`q(;vEHg#goW`5m@6E5n``Rq1H$ZW(87%+4kP-zkbdjn&^xC+3 zr(!?vGhqpSX6zUfzjEW12T7Hr6%B%~LE7{D0*3e{>}3QtSDiH)xLcJ&Fjjt$(h?!l za0TurS5OfhC$Rq%39r5VcWQ^de(@#LYd1<tAXO5Vo)hA7c4CL{yTQiKWt7C#hx2`? zc|ALBo{GT^F;m5m2{j@K&ZiGyTc8SxKfi`9NoXInAA=c~?&)i%OAOnB$<%VVru0Ab z3Zg%U918zk2#o}#pL{r`(C{L&EKpk<e<Fp$w0$d=nLNSi^b+vtEtKU&TOkbIwYcNw z^hnF%U6iy%&alg$UG%_=ghW~|eY*%|PZ5XX={x?qT09sRw1O@p(N8_C*~-|?kQ>f; zArb&Dx8!iVDR_X`RzU``HIK(NIfx0)EDs1>6e}s0fXw?R>@jmlr~)5Bl>Z2+W`=!u zx^_PHcBgpXMwkxhsE9!MmM+PVK0m)}rL=g*A#EHm+#hD==PG$R6IBgfMooD3xw_tZ zGMKyeOn^=77t=lQx%X=lUotV-vvE|7iy>snq9_e|XG_0W>>-vPDvA|p*kf(+`s)`j zn(bOdsn3xDN)pEKu649g_B}KOfa!VA+hEVu(3bg^5i$>3h|B=syuge}mSg~P2+WNH zeJk-RKoz^BD~k0%@oiGrUz=NT<p;QCRO1@f$47b2%V(_ifBC226ObT`vHS${dGI~C zkcgDsZ4az;z9_J89x#9Dn>RB(eGc<izikim9lI|AS<&V=pNF6nfzkQjq#yZ0m^Y{K zcPWW6Z^wH(Awc(d4Mvv&Y_%)hzG*T%Bp}NRlBHIGE1>QvtJc&XPpivg^Tj(~s>kCw zo(&S4Mk(~f!5-iyH>^<Bo}!1O6&xow2mmZ@4hCCyW$y)i1}i}a0-e0om43GkidF?A zoo7YWy)M08aW8^X(mmMRB!J$k*3KZzg}O*q43PGvaBhS15j<2wZhSyDQbMU4DnaQw zvUtf?MISbw6@(wFNBxm2Hm3Eaz}43;LaEPKEGI<y^pqBdtr1HGSMSQs_P>Z^LmZ)# zn}_hi00~IU-uVms6MXxGQq-J)V;_<kWqp5=cHdJAk#IH@!DklsOmcYm??8l)!nyF} zCCv2(QU>YU9|KaM5s9N(bqZdfImezYSdQ0UwJq;jeDCY8fNt-ksvfUJ3u)@o#&`PU zMZD8VJv3HcT3|@y+y0}u@Y!?FSYX$f_S<&zO>-Gswz(!>X$)oz-Z+7!-z5g|l$y5y zfO9KW9fBzlSMxoMN+C;>)W1;9wvVE<S_nt_iE~v&FK*-MN~r3vO6pu610Q-y<LFoI z#zl>K5px}x*9xoF2m#E{>*)J&G_GEw#jZ}E{w+qts>X!0<+YGO_eAad5>_w>o4-Xg zDKBFk!zuheOwC(^YoNz}Zs)MHGD`u=$PR~4)D|K+Hb%(H>I-<a$#zxM^0$kJ5wem6 zS5mW<fGt+d&b&npr@Wo`vE*54o@l+jhAZ~$K|?>Ya4Cvppy=)K($E+v$eMK}bYox9 zrCH`#+Bef%jEd)GNL@w9t4Nf{1%NX`>kkw}9Kuy-shWE1hG=3Zolb&`)pQ?f4++%1 z*r~%cDW66Nol6*WGarMIB2N9}jE8ay+-8WlA(e%3J|{U8{!bQL^h?ee{fS3pxRP_c zuddtAjeZ%McP}KACwEYuJ(H?>)D|CQPm`m!pPi7yn1vCM>(QGTU10r<u4G-@)Y`tE z7PrmcEbhtolA|>aCkn>c-2?j{U_^0NuimGhdQP*$?%Q7OwpCmQrPMhrnS7Y|2jq}1 zTof8Sb{OXkJrkghz^|gXCoN+gw}^`bHyCt^L}As9w_Tw_|IV%t7#`!A(X27P*ODD4 z=Y9adPSV>p4yuXI60^e!ojb1;XL^?yubx}$=zAqXMFDC|4<=*shgPW!&z{ssTe*Gh z^XJN9cY-0&rpLiKjDW+d{Ff^0{={9D2nCeVt;)SK&^Wh0P11bppAgURS%DymlGK}# z$8ZI`0Rsz|z`NRa2pv_jR8Z%ourWPriKe=&JKy?GTMmW)lf@LmyCBMSEVL=~CqQ3R zEtSjlviZxv9v!#b<;EUuQDVqe{J7d{sDEyvuy|99(<I(fhV@ZO?~;<mu%|S+|FV}= zF}#CaiTVOuH?TF@3QnnziBto#&T69kC$KiI*o&1=&lZSd8PvS4%1Gy0@avii2_<DV zj2Xd{GQU3u!OTMjPF6y%R+leQcFqRH>wO-|=jY?rK934NLq}L_zGjPoJNe(Y?&W_r zo}-t$)rlQWZdatrSlXt(6oO<fF>DtWh5~PUI-Q_gSwkGs&_W+$O^O_}>5d@<#VWu> z0h^-7tB%gN@_)-OU!W^(ypOjUvo|b8ISIuY9H^E8PgCr(KZ?>u&hOaUD(9%fFyU(l zjfQUJeE;0R@dwG4y$_i+5i=BjcQDSri!hFj-*yBJnY>w*Sn51QL1fr&D_Y)HdSS?Y zHB_KxKFy~shr)kXvaSJD{%A3)U#-vv%av#P{*^bZK#0Dc)@7>H&o>^$qWQ=ROl-BE z`odcr3WZ_m5D>4R*1iY-EV{?waQnIuMN&=CJw&kI)%&KJ)HqbNBY8@j=Z34YDULFP zw)1N-7!tJX=(bis-FR6!R_$JVElVt3#gsi84*ckFlc$+m$1d9VMPY3-6x?8~TA#gm z9(`5C969<4NfKN{^`W5?;E+NXG<+>m^$yG^G4uir(sw29*C+E{YqlABI%5$!f)-Yh zEno4$B?asJi<gL%HV0rOgoE3O%5BhjOsS1l{*s1xiYX-y0Zg+f{nd*X{eT)6`gk-$ z{kp)-m(8=MVx^ZSJB=X>uy2q?lN*aaX#pi|nKL|~ZzGn?fo0qYvCp5(Lmqt6$7L#R zKY^MZJxfH>FyW1&RJ#(j55Q_zAm%Wa89io1Yx;t3ukVQ=jrP5_L8{H9%s?s@1}plg zPm=Ax)&ABXHcD4g*cknvm>i_NcO~;rx1DSa5)Xrkc~Y+TdX`vRSh%Y_xYZYRmCwOC zJ(r%NEK<xXH0e!d`YM(E^SplP0;imbfSlJ3Ez91fg#8Sqj9(u=s*J**@Lo`)isFl7 zm<LNi%*&stsUV48wFZlx6}Z|2oz7J@^#yoclel?Rh%}nRFoCaNRFmsdSO-EL+TrWE zhWAwyS<X&2`eEteqZF2ij|%jnI&lX^kE#ygmy|w9wzd7KL^)K$WVu%q$L4tSI4P<h zW_6}5bJLSxU%E1B4mXyyk6}7T8pwGeV2ZN*F!jJ#!g7r*X8&A6b(Ejaj6ngwo3IwN z-@ffxxN4M~YAQ%Zwf4bf+@?prFi?*`hM=F0hSMfx{{t7JKpEsdQ&dA7t_^mOlz4k% zu#^%NOJy4;2impBGtH#dW(1b`N|2vQz2x(!EQi8>S5lK#@DoK~riWF2%d(zhxTHU_ za1ni`irz)l#w4bFn-8v|jdi2eyZ9`+2hry)8wRH4Dh(c1b(d2Q0HbNqj}kfj!}~QB z&mmok%v-S^j6X?8cidK)(&vI4(2|S<I*VHmzc;C(0()C$6j|UZ#IfZ1&Crn+%#!ep z(R?Z5tLeh}LJf%|S0V;ajv$yzJ!TwGd}ksi4vrM0u#7FxdTKidw>DlL0M`erl@I;g z;g!;vfrb_)$@dE|ll)BS*s>_d1p2c878Yrra`{#-;rkXU!t>7g=*xsk&}+uTYMyP( z<<^^jO{%Y#L4I2q4NWk6LFcR1JlC)b)2s2+0i3h(z{Xer9N_;qNjIK-pEq-&wg!w# zwLE=*j832#@1+|?Y@8G=(lcEt3YN``Pf-qq{|-f#3;ZEp!}I+ekkYg%h3rEr^>ixP z1`aWiMf<84oX%3l^@0=!@S-rjokFaTJX|suTjv)EcVg!I@jguFikHKy?#(+?uV?_I zR3$YKCR8sLGtrG~tY!d^z>#$rpN<)#Dbp~%YQ|2jb|Cd>be|L&QoRQr&qo{g7Gak1 zttF+vRT!10k2l>1uY4!zdSz^58yO9mlW!afDSK~57?(5#NmpHxF#_X>DneF?9{!Mz z3<<#Wn#7Q*pyiKizg~l>$>KFy-BLaEN^eq0X+A-XnX50JS{djPrGtNrxPpD#yuf0_ zyf9R4*U=Eg%O|rZp%ggVZZHG}up)hT-Y`*nq`*t=48hJ=)20$)!V!T}WT;52KdLW+ z=JYe!PmDSy{T0|X=06oV6#hF9G15;}V(s)J$$PP0t-7#F`%)<Z+C9#9mk03aNK$qB z7Zc=2A;GemXJZSR<SPRNGU7koxH1JJR=<@|t(lDHh#+mz%|Ypgrw{gQgx}DAR!N(q zjWN*`ajhP=AJHA%Omq1;O><jpXc*M$s6o-v!(72$FWBm}lU<jmvsGz8Q!T6@6iC_5 zy6(~D+C9c^mCed#RwR7Di|J}9eO75tp7AJ^N_`bttED&gP>A1GNm*;kRt%teYToyQ z6kJke<3)u~U~;W===uI5>LJ%_z1G=qMkDWWDx1X7x$7Y?C&UPpV2Dl8{BBMg!|B?1 zHgBnH!H|omPXKSNTrMAzwjm+4_?*m{6_(_bCl6e1E0a5iy7c=(wtslJru;%Tzrjg4 zudBH6uEP-U?}D9^SFpAwM&FM&Izwbt_3W>LaozgE$6zi7lP~#4YcF6LPjDR0mSIC= z<Q}?lU*-805-5|QA0DMcCP<e>v>N?}R(v8W@1gMDkw`02dPk`%`fY(ho#Cfzb-pX% zN^86bui6R)!c+7(2#nskFA}{DeRA0{RBmsLp_NgQT)OY%-nMw_#x}63bL50e`@F}} z?Yu!=f&@9oa@fNMu7s(dQ}#(+6)E;n;#{FLj;Ob8WHCFpwxcJ=DE#*NMHyF5fm;DK zinYgs$Ne*$?+D8!<OCwnprMY5%NQ@O#P_c0d!i{=s=%`$xMh~-@WBxFz)bWrP6X+G zj7V^5Y`LDzLx;EiuFF`iuVH@nl$!~HvNJ#}%)RYp()ROihI4KV!b>uyXlA{OZ@7jx zuQ+@BgOZwhITwVY!s6?ta|LL(n2<aPW$*odkA1uCIMu1b=y)jPPt?wb9BP?wuc3bb zQYx8BHEiB~C=T@T%c1b!g(NJL-jP;DOXGr19AbKxA`tZCUw)gh4IxRk+uo$Ku$)`+ z_?q+SvCb4n`rMBYc0{Tl?jeR^2m?dyl45DlyMPU@i-$4O@KP4jO0cV$(ce)I8=<Rz zM=Ru9fnQk3-b*ZN+WzStIG8rkV=C)fw~#CWN*=2%vRh?SqX0{XiTw-#tvBzx)<>>8 zsh^c8U8;`7bNO?3JSjI!MhK%(^^hSCa!;mlNmQf;!$YkKB#6@4k~a4Q1tn<Pe)&u* z=*gQU@i)%pZPt*?DR}c3x}x_m#oNePnB>QZDcVblcz++gHc5-zge<mGL(w<k{1_}_ zBPD<ODdG=~3P`c3tLEijw|)NnEiBF3=i)pOQvF1f{-N;Sg$Ub&v|-9m2&_xBGPI%_ z&6Dv)CX;z$hrCH9Sh)bt)v3Yxnli_*ep%_9v;?{cEHIM0`p2C~55|1=eOOc}nv(Fv z%#j32QAScmFfS@=f@Jj!3RNzk<LExTg&6n`ZiQ!1HxADM!Ja8dk!GZjF|HWl3O<1< zW;HCp;C_jNke_)m#mM_C;1`Q&;q!0>c(EVj8_0H~hU&^a>TT{g=5B2UEHa?g1fifo z^zC>I^J5)Q-%@mbB~1{N{3qVs<99Z@H&R`(*Im#HT-QelB}g$FZY|W1i@E(L4Ph+| z!Rc5<VGhmQk|H}?k&V+DuR+@wqotUgWE<q}b@Qz%2~p(;mtyE-bHTGvUOdw}{{~A- z`(c@fN#2+IaI9MQU0`M&_X1~HCQS|(O@>RMoPwPbGNCgy&f@eVT;6}eP!&XE)d<qw zcAL{XoGy)#3_(Ks;V_P*cOu64_5``QV*6o)jx4AmT_?etGWpoLIZc4QrSR@S?fVuM z56YJ-$Lnj+IhwzE4iAst!~ExOfWA95Xa7O^ImsjAFn{?0#>)pLhr)lC@?#58n0OYb zGmOEk@k7_Yop@N98MjL=Js6JX4n+FeH^MI;ye>4Q=f?{&81=mQnzA;EDe9Tv%;<1H z-BR@TuS*ZK_wJ7LF+e^SzJ4zst2;mIntC(1BWk6d(Z(w&qI%)r){3(Ftq_#O?UlI? z5v{<Io^ddEymX$0kF(nG!^C1E7#>6^^WWmRZ$dPFucW&lCC+sq9sV!J__eDcIs3o{ z7}eRpGZav&TC!0f*8((nj(fuQdtt-s?ndIn(&kR{9aE@92ZrgjT?lLeqjTix^3&oT zHNJy5U@4c0g_jS#Z-_`K;<sKNY5%MEHs{jaFKp!qmWT5a(o{e~E;oPpJv@H%k5K;d zPcZ$ZLHsXYLSZ9qF)j)y$7+R-PY#9uPQ)~gEC%kU{cuq$ynnf!f&DRBlE0Kl%)70& zW!6RAZ%y10U-%7%Kf6=W{01Q3M}@@or!Kw5em^h9aWP1OV~U=7xR~t_QKqaMqWj!N zvL7Fa!I_fFTaVs{r#DsVlhB#{>O<vnx4g}o3|EZz)e;ad%jeWk0?m3*1H(H8V(3{+ z&v0r_>SKwz_Q?RCZaksE|C(y0v3t*~iyU#=PpT4P3LBt#?L<+D&yT0w18uB*GKc4* zv$+eG=4)~I*`6=|p1<as$>*G#fpB-*kR2rxN;;t+D4F2c@1>`{`Rgv`5s$e1nPB=E z-bWZp48KWe<H=v%5H5_vpNF0If0E$v+sTb*Tj86WqzSP1X4ahjqJ^>qxw&})<*&Ym zdjB2JH-85D%Rd8r^DPwaw#VgoD@lwW%g47e9}53nNkz*EHb(Q0IfSe}nTxM%;`$Lo zNI$z+{2Oi+o)tYU?qW*I6CI-rZQP<V2tC#tsumzCQ&CWL3#HT9@BCW5VjBZ&mo69( z0Z3;iiy6xzT)?XN_2+}4f4mtufxo)PzREVmbNy{Uv{AT1_H%n#bukJqAtPcS)F2~* zDO9OKXVuc=+8oj)3J$7Q*DKZ=wW#F)j!ZzF7qm+ERh;|w`2tIWTggKWcFakr*WUJ& zbTQ{}25DhRfrO<Rn~u+Qk%9?DG81%gG}h4}?S~+!#MHd>`AUrrRL}R)_fa3W1`j{G zgLN{-eLOF-@B|#YaFU?!>B*AXoT=)XP~K=>h*VQ_v?21z`HP-!c%Pt$j`GI-<Kde0 z(~I$-dF>E6C>>QNgZ!x*DsjDOmh7VFg3@-ze*YHgd{o$Gu9#(#82%bEfj~Qa?D9b| zLl||=stiS&AziWQ-3<Equm1?km4&q)POyIS9V~xtp``Ec0UuAAff83`C;SNHQ26ga zgv3+32liv7YW@y@EBLG#Ob(_0I?-64e0})~Kd+{-^s~Ntka?&zyg(OU1cg9%)&VU+ z#IGHNZsUbu0{Y(?1L?Di1hAaZ7YMq0=XE%rE?YP#2TwpG3{_$fJ&IDK1bKTi995c- z<|>a@m)#m(yN#cfl`yiYCr5~~0y7G#DS5jF?{)?qKCiWhLX0A9i?nKdV(%XM`?Y!v zd^I4HwVyK^Sm|r60_}yM=9W~i1w#(3&p?5(#BLa3pgbCZ<-x`>l~K8DMCR(YLHXSL z3Y?7Z2BV?_t_MqMx}~}cLs_|X*qQ5Z61t##_I>~1FIVPyOEYTr_ck#WyBmjotYVey z1f9q;g|Ugw$ZNf198O-{TE~oVflr(ni4bdWHdwpS(Dx;6i~s<gn2GTmRJi)nU+FVe zPwhL?6#w_%0RH9AP+NSzzIp|4QsOA@w<5vEusR<K|6K^d5AK?^C;IG~_vj4c^HUx2 z79wBrS-m9xwtIn?>{V?;Ca&1xb^ONeP3XG;@q2Nc?(wE$+li{jvYlX-VE?F759Ynw z7*KaSTWn`j{(4?z^?PVwthc{?C@pWFbIqwQfil)gaed_11n1A2FR(dA>~lEVz8b%; zG3UjUE4Y%+%7I{jWn=7Kg$Ohiudwmfyn9jVa?Zujh#pr(GX#iwr#mtT-xq!LT1`D5 z7y+*eL9g>>6eZ9pYVXg?-oBOs>}p_~FPq)u;%a(r<j-yaXv~V(-TneRAE;3J`<(x= zyQl1h`1<2-1>f?$Y^<{EDUkfk=hJT<x&GcxXe|HT8UA?jTmy`62H>T+I-h$*XJyP{ zg@LwR`104VJiV1PP$p3pYKs4K-(va=-v7(D%>a1Wy#4k*lT!64@S)40@ZXh?D(N5V z;QJ#jAg0AZ>9{*TNoH}ddcOCi#V_D=NS+KS6n#0(xgU#$w{X`gb5A3m^HN@aEU1YC zAV1W*BF0ih$xt*1w$^n!vdtjJ0Y>FN`>*s5Vqy11Ihy3i>l%k7gId|MTfMz4E7YR> z=BbVZJ5Og6PQ!)o44UgGMq7PoKaZNiXh=cK9~wuY^!uz`2G0wo1li6E1^4jtJV`ZR z>d(#8V)r2BblaBAyPvo$?rtu)8ctL5?$;SLOQb^(yVp1xps!mz@QojJ&V#=BLkYxh zI{V(|_2PnuV{2PC^nTcoAkG|*VLr$>s*H09@zVQD_~&kH=fmgkg&kY6LfBw90bKp< z?_lMkYPCowLndOf#}BXW8^qr{{RZ{ZeKQ0WDbX|@@&lAZrMv@C6pj=a{E;)t4@pVH zDTi>oC-mW4j~|-Q+#t7WUEB#qX1Euf;gr3sj0s0Y&fMxLL9p)apOfXFFWd<|$j2p9 z-b8e^7;iZM`SV>q0N9t8xGgYq5lSe3RluB&$boN5)2l^(;+XEyR11TUWfRo3FOcR5 zi-&HXJEB2Ljof>CR7F-_XchZ6r4563?Hf<}COA7^>%&iNVQpize#CXsls>rsvq0%m zrOv_tvsKJn)h>|zAbETO(53=EXi!Mem`yv%Di!sl|3VJyX!mIa4x}aZbvyQz8e1I8 z=dssuVfUr}Bt*SCd1m;jTUl|SK7q=?u%HCukiqLy<1uwdYxJmnp0-5MP=Kaf5&LMi zaxo3hjS)w{>SjJ*G%_#A-f=aGf<mU)^WL{Ic;!)Zn!knmJoL|d-H33Ps&Y4CVyTZM zjOKJ``My|v1vl~ya_Eh!o_-DV)ze6eojGk#zHcX(Pp?{R|Jw%he*^UD0qF5j5vcOv z`uc~$f0t6%fERz9csdmrDI^8)f4FJ<*$XXg2Cq>FTi_Muu)sE^7|Mvw7d5fHsORYS z)UVO?_jM>>%Rw8s0<1$EzkiHpKW~C=Yg&{sv4>(WLWAl30&y6WwgRoDJl(tZ@smOF zwg6gTA5W`Vt2QX|*feiD+4&2lr4{X1KDXvw7wCpdpNoyB&vnnG2o8UzUTT?R>Lq^B z;_rTCQUig#tYy{EK{A%NDU|D~nduS3$4wC(J_R3(6*M#R8&o7{k9Wrlh<TKS_1f)q z)F&vS_AMg-3~#{gjp{PqatxTdnBjH4{dk<cI``X0aZkcC_F_nOeGx`@i(_0ma3{?D zlqD>GUlA_CZV0fKis(#)H(f&yCy`Dj8-o8^d+*XDNs^<90cz$Rk&&<Jp4#4-Auptm zhvk7Hyz#=H!UO*Se;WA*D7;aGCyG#nToKB09y`<BQ&ZhlU6mOb4>QFoUaBy2a}Re9 z&#cT!Lw8h0xSOe|y%fL);KSo1r2qVHVJK@mKjRNxq9Ujg8-9=F@w}BRez?6WN?$oZ z(r22%zbgETh*{SK@_A0FhOLkGlBdtsO8ioU7u%6N#Dr=*$yz|r${tn2cMcbZrEN#? z&J-}2chHQ7_HR{r1hpanV84gt$V1FAI3ymUt{zWJk^`s=cdT$!@paV@^_h%JFyl&> z@<Ln_f+m<uR?SdLrW1Yv^Trh@Am#9%$7N0b1BE<9@}JglN1lBPX?5bkkB~q71kw!= z-B@b-D9?e-i^s?c`6s`Zeo&hZU$@R$S=U&<U3XXhV<lAL_S9R64M~F)-mRY*j6v9w z>~5~jFk<zf)e!Acv<teFh!-%OiA$<n^a}^1IOej{3Xem}673RFO}}nDjE6#Fhs955 z`S{0(nuT$^<#EZ-kjW+ouTwzHEQwN{0BOci{(tkT#PeSWLDDvR%V{+l3zjbleROvV zRN#Gl_n}w?k7|6v{&)VDl2zefK%~V6XFQ`O|C~Ke(zs#=8Dav_D(4C_xRnL{hAVAy zRY;g!Q8&e}3Bavn%j>t%zDH|nRM!w*PztQwWi4>ac{*NH+^+b^I^NYE09C}JdBCnr zWvEb+B#ZWFWlp0qkqx{gsp863{v;)QOiqJN_Y3}^B_ofTwJzIOYgEusRLYvgFRgsT zBPY--ng1X8&`BY5?;}pZu+et)I*yb$g4XY6Keu9RNcvkE>RO-e_+p$1DN2H2cqmZ* z`|?@wu7okL7sCxxYiPvRf}}MvNDl=P*k9QTrA!Tb`N7=n=1d??36d<iPf3u<AXnx3 zvd7-pNilhj*GbNVKiOR0Xjnl?>XIl3zz5UIwy)h)f5z&3I%^ce-+67MJO)Y5aWN}6 z;Uolgn#hd+y(`mF;w6QiY1g!^BORfzGs2FcbnMhy?A?$RuB?&rNK!V%d27gOz&E9z z4V{_Hpfdh-`pnp~E?p621AV{nq1?^C`CWNz2N=pd9mq<0B`nY@y>I!B9*U>`u6X)C zYg|7K+1!h#8ytNqSrz`Jgx~Eh9rf@Zl8G}#pPz;JrHN2-vLV%5BAX}yhp{uj4x`!9 z82TSHczgc2!^2B*@Og+xW!>JP+VCU!2EEQzYd6(1jZAlDICRPD+nc?p(F6+f$rG>2 za|?V@6)CW>qZjRDI~hV9@;FW|92**4I6We-!>8bE)5Kfz8fpJ@9Pi8ck5YG^V41Q- zXoR5!D;zF&h#oC`Q<aqu;@k^FJNM1G_9b?I#qfM@b@83l&*Pr^Iq)@naShHhpi0P% zqdLPn3HO%U$=C_hq{1WR>;=LM7`cmG?GZz5a`oqYuYUKKml=npn!`KSY7wP6AjzJ+ z6ygrqsV^C2VnhASOBi3ifIfS#275<>5y|1P;qG1<@X-7BkRM81UvgqTA5Oki;a^H* zF&v*2YHy&X{vkZxtIB_-XuQ3@K~N5PFeFC5%@p8g_fm;!d0b4et4hN8!Fm7DJH+|V z#AJi$^-mM=T<1QW%*5($-X_=QuWMDVKXC!dLQpfx4}1MBj@`yqBw)HK@-^b0&J79k z)P*PCP>5cz<~CpNLRS+PWw&5h)V}kYoHxyc2UF4=pn0ANxeV{;^4a}P3*dI{>N!c$ zqJa3_Bn;!QYYO+*356Vi$swuFD<=d3XNA9cQ7(ciea_O)I3?ThWH2#1UTnmP@9Gw# z9+0mT?lmKi{B&6+NvNmpjpoJH#6K<i*HlOU7Z*(}eB$q?W?g@y;|K6(WZzYr+?POB zH8EB^X?8Z9Sb5^ePYtCw>QWG<{%^jOcS=6CB*{q&W0HtZi3F7Q?)F`Y>)!$v0{}xJ z>@O{=!oPqx+Q$E6I9~*)^cs9KFh83o<%<-1WBH}-%hlar{T!h!6FI>Bt}xkqZn0_= zPc)0!)3LT5>ZjAcm(u`t7ryG<LxcTg>E+)o6Q5Tzuc4RH_R$j5$ec>&0tm`@a_sU; z=NcKj8PCj1!kIEL3&Z*8v&H<g5#;MUQ>7}7=iw}F=y6Y(j{lp&r9jD=7v?ikg`bCa zp!o%s+W;_w<-u~e%rL9Qg-=2;16BN<;OCG$LR|iYKOiZ2k0^=jYst=+?Ybn!2+_Xi zJ-1f9sw!T`?(7E^-r=d*+3zIm*Mw?Hp6W3-#b;+)--H^rIeMtD?U4vT#WpZLk8u1N zhhbAQzUA08nUZYy{qw<@(FW*-Jerf0>{*A3I>~J;VfpUOTX^VgCu5lOXHOzjz@hs= zDqY~Q>`>zR`CWO2?;Z>pJk2!v7_ut-3rQ`9*=vP9vA_M7dqfRngcWqk+px;6G)Pr+ z%@#`xj$g=56?^PHhi#WiH%dFIu=(u4IA}%yP6l(pu~C82B);zuG<ePi%)NNBwywF_ zE-G1-Z&s$Sl-hzFy=GCOt{`)+=Ot9Px?UNX8$k;nXgL`{;Ls13GKG==@ybytNqzC< zay4g%lX_@dOGW8QTt6GVE!nAO^W2hnnb{DFeE*j+SN@JYWcAeR>2nT)gr6;GuS=SV z?fC+-_M_|%l3h)PHfY(FzE+mV=_Bi_3nM=hMcF5Y^NUx{C46b4Bg_}e<krn*BcwbX zk8M2r2{z5Jm@NoiIMEEiU{vmGxMzvR>A2Of131WouL|$fvN4?fsNp0@l;OBfD}6<b z&`Rw56w(uUPc_^V&zJvWWtoZz`79cz#?o2rH)|R{L!?a>BU0=4%B|>N`<riyAv(zO z<%V&xoC}E@GJP|5iX4i;`tSj|5BD(sd}~U7_{CTARpDPq<OLq{%yvIx6kC{e{akO) zMFiDD&RuyCuTa{MP8X-37|~%p{c|Z~OEaiHTUlDF@pkWTuRqdgBTri#H|UuRV)w<= zJ<^$>L3!e-*2&hn>%MVLlQ2SxMo5?gK%>dEc{_rUo4ek2v>#dU%jcTyuiFW4e!^A} zptyYv3#1+UwH;sDlX6UX;Mo1Gc=x21gUz+g*}XEalE`Klu@|l(7B|Ac824HYdu}BS z6~(9oEz%f$3%MwK++6Eto4zJ@&D9eBJ(QF0OYFW9y%0<<6pXtIf}=Rt&I@+JI{u&C z2T;#t`uCGk4a`f34`c;z!O1x|7YAVSSj*PjD1JImpb<+&8{IFr@;#)T8O{%nha56; z;DB1oYxy@x%i>3V#=iY4P_ux*fqD;Txnb@VR>H+_e^-<~d-^{Wc>h5R(DPiv%gCxx zE-BBvk11n_Pws)FV3l)(y@-*M^{!Ak!c8fVtr(t&lW>|M#<wBQdX?5i&f>nU{cBT{ zu0$vID_9Se51*_I4XC07sxRkv+Lir|_E=^+!)R12T#wr52B79D<gnNU$DZP`Kx6P8 z2I1cYIdHr+<^$xqf(B!!ji^f7_{`-(KNLs#5gu><qBOmB0V~>J$$@UjC@zpRvp;D_ zVZs<__otTp<yMjziE3&d2Ydb7D}bwOWzA%6^^JDwTfB@Oe%p3d-bd9N^)@e)8)E;a z)LODq4L{J15xV%UFW=6cqwMSyIQyR^RjE$j5w-aU4koFZg_Det^r^LO&jb`H+IhC^ z{7z*)qxf_~wIV1*)J0D)F~if#8=$RLO>|?hMroP=N@5V``#L<{LI2?n#&@@n9z4(H zms!bsSOBL$Sij}jqKs!&o^h6sUmgxlSmFnPJ*JITYDuOrkH};q_$4qkf4;(rdYiG+ zMfj$y)wkuYjSATcS&PGi28}0MI8vxs%FZBXFSv^s1d%5nri&Pbe?8f+2OB~k9{H{Z z&#+HMPaDUXFAxgL*E)F5J&+`R%hE^N&lPW+S0&4#>PdPjT`e=D!2D>(6!a<dC$Nk< zP-0QiJQPO+&^p~A)XsFXQxH8gbXl(VSdf?Q?&or!9+cTmyynzAv#y(5b5ZDg-0ajU z>SZ~^syzO^dGkfORtA_V)SlFk;5EE2P=1NUbE016QoqfM^4xSxR=E6D^M`C8BvUk< z86*5Zd~7F)UbCPEohKn`yMXC?z5CSIU*TEUw+`$;_#P^`<jR4&Mhr#my+;zcK`?!i zJqP2618WJKc+s3sF@|?Tk(BycdikSg+Cwvb^_sW$MGZymrYO$>@6wldV+cq>&c^V_ z4tj#_x4!}S{U79c>BiW?y!Gx#NlRV%&1U0uc(|3$jZ7w$@Rb;eXn7%@QdSTD0#a{T zI^TYBZ&09H>*5Ahw|zk@?yDBRPvTMcD6wIM;VNF-;6eGjWs<Ago|_Vc=4pCkn1pQs ze3B{8jRS-ITSqnh7gOvbKlQVd(c5+uo|&bC*h1{y*S<yBmAaxFxhmNDB|`I7bucwi z43Q8+z`jni<7>j5n>4$}XR{;0wuHkTpf4W&<J||4hWBX5N!-?RCX!t^j<5F?Z^ibL zjcKn!$VSB*2a}~3K^W9Dp|A{kQ|58=LisR+Y$w6KEL<Ls>#_S1>#vGrf?{~qd!v4Z zsum8v&kcDB+nuTxDs;VnM+$dk-_J2ZcD%(eXfmbAX4S)oW5o$|_L)U<@hdQm+)pTt z>%Rg|Ie;+vJ{}V}Z2QgI;*e=W-D*tMu#e@nNOEg?X2meLe*k=6;QjaS0UpOzR{IHM zRrnW>W(($1p5Cd5K}t2_+Nn+P6I*~^gNOpS=D4wTQ>*Y`NT|!9=}wL5XRAGr_rxJ) zaN~d`iYoh<Gk$X06?8b-`H`5v#lKUny3@}n>=&gjyt8<Hja#~xWBbK_<(V@AAa&eZ zvLBLmymKWg9<1y7>d@OY2&H%q0Qg^Vww0ll5f;vD#s?VQeFqy`krK5QyfJj*1U>4_ zF_rG1(;UsSl6Wgl5*_DBYPErm!^^zUwkY_U8$}jUQR2Nb<sEpTnTLOq>Ox=dD+cQ$ zEs(Ap7!vnvGLPH0&*9)Wdyivt6i?LQe#9DHk~2CIRor<ez!G2WJNNGMj(%&;$w{z2 zE#yiqpc{2gGYe+&BvLbY?X)fWfh{}~o<n)oUvLFSFZ>g1I7(~i+z6|(Ic)9>jAlf0 zxgI^-LAw1=6o2_)G}q?xlyP<`Srz^TM1Xw8{iMwjoFV%73PD32EWV7kfUa_R(U%do zAj)jwp%@(P@$5*ItfgmGhUWStlTp;7<O!r;tmQ!P0->>si{IOXp;jE~x>L80`2aH4 zz;o}Pga6AO$P6NN;+q^o4s{DNWWeCZ;c=*+6ZtuV#lHb|c+CBJnZi(`0Gk0@-iu^# zJ___dh@mC$bJt6SKpt;_?%r7&Qfu~OirabiHEN-4YjX}Bumce_fzR)!of;|e3A6M0 zLQI1$(_SVh@9#CktQ#dyakzDlpX-PTZaJ~91FbHwhX&(Tjt~T6;0aYMO@q+e13jSt zc<vI`2<rqMPJWYh#h%5;_bz!5o}iX`Ky-DD6L!eGqaLBKcQ-+3X(Z<<JAY3au@vas zEm3r5<EgHPoveL(cpM-~;SCeywLCKtg8I#CNzU~?6hI9P6eT~7wPdf82vgEAA0Eo{ zcMrKBS@_{WD0t;~^1Ob0M!r?yUqrC3oAC5=zi$Y6quindhY8OeZ<W2GkZJKMw@H+b zi@fV0LzialQzy_M>Py_b2ld7!J3j9sJ&N8;Lju)%C8wutXh6;mL$~eQ*ugj;n!xq= z#EHAIs#-HstMxDAZl(6TErB#gHQei?R$6BTfmMOFxn!6oMHK)alP+uQtusyO-Cg;t z7#Uo14=}mD+Tzwet;9XDvrWa>^i1JrM^{2}9%$JjU%NhOIVL6p+ewXxu6S(7{p<Pr z;<2Z`n;KXP!e#`w!Bv9?Apjorxbpcyr87kZ^m01c*6y90080q%IoR18mp;_L(|o&~ zqs)$%bX`J#ebsZi$01d+d=)V~E2l~|iedLG<k6ly1L2s#>DM`E9!T53zo+RugK%C? zdizZY2MzXqDvYo1mp%JzKy|6U4x?73D%{WQbnlDOzyBa5eu8j;F@JvK1%Fle7nZY2 zE^N)F+IZxbEZ+FFi}`Nl%}v*V-`qX%3I*!F8A4~esx@Ka?NY6O*Oy#j_$(GYTmEC< zY<FcYihUf-*3$<-{HoxkMVNmsQ&u^u%9!d|+IHZJD>Ow7-ah~;<mfn42~G<{?x|+; zKK71SD8C(4UN+fybl+&uZ_pC0s>iE5eR6>w9?LO`$MfL_U}h6vcx%N(OoXfmObDA~ zjg#uNRDN^!(8Tj{HbhHW^rSKQ@p8dUf$v|vg1p(Nx7_u$K7UE<EXsB$b^4kFHa89q zyt%adwL|CPnNs_#jfS)B^;0-PSCZ}EMo~QYa=Tor5W9!2k5kEqwn`0&;W0xtI~!xi zu5(N8Kn;O`DS3ohp1?k1BVOP3-|1)5zokPX*XfUC&NnY0_gxJ)dDTk&oh3IGPkvlF z%?d7tW_dRr?g2jB0o>j~dK{s}JI*tJj+Is6UqU=%hY0eyH_Tu3Njb<WA5Bm*%M-); zU00LjSjO=3Id^<ho?*eHmaXpXHqaDKWMyMAj$Zg$1Nlv!k|wv3zWs{blZUA71YRB} z3y*umGX<SZ(b*}9BxS=fB9wq&*oI!x=EwK3TnTGMO7QR}5UNA#8n<mV)>#wbc=anr z1>E1kIQ*q7fcFAtI;5%b`dD^VEi5J3L2GL(3O+Zl3ao6<5Qj<UO7p~8`GSTO`j;<( zuD5cH`fdX~$7YXs^HSey-}97qEv(7SYuCg%1op|k<4(QJrnGmqMufBn84#4iyxB_1 zgYn+@->#fpd7^|F@I;z#I`G%I3!B(OAwOj2dstA^^AJb%;9<&=Sq*1Hr7RTZ#t+n7 z9+W(qT*5!y^+q@a(8@yP@$#lRb`0gifJsGxG(2K-fLrf=xEIcSes>A!+)Hz`tP1}E zBHjd|aF0{U=lSL-Yl9TYKE9T$Ca%j*tJeQ0!iu@inw5?>qrSKjw`?`O!tG8_WAiKr z7v+T&M@q$@5pskt8t8lktcfwl0K%`i<qF6)6{>G=x+m>vOx=^Hj4T9ON5G^l%{1D0 zOsNz>RqP(i_c(T+t;)nPRGyl7NZkExxMhy<55*9=l{P$Mo|b#%!LS2|Cfulz)8g~# zl_-aK^rVxZ#_BmtKvn}!vn;X95PRyi)Q->b<r~N^-pE*@z>Bhfs?7->k#ieUK?Yz( zrWBR?!lepUqq(8=meM{IZkwa#iP$qR28A6DuKfgd=bMrvhF)o*&o($EEqmE)h=~CM zOk^ncN%9gP*XA;(2+fd#K+0Fquv5h{QQCRP3EH~~_PEQ>Xmf6;nqLcw+!J0|!<%^- zKmRI?s%#xFBZI>6#u9?cT;luYM!NNBw^WehNkAzZelKeY32Be!l33TutlQIL8K)?H zhWJzYfk!&=DxWT^!oQICqS<V_vqk{*4ztBL0qA2t0bjBBGLScAfw-{?ol~{$D}$CP zG=J^0{ZAC0Df8+j2ZfxhTN^{fd7h=89UNYm*B%IezD)|=e%uFVD1d+Oe=GA`3Nd&v z<iMiwQQ1>#FeBSqI0D<y5{7cD-05bg&#fLr6b~mfJ=_)*`>yQw7g54;t&)dU4CTV+ z9XSNA^Uzft>orD0SnQnZg08o5(hZM|=uyKg#PqoZmW7Ra{i)x`_gy~x!d2s_cz6>7 z5BI1<LqOyipj^W#%O2`-Ccb_-hoHmx`+@cu8vZS+Qcuh?AiA6hs8U5&n6m(LuV_`T z*DYR-nB;e|gvy1T>XM_DOMkZ8P99!zL#P227}jE#c=fn&4mFZOfsGI+hn*k`VnlJ} z%FUaKsMPp7Y0gNU3mP{>h>b|tk3+YdrC+@J{*k@>JLTa2$g(Q@i%2W3ND0r=_f-fQ zr7k|B-}NfT3O*#s*oSRkM>D9_mnpBQ^<*Hdkw1N*J<cB?*F1U~UJ9_d4sY#e_AJv! z%-IbQm??CC9hkBzXx|rbMt0H+8kuXt25rI=T@ZynDgr>Y>5vVc%~<g7>ReNeJ98Nw z4j+ory#?&q6YIi)9ad)G5P7QeOQyUXhE*oJ);k~^%#<M2#a2G9q)BCZMaic(uN2DX zntxVq<zF=?j>wdjfGp0GaC}dvM*s3NaNJ#2)P@!}Z?XUBT<Z6+JQLl_(2xaZ;`5J^ z0@VUKi?3TbVmBT0DZu&I(z*%LAn81Jp!eO;eG-decIITBE!2b44a11pw}{~~3Mg%u zmu{0JJ6{?+m2i;uvru%!w`a7_4g74q19X0tL&EnWp`iiA!~gKU#PlDE(jR6>*X3nZ z_!p6~h~-XTpnT>PO*xsjcrEaLg@`}$1F#CnTOdhdFU!B%LE~$z(-Jksh!*#@ns_i8 z-ns~yC+8lVECIE*d0c>u_uWdiI8&S%xujSiwYd2l5tw-Jxz*{*(rnCnl0=tn>9)tl zlZvW0MCy%<uGfOH7WTc|JpODrY_6WWInaiHDB_!iNlbN%mGp^+m2563EQeW?HHWQ= zH&f#F&_9%G+szcVVXK&|H@SLTY!r;Bed$Mw&AZWJc0`56`;@E_&YA!{T7C4OJq{Rx zg*R^ju3pJq67QZ9_sDu4JOS#vg5`L)a0MiVZpnxVs6x&N&fiS#5SUU?I6fE)1|7qK z_bK@o9ahHJyr{bn=3zv?t&>JDc8#A>%<?TIeMWh{$*T~aR_Oc2UTWx5`yn7q+Sx7( zHE(*35CF~0Ej8Wds2jr6Xo+cZ>~VAUeMi&#;&ZRt@jb_lz9$7Kp%*uhhAic#NNU1M zxCHDzCtpX5NQ!a|1Iha(p)VAL&p9pJbVou{$JtIN%c}4%A{V$j26<wyp2o4(@+y}Q zQ^=Vcp|IDdabU|hn?U8worRXv7VzAd05>@TO}(@RpSzz<LZK|mGtYRrgEw9q-OPa` zt?Pri>^w(iN2+q0&5hbvXvTwk^$||@cs&KmUiIiL5bDDuTY8f=pkYI5xc{jv@`L0F zAhsB*^Q()}%u0JSl<~*%;Sfi)Z~;lY8EcRFW?VHzC!_VLHl1T7-QBz_KR1$P&#-*o zZ>u?WoN)d9rYY>%Za7h|$BwyQ4z2=3ht~;D76(u6?4Z@xKYi31bt1UP61VqgOX$ST zXTiYR`6gNs^*<*H)mzp|Ll2uc5*E8w`tvwvZ)TM&mnP+iu_dq~+)8+aOdjPw4}%z| z-A9V$t_uHxqVGjI<2|QJpuwV$SGvS{vC4_U9rgN~jd%%@_$W2cx9Y~E#54`!#|kw& z-0ZRb`N~l}_K9AwntXD0J@w*+>CPDUINP5?1+@_jHRHj@Qv+dYl-OBR=+oN+LuCll zfBBeXL_Eqlt_r>dnV=y8sDtUbG@=$+#O$1jsc)qvF_Vq_2=Mrqvel!uw?(MoE174X zEQw72?RsrWGp!g%^&WJ6Z%%Wni?e4^x9*3on9CerTmij&E#-708w?V+PjFK9IS<>T z{t{xxIPtt^FD@+^UWN!97r@@D*!LyCx%aGIX42#{`TQ7i7c&kd{?T$P^qa$S?D-tp zXWzN1E}vcO`bCty=QywvKJqc=vsUmb7^HQ-P>sXm-CT(Rms~t*Ap|#o83i0IHb0BB z1&4toc@G~sreEHbk!r5YrDaw4mlU?R&t}@w2036aX(d=$mHsD|;(Kn&3wH$?lNLpa zl4<K%y;Xn9``YU2<wp@-H31MO-akP5wNh`!SI}AfNEdSN{UIz}7Pl4y!gldALLW;$ zBQpftI|-h@L%M<{F?gMGwE+0LY=l1@XjK9ZSy5(FqF|u}aBsgR1VY6%d}hhm+*rRz zI>h5U$nSrEO^M6XsEK2&Osc{X12@%0P|0!#ytz#E*in_O0QM3;)j4ZPqDE4kj#?ux zHm9p%#Jwpoo9k=oYu+)zkih?^zhKz0R~HZ%nyW<rv1<~I0gQ?(m>SJSTa;ZzYWHM8 zE$sNlNf{H;iHlQ>t4D>1(MXw{=P^S2j@69dopVL6ynu=E8OMyiI3eyu+Llo;OR%MI z&XdhB%uxxZBs{@HQr9o+*73X290PUO^!@54xf|_S_7Xk`1Mqos);>e~%*L1VS<mKE zvMT&bN%hq5yxU|hktf=qB_}1i{7qkFzi=E+y0+b@SELPUI?HT_Mw;Ww&f@1)8O~%U zamY)#vU=%x=8gEekJPr01V4@-xkvAv>!SDK=z>&uVmziN971RbtLM-Tq4oTpi!hG4 zp0`r)8mb}r45N-|Bsdn}SE$oGb>s{9cn8DJe};Y&GM0s<Oy{mtuW8~5OPQo!FEfPo zdS`hj8lFIEQP@4HJt)d&obxB`tT?1lV0^s3QBQZ1ggRHx7LN9$WILW}x6tKm^7nMq zxFUGq11e$^@Bf~vjfLL;T8Fm9xu%&R3CBSBdn86`<kZgdJ-rj-&gVFNep#kQg+7bq zdYBe=1^{QRRAj^5>FKeSR#U3at-TA7XQ$gpo5cQUSQ+4^gaEm_9zyLlsiM!csBJkf zz5dz{LciBXb=2fZ-fnm(Ms`v5#oLEKhIL6<75=5fvYdsU;yL9j<`J6h@DXghRSpR! zUv*ZG-o5T6i#Fv!ynMjcd?t35i$5aR6ta6CEpZQOXrLL1Fi9-3Pr&Pc$(6OZtTFo? zr(;K@W17Wbey=cyNrw7ki<^@IRc}YP6-2|56m@QcWhSdamZZF4Y<=xzUDMxF&x_c5 z7=QQ!Y<fc{FtX<*?JOJSJ&l9H?s?gw9xf9!@*$Y?FJPlYqN%T5L3;J3!1zg8PIuh~ z&XtU2$V7#HgaX{Tzg458@bB{5K0yJNKA#_pRhE-5d7|LCweOA?!PHB=l;lhyyMA#7 zhd(0k@etli@_OcOYMdZUkgz&bcm`u-LoIK`Y5D7<!4T(j0J^O!hK8{5u@Q{cr_tC1 zb-$)^)h4rhldT8FpoBae*H1aad3SBUtP1~P;+}a2N0F91Io7J`e-T0Je#MI>m+8IQ z!a##T-n*@FAt>_t`Q!s#fTOwY(BuRQ#`cdZdRJVPHaEbXR8s>G!l=~%U`D#yep3fC zBgN)pN?bjBtrGA|eoyjBp;liFmnLNM%(l{LIiG$m_2vwf<U620|4UieMnO$DE1#=b z+$oDNtzSErn6!xV?`GFxG(<wv4w5k-T7l)O;@Q8xDPGu><P$K#4y5yzJvmz6wMqHs z@jiwx_r<d(_RHkoOZjsgGSPWbAYeL2T5kdy8J{YjJ5{E}+wQU#t>T4!Y#T=iG(W{Z zn~|W&!}+udb@OKaE^7p(VRrIWZ=40SDo;zZ#oL$46pRCkv8?5#VN%u#nhArLBLcY# zA4B_N>lk?{Srz^TM3OE#?Fz<3B158+)pfG<Uu-~X(CaFnSYjg-&X&C{f4$a)v$vAG zSmGvL_t+psWkmzmeENBY+F?(RVdOW_0vPp~nG~s69tT=HzSdiNhrZk>Y;_pY1R>IW z9mZEWU(!x`{Lx5;(&jqv!OOn%6Y~<YapUtmM$I6|4jtH!C<9~qBP~Zl!O<UJ`2G(C zRu=-};%jLlBqqz&1E~D>ve=Ce66-{wS+2j%NvaAg#5D#3i04;J5zXM>K6gA@xVkRN z{taYi#p^f;@Ji-RXBy;k4?7K4NaaGvWQ@p+mnS05Dm)g&auY-`4?EvZ?FgLgm&qTr zU^P;zMwBqz^_rBJ^roCB76u@BErj6@Y-~B&&hZb+oPAG;61$cNCx4?k=k~12!Hss= z?k}aO(IF*~QV}vTXCqV{Yp6-24$kJmhg+wECds0Lp}eeop43rDp~^MJ@2D#uGqHk} zd2Jzagi2}jbR+8$DK#XVWQ+E08Kj>%UxnWn?o62VT@tj6-(T_YM`^6XrJrMhiy2v~ z!oQRREAZS~<5uORVVqUY7g6+A8=$RrNR!3LJmZ!LZres^8k&dhQlaRO2<32krNsrP z3f^7e02Z|Ie4D!7QcV_gkJHOLMem!pPp<U6dcc%lnx3?>=Rr8IjhT#oxIW|(1K{KI zWuYvKAS0Y~2l>x`ENj~!R*s&U5#eO0&1}#-A@<Ny*H^xP8jFwD0;tAlC$;6t`S+dl z$6~2{S%lxb1}sWnp!`j*3+l<Hc)TuTS^YB=H}TlrotuC5#L%#;HWA>&8Sid=30+a0 z)Td_SM(O-#pHyO`G>O|k1xnD)>;R%Yf_tyra%Ds{^5bW;{CnL&Bx}`%{OZOqfam?h z<0`_YA>ZU7obWzN7>0>>MlQ|MtbH-k2fEBngjL~RUI?BS=ki3V=PXRCTvUc)t#@y> zrl80?QCFr^J${!=wBy;~?04{te{TZAVx9Wj(~n*VhB2BFw6Pc+7;O$UdCp3xeJ<=t zXEbDj>=^?kK9Pk6nqsX8MYgPW&?G&|wuqS_BVTHB6;8rKhCrA_?}zWpa(5@NerBpx zfjJE#W&5B{lSIMjy*TBsUlTswm~*|R3fa@7E1B+&=Fttsqc7epGwE%MvWH$W@}(Ki zGv6t`d`<yOxcr{y+8GdxrvQK&pIcqH7vcVW)7u*wSb}@;*xiqkTI#tx^y1N%ex`#$ zqnbrcuv2e8wOBXJ7t-0eMrghQWzjg+=agX2S~^b<wARio@wJURKLMSZy)gXDao0o% zA9xw7Rz*1JRjThbwFE$jYlIrCQf(v9kt%$6d?-MHAq_`_B94(&;a^I`ptNpaXw4kp zM8QyNyej<9MkKzSABxAA<PUDzqTY4O7WdmVdVO|K?u&i~bIOmSl}zcPqp)-<)YJ7a zEI`macBR(!v9O^Dp0RETKaHJFljbH1FqzUwT|0uUW691NEF2;29XRLR&pjJtOj^1( zZn^?%-oyCQzm{F@<QbNDJz3eD$JagE-;S5eZcvniwA7CK(%T$(-d$9Z1hwq0ldgHA zam0ffF6`OAd;{I9S1@jiv9U!ntS<0A=~}M*2&5Gp^($=mr(WUu8rwbL1%ITf@NhPz zy-r;CqIO%P=`&C<g4v+1u;DD+&>jFAI%R-q`CFYJ?6n5$1;LiMq42vk>S<+gI&Ww7 z6J?Fz7#Lgp>ibg-MdOO#kdnSdBk0v0)yS<~Dsyc@6us|SdRUEFtUV#(wGf3YMXR7} zdR$hT-FTsJEwUxH+I5n#yu@`h;ErmCPC_d~)m=3r#LSQqV|di(I-L7+WL5Z=5;8@d z^O@*NfQ43F|5=F>>x^3^<r!&I5<X~9x%hE8SE$Y(0Z+P#%K<=B=ANME+2>kszw2DM zQQ%q0^Q>>xE2%C#7-LJM-n1tj)?V-c#tUw6zc0EuPxOW>+E|Jx`*Ot8nmncP4jziK z=T^6=mYQM6YSvkHoyL-#W)$pdNuHfwRG3qx%B(DXVSwgAo5CnCs6wmU`S2yAS1)1U z*eH|!^j%xXFCes(@TWIq;bZq=!)K-&=@hrykde5s)aPqQk<-qf&qjlnT=XNw^`<IG z%hyN&@miRhD?m+n3&vi!ZUpfin1=u;{IG%c^aa2GxzptNqw4O*=10{)V8!2AOXUph zn3H?1YXYwoH5`SyrbtIlDbe#>15N1T_&vAH<=T8D!@A5fyej+)3gw!245!0~1Qvq4 zRJdl93yJ|aK-cA^eU&8ZTt3GZGnYlo)`Lk#eOQfL(aWoC-i>^l8^?L{@WmI-4K;AD z-N$HbU16>*+h}qE?Ekd(pdkF={Dt%g;O`^>ZmK|bLKBE};Z6pDrh<NqKmDQj|M!}d zH+r(+WF&w(Un3srEIqIGE5Hibk46II9<wCG4uf3~FeHO2JSui_@#^vI+XCrd0Tl1P z>$kQDo0fgH7$hrkiOI)JG0iT#dvUSMuqwM_1l_a4U87>j$UqDXk-ph8J%|c<>%H1H z+*~D*IWKaqmh32Ay=~=*a0jayCePol!Q+461Ywzq--E-a7{V^l#_}-1<h~kIul*dd zRc@pJ(@(eMzf*W08c~<_Uhv0LwoJ)#Q3m0;ND^)v1pUevfb~~}e@Ve+ZJxZmU~1l5 z`1qNx>M9o!z7PG_N%nWTDKE~A#i2<T18*)P@Z0R22bg*I!Ak-E8(R{e^Xu0nzLXjS zNmQw3z4pBt7xTDgn1h5xy{yn6b*u~51PqY<Jo~@>&ph$)U=N8Gq<a{C_(M7Whcd_@ zK?CkM=dEp>F;8OXq-Wi_4%$NM3IBDjs<cu~<l-hZBWQl!5E2}9R%3a0`kU8~UVj6$ z*_0c_9Qm8dX&$cp^QA*G=mJO{u65)^h!R6Hgooej#&G8&?uw&AX|{wrLCj<<IL85* zj%heFcpUnW)GMA+<8+uQTmo-DJ^2vk_o*7h^1WV@`ACz!_MGN?&Qlmaj&LX(+dh?R z%A6gRwCZcWdh=*Wh`I4hn7u}Z4rn+=<Nl7Y2^we>I2z6xgyVvYN*nU|-8EfSg?~xG z__Ci?R;L}wtecsv`uaJocj&VbCerAXQ?tZ~%fByt$yY`#bm)&Qb7*nGodt3!zV8ab zIqa!rqt~RsnW2zFCVR{qs50d0wGZ5u7A%Pfe4eh|vn2g>=EO0Vw}fuBX*z&)2#`gq ztYCvUGzmIx=lR3$i*kKbN1f}La(oA35Bmo1J1VY6A1oZfBq_b>ar2(E<?tx^6B?G_ z9eceDHLOtP{o)$(H!p#%Zh-nSK6_j{F#ZL4jicQkNI=1=$C>v%fA1a1VUfJag|Gm5 zT_FT?$m7`yE4;wo=xfXY1S2NhohV9*ql8a*gmbCJj>6IsWB2EHX>K{>zS#UUZ$$e> zObi6?)|WLI4q^>Em|^OQ6~cYz#~5?#b~l6er+5MRq4Zg!(ABbTCND)e#0Z`oDAc$Z z!h2kK;&jk#0Q2kVrG|buF#z-!m2MZ^XaMQ-2t#@HJzJkYb9SsVWmP8^7p(r!X?xki zfpFg2xymOKCEqMd+w1ZoUyZsL`pRsOWp)NTqG{|MnxZEAJ~gbbF8aJM3^@?fcw&%w zC6!GMrUn7JGNkI0%by7%feM<!Yz9QO<RMwle1Y^ui7bPdxx)dhz2_Z_KmJp3rn4m~ z`YB91{i!7NM){luTNGs%<9PS3VP;%t&mL9K$!T|Oruw&f?yO4k)eC@czA47n3m8P% zZ*A^#gZunNa(4`xz~<5Gb@G?V@q&jd_bY^G=3}=?4%LDlooUL9!g2S>RO!LYAURGf zTN83MXpT*M)Ofc2z^);xBu$=t8n2&KsLWUa+uBGzK={T!B1bb0ETHwdre;*hzxF)Y zvp!*NwA_*8R*5NW-L4T+X$;=$@a+m(ho~CztB67~7Lz#(9#Y`{j&}WoJA>aBF441+ zRp;|K_i0`g{-s4Y%H6U%O|m^0tH%EVBq#A1`&T`T-5_Oa9!pNKrnm(cy?h@kd>>$5 z_vl@5d!wEiK0EdD9qzQxblK8{sdm%LQ2TE?4DID{VBTAB$g5FMjSArKt3A&WS}053 z;|It;{sVNy*ctHwyd0_TlbvpZ(sgQCJN)tWk|a8?xN4v#ro0gI_wbl`U#Y7wJ!fFk zb&1t~`wie$Ywi02=L?5P^>{zl@-}<3!!635jcwauMS5msWwex-7>D%ygvl=TaX7R> zSSzH-SfZScURTHzm_hjDO85A6shTEJ8)Y^pH*Or+@NcIv_X>r#ufy;9M>W9qUDSP3 z7bKz?a)|SG*lGmlprq>=*n1?#L4puM!kX(6%NmDKGxQUt7S5>l3ARTzsVU6iLCl&H zLtLik2K_Vi!>aHvB6_pLA|tGj>6nhYF$&cb;Kvx5ytQcic9qXf<lXfAv)`~)&Lu#- z*z3%8N9Mn>)Z{()Pz+WlqAA=f15Z8o7(dmoLmhq{7Zh9|uw#>W;$$`9SzQo`4ff#s z=3p>n`P>)P9F)EKA4N$sRWHx_{zn*p{s)knA?CuTUXH<wri2a97d)u{YIBh0rHbP+ zjOMZQOiBhFHDrU#>q=H$1y~e2b;eN+vIt`F?9-dKB@)22x!a=bugY9f&~pTEX$q?f zPaFCW5*lJ|gJL_}e_;^A#IJ>~1~~EVt!|B3*JklMZ&xfl3o{SBBg^&JS;0xGB&ZpB zz|_N1$uNS4Xndwi8kwYX=ctXLvQ9*<CO)O|-5Rl=HblcET18M<Crux!*J^8@cZ|e( zy(Xh;h@pPi$#Yo8H|0rcz1A|uxQy=RAS|F~(B9h)b;3x^m`;WUkdsA$zAHKbz2`Yw zBPkm6d<^{=9YrYr8w?3_<APtSE_)Eo$}Ty?_F$n7x##C+WULDRW6F_t#g;+&9gWFr zmCs#TiCbS3TK6WEm-n{3lnJcR#uDjTqMRb6P0{*7K{SVmK|K#oxQgD+Jd57TI1R?< zUpvnwF-XCwXZiRXTh#I*-A9lFz~G&je=N2AB+8Vz!aL+%-9JLU{SG?e4(|;Suz3hV zq13t!GR;<dKek387D{OCZnK&sdC)o7xwh+}=f%l#;<uJXG+$ps`pp}Fn`@x0mERe$ z&S{x5r^>D$zPm+?s~-@eg|YTzVxx{b-vxcQ+40+Fo=l3Ml+k{zUCFy*Zr56KCM>(& zpQ#KLPJUVlo(lM<Xz)<WY>%hDc&LfT?h3v=GxO_nXu57l>$&D#^sF8eXCFkWUWD35 zCnSg_L$9EY7B~ZvmHwGg?`@<T5}^Njd9RXU>5spwdhGMG#H#QwA-?b-8uYUOFC7qh zX3&>fTQu;fWn-1kK;#9!F5d3jO)<nDtnL?~b)uu@NQK0#>Pciz{Q>pvLp}Wza)f3q z08}r*>2kq5d*KG~^~M<Zvd_WMUfD)t@?Li@q$J#7F8TOeV_%d;U-rK%p8f5=Ft3fB z2DwuN4VQEjLg!Me04834J&pRkD6}+3V^K=;ym^aOCX{KvyaxPjfmB|wXWs*r5CJFY z4e)8-m0f*Jlex>kxH}`7{N7L-H~jWxz}MoiH~<DOx?X=$rBY+=M(AYTs9S(;cnNWl z#F|I(?1?>lP;assn^d2#<umf1Jo_w6Re%L&aipf`Co%Eq$eH#lHHy~&<5jY23cKZr zQ9OKxGef(gXJH!|F+(%Fb;BC25iSHP6ZKaY!<<@oJ$(U_8S{F@s3!YLo{Sko9vWie zD!xC`Yk`w+-55Jh->(Y)65=qtAiVr6dvE(A2p?P|JKsDXN)Vu2SmjF)e&?@>^7$<* zp9e?-zsN}v+B_TCt1x`A<Cj_t#MEwcWgT;?Kj#*Y_ZQt4OxwrlUX>J$+5#%b0W|U- zHb6WV%YK9@%$xD};%?vNO^J&a&wBXrk3hG75+y%cB4KvQ&zX0no{hoO(7qGJVhfw2 z06O_BkZ0CZrMdv~UmY{<F?`CZh`WfrdI{azw*c3w?9+B5%HBv#KY>uaQNDy%$au2V z{+Bk+6W#hEWf?qZvFz$l`<x~UR5r`akkEZx34&5N^E~*v!(%bDw6mco`<(52JaE9L zCUjWjxd$)=3m{TK&(B|)5Fex5^5J8}ujg>RA4$V#)7$~=`H<=+&Di$q%_DcBh<!7C zb(1tv12cxRqZbsyeD+QYOy7y=Rc|F2&91yj(l)mQ47o(l8e_>ec-|AgD*OwGl*q_$ zsH{kE8o0>!uQRJrytUNeyF{zT{}M#A;Y%!h+d=>B6+CVRD~Yp&AIrq85WK^**_C33 zNur;7`d|z7nCqfc#b|3|K0wXbH|3<&0wE(gTShSHn2ZK^<T2}zWdSNmbj#fPk1+i3 zM~T~aJ+EY44P$tHwInAo2fEiE*G%P^HwvkF{s5Ir9i_~Uzw0K8t!71!GUtI~qTJJ- zo8er)5@s{b#%Dvvi|vq3Nh!O$q$iAf|2F35_<VJ}h}n}1<0I}QY~ftzQm6)S!k>6_ zi$Tjhm$*Kt*Uk6bJ3exCgQ3%>M~a_TCvJGk@keR~XgD<tc`cK3U6N|>EZ9pJe&E3V zVstkHbuvT+Yf~*3bp%%8^kPJ3Airi7E=#E0{jzPtc`W>yRIylTVFs#M`e}ZAvJ8Iy zXn553L7@F+Ae|GwS{42U#o_!zP|`jjL~9UY9-Xe@0B79PRel+91<oWfo4>j$F#o7! zHST6)sNC~7LXG29Md?XHJ|O8rB(JxHTV-)@FMm`;;7N8h&YEe$1ujO#IGqQWr|0bB zku!k3zb=1(#~=O~`ut;2=$5o)Wk$)_z(AGk$P9RpE$;8<gU~sX-o(!`52r>wJcFD& zrADQ%4Rb~|CSJXf(z~(5?8jnIVe#y>Gp~8}IQQ5-mb4H>UW~!-%F?_V+u@Y8;}D^Z zbFUxVX5koUc_b{_w*VU|L9IMZKC|bhLHTiPIyg7llal~5B)}_4YWXNm$JsxgZ!_H% z4z0DcJXhTfr~A{Avd0<*(PlXoSBDlr={V<MFpP;DuGe?PsjSsochgHff2qB<a0DNR zlWi4Y$Tb1f4FPGKYxKG;@s-X}4XeVxgt#|u@ziq#!+&QVuJQ#*8U=)g?wg{RibKju zKRJMFj+|gX=H1W`;&T83o5zLSy=r&EM{fmdUc8K!o9ck<P7lF^iq;wSq|+RLcL*DV zNeC5YEee(H-b4QG_r;6;N#gm~Z_E(U`B61Q6)|8%4K`d9?UUmRY^$ZO=!T5b=|YJ; z4EFAL8(rwGATfj9+ixHh7$5ucHyZ`4;Mqu!P+AK83_gw2unFupG@Nm7!W6f-osCIT z#dwY^dB#598o!Sy3N42umgkAVqonzP;aI9*4FAecvwaRL?<%unPp<!bUa&mE9s^~^ zy*e-*{VFN#4=ZcZT${!)*+JuE0Gk?WK8}5s4QZ<eJA~3)Gn^e2q+(PX?a;FxlU3ng zKy>M|$R(Y5KRkYFmK>UyH?9i*ixbJ=a7Mnj1+sowU}^4pRlET%xq0*?JL}?Q@3!r1 z)SjNPZYze@<ZG}fOI$k{-DYKFl-}7|x<=;FXJalCq=C??2Kb<Zd@fl8ya;hjyw5QF zR3QAH{|I>XP`sC|l-&{c<EBnvcbIvis+>)!hk9mTP?i0+3(0#biWczxqpbSeU5rBm z3UQYIUR?ved;#gVZ-qf`+;V&3jX};DI#XWvQK}+7)oUUwgr=^Noxc$b?uaXWmt}9? zj2tuO_6kmEjSovS7MmfFb2KdGae4~@*fZ8z)rI+k*5>a&+S0UfKl&2F29w-7fR@l1 zKO?B?VkL(Hem6*x4b=c0nnIr$G#C*ynv+(6l%{$cW~5}XbPdR1Wu=VgWVtVo4}v&3 z`HsT+gH)i@+zj&Bf(3B0Rkf7kmUk;zePumoH{`+W@)BN?4w&OmDR^1NblS7u!zve( z`s$=}>?nJXeJmn46SZ08GZA6wD%t>lvw?0~Ud|uxiX%<1k?vUjf{&I}jg~Z*WI2e% zU9!+7tcjhzW@|jqLP^;&+4-+Q7bcAn8eCtgXpI1e7Z0&e%iM|wkK-*o{^ef;%16$` z7w@B=(zn=wl~BZC=A`99DH%Mkv*z#E^Us-TCoo3_h}=B3XWWt&6ETrb`JRmrZiMsd z2J-ewnv*0;cY20%^K8Kg(a3+XdaACx>hfE|6?;7V8f$O45Kjsnc7ofnrnw#ntxtn{ zl*uynHIxuy|6upEK7Z5Lvfi%*NB3vfEVjKK_0kIG&q`XjHlEYk&dtpi`z;~NlkWf) zj4hf5DI89(+4I#9(~uO^easm%RD;_cb3Ouw)KLf1YAK~fwlKwRd{~WM1&kUQ7uCDi zkPyEk8h$YYj&fzGJoe34J^YU#XDMm7ECE_c9{#cm+$zr#R|m+AuU;1D`>(IyVN<;Q z;t?cQgrrD-^jH_qHESKeDQPqmfCkO&5ZHVqLkEoSn>1FNhta|Vx}T7<jGE-5>yCAx z;}$@vX!XrTyd!x3eKAJ<xg7VsxBxV2mVHblbEwkRZ}2)}=^f&YLPGgq?R^grjDTqz zvl_YfkdJKiY;H;@<QvFuUPCUPJ#4n-2@%jQ{5Gh*SEPjR)vgi=zXod^XJa@wJlS>_ z%j@=e!)~;RHnKP}&Nrb(0UI2McHPV?1!#?wf*}{bufM0A*&na$p((A2s1B{a?(6_C zEteyLxb=jL%1cuhy4}t9pBN9(a-!L>+m4f54L2;ZUqk+2;N;u^BKLq#k1cdH64mqf z(!ONjtz`f6fQjFWWW5^DzV~Vl%@y!#pxF>^h#V`c!oRREQ37{VLpokr%dO^xlbQCC zmg#6x6AA?UrOPk9Q2(-G3n(l7@=$EQeF5-tQ<U=q^pB&wxC3Z|o6$1$SxJwUO)pi^ zwQoFBFM~y?m{G}S41#rXA70Q$s}kV$nqAgLI^5u6<ji=^8XoT<|NQ%+@V_g_!=sYv za7<mihRzbKbUdcSl|Fb~s_P5?J`Vc4D&ml#B_?EWZhS8<R7KAi1T&9iy(m(~pztQW zEMEO@UjyBgc>U&DGVvw*K7S_953k<CsHRY|5Q*m|<PeoXU&5!}8Vke!-Kfcx-Ap<0 zUk4L@5h`3Q<&>UmM-8^l<Vj{DOjn>YuVIeHfIWx#nTrxJ6di&J)Rb{MKZCO|?1Z^` z;R0#9hfVcK)h%ZVaN6f!z8cvxDjBfRVl#?lD`l1a?Q7+hH1zD@QIcn!5=X{In>RUt z&awMJ-aXO#whikEM%fZJ(kSnuXD^<Vt4d!9>Bc=&5+q3uFB`E@KcwTG%oWnVuxOc* zb?aKL^w<%HwiKQ<nn-&S2&ND7mnis!&;Pknp<cPI1N3h<K-U|1`1ua{4;i+s)Vktr zr_p@}Bj5m7N=ov0B7)L($`v+(o!U63{PYIvbC<3WxwNOi_qlk@R22Q)Kf%M}U0G}% zxJeyY@7Zviwg8bOrudv3ss`CDwPrBlX^qvxY-B1}neaGP$q9E#s?*E4<P9e`-a!BA zjq>9cML*5h|2#yl%BTvW`bq(Jt(`7|#AiQAynj}SF+T=9o#>~+b5PZ@XP_g;IV_9D zdVuJ?N<=?C?meGw*PbnkS^IWPr>M!K$gpmQvrPL6i&J>_rn^kfDtr*hq8M_=7(=l4 z(LH^z92!qDO_*!Ni5eFzZzWc^kk#ln6f77kK5q_*CC~2n@Jg=7_Q&qj8@wv~3yYI7 zoO&lzwYz780ektnmF2Hd<R!jNu)Tf>@a_@DKi?_gNp37$0@VlBu5611<hI~Q>^!6G z3+f4&w_VpT`<r?G#S_W@R2=^A1uE=QvN+(TdJT0U5yAQxdlFQ9thw_Yd-=&S<{R>g znp}rY=jtY{kCn9%5AU-n^LqOh`j@X^WRJf5P2FeY)d$f9mLz1tE2mdjU>6KD-^OCR zjMg-=m*#y=5an5%I@8lJeXNwldq7&8mJTkDM+>{{ddl^-!mjr`fxLqeK(p~C-n+-3 zoe7YZ6Z@v0VST8{&X0_PLLZd|>Bs$^is!r1L?7D?rL4+Z!e739w(~`c*0Zv{dz3`Z zwCTi%bqmn*R9Zd)sG|4lmUFvzWTBIquV?u$PE9?~WUiXy(=}88j@LG;!oRQxDM@M; z<}p$q15}7G^65_(;8nhO@wTnJcyMO%>jYg<cJ$*1pq~dA%L39vr*yVr02osjuMK^# z(D9&|_2M1pK_Gi3uaVe(vXVXSxMdu<R6Kbg?kWF68RWh!L)YNUUfwp>p=nf3kV*}n zt!tk%0&0!1E{fb8&%59$9dPqTe6bSf89f_74G5&NICA8mc=P!B2GZ--1(w{F@wD3= zPE^e2BdS+SJNPP#3(C|LeBMnstHp_A=ebX@FTn*-avaD#+@*1Rvxc@TZCuxz0M}Q9 zV5az=lIXMMeei$u-1m%((2@|LVqZ`ZEFf3*`n&_tUc+IrTA+IFEkW9|_QkQ!%~<t+ zCM?$+hI@RF(2PD7&G>Sz3Uc&<LRl(6Bh|7*Eq%|lmLG!gENBD2N9c9<xxKUP0R%V_ z4O<odg~eWViNzlaWjCd6H&SkLXBnPYGptwn6$u)wEAhV)2mDQe^xs|szI%X&Ki?M5 z;XxGoSTtk5?V<OW-iL<*frEI#5<bGBJh`k#o;iNvIP2cJqvfAUjPbTSON=1!!o;O@ zTL}Z8Q8>$}0w3C%k(;k1HTFogUKnH%)7ZTv@v(<#iyTvb)^?y2p;2SrlVqh%4U%%; z?)9qz<G(6O|0U$U6ZlfzgRIB?Tt$+$xHM|Bt$;e!_-(GV+Olm>GOkQgRSr`H+PwT+ zffYOi*?UC<U+!d#hfABqV+1#1XKkv=&q9m2Wu^n3ct410^+6TH2|{2`#S>-BR=#S= zS1-0|W@%st?fm??nHUct{4}`_bspA30s~ABU|zI!7WCUWdUn0?t!U5sVr<_RV_M2p z<<8c7r^&JA;d3&sKtejUx6*CbNYnGgU=e3g$&tiZ?vfccqnSSz2H-KfdYVVes_;LG zV71ha+*(Uz_>UBVSEpb#zp!}8$ttId#sdK!dXDe+uzmZc{E?LIv^eGuA4b>~$nf@$ z-@%I)TS#|<dgndL!qiK!p1aKzW8k4IK)J-`=}uYwvMTAmz~$O2&0>zOrkKgA>2pH| zTxkQeTI}XwdHH`=4TfAjqFv{lBq@<Q3J#pje05zAjS{PWQ_gXFRTO8(7KAWO;`sSL zKXQ#8vy?7YarY2nC?)PiVN-$$4|-6v5!!zesI9t*;t+6f(ea6yVp-mXqz&$KB|iz# zgJ`s+Y=(Lr937Y$3p1yC{}~5sM?7xsJXb3s^!QwF-w^vYo0L6Y(Y@%Fha=zfFWjS^ z4Rf$CoxM}dtg>tEM|2c`pu+i*?=`w%p#Xih_S$+lQiEZWjM$2vr509&e_`PV{eWG* zSHu#GchbBh&fM<c^A>(chlinfmY@4fKX2I%8uJ$YzbXG-0X&wKXZvsd8tAHr7ysqI z0r>a7hsSsSFZ9>fFl27hQx=CiPTb=MXM}Dj7D9>FOUoJW`%yvxgDf&7@`u(}UcP9G zN6Pn5JqpPMFdEV99=mxXslyt7RDa4nC#>FmZ3biuRxx2@{KOQw%!hyTvc&5PYEgbN zBU!+i>&d*T&sS7Ql=B~k35r_1LxYBw3kYfY>kNg*L)%|ko`Qg*SAVYZ-v^)X%mem_ zXRbH6T?rd#=VFD+Is3eM_2Z}scnxV`CpnBqFbNq5bdK2aE}~IDhUPFbSB!BkZ&lPG z!=ziP9bB{f6F5RpLuK3xqC88zw~F+HK+Dz9Iq7&*%NU=Rh`B2K3(HeR_L9g=CbVu< z42@oi$HUSS!slMrCW|_^D*epo`MHZPPqGFHqg+wo%s9tMY{j60?+Qf!^B<vqRiN~I zs|{PqYBDl04Kw*Y@~UuMTg}QE(dqAq3Imey;&+g0td+}yxap5HRpI9kQY&?`)l|!w zI6vS~J`f%O3paVA7AI_x>H)98PrbC*<+^&N$i=G{KqXes@&AFJ8!x~5<PC057r9na z2UUz)&ZB6UJ~%Mzz@B@l4io1{rqpHW3&h@@GcD$j3s9Tl;BmfOC1kSPf>>Ra&Uhu| z@MqkLxKGN{yBV1XlPOWG$iytsF6r}t-f*EFz<NaFw$kWRG|ic6=iJuL&him5g?-&V zB};!Xr3;3F(JUuGjaL+Ogp=;Ocjf)uXzvVB)}1A}P>autf<{FW{M}YG%}IJvWxL?> z=ux>cDxZGtpt3e}$wEd3ATJm{Psv(`e_@%BLY%UnS$}NZVT9gJ9OsvkRoP!wgpUE( z;42aSzx*f#++4da3&+cE3*vtT4`tzdD4!)!*=6DAu0dEJLYFQn6LOE-JSwmCC5zK` z;%SWftR<{<{|Q@<ZcW1LOqn+?#>?&ecUAt`7NfH7)duL>e+&J$1)V5RK5nlk;h(U~ z!<QhMolO(3Po=r+DJ-i-kpnSiu9qhKZ6D6xpp)JyQ-t2)Soj={4zbaJkMTILfuOds zeY#gW9bO)1-)SE){V;maehZGhPCIA{xn<?erl3;y)~~0tHp7CAXGYfUS~z76*_e3J zq8NP1jqi3oElgppth4j!LqH^KmXzCjcjo)Ddia+XJn{wE5vu=al9a;Z&xzNp@&!va z(%haC=Smd(AO2%O>Nl|Yul{RM&fV`4bT6(U|M`z_|M&mC`1Kz^$a1WHU9#xAu0A_t z-R*p_l~O3QgeD_`5#wOgY+?20TM#r^ds`_PYqe0|_4hT#X@=@>bp_q4*Tu7c1$g}u zNMKL2xTr2xUm~d#D+Z2nQ@IDY>8f{M33E`JA88U|Thwt{hZo}RjsPf;4$QX$u*Apx zt3XJXP-DwOIgAXA0EbKbXmPyOvX=I3wj<HLm><!*^9fRo(*bwN@Al4+?Ekd$&18Od z9e0$f@I@rsT>ApZa?SUW$tdjl&2tOfG!^655C9IPK~ebXp;HZyxv%Fn_TEO<4P_;v zN|vQ>41Czud1tG1bilL!{hT!v0nKtaQC1KC!lDZTK+VEFR{Ui}H6ZIk@MSnYt6We7 z;uR*&O;MHuynFWt=>PV2(7!Hf{SSW<LLGichM#_fwD}FB|Kh(b58i(!em*ZM9H$pA z6H|+=1J>qFok6Ms-Lq$JirtA#venMl?rS_D%Pdab&s!#H4Ms*we&Pj<+4^3+g8p}Z z4e8r&i^Bg#81qEwRS)#b6|(Ecnfeuy2AIIaY^vv&F&1^W8AS4Nz}p4zrMVYsBtLk9 zWyJSkbSE^!Q!WF!7r*<Be4az^fWu<X7c{=H>_0vQf+xGZaUKUN;$(9oh}Sk=NM{s; z9rOfdS^08!%Zld!mUOqvx#p)Z0hB!Nb>b-)I?7$&p|V&qTPEkj$St{lkct!DU&3Xf zEVahm%^-H8kBtVk4MsB#Mom`b_ZC`pgraIZzfr4d_%nWw_&Q)9eS+iej?1d>Ka!jb zaa)22hFBiWE&f%$3gIWGH&UI4ckkfU%WnW~UckdR!p*n8EiwDDc$Jtwa+dq+Eo|?g z!27%)blRe`D3T#L4>$^mhj8s@rMZ>te`|o^j)E^ng5a7`rwCo!>aA;`B`5KC<?n%$ z)wtr0sd2yk9i+eh8$ijzzgA)(m^X8QF9g6}5;KAxhw9R*!#WGUO}yRkK34@bP3=SS z?;X01*NNbu(vGu}O<nEkw>sqV{)_eL7HbwyHXU=fc8v<N5mj-Ixt*7Ff!quGqdli% zQ@8=s_ACfF9zxzhFZR~-Tfc{@*deU55Rn$zp*L>125QzwhrAE>lDsMVs1^q9CfS%0 z^Q;`OAwZtB;tn1<o*)2(qH+}-uo-5{jqy3UK~}HPk+LfM3k%u8E18%l?57K_GIKR2 zU{&^Cqi7Nr7NuR{^KV`QT>l30!#(uZSHj_*etv-A-@KGAcf<er|G?e<_J6>Zy^I%c zg*A_vxtOm#P0-;`kBqkPCa7A4tW_=;x-=n7AqcEs7II<(LA25a`eKmaZ~qG5ci+PJ z+uy?QvOxL0)zzPB_<rUh_lAF|s7Qm2C(09dl}|-XCZFxv^mFRn^YVCnKJa9@-kj>T zQNli@>?!OsHA>uB5n?F&dndH9I}>m^=bgji<Y@Inf?#M7>?rFwyC1aM1Jg6`+WBx6 z^EKGk<x~`Xe*g21r>*nvYZwM`HeYI6K>^4uH_|WyeRp}Ua;_QtWVNrHj48b)&<q#A zNu#P$WR2lpSTrF?vn)@^kX7qG*N5&)hdx(1Uu3}`UOEa$f0yC$Pj_(j+uxMuLhJA0 zZ@-1~|NSGp|NDQ07yq_+|NrBE5BXhLTyK9aO8+Oo`+FEayw}YA0zG%mqGuH!m82!E z(XPoH)<meG0QoBCy8d&#lNX%v^-I8;8yIeiCx3OLae7{i#>fWt>l1mmi%~_71v|f* zmZDZp_$L=+stRPPqs&ZNRE`s0e*d^pJ09G2Np`*U!`trphUd<)@u!YWG>Op*VP2eq z2gNCLsDr67dWuN0<k+<S1Zg0@f7vPpEctPkW59IE`Q3X>gtfE)CDKuH2IkaPCd*{g zSL2$p-*c+2wito(K!H$ekA|w$_l=BvDA~2B6@0-cI^mYB3je~Q2~nVl8CWYX5AETY zVDXqnjjMv?0>A{gS>-DdWbAxfUi`Z|*!<i>?tT#OK0iLf<Ny8l@Z!}Ky!;>j8-dcX z7zI$)8r&8%;LS@(CPHq(d;boG+q*KxgFw&&m)s#}IUG6AUfqz|$RioShm_sPVx`^l zq`^*s>EUJznUnIk(aq+{>X4Jf=sivFSF7wn-%UY5_!?hbmzb_`;#r(nsui+!#<Oh5 z9SB=VTpZd3-G;vy4UrEoJ(`kpt`237JH+<k9U7w*XWv-(CRc}Kt}UMO!uK3+7pACC z`K07-9zqbM8x{B=%AtG^F-mAmcX`)fO43!5ppuqn?J}2)iH~=c76~{?NK7ap3>jdP zZm@BBGFHjkdttoPswy||oSiV2d!L1Wg7XFVCCyxlnaHTuEw{#Xwv_p4Uj$bEN$PI= zS@ZkH<$zty4e?43$;CP0Ra+JQM-VNaIAMGKPfgO$7LfUzLfBP)J(994Tz&DNAFl4o zLi1kYdz)7YHgA8Ytu^x_bPpN<^ky}gmGjG+GC=XX=`HlU;FNFxd;i_L4?uSxAU!<j z;#Xq(gCGFfs8t%*$Ya+l7e7ClOpe2_;)W9lizk2m0><qIXv@o>dNjfk`L(euvf|=6 zK8H?%lOLO+)Hj%x&323^h*$R<c<sIQs=C2nSInAVazgdf3d+jz3o{Ikgj2R_$K zd?^w1Q5N$AY%iLPwo?liLM22KSbqsUP=vIfl)lO5q+YixWmeQlFOa1NsAsJ-xB#9( z8LyOZ<`*Lm-+xes%4#r~az^K(@ILouqyg3DIJF#8Yw(0vts0P?G|S50?vc!mr`ac+ zpHX5bJgsSt%c}4%EdDM%Cpkha&h%@Gey(zgNUWZevwQnjZ$K6*;c6e%OW?*jwP!uG zxUWBd(u7I9bdLW)QPlLNc%(nShy0hn2qoz7@F-mN+>(|$R_tZxzOxq1Wr6D%KUC^l z+-_C*b4y$CSlOe0lx4U33UMfTH;1eVX<awbgJJ+7HWj!7+b<v>o{29F(rue1Sl?l1 zf{@&+Pr<Q{xxmzD&@+LRSti&`tz6nS<OGDea36Ql4bb3g<mbl|Mt2lH?fr}FKcmCi zQKrB}-7i7QQ)v^1dfmK94OKQtJ6lu)KuD(zS`D~_=30zqH2hRTXK9pn)84RFVE7V3 z!>(874U`XG_$$hBhGD32cmCURWVF<;LHqKKlfKh(SIJ13LhlPR#R1RI_^ZOdv`E8) zO+nM{%gy4L=F@jXZmRuTGg2*^)fu8z`O1Vj@VNmKUMb0ke2BF)Nr`n}5-F!zW`u?m z*;<#(gMYDwv3R9;cP&f1ln8zE7IIPesd(*ad&NAwaYR83BmpU#V##D-{sGP<|M<0t zK)v_(&}}z0t3G)ao<PC4&$$<wDD=@hS-$5?Gu&a_ro#Q;YeW{s6rFSh6%sf#v-5b& z#n%|&Krm`q&4{O}yCqZIS!d@-a4B}M!zr?34SnKzHQ~fxI{!U>szc-aTaU5i&EABN zBZ1`PJocGeSulMklz^(M?T5X01C5TzX03bY!G>((a`J3}=cyRG<t0e>4{-PX7Ovr{ z!1kmNJQG*(d(ron@A4>lGb0<v9HK49>c<{NGpTZzpH3Gmv!xWuP_E%z=2B4Dzf%LB zjRj=A0H?e6uqyn^%Q<VHC3avnD;2O3FX5C_|5biP(!96WAH9ODM3cYcUcy^%@m>+h z$Ty-L?q7dZ79XyiXV0|2|M_ZLAj6H;(AQD1%_6Ju;4p1y2Qr!K*C!0|j>3!JUGiFc z)pq?B*X0FeTDL#(a(Z4HOginIG=fekdgu0O3sIPYZ`P&d(gmYs`p;}kT!i+~3|Blj zHEO@po^5;+Ob~1ED=wcSf$hSsqy)VpS-&A8;L)6xEZhsJ7<h?mV7Dl!1xoJ;HFWef zLq)~njj$NMS3k6JShS=-MFEudH@sD(%zJ{caY7(!qo3MY5>A)3Ebr1H&eaG<<7+Gy z3&Kcf0<iM{tnzh;FH{u{K6UvvR7hfj{FBKt$CH6aB#n-wD*5&f;9WtZ27^uACG`c= zI^d;DXDu+lYT*b7w@rN*_P(VNeVHug3+qFKyr-pP1cyrV5SAAIr`L_WW3nP2)bn@l zdI1!q*Md^^#<hu_nBTr_oLr_c2DBeoIQKk53fi@5h6gPg!eI=8S!_QT2c7^&1XFX? zc<=K7!;e3~mcw5_wOu4S6|N=MB@Y2$xbK-NVuF_RA#=8Tj8<MqZA>16lfSu7Nzws@ zX9Gg`?nGHV{0oaD$#~-sIOes3+6X~gmx5WFt1b4mN_7m9=jT4Zfs!bNobB0EUjFX! z0qDNOBKup&cejAwyeYqzcz)VQMzPZGs_U|vox9>BEt$PO&V_)v$7qV(br%?Rcfc8t zF)-ISZIvhXkxiib;n3V|3J=r?8Un-E5q_p~+p~(By*v*L?Qf&9oxz(<o-=<Rv}_-D zl)lbi$K(b!N&;l#`!Tb9uUZ$ZK*h$z+K)myOx~6E<=w4#-28VZL_6ByQ7Lw{g)R9Q z32MFtZ@=j++dk*YR>?Hk+@X@U6Q(`8R~ka|`6q25oz!Tr90_NjzpKK(u*jpBY^6Tx zhUnw#FcQQ#8m#iQ3daV=lnp}VrQrE0mmWA{--u8iA0fYgXA5{)MBWvc|A!v|8Q#CS z0eJHUsQl|CV?e9QpNkqpz`a)sLNZ9M#Jv7KLf)~ye41DmJbt;};#TVl5ckhsBDw6` z>8)a=n80b0*?1s#B8)HoxpLkRRO93T&BHZFbjPp|m>Xu{0W%hM(1hUO=Y@O$)C@(= zvoqTB<Oh)hBpSm~tt^1q@(aA9p){!~XqaT`qcTxqHKZ(5z`vK^I=A9wc1u6U<lQ7? z^V}|jT!~I{b%>uKFts5GiB`kUnt$7?v4a-pxBs2v1gr}G!qP14Q}!@HsFpo?t>)@m zeWgN%76lLO86pb_*XJ`G=)+x!L*5IIekZZRL6yn<hZ3XgV7z@N`*i(Q7MoR;1*^Ww zLc)XBeNzGeHQ}yG_4Hfo+#D!aoncNqLQc%GTQ7=9!ov0??jz<gj}<hBBdcqvs}Z)I z3j6jw`TP-|g!282k(Jt{6J3MSmqx&y5uh3b%eer{=c91Dv6mVSZf&W+uyh5;YY4&a zPnZE?uiFyxdi?2U*r2jtVo0X;`BY>FaVIpBtX|&@34<l%c<kl(DH*?lRqT1k$?ZmC z_??!_QVNgM8LDA*A}=gx$sTa_3pF(WR{2^5Ta51#Mjtx|Majd%gZ$8kQ8Kj$SqQBr znY*hok<Y6)(7$@2iD;|6xmOy5jxgNa8y`LZ%s2_OLt6OEluD+2@cMde-utxAWBT&H z0~zp+E!#g~)HpiAbfKx%%EC;hlJk+6vc{8|M<2(Ep@Z<j#Iu(RmcvYz7sl%4`}N5b zjI4HKSIizgm?rtPjqPjcrU!Qe<PY~1Ngt6*W?PMcRI<!Yx&B_0^Qws;zk6>B&cx4> z_oah*@*lZju+bzyZ?8K(Locl6`^ANC?l8sg_KMq@9`F$|RcXhieYY({uxkFVPROB0 zX;@=npXZ3Mhdr`8p-O+a1L1X)<mEw$bV?i=U%v%-{RZ+YCht-9yjAHR7Rg~}rNA8P zTfSyJV6D(@p1x<e=VZXF#fW2E-x>KjvN~*RN72MG`#w7<3r;fOo0@`FFAq@R<Qf;B zm;WS~7zWA%qVaMJ#tOx9xOoj;6s+n}!wk8zl09DAhK6d;RM{t|kA{Yn){;=;4d5%* zMf)ju*K%#FjE~Q-H;P(}wpC5om;RE_OI`e^yN?R<3*SY7Rr)f{Bj30me=OU{h|+di z$Z>e6bXAS79)r=5pi(QShu=X3d@|MYP4VhSYj(tQWZgb0K~Q;&1ZB(IAA?IlaHE7f zqZ;OXFTAwySXmYRrDgW8p0-+Ayr=%Ps`{^0$esnb2&9jq=%p3sivrV+*W&F<t_dss z?JFQ=(R+CVSbnI#vBi?ZO9QM>(_!J;!%BuJjMMg}e&`9fut1(e*s|PPPqYXntMXPI zrr`f%h*W)ds7gH>vWR<p-~+Z>&6RVH+mw7<gR)r!2)i8+p>93%&I!zLP#eQa|Dh(+ z`9=u4X(APMr%9gQ(@&#%%P8aLSuPc|K}hXjikt?i9Yre?GW2(kFuuPPW=fq?42kF@ z*$9A9jXW-!Qzp#Dy4WfW8kMgerS-Q^15inIv*Qt*(AP7ciy92xnJ=EoDY7d3ON&1T zCkIPB3}C&KZ~{QB@)e3Nx@R`VY9d|*$ik5~sd$G)k>9)$q7doE)otWE?(&$80<;$o za#i-n3)kcikcM32#*&X?iYLb1^|R^TOSw9clt%n*qOjJx;$kmxp2$Z{dCrM*mJASp zrUz+v)v3DB?N6i@x}+{7C61HrWjyctX5&WPsxE(S2EqKe9>&0_f}3yS|5g?3ZHw7? z8s-MbIM?}($!i6G@%w1cfim4enf21+1B^^>%Sm?Dx>gwVNa1%$)Puk^fV*#w&owzg z5;ddA4I}wBX}Y4ki%f`>w7-h9;1zmZ9Z!%BI89cCe`(=6HzbR_j)ylzg|s~Ohn}r! z*}25HRbCg|uTg~TD=hZsn0?g`Ba8JG?-Mx*uhVSrB=y8lp<-G0vJ;T3hUO`vilZok z63eGopv{ja8#Kj*nOf4<qV6R_2y*+x6`Ff^&ZL(ic>lU?q~H$6f=Y^Wi4v+XJWPOl z&e7K@_xuT5f#L+7Z*vNBtKYc|>e#yelV->A<U_4dH!9kt$1*5=*VL}Oy(T~rh8AY= zQan8$nsEOMuRa^XY%Fk?=KBKYGi9#7iOG$VWcLWG0Z<bQEz>?GNcz8-IrkWCvu8k? z;Ed2<2W~XT2w!<$`zr|#$fyaei=SQ5BU31%tVw$xK`7V9>hN~Zz<WSwmLaZ7?kb=V zqPyqM5i3^`k3K1pM-rGYkn_l2VbQP4@+!v&Uo(!}kKCVN%utC`A{6cSA->$y6nyne ztEcD<UuH@>`&J+&XMU&Lc{qCI*z4^#1-g?B4=^d0i>N~UUjM5<I8*kXz?Q;i@o8(| zgqym%MvKp_mAm;)B-H?VoIKf&Y&O-cZ-dP)gKmSvBftl(Y?1dNTcTu`b$o9|OJa;f zK{(6kcac67HY;u1<8|B`*B6@JO)tGIq!d%SFs7E@K<9;fSB3vE1fjMrJVw03lHC8? zta*Gkmb=QAE97<{Tte|`Mz=z&;^)Ln@34%ma=s`+l7*2@J+>?fykyN^TmjJ1jsUH^ zD<<L5@^J({XM&!ziz1SHtu*cB9+hN0tZi41w{%`%Kgm#kB0GQIlSH%mNWvojou(v1 z80JN@iU-e2+~N2Sd%IFIAghcq$bD#TRJI6eET4-9=Sw@vz0805UPCkhlP-vn6|c=r zdG%{!93#o8>$!XQJnon$V|r_9KLZAK>BUxI{bafT+Zg9&cdZKlvk;4iCF}NN+~v5g z!C$YK+d^R>IBytTC^)N_TWpS5VbWE<UtahwaU6GKf|eS0wnd)yXzpVMU6S042s(0i zul@JXKy96C20~>BMQ>I^yFZqQM&y<3bl}L%%crHWTyeW+Y<tpd7z(hzhZC022j57} zBZrw`!z7f)UX6lDNn?O41c_nF<oE9ZZ*N<y9{_eaR;I~|F;P#xbx1!LFWxmT5(SZ= z3}RfchrihZlpl27e4Jq_XTHCy!oRejL30N?Ld<q?5(Z}K8ax)1cj|dp`Fg}VYW1~o z$vJ}3U5lTiPRf+1Weu+y|DqD%XinTCV>A>6LT#I>sCa=t@P7Mt`^dMFA%Y4~`vJ6c z4dPtLe@OZvD8R0zcC1P|Db4i#lj(FtCl(dHK<0z~PQIP^{qr+0bYs-*EqZ`S)qjHB zpiw=`OjBpaes4VYHeJN&7(Ab1*w)~-MZ>b4e<U8_qC4KqNDadk)@vxS`+xZ!y3|X@ zNS^#ieka^>AHnI-q?~Zh_3!_BUIjO-;;xAEipU!4=aS9=M((i7jF;V3iFQCC{d&)> zT>BhZ75=5gA8aq-IA#a=hn!&pR$0Y`S-74hW;m#dP;bf!I#;>8_#2$>A5EDYfXgu| zWLSpp%>$o(3N`M(ID>`lNx=rGywHwHraN<`an+cQEB4m;&nB{XjAY&EXy=G=JFG)c z(}Ma9HN~HHhu=Lr+~vyG(1cRc@`aqtrM7bhNs5*_cDpYbLPXvY{Nx>ZC}X|*0NvdK zj0r6lFC5d2F^AX5@&<zEt_EFJ^df75q9}HSR_cB-MbG*NLm>Jq!0T(so#7%0;1bM; za(Q5t3kz8ThNl(x6uLsSG|660SY?&xQlL^xUa}Msj%%)kA0JQTy=A5JI0!#FWtw<? zi?FrHsf+QkC{0_6TGoG4IuJgi3e^V`ee&;Is<JZgdEuZY)<k9jzDJ*04vBA1pqmst z_F9YJ@c(RAF?F++N!Hl8AKa9&zh*L0xo?l(eGdg*)1gMF$#-&?SyHur7e2RS$YlP1 z@;PD4%ShG@55gD{%H?8!YtGE?H7q1`_#GF1K@Gh;Lp!Wxc^4MuS>8Mr$mmtMsqgg0 z?~~4+Qu10CX^_BB(`djO2_%bj{taH`%aoL@4!6Le88(0?XX^7a)eA!x-g9zSUm@k* zIXc(J>Y`E=0BI~g-`+!FisEt~#{$)+T10Qnkak$|jyMK2uJ#E#pTqyGv~ix&g#ZgY z_Kqz}9X!=7!CBvGyg%UUWW27_Cy3entV)O;!5^$NYkDion?M=`{AMy6n0)R3k?Tel z4&8~-BTQrBdT^Vu8USq2ju;FHrogePh8oGwjaFBtq;2gq?7<Ye#k=osKLBpGGS6BT zXU9gXFwi^z#V3R)3w=%3dpmH)$?QhLDA$>4JU@nGlGF}zn~TKEdN<e5^S`oRl15qy z@L7u9W%ckcEK&yQzn`+7EYP`8HfDWjE`;Q)@+Hez&j8ATF5yh}G@&idOJ2|_my-(X z3*D{;k$ZJNcjEajOR#v9og_8%e~x)L&cE5}lLUVpC*Qzy><Hmg4JTD+-TtoEXTCcu zqZYKMl0c0S+^IZpW$%PP<{DcvwAG|UV+EnMC(q*}<Ujl?q@wHvjc|h^c=!Id7X(UP z0%8L-5?Z%E1P?lxO$HJ9$k~c6Nm=<gsg`5+sWWq^vvRyHomF$cB&)*z7{V6em`{2c zz4lhhIx0T3uF7AnScu2|fOA9}tfIIM*rT!Z>LRr!?R|V<Z}su+PCM5LRBXyaZF}_) z=gR7sP~fB~OfVXtmGoA}d9q`g{_H=3c$V#W&=eM<0x7q|J~AwxhOoPxQzW{9rnu{8 zG%X388=}Gy!7+F<l%$-^m3Dyq=Lu|`Bs>pw;?jnO77T~znXz+q{{ZmAPmqQOn2Uxk zk}&F_mEJiMVq^BxQbWnqXMTRr(n)2kgq>Uf&Bx6~ab5Q1w67O0$_Xe(2Dis%BY^Vr zVEibn!oRRE{317e(;an_&@6uz_wp5L3opScU!U;8z}+~w=?I;dxThtaqbxj#o#ljD z7p_%4iS(SYfB&crQB=b#gdRdP?(Hj7tQ_tK7iN6oD{-G&Y1-6Fha*r!-fx7!<g+6w z!0I*UC@9n=gTuZ0E`9*bP+9c$IXr>>Z<u&;&v*=dfXGKP?;nEkV`c`m$?-kV%y)!` z;Ssu`@W+4sUg)P4l}Ec+S?5rWC1Dx9S4N-gAr!Kzs)U+NFCIQQr@u80A`h*kI$LGC z)MsOJRh0eJl^R=2<I9xSaM`MwRpEaW(LUD+jx3(ABoD)(KrC#qI1@PK;woR2u(!b7 z?buH|mym(b+cU@_B0aWI*}B#z>eC1>{^Q-f@~4{}FR@(lfOk!=h@L+sb0y$)^)*S* z1J&Yjk9E-Fs2zf4TskkJ3Um9`9wYak-61U_-#~$#=)m-390;%f^X-x)>Vd%K4bjk! zczRv4l}4)~_ETv_R#uHK`4v1y|M&>Qci%(r<qYPH=S-DJK&XCga(s;&p{g2j^0I}d zMxHOA$&g-upX|EnR%#fOUrNlLin3>RO$J|bm!xFU&-qeSHLJph%U#SSn!s_)!b<i+ z)(6szYE9VC*{$;R2`?xT%X?N>#}+*Q%RI-GcjwcIBy+LODN3I`+g5(3-Xkq+e{`RD zhpA1}ptD%Bg$ON3+%S1k6j32=Bie!Uge-Z@eO5X=)sWpjgYkoSi6Z9Nyh11-+xd`c zO!>O=aQoO`{F;dmx?K$ekQxoy34tC;*yYEcVf^`>c+{O;F033F4B=yKaVzV4vL-jE zz3r2G{FU}N394pVUw#;kKn{mcp1#)1zw+V+@{4O@*3-B>;qh#($I7bkO)jtWW3Q_x zHx-L{*W!0ud0n2rHfb@`XKH7{x-f~PXW9^}oGg-rTVVY>Xe`(DeGYMMm6jNmb3R%V z$GY$(?)#~u-eT9P`?Jg=^3vDjHku|1qVGdfHNDrPUzq1yaNi%agYSbHu+2eIP_#VX zSu5(3BnamEeYW*<UJB^1l!O*I7vXV)hkyAVwj7)9nP5i|1gVLiu;xL{AE-6-+$Aq{ zcE8Y$<H(A?yQ3LkR8beSN*OuQ)kYM4O1%*iCQY;W2)^D|UzX<<HIpV>Lg@<>&j}!p z9SnVeifG>79v5dJk1RrcX_GaM#*cB=SNZHk+NMa$&0fla@Wd=#eriUp_9-#sp~M1{ z!TYPO|MbF@Sv`CCABxg{)7uJ=Ep>7u2!N2QNX*wjV_pZw{X7gyTnbL=;xS|$2x=1= z;q=Ew<(jJIkAs*Wz4IAV4?BM5{dzSA!4HXE*;hr=M|LMo=F#uW12i9s!kC1)X5V{@ z;VkE;n7t}^tSEr|&Lx1RsC)ZdkQJLBmAL)zum1`giO1`QeVNcu4$viq?g3lNayd_) zaPAc;U~gP5d#5%237f7*^Yp1x?;bZAhTw8pdMsYNGGTTvUIA@;Ild_UjuD0OlXT9P zd}n|49zH$+i^xNfFw~7+K**u`={yb^6x3pH!d?|D##O#Pk)@(b?%13x1fCG;?QU%m z#1+*0<f3@C7;y0ZUKQNv=D!2nPIz<v^6pIeIjY*(8q~u2bYQwq&tWHToMzuSy}18? z$L{lFyQM>A>m^coZ{^yIiiSAyVXT1M<i<2SXg0ncTaPu(=J(%tbB>SW@BrzjcaVR4 zSG{+Z(e{bG1;9`jj>Ai}9yZh<TX)3Dp^p(UwtNCe;6y>J$Qe0cz5smn3h>1X;mHt1 z?*;xA3c9p`{;PlcBSFPJ*~eFXzL;_kU7+^8U}!j*CRbs2*pHk0D!)#lS_}0YXb*+C z3^BFm&z~z;<x`0V<%IC)?$JuRJiIR2AD~9JgBMCPX|57wX*Fp){j<+OiEt(*VVL$S z{R2y(n<s8ex<bc2=Uw6ctml`a!5Lusc?cmkmQj4(;BX6nvRALlo|NlAhHvNYxlsf# zDSOnhRhi@MJ&b?&BW(FQ>ue{b(Vm@eWzt-V*JyC<*nC{;R9HLSIR=tpaz0AA>ui2% z+DYv`m*3r#xP8CTxp8K`vHKkxCOK7BroD>`nnyL7HG9~mBDelQEmYxpmW)rQZSXNJ zZR@S_SxaiM)^z5r=N78cpk>=N2sdzxukvX{?m@Y)Ls2yS>l=wZGd%2lw#b{J7?fPI z-~-pXOLxQ#BnvN!zMRE`9P;0%^cO#O%Z2|zIw{9s$~!-a7fcM&`kfiPm4#F{HRqQM zMb-Fywu@J5?XiWxT0k|4qRwOr`Th=u@4ka>)LsIf(C9Vsi7RBdvITpqk_q?#I?!H5 z=d#OWrLJz&h@+RaRkF<o(pHTO<RBestD%=K0k3Zw!A{cTK>Ubr=Zf!r1aW)vXe%um z<(`l-8*P=ZQNjv)&fb!xJx+`VA9a;qMg-h_93eBwiz#;3<yEH#<rlrvr?U4Wv5ZKb z9r(F2)zcNWu66WhNmN0Od-#tpjJA?ic01?Aw?IhE+PB{a4>-gh1p}zW&F>%Xp!?w` zfFFNWQY26GgEVPvTX%|oR>MW_2lq(Rl7bQ1uEF^5Fw1C89CzI-pLQ>93Y34Xnf&7I zCumpn<GrD)!v7e8P+^&;UIT}DJjU<HRV$}j75-N%&&}hoFp&t6(Sfe=ON%HO=Gh;z zC6<B4^aCV4NmvKin}r_*XZP2;w|y*`hY)tc4hw%T&3^)acfmMiqr#H`LrI;O0@={e z+_Lg9%tb@tv#ZESagUF%y?cP6K>0mq*Msh!EHTfS{6NTZ*z<oZ-}P-o%dcvPOF$+h z>CTLS`km$+^qngEflZMD;pdx|K-brLkGfP713$K`3jZTWy>UmpaM>(N@3y4yPgL_O z0Q*%6FK}I&qCY2(;bb7j)>Rj&)yMxOMY8tUxPNzNjj^i6tX@LSyIDn_W$pz|e~&fx z{x=In*pk}6)7?oZ$pu=kt$MNTTfKPOnOXL#6Hg)Wt`YULt4o?__u&EZKmMUO<b%TM zR<TMon~}Zt&WxwLbB^8}P!st0y&5&9FZ&MFIFl)3s#NKfm%r$Q{^cu3H#d-Z|Gu+a zuTQhauL}Poh&-obve?oS+d7ED6sWXYFA8f}-d8DFmX)9N2qaVZ+2e~<;qSionAWA> zQwf?PN}SzYyiKpp(K0lB`@NHzD=hH?+`|u?@;hUH;w0aRUm6f}PZW&Ged2w)_g=@b z?{X*E=~#8VB{ZTcV;w7Ls0u=|o}vB(&iH3tNF^gmz4iRz4&YCJhV<b*;AU$y##qln z=gc(xkL3`fAqUgWgb{I5R03-^KIt!Dy#>%R?IknIY;GyLD~cXoU2CarQTDmx?t41p zPoeVHxBR1tWwPQiTdGJYRx%)pPAt4$<yR%LR9MgKbD4Er$IJ7S<{<IxczJ!^KDlr_ zmwEPwQQN{g1WcyPX3D!N9?K>_SXxJ)LVdu&O#ggDOXp~~_(-|tFf=4z*VC+uJ`a}p z4<7&2#zYMB&yW!|IvBd=dVBoKI~czEQwbYf$<AUos3irabG5#oneU*JBbJRVGG@$$ z<yg`{#~Aix)s}mbrX)R?D*u6m3V4dwB{cBnHRNJ!K;H{<U!qTo;I0b)BZ<G9$JpCw z+dDZ1fYLa&gq2mkHW81+nAy&I1Q2Sb^CE-eD!;Vw>&?*qV~OWWT$r+z<blG;Ty8en zkMBctFc$7o4T4|tRxjx>oc|dS0xS7L^@@cwM{*pf)$>cWN&DRc=6b4ROp*a=D1VQ` zJ>cyvjKBW_Y<h#yDJXGJ!^3$mlz*pu*Y%pUA1VyIqpAMR$1K}q?LMgEwQ;jGFp~9s zyMgZQs}jH8a?^XOx&QR)cUAZo7L7M)B2GTaHAAVtgtU<d3^{&OV5~em=u4jlSQqH8 zPZ-Kf=^B(=<jnhD6vo)0c|8~w-~m?oJcMt_rre)V;`yrnoN$GC*HzzSa|GItL;NtS zU6(PbQSIVDlQy&d6I|yfCGL0VrwZ<d7Ai;n*fvzeoJ7Nl(v-Dx)OC*?`D96M0JXNA zcdGmaY#3N691=I08tY0aZc|aL+$FcW{Q&p>^v|$iuU)YAL9+a#;nb(zK-DC6Rx)^> zef^Bu+fcaeD@C<+)Z=?3l=-8K?G1JKym&2V^kD4Ee#Ns-S2xfV&mPMUBM1Ej+uj~4 ztHS?iax7_%^=jsazAM&WfqN+RwX@O*jQp$oszhPJvqgfDWY5vU*sFXtB08dYgF}fi zcVL_x=Cx*TZAYznOx-Vd`#JF6Pu86pdTyR9#Bpf$bEy+r`go_@&^9FP8xQ8NYtY-t zeY7hcdFh;H!0G;$<6>D*?fiKNNtP?x{_Ym?AN~lNhX)u+TpkHpw?Om6(G<Es^4=7O z_XoC_y=2d>A%|Qy>Y+0%FV~bP!1+}B*Q0O*^w7U}Es1(#aqI?;*(dnLR`pfke<V3I zh9;F6(6Uuh@%V|_<GjjOCTQ`^GnNIXSoObHQd*y*&rSG_jFw{|k>bbVB|Z$QeDZ7! z(YRmj^m<6<N<S*v82e{$TSC{Ql(e2qx7Tw&bj&;rA3T*`pmRx8p<{L9ot~B?R-Z^i z9Z2EKtyGVwCFp^lvpG+!uG5DHfZu-y^z(c1v{UG7(il#$!t;aYv2;x>CjZ^fhu_Iz zw61)59;r6X(Sgxe`-Wo3q!%wBzy3y?IV?Zjru#+Rw^iYPB#}lHaEcXYJ(i0liseBc z@@S+G>q7mt3d4jcA78Q)z*6Dsmc`VN(lsUT^Ao;ba+~aLj#rO&_t1}a|G>OQFGVv* zd+)m9G41;26VJ__rR7kbz@f1?6tN%fS7*jc-6T%VY<7%iNRr~9-+5o$F<YD-3~Hyw z#V^-lG%SQPIZ&FPbpn-3DO_1c^Y`CF{_#hR#aqQd;yQmT9#DfrJ$uchH+7h^HUF^? zinOf7n)vE9`=b0=v6p{GgAJ5k*Qj`}OGxg`ixREfh<7g>1HYuUhgIQUTpCn{GvbHC zA`no&ksjz{u^{^r|B=4QpSa9smCr*2&~*3eWNuyaI4T?rt9+*7Z?^;7+*R-K_D*8% z8q|YI+3H??B)jo3Cgb=v3ts+QXl5WR{e4!(Kg|(}5H}DiUE`xC&7^mJenGXQyv^xu z<pQ)Az5)Vb497|)K2yov=MHrDchLX&Cm6o_ml8AY%t%7%MCTO%3JR!0h2ZJ>ZSI-T z1<Txzq(3$r7)fc1S^0OhvWmoLlZ^ecmUnNy0W3<N+5N;wU{<|f7`0s${zsD*PdZj4 z$)oG#hdXN>Y;dv4uUZ_Q6glA$5RZpLkeM3O8fW{jF8&uGe5*TV-D4)cOtl4UWSdoG zx_m>cI`Oq9WTMw&>`~-%Wsf^G=li$UoRb`W;(ls)R;0=yeA3@3jHe+6te(8U`Eu^m zxaxf*D|=G_zn9PP@e%MZMcMx&H^td#K12;;%&(c|xMU;5VR0{?v+p_Kk6YVHR(@`c zjCgK6D|;@{EAJLjHXa2vNJd5u-n;>N$>cg)plv6mev`a~Ur1Jk|B*yUQ$n}A{etb~ z;GbfJVu|1TSF3z|g7%;6OnE1uJfa&UVK1Af;$GA$pSzGP1)}sGMi@A}XF8&C#><A9 zBu1Xhwr@O3Zu5jbSK!HF%#&?rnf{C8EjS-jBcXNr3|q*5vv+^2b0e4*EjMGpJ$Lj! z$xD{?VzidG60p~2oQ+(jD*o|jc>IUoi+7%TJ%+wQErVX?YM)hVUYeYbc<zk0a;aod z>YXY0dD5g*R%Fe=5Ico)`Ch&PeD$)RwO7S=^TcbvvmLA+{;x=CQV3XS$T2%*C4NxO z=s}E4k{5b7sw>tizd{kZPF@_wDctyk7<gIsd6atJp2MW+q#;)Mg~S>7<dx?nv%3NE z-GhdYrK}DcBs~nEDQNki;!l=TrZ%84dCru3tS`^82Ruv_y4RL3LI0|JJAvY9a1H>A z+v5mM7b#wR9xXaSq9Mg!&zjZFNqS7KgPdU>l(7tDIRM#8mGv`@jg#a(!Mpcx{~!Oc zc;DsS5n5_huI`wpV9X>lnEZEV{gYO>Ns%&->sPPeD;*FmX0NBEC_Tqw9wcr*a=ra_ zqj?zIURUEXFokJ;8Cl!oeoR4Fuy~)|N!}G7V3n^<Y&m|`Gr)Nu95WOTuZ-BQq5LmS zSiuhu1)|>e5}si%x#k67{SWw^Wd}i?8*DLg@(gz0l9rqB92+`pS?yDUVA4zm_32f^ z0NUpv;jkW$9UUXlK|v#QTgojo0onHlYkNy#437zGnq2qOEewDEkFa6mg2`|sVbxmQ zqV6H^ztzNLYh50dxpu$nBt9>plyT6INIQ8oScuFxqT-N^GA}k^=+!Iezj*_&-NI0| z?KwQl2E%6*mgV%sA7Pb`BKi^>f3p>4T9y1N8u1*3FC0$R`5yjH9^+g~^1e(#Z?f}2 z`mVEh8C{-u*)cZkF@UKjIbVe^)S&y{@!z-|?AbRed!l)6fGTaTE>M2_nbpZ-%Ql0Y zQ{~Oi4(Ge#H-p7VcEDNhi=yXv`j5Bp_;>$Qe!nluwG-nAox{S=darbdRK5|>04LY6 z61_wQz$3OqNJb#s(BY6RkF(KJ8cUo$y?qOGa}7g57g*U3Ou8cm!si2|GnVmx!73M% zY53}dg;tckbz%awKKTV1@zb<TT;=l-_AbV<7-?LPj^@&@%L*@8%xGreLSYKS&u7tD z<v4LQE;afe?gZA)=6$L{be;wbKjuBluhV<(ThPqoKWRd8aH?J7+^OM;5~<_2^(vd< z1XI>oJaM%E^naX;_o%%168u(0pTyheO#MNi{bVq>7z3CE^<V}{Z7x)fSd5|J@BRa9 z^F!H!%kK)Rm9Tl^otE4=Xa2O)oz5qF8MJ%*%xVmZ5ae*J+<(iIghf?W>JkDNm@W6s zZvk%#l%KZcm>XdU?6QYxJ`)e0^8d<h1OBJg%fGC|SMcatU6DF$1Zz0SZC&MS5_y2y zB7Te*u&cDr#>*}(I=T8|l`mEVttlF7yuH`Ig+xZb(}r-ku*ZH8YKD_)?9t&za5F^e z=q(G5AX5KY<Da-YKvn*(WK|Jnf!oLIOnNqR0?Sosb^97AKw+w5W&FU&aesLS5C7rs zBqu;zB&Nr$Zvk0@o4kWkFl?W#;~)T;kuu#T*&^Iw$eXZviCK|R0f(aCH?O{d?#)Zc zoo3@FZn%@%xu1gael#gMzFdd@IpM$icYj|z%m1!imH!1+xtJ&nbXqNxybb~zC5=su zoWQrL`>#^a>X1*B;?baY48!(3t@34yWbku4-J<l-U}@!6%F=BQt%Pq^x3~h<@Q^+i z*gSoFy7Qj&UQi-@s0yElF*_2SE7lIMlcbOBQI^aC&$1sBDM)4jc<CLVQ{Obd9fRkn z)dhVypY2aS!NcGG|FD%fdz$7D08Ek($ztv_&&0QcJTe2O3Svzd6eJ_$+Myv1Vo>)* z^}l)v@b<SbZnl6uTJ?mxe4l}+GylJ{j%Qu}p8%?@VSy<YIX(aY002ovPDHLkV1lyq BXfXf) literal 0 HcmV?d00001 diff --git a/apps/frontend/src/app/colors.scss b/apps/frontend/src/app/colors.scss index 9ae668e9..924db4a7 100644 --- a/apps/frontend/src/app/colors.scss +++ b/apps/frontend/src/app/colors.scss @@ -28,12 +28,26 @@ --new-menu-hover: #fff; --menu-shadow: 0 8px 30px 0 rgba(0, 0, 0, 0.5); --popup-color: rgba(65, 64, 66, 0.3); + --border-preview: transparent; + --preview-box-shadow: none; + --linkedin-border: #2e3438; + --linkedin-bg: #1b1f23; + --linkedin-text: #c6c7c8; + --facebook-bg: #242526; + --facebook-bg-comment: #333334; + --instagram-bg: #0b1014; + --tiktok-item-bg: #2a2a2a; + --tiktok-item-icon-bg: #FFF; + --youtube-bg: #0F0F0F; + --youtube-button: #F1F1F1; + --youtube-action-color: #272727; + --youtube-svg-border: #A0A0A0; } .light { --new-back-drop: #2d1b57; - --new-settings: #ECEEF1; - --new-sep: #D5D9DD; - --new-border: #EAECEE; + --new-settings: #eceef1; + --new-sep: #d5d9dd; + --new-border: #eaecee; --new-bgColor: #f0f2f4; --new-bgColorInner: #ffffff; --new-bgLineColor: #e7e9eb; @@ -62,6 +76,23 @@ -3px 13px 14px 0 rgba(55, 52, 75, 0.09), -1px 3px 8px 0 rgba(55, 52, 75, 0.1); --popup-color: rgba(55, 37, 97, 0.2); + --border-preview: #f1f0f3; + --preview-box-shadow: 0 386px 108px 0 rgba(38, 32, 64, 0), + 0 247px 99px 0 rgba(38, 32, 64, 0.01), + 0 139px 83px 0 rgba(38, 32, 64, 0.03), + 0 62px 62px 0 rgba(38, 32, 64, 0.04), 0 15px 34px 0 rgba(38, 32, 64, 0.05); + --linkedin-border: #e9e5df; + --linkedin-bg: #fff; + --linkedin-text: #707070; + --facebook-bg: #f1f0f3; + --facebook-bg-comment: #f6f6f6; + --instagram-bg: #fff; + --tiktok-item-bg: #EEF1F0; + --tiktok-item-icon-bg: #454645; + --youtube-bg: #FFF; + --youtube-button: #000; + --youtube-action-color: #F1F1F1; + --youtube-svg-border: #1A1A1A; } } diff --git a/apps/frontend/src/components/launches/general.preview.component.tsx b/apps/frontend/src/components/launches/general.preview.component.tsx index 4a85731c..049891a7 100644 --- a/apps/frontend/src/components/launches/general.preview.component.tsx +++ b/apps/frontend/src/components/launches/general.preview.component.tsx @@ -49,7 +49,7 @@ export const GeneralPreviewComponent: FC<{ }); return ( - <div className={clsx('w-full md:w-[555px]')}> + <div className={clsx('w-full p-[15px]')}> <div className="w-full h-full relative flex flex-col"> {renderContent.map((value, index) => ( <div diff --git a/apps/frontend/src/components/launches/helpers/pick.platform.component.tsx b/apps/frontend/src/components/launches/helpers/pick.platform.component.tsx index 5dba1827..58d21896 100644 --- a/apps/frontend/src/components/launches/helpers/pick.platform.component.tsx +++ b/apps/frontend/src/components/launches/helpers/pick.platform.component.tsx @@ -254,7 +254,7 @@ export const PickPlatforms: FC<{ {integration.identifier === 'youtube' ? ( <img src="/icons/platforms/youtube.svg" - className="absolute z-10 -bottom-[5px] -end-[5px]" + className="absolute z-10 bottom-0 -end-[5px]" width={20} /> ) : ( diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index 2a1ea2d7..5ddf682f 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -288,7 +288,7 @@ export const MenuComponent: FC< <ImageWithFallback fallbackSrc={`/icons/platforms/${integration.identifier}.png`} src={integration.picture || '/no-picture.jpg'} - className="rounded-[8px]" + className="rounded-[8px] min-w-[36px] min-h-[36px]" alt={integration.identifier} width={36} height={36} @@ -499,7 +499,7 @@ export const LaunchesComponent = () => { > <div className={clsx( - 'bg-newBgColorInner p-[20px] flex flex-col gap-[15px] transition-all absolute start-0 top-0 w-full h-full overflow-auto scrollbar scrollbar-thumb-fifth scrollbar-track-newBgColor' + 'bg-newBgColorInner p-[20px] flex flex-col gap-[15px] transition-all absolute start-0 top-0 w-full h-full overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-fifth scrollbar-track-newBgColor' )} > <div className="flex items-center"> diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 52a2cfa5..9f2c5731 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -46,6 +46,8 @@ import { VerticalDividerIcon, NoMediaIcon, } from '@gitroom/frontend/components/ui/icons'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useShallow } from 'zustand/react/shallow'; const Polonto = dynamic( () => import('@gitroom/frontend/components/launches/polonto') ); @@ -432,7 +434,7 @@ export const MediaBox: FC<{ onClick={addRemoveSelected(media)} > {!!selected.find((p: any) => p.id === media.id) ? ( - <div className="flex justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]"> + <div className="text-white flex justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]"> {selected.findIndex((z: any) => z.id === media.id) + 1} </div> ) : ( @@ -492,6 +494,7 @@ export const MediaBox: FC<{ export const MultiMediaComponent: FC<{ label: string; description: string; + mediaNotAvailable?: boolean; dummy: boolean; allData: { content: string; @@ -535,6 +538,7 @@ export const MultiMediaComponent: FC<{ dummy, toolBar, information, + mediaNotAvailable, } = props; const user = useUser(); const modals = useModals(); @@ -688,46 +692,50 @@ export const MultiMediaComponent: FC<{ </div> {!dummy && ( <div className="flex gap-[8px] px-[12px] border-t border-newColColor w-full b1 text-textColor"> - <div className="flex py-[10px] b2 items-center gap-[4px]"> - <div - onClick={showModal} - className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" - > - <div className="flex gap-[8px] items-center"> - <div> - <InsertMediaIcon /> - </div> - <div className="text-[10px] font-[600] maxMedia:hidden block"> - {t('insert_media', 'Insert Media')} + {!mediaNotAvailable && ( + <div className="flex py-[10px] b2 items-center gap-[4px]"> + <div + onClick={showModal} + className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" + > + <div className="flex gap-[8px] items-center"> + <div> + <InsertMediaIcon /> + </div> + <div className="text-[10px] font-[600] maxMedia:hidden block"> + {t('insert_media', 'Insert Media')} + </div> </div> </div> - </div> - <div - onClick={designMedia} - className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" - > - <div className="flex gap-[5px] items-center"> - <div> - <DesignMediaIcon /> - </div> - <div className="text-[10px] font-[600] iconBreak:hidden block"> - {t('design_media', 'Design Media')} + <div + onClick={designMedia} + className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" + > + <div className="flex gap-[5px] items-center"> + <div> + <DesignMediaIcon /> + </div> + <div className="text-[10px] font-[600] iconBreak:hidden block"> + {t('design_media', 'Design Media')} + </div> </div> </div> + + <ThirdPartyMedia allData={allData} onChange={changeMedia} /> + + {!!user?.tier?.ai && ( + <> + <AiImage value={text} onChange={changeMedia} /> + <AiVideo value={text} onChange={changeMedia} /> + </> + )} </div> - - <ThirdPartyMedia allData={allData} onChange={changeMedia} /> - - {!!user?.tier?.ai && ( - <> - <AiImage value={text} onChange={changeMedia} /> - <AiVideo value={text} onChange={changeMedia} /> - </> - )} - </div> - <div className="text-newColColor h-full flex items-center"> - <VerticalDividerIcon /> - </div> + )} + {!mediaNotAvailable && ( + <div className="text-newColColor h-full flex items-center"> + <VerticalDividerIcon /> + </div> + )} {!!toolBar && ( <div className="flex py-[10px] b2 items-center gap-[4px]"> {toolBar} @@ -778,16 +786,28 @@ export const MediaComponent: FC<{ setCurrentMedia(settings); } }, []); - const [modal, setShowModal] = useState(false); - const [mediaModal, setMediaModal] = useState(false); const [currentMedia, setCurrentMedia] = useState(value); + const modals = useModals(); const mediaDirectory = useMediaDirectory(); - const closeDesignModal = useCallback(() => { - setMediaModal(false); - }, [modal]); + const showDesignModal = useCallback(() => { - setMediaModal(true); - }, [modal]); + modals.openModal({ + title: 'Media Editor', + askClose: false, + closeOnEscape: true, + fullScreen: true, + size: 'calc(100% - 80px)', + height: 'calc(100% - 80px)', + children: (close) => ( + <Polonto + width={width} + height={height} + setMedia={changeMedia} + closeModal={close} + /> + ), + }); + }, []); const changeMedia = useCallback((m: { path: string; id: string }[]) => { setCurrentMedia(m[0]); onChange({ @@ -798,8 +818,18 @@ export const MediaComponent: FC<{ }); }, []); const showModal = useCallback(() => { - setShowModal(!modal); - }, [modal]); + modals.openModal({ + title: 'Media Library', + askClose: false, + closeOnEscape: true, + fullScreen: true, + size: 'calc(100% - 80px)', + height: 'calc(100% - 80px)', + children: (close) => ( + <MediaBox setMedia={changeMedia} closeModal={close} type={type} /> + ), + }); + }, []); const clearMedia = useCallback(() => { setCurrentMedia(undefined); onChange({ @@ -811,17 +841,6 @@ export const MediaComponent: FC<{ }, [value]); return ( <div className="flex flex-col gap-[8px]"> - {modal && ( - <MediaBox setMedia={changeMedia} closeModal={showModal} type={type} /> - )} - {mediaModal && !!user?.tier?.ai && ( - <Polonto - width={width} - height={height} - setMedia={changeMedia} - closeModal={closeDesignModal} - /> - )} <div className="text-[14px]">{label}</div> <div className="text-[12px]">{description}</div> {!!currentMedia && ( diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 9a07161a..5f995411 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -129,11 +129,13 @@ export const EditorWrapper: FC<{ setLoadedState, selectedIntegration, chars, + comments, } = useLaunchStore( useShallow((state) => ({ internal: state.internal.find((p) => p.integration.id === state.current), internalFromAll: state.integrations.find((p) => p.id === state.current), global: state.global, + comments: state.comments, current: state.current, addRemoveInternal: state.addRemoveInternal, dummy: state.dummy, @@ -343,8 +345,9 @@ export const EditorWrapper: FC<{ <div className={clsx( 'relative flex-col gap-[20px] flex-1', - (items.length === 1 || !canEdit) && 'flex', - !canEdit && !isCreateSet && 'bg-newSettings rounded-[12px]' + (items.length === 1 || !canEdit || !comments) && 'flex', + ((!canEdit && !isCreateSet) || !comments) && + 'bg-newSettings rounded-[12px]' )} > {isCreateSet && current !== 'global' && ( @@ -370,7 +373,7 @@ export const EditorWrapper: FC<{ setLoaded(false); addRemoveInternal(current); }} - className="text-center absolute w-full h-full left-0 top-0 items-center justify-center flex z-[101] flex-col gap-[16px]" + className="text-center absolute w-full h-full p-[20px] left-0 top-0 items-center justify-center flex z-[101] flex-col gap-[16px]" > <div> <div className="w-[54px] h-[54px] rounded-full absolute z-[101] flex justify-center items-center"> @@ -398,9 +401,9 @@ export const EditorWrapper: FC<{ className={clsx( 'relative flex flex-col gap-[20px] flex-1 bg-newSettings', index === 0 && 'rounded-t-[12px]', - index === items.length - 1 && 'rounded-b-[12px]', + (index === items.length - 1 || !comments) && 'rounded-b-[12px]', !canEdit && !isCreateSet && 'blur-s', - !canEdit && index > 0 && 'hidden' + ((!canEdit && index > 0) || (!comments && index > 0)) && 'hidden' )} > <div className="flex gap-[5px] flex-1 w-full"> @@ -411,6 +414,7 @@ export const EditorWrapper: FC<{ </div> )} <Editor + comments={comments} editorType={editor} allValues={items} onChange={changeValue(index)} @@ -430,14 +434,16 @@ export const EditorWrapper: FC<{ chars={chars} childButton={ <> - {canEdit && items.length - 1 === index ? ( + {((canEdit && items.length - 1 === index) || !comments) ? ( <div className="flex items-center"> <div className="flex-1"> - <AddPostButton - num={index} - onClick={addValue(index)} - postComment={postComment} - /> + {comments && ( + <AddPostButton + num={index} + onClick={addValue(index)} + postComment={postComment} + /> + )} </div> {!!internal && !existingData?.integration && ( <div @@ -466,21 +472,23 @@ export const EditorWrapper: FC<{ } /> </div> - <div className="flex flex-col items-center gap-[10px] pe-[12px]"> - <UpDownArrow - isUp={index !== 0} - isDown={index !== items.length - 1} - onChange={changeOrder(index)} - /> - {items.length > 1 && ( - <TrashIcon - onClick={deletePost(index)} - data-tooltip-id="tooltip" - data-tooltip-content="Delete Post" - className="cursor-pointer text-[#FF3F3F]" + {comments && ( + <div className="flex flex-col items-center gap-[10px] pe-[12px]"> + <UpDownArrow + isUp={index !== 0} + isDown={index !== items.length - 1} + onChange={changeOrder(index)} /> - )} - </div> + {items.length > 1 && ( + <TrashIcon + onClick={deletePost(index)} + data-tooltip-id="tooltip" + data-tooltip-content="Delete Post" + className="cursor-pointer text-[#FF3F3F]" + /> + )} + </div> + )} </div> </div> ))} @@ -500,6 +508,7 @@ export const Editor: FC<{ appendImages?: (value: any[]) => void; autoComplete?: boolean; validateChars?: boolean; + comments: boolean | 'no-media'; identifier?: string; totalChars?: number; selectedIntegration: SelectedIntegrations[]; @@ -513,17 +522,14 @@ export const Editor: FC<{ pictures, setImages, num, - validateChars, identifier, appendImages, - selectedIntegration, dummy, chars, childButton, + comments, } = props; - const user = useUser(); const [id] = useState(makeId(10)); - const newRef = useRef<any>(null); const [emojiPickerOpen, setEmojiPickerOpen] = useState(false); const t = useT(); const editorRef = useRef<undefined | { editor: any }>(); @@ -547,6 +553,9 @@ export const Editor: FC<{ const paste = useCallback( async (event: ClipboardEvent | File[]) => { + if (num > 0 && comments === 'no-media') { + return; + } // @ts-ignore const clipboardItems = event.clipboardData?.items; if (!clipboardItems) { @@ -563,10 +572,13 @@ export const Editor: FC<{ } } }, - [uppy] + [uppy, num, comments] ); - const { getRootProps, isDragActive } = useDropzone({ onDrop }); + const { getRootProps, isDragActive } = useDropzone({ + onDrop, + noDrag: num > 0 && comments === 'no-media', + }); const valueWithoutHtml = useMemo(() => { return stripHtmlValidation('normal', props.value || '', true); @@ -667,6 +679,7 @@ export const Editor: FC<{ <div className="flex bg-newBgColorInner rounded-b-[6px] cursor-default"> {setImages && ( <MultiMediaComponent + mediaNotAvailable={num > 0 && comments === 'no-media'} allData={allValues} text={valueWithoutHtml} label={t('attachments', 'Attachments')} diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 8bbf5ccf..53923e43 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -1,6 +1,13 @@ 'use client'; -import React, { FC, useCallback, useMemo, useRef, useState } from 'react'; +import React, { + FC, + ReactNode, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; import { AddEditModalProps } from '@gitroom/frontend/components/new-launch/add.edit.modal'; import clsx from 'clsx'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -32,6 +39,7 @@ import { TrashIcon, DropdownArrowSmallIcon, } from '@gitroom/frontend/components/ui/icons'; +import { useHasScroll } from '@gitroom/frontend/components/ui/is.scroll.hook'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -388,14 +396,17 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { {currentIntegrationText} Settings </div> <div> - <ChevronDownIcon rotated={showSettings} className="text-white" /> + <ChevronDownIcon + rotated={showSettings} + className="text-white" + /> </div> </div> <div id="social-settings" className={clsx( !showSettings && 'hidden', - 'px-[12px] pb-[12px] text-[14px] text-textColor font-[500] max-h-[400px] overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner' + 'px-[12px] pb-[12px] text-[14px] text-textColor font-[500] max-h-[300px] overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor' )} /> <style> @@ -413,9 +424,12 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { </div> </div> <div className="flex-1 relative"> - <div className="absolute top-0 p-[20px] left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner"> + <Scrollable + scrollClasses="!pr-[20px]" + className="absolute top-0 p-[20px] pr-[8px] left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" + > <ShowAllProviders ref={ref} /> - </div> + </Scrollable> </div> </div> </div> @@ -438,7 +452,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { </div> <div className="pr-[20px] flex items-center justify-end gap-[8px]"> {existingData?.integration && ( - <button onClick={deletePost} className="cursor-pointer flex text-[#FF3F3F] gap-[8px] items-center text-[15px] font-[600]"> + <button + onClick={deletePost} + className="cursor-pointer flex text-[#FF3F3F] gap-[8px] items-center text-[15px] font-[600]" + > <div> <TrashIcon /> </div> @@ -498,7 +515,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { disabled={ selectedIntegrations.length === 0 || loading || locked } - className="disabled:cursor-not-allowed disabled:opacity-80 hidden group-hover:flex absolute bottom-[100%] -left-[12px] p-[12px] w-[206px] bg-newBgColorInner" + className="rounded-[8px] z-[300] disabled:cursor-not-allowed disabled:opacity-80 hidden group-hover:flex absolute bottom-[100%] -left-[12px] p-[12px] w-[206px] bg-newBgColorInner" > <div className="text-white rounded-[8px] bg-[#D82D7E] h-[44px] w-full flex justify-center items-center post-now"> Post Now @@ -531,3 +548,17 @@ After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} f </div> ); }; + +const Scrollable: FC<{ + className: string; + scrollClasses: string; + children: ReactNode; +}> = ({ className, scrollClasses, children }) => { + const ref = useRef(); + const hasScroll = useHasScroll(ref); + return ( + <div className={clsx(className, hasScroll && scrollClasses)} ref={ref}> + {children} + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/picks.socials.component.tsx b/apps/frontend/src/components/new-launch/picks.socials.component.tsx index 6451ea66..fabd5f30 100644 --- a/apps/frontend/src/components/new-launch/picks.socials.component.tsx +++ b/apps/frontend/src/components/new-launch/picks.socials.component.tsx @@ -81,7 +81,7 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ {integration.identifier === 'youtube' ? ( <img src="/icons/platforms/youtube.svg" - className="absolute z-10 -bottom-[5px] -end-[5px] min-w-[16px]" + className="absolute z-10 bottom-0 -end-[5px] min-w-[16px]" width={16} /> ) : ( diff --git a/apps/frontend/src/components/new-launch/providers/facebook/facebook.preview.tsx b/apps/frontend/src/components/new-launch/providers/facebook/facebook.preview.tsx new file mode 100644 index 00000000..a782ab54 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/facebook/facebook.preview.tsx @@ -0,0 +1,338 @@ +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { textSlicer } from '@gitroom/helpers/utils/count.length'; +import { FC } from 'react'; +import { VideoOrImage } from '@gitroom/react/helpers/video.or.image'; + +const Icons = () => { + return ( + <svg + width="31" + height="16" + viewBox="0 0 31 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M8 0C5.87827 0 3.84344 0.842855 2.34315 2.34315C0.842855 3.84344 0 5.87827 0 8C0 10.1217 0.842855 12.1566 2.34315 13.6569C3.84344 15.1571 5.87827 16 8 16C10.1217 16 12.1566 15.1571 13.6569 13.6569C15.1571 12.1566 16 10.1217 16 8C16 5.87827 15.1571 3.84344 13.6569 2.34315C12.1566 0.842855 10.1217 0 8 0Z" + fill="url(#paint0_linear_2511_139661)" + /> + <path + d="M12.162 7.338C12.338 7.461 12.5 7.583 12.5 8.012C12.5 8.442 12.271 8.616 12.026 8.737C12.1262 8.90028 12.1581 9.09637 12.115 9.283C12.038 9.627 11.723 9.894 11.443 9.973C11.564 10.167 11.602 10.358 11.458 10.593C11.273 10.888 11.112 11 10.4 11H7.5C6.512 11 6 10.454 6 10V7.665C6 6.435 7.467 5.39 7.467 4.535L7.361 3.47C7.356 3.405 7.369 3.246 7.419 3.2C7.499 3.121 7.72 3 8.054 3C8.272 3 8.417 3.041 8.588 3.123C9.169 3.4 9.32 4.101 9.32 4.665C9.32 4.936 8.906 5.748 8.85 6.029C8.85 6.029 9.717 5.837 10.729 5.83C11.79 5.824 12.478 6.02 12.478 6.672C12.478 6.933 12.259 7.195 12.162 7.338ZM3.6 7H4.4C4.55913 7 4.71174 7.06321 4.82426 7.17574C4.93679 7.28826 5 7.44087 5 7.6V11.4C5 11.5591 4.93679 11.7117 4.82426 11.8243C4.71174 11.9368 4.55913 12 4.4 12H3.6C3.44087 12 3.28826 11.9368 3.17574 11.8243C3.06321 11.7117 3 11.5591 3 11.4V7.6C3 7.44087 3.06321 7.28826 3.17574 7.17574C3.28826 7.06321 3.44087 7 3.6 7Z" + fill="white" + /> + <path + d="M23 0C20.8783 0 18.8434 0.842855 17.3431 2.34315C15.8429 3.84344 15 5.87827 15 8C15 10.1217 15.8429 12.1566 17.3431 13.6569C18.8434 15.1571 20.8783 16 23 16C25.1217 16 27.1566 15.1571 28.6569 13.6569C30.1571 12.1566 31 10.1217 31 8C31 5.87827 30.1571 3.84344 28.6569 2.34315C27.1566 0.842855 25.1217 0 23 0Z" + fill="url(#paint1_linear_2511_139661)" + /> + <path + d="M25.473 4C23.275 4 23 5.824 23 5.824C23 5.824 22.726 4 20.528 4C18.414 4 17.798 6.222 18.056 7.41C18.736 10.55 23 12.75 23 12.75C23 12.75 27.265 10.55 27.945 7.41C28.202 6.222 27.585 4 25.473 4Z" + fill="white" + /> + <defs> + <linearGradient + id="paint0_linear_2511_139661" + x1="8" + y1="0" + x2="8" + y2="16" + gradientUnits="userSpaceOnUse" + > + <stop stopColor="#18AFFF" /> + <stop offset="1" stopColor="#0062DF" /> + </linearGradient> + <linearGradient + id="paint1_linear_2511_139661" + x1="23" + y1="0" + x2="23" + y2="16" + gradientUnits="userSpaceOnUse" + > + <stop stopColor="#FF6680" /> + <stop offset="1" stopColor="#E61739" /> + </linearGradient> + </defs> + </svg> + ); +}; + +export const FacebookPreview: FC<{ + maximumCharacters?: number; +}> = (props) => { + const { value: topValue, integration } = useIntegration(); + const current = useLaunchStore((state) => state.current); + const mediaDir = useMediaDirectory(); + + const renderContent = topValue.map((p) => { + const newContent = stripHtmlValidation( + 'normal', + p.content.replace( + /<span.*?data-mention-id="([.\s\S]*?)"[.\s\S]*?>([.\s\S]*?)<\/span>/gi, + (match, match1, match2) => { + return `[[[${match2}]]]`; + } + ), + true + ); + + const { start, end } = textSlicer( + integration?.identifier || '', + props.maximumCharacters || 10000, + newContent + ); + + const finalValue = + newContent + .slice(start, end) + .replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">` + + newContent.slice(end).replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `</mark>`; + + return { text: finalValue, images: p.image }; + }); + return ( + <div className="py-[15px] flex flex-col px-[15px] w-full gap-[20px] bg-bgFacebook rounded-[12px]"> + <div className="flex gap-[8px]"> + <div className="w-[36px] h-[36px]"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] w-[36px] h-[36px]" + /> + </div> + <div className="flex flex-col leading-[18px]"> + <div className="text-[14px] font-[500]">{integration?.name}</div> + <div className="text-[12px] font-[400] text-[#A3A3A3] flex gap-[4px] items-center"> + <span>30m •</span> + <span> + <svg + xmlns="http://www.w3.org/2000/svg" + width="10" + height="10" + viewBox="0 0 10 10" + fill="none" + > + <path + d="M5 10C4.30833 10 3.65833 9.86867 3.05 9.606C2.44167 9.34367 1.9125 8.9875 1.4625 8.5375C1.0125 8.0875 0.656333 7.55833 0.394 6.95C0.131333 6.34167 0 5.69167 0 5C0 4.30833 0.131333 3.65833 0.394 3.05C0.656333 2.44167 1.0125 1.9125 1.4625 1.4625C1.9125 1.0125 2.44167 0.656167 3.05 0.3935C3.65833 0.131167 4.30833 0 5 0C5.69167 0 6.34167 0.131167 6.95 0.3935C7.55833 0.656167 8.0875 1.0125 8.5375 1.4625C8.9875 1.9125 9.34367 2.44167 9.606 3.05C9.86867 3.65833 10 4.30833 10 5C10 5.69167 9.86867 6.34167 9.606 6.95C9.34367 7.55833 8.9875 8.0875 8.5375 8.5375C8.0875 8.9875 7.55833 9.34367 6.95 9.606C6.34167 9.86867 5.69167 10 5 10ZM4.5 8.975V8C4.225 8 3.98967 7.90217 3.794 7.7065C3.598 7.5105 3.5 7.275 3.5 7V6.5L1.1 4.1C1.075 4.25 1.052 4.4 1.031 4.55C1.01033 4.7 1 4.85 1 5C1 6.00833 1.33133 6.89167 1.994 7.65C2.65633 8.40833 3.49167 8.85 4.5 8.975ZM7.95 7.7C8.11667 7.51667 8.26667 7.31867 8.4 7.106C8.53333 6.89367 8.64383 6.67283 8.7315 6.4435C8.81883 6.2145 8.8855 5.97917 8.9315 5.7375C8.97717 5.49583 9 5.25 9 5C9 4.18333 8.773 3.4375 8.319 2.7625C7.86467 2.0875 7.25833 1.6 6.5 1.3V1.5C6.5 1.775 6.40217 2.01033 6.2065 2.206C6.0105 2.402 5.775 2.5 5.5 2.5H4.5V3.5C4.5 3.64167 4.45217 3.76033 4.3565 3.856C4.2605 3.952 4.14167 4 4 4H3V5H6C6.14167 5 6.2605 5.04783 6.3565 5.1435C6.45217 5.2395 6.5 5.35833 6.5 5.5V7H7C7.21667 7 7.4125 7.0645 7.5875 7.1935C7.7625 7.32283 7.88333 7.49167 7.95 7.7Z" + fill="currentColor" + /> + </svg> + </span> + </div> + </div> + </div> + <div + className="text-[14px] font-[400] whitespace-pre-line" + dangerouslySetInnerHTML={{ + __html: renderContent?.[0]?.text, + }} + /> + {!!renderContent?.[0]?.images?.length && ( + <div className="h-[280px] -mx-[15px] overflow-hidden flex"> + {renderContent?.[0]?.images.map((image, index) => ( + <a + key={`image_${index}`} + className="flex-1" + href={mediaDir.set(image.path)} + target="_blank" + > + <VideoOrImage autoplay={true} src={mediaDir.set(image.path)} /> + </a> + ))} + </div> + )} + <div className="flex text-textLinkedin text-[12px] font-[400] items-center"> + <div className="flex flex-1 gap-[10px] items-center"> + <Icons /> + <div className="">You & 12 other</div> + </div> + <div className="gap-[9px] items-center flex"> + <div>20 Comments</div> + </div> + </div> + <div className="pt-[8px] flex text-[14px] font-[700] px-[32px] justify-between border-t border-borderLinkedin text-textLinkedin"> + <div className="flex gap-[4px] items-center"> + <svg + width="24" + height="24" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M20.2983 12.23C20.5762 12.435 20.832 12.6383 20.832 13.3533C20.832 14.07 20.4705 14.36 20.0836 14.5617C20.2417 14.8338 20.2922 15.1606 20.2241 15.4717C20.1026 16.045 19.6052 16.49 19.1631 16.6217C19.3541 16.945 19.4141 17.2633 19.1868 17.655C18.8947 18.1467 18.6405 18.3333 17.5162 18.3333H12.9373C11.3773 18.3333 10.5689 17.4233 10.5689 16.6667V12.775C10.5689 10.725 12.8852 8.98333 12.8852 7.55833L12.7178 5.78333C12.7099 5.675 12.7305 5.41 12.8094 5.33333C12.9357 5.20167 13.2847 5 13.812 5C14.1562 5 14.3852 5.06833 14.6552 5.205C15.5726 5.66667 15.811 6.835 15.811 7.775C15.811 8.22667 15.1573 9.58 15.0689 10.0483C15.0689 10.0483 16.4378 9.72833 18.0357 9.71667C19.711 9.70667 20.7973 10.0333 20.7973 11.12C20.7973 11.555 20.4515 11.9917 20.2983 12.23ZM6.7794 11.6667H8.04256C8.29382 11.6667 8.53478 11.772 8.71245 11.9596C8.89011 12.1471 8.98993 12.4015 8.98993 12.6667V19C8.98993 19.2652 8.89011 19.5196 8.71245 19.7071C8.53478 19.8946 8.29382 20 8.04256 20H6.7794C6.52814 20 6.28718 19.8946 6.10951 19.7071C5.93184 19.5196 5.83203 19.2652 5.83203 19V12.6667C5.83203 12.4015 5.93184 12.1471 6.10951 11.9596C6.28718 11.772 6.52814 11.6667 6.7794 11.6667Z" + fill="currentColor" + /> + </svg> + <div>Like</div> + </div> + <div className="flex gap-[4px] items-center"> + <svg + width="24" + height="24" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <mask + id="mask0_2511_139673" + style={{ maskType: 'alpha' }} + maskUnits="userSpaceOnUse" + x="-1" + y="2" + width="21" + height="21" + > + <rect + x="-0.195312" + y="2.88281" + width="19.6964" + height="19.6964" + fill="currentColor" + /> + </mask> + <g mask="url(#mask0_2511_139673)"> + <path + d="M1.44531 20.9371V6.1648C1.44531 5.71343 1.60617 5.32689 1.92787 5.00518C2.24903 4.68402 2.6353 4.52344 3.08668 4.52344H16.2176C16.669 4.52344 17.0555 4.68402 17.3772 5.00518C17.6984 5.32689 17.859 5.71343 17.859 6.1648V16.013C17.859 16.4644 17.6984 16.8509 17.3772 17.1726C17.0555 17.4938 16.669 17.6544 16.2176 17.6544H4.72805L1.44531 20.9371ZM3.08668 16.9773L4.05098 16.013H16.2176V6.1648H3.08668V16.9773Z" + fill="currentColor" + /> + </g> + </svg> + <div>Comments</div> + </div> + <div className="flex gap-[4px] items-center"> + <svg + width="24" + height="24" + viewBox="0 0 24 24" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M4.21168 20.2107C4.14582 20.2111 4.08035 20.2005 4.01793 20.1795C3.88741 20.1371 3.77445 20.0529 3.69651 19.94C3.61857 19.827 3.57998 19.6915 3.58668 19.5545C3.58668 19.4607 4.23043 10.3857 13.3304 9.6732V6.4107C13.3303 6.28645 13.3673 6.16498 13.4365 6.06181C13.5058 5.95864 13.6042 5.87846 13.7192 5.8315C13.8343 5.78455 13.9607 5.77295 14.0824 5.79819C14.2041 5.82343 14.3154 5.88436 14.4023 5.9732L20.5711 12.2732C20.6856 12.39 20.7497 12.5471 20.7497 12.7107C20.7497 12.8743 20.6856 13.0314 20.5711 13.1482L14.4023 19.4482C14.3154 19.537 14.2041 19.598 14.0824 19.6232C13.9607 19.6485 13.8343 19.6369 13.7192 19.5899C13.6042 19.5429 13.5058 19.4628 13.4365 19.3596C13.3673 19.2564 13.3303 19.135 13.3304 19.0107V15.8107C7.25543 16.042 4.76481 19.8732 4.73981 19.9201C4.68342 20.0091 4.60544 20.0825 4.5131 20.1333C4.42076 20.1841 4.31708 20.2107 4.21168 20.2107ZM14.5804 7.94195V10.2576C14.5805 10.4196 14.5177 10.5754 14.4052 10.692C14.2927 10.8086 14.1392 10.8769 13.9773 10.8826C8.08981 11.0982 5.98356 15.0201 5.22731 17.542C6.78981 16.192 9.57418 14.542 13.9179 14.542H13.9461C14.1118 14.542 14.2708 14.6078 14.388 14.725C14.5052 14.8422 14.5711 15.0012 14.5711 15.167V17.4826L19.2586 12.7138L14.5804 7.94195Z" + fill="currentColor" + /> + </svg> + <div>Share</div> + </div> + </div> + {renderContent.length > 1 && ( + <> + <div className="flex items-center"> + <div className="text-[14px] font-[700]">Most relevant</div> + <div> + <svg + width="20" + height="20" + viewBox="0 0 20 20" + fill="currentColor" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M12.5437 8L7.4563 8C7.05059 8 6.84741 8.56798 7.13429 8.90016L9.67799 11.8456C9.85583 12.0515 10.1442 12.0515 10.322 11.8456L12.8657 8.90016C13.1526 8.56798 12.9494 8 12.5437 8Z" + fill="#A3A3A3" + /> + </svg> + </div> + </div> + {renderContent.slice(1).map((value, index) => ( + <div key={index} className="flex flex-col gap-[12px]"> + <div className="flex gap-[6px] leading-[17px]"> + <div className="h-[34px]"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] h-[34px] w-[34px]" + /> + </div> + <div className="flex flex-col gap-[6px] min-w-[150px]"> + <div className="flex flex-col gap-[4px] bg-bgCommentFacebook py-[8px] px-[12px] rounded-[12px]"> + <div className="flex flex-col"> + <div className="flex items-center gap-[6px]"> + <div className="text-[13px] font-[500]"> + {integration?.name} + </div> + </div> + </div> + <div + className="whitespace-pre-line text-[14px] font-[400]" + dangerouslySetInnerHTML={{ + __html: value.text, + }} + /> + {!!value.images?.length && ( + <div className="h-[100px] mt-[12px] -mx-[15px] overflow-hidden flex"> + {value.images.map((image, index) => ( + <a + key={`image_${index}`} + className="flex-1" + href={mediaDir.set(image.path)} + target="_blank" + > + <VideoOrImage + autoplay={true} + src={mediaDir.set(image.path)} + /> + </a> + ))} + </div> + )} + </div> + <div className="flex font-[400] text-[12px] text-textLinkedin items-center"> + <div className="flex gap-[16px] flex-1"> + <div className="font-[700]">9h</div> + <div className="font-[700]">Like</div> + <div className="font-[700]">Reply</div> + </div> + <div className="flex gap-[4px]"> + <div>2</div> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + > + <path + d="M8 0C5.87827 0 3.84344 0.842855 2.34315 2.34315C0.842855 3.84344 0 5.87827 0 8C0 10.1217 0.842855 12.1566 2.34315 13.6569C3.84344 15.1571 5.87827 16 8 16C10.1217 16 12.1566 15.1571 13.6569 13.6569C15.1571 12.1566 16 10.1217 16 8C16 5.87827 15.1571 3.84344 13.6569 2.34315C12.1566 0.842855 10.1217 0 8 0Z" + fill="url(#paint0_linear_2511_139920)" + /> + <path + d="M10.473 4C8.275 4 8 5.824 8 5.824C8 5.824 7.726 4 5.528 4C3.414 4 2.798 6.222 3.056 7.41C3.736 10.55 8 12.75 8 12.75C8 12.75 12.265 10.55 12.945 7.41C13.202 6.222 12.585 4 10.473 4Z" + fill="white" + /> + <defs> + <linearGradient + id="paint0_linear_2511_139920" + x1="8" + y1="0" + x2="8" + y2="16" + gradientUnits="userSpaceOnUse" + > + <stop stopColor="#FF6680" /> + <stop offset="1" stopColor="#E61739" /> + </linearGradient> + </defs> + </svg> + </div> + </div> + </div> + </div> + </div> + </div> + ))} + </> + )} + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/facebook/facebook.provider.tsx b/apps/frontend/src/components/new-launch/providers/facebook/facebook.provider.tsx index 68fdbf20..4b246b4a 100644 --- a/apps/frontend/src/components/new-launch/providers/facebook/facebook.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/facebook/facebook.provider.tsx @@ -7,6 +7,7 @@ import { import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/facebook.dto'; import { Input } from '@gitroom/react/form/input'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { FacebookPreview } from '@gitroom/frontend/components/new-launch/providers/facebook/facebook.preview'; export const FacebookSettings = () => { const { register } = useSettings(); @@ -25,7 +26,7 @@ export default withProvider({ postComment: PostComment.COMMENT, minimumCharacters: [], SettingsComponent: FacebookSettings, - CustomPreviewComponent: undefined, + CustomPreviewComponent: FacebookPreview, dto: FacebookDto, checkValidity: undefined, maximumCharacters: 63206, diff --git a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx index 82bf421e..ff658e2d 100644 --- a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx @@ -42,6 +42,7 @@ interface CharacterCondition { } export const withProvider = function <T extends object>(params: { + comments?: boolean | 'no-media'; postComment: PostComment; minimumCharacters: CharacterCondition[]; SettingsComponent: FC<{ @@ -84,7 +85,6 @@ export const withProvider = function <T extends object>(params: { date, isGlobal, tab, - setTab, setTotalChars, justCurrent, allIntegrations, @@ -92,20 +92,23 @@ export const withProvider = function <T extends object>(params: { setEditor, dummy, setChars, + setComments, + setHide } = useLaunchStore( useShallow((state) => ({ date: state.date, tab: state.tab, - setTab: state.setTab, global: state.global, dummy: state.dummy, internal: state.internal.find((p) => p.integration.id === props.id), integrations: state.selectedIntegrations, + setHide: state.setHide, allIntegrations: state.integrations, justCurrent: state.current, current: state.current === props.id, isGlobal: state.current === 'global', setCurrent: state.setCurrent, + setComments: state.setComments, setTotalChars: state.setTotalChars, setPostComment: state.setPostComment, setEditor: state.setEditor, @@ -133,12 +136,14 @@ export const withProvider = function <T extends object>(params: { ); if (isGlobal) { + setComments(true); setPostComment(PostComment.ALL); setTotalChars(0); setEditor('normal'); } if (current) { + setComments(typeof params.comments === 'undefined' ? true : params.comments); setEditor(selectedIntegration?.integration.editor); setPostComment(postComment); setTotalChars( @@ -217,12 +222,12 @@ export const withProvider = function <T extends object>(params: { ) ), fix: () => { - setTab(1); setCurrent(props.id); + setHide(true); }, preview: () => { - setTab(0); setCurrent(props.id); + setHide(true); }, }; }, @@ -255,7 +260,7 @@ export const withProvider = function <T extends object>(params: { }} > <FormProvider {...form}> - <div className={current ? '' : 'hidden'}> + <div className={clsx('border border-borderPreview rounded-[12px] shadow-previewShadow', !current && 'hidden')}> {current && (tab === 0 || (!SettingsComponent && !data?.internalPlugs?.length)) && diff --git a/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx b/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx index c269fc64..14c6e901 100644 --- a/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx +++ b/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx @@ -10,6 +10,7 @@ import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.v import { InstagramDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/instagram.dto'; import { InstagramCollaboratorsTags } from '@gitroom/frontend/components/new-launch/providers/instagram/instagram.tags'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { InstagramPreview } from '@gitroom/frontend/components/new-launch/providers/instagram/instagram.preview'; const postType = [ { value: 'post', @@ -57,7 +58,7 @@ export default withProvider<InstagramDto>({ postComment: PostComment.COMMENT, minimumCharacters: [], SettingsComponent: InstagramCollaborators, - CustomPreviewComponent: undefined, + CustomPreviewComponent: InstagramPreview, dto: InstagramDto, checkValidity: async ([firstPost, ...otherPosts], settings) => { if (!firstPost.length) { @@ -92,4 +93,5 @@ export default withProvider<InstagramDto>({ return true; }, maximumCharacters: 2200, + comments: 'no-media' }); diff --git a/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx b/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx index e69de29b..edf87cbb 100644 --- a/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx +++ b/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx @@ -0,0 +1,220 @@ +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { textSlicer } from '@gitroom/helpers/utils/count.length'; +import { FC } from 'react'; +import { VideoOrImage } from '@gitroom/react/helpers/video.or.image'; +import { SliderComponent } from '@gitroom/frontend/components/third-parties/slider.component'; + +export const InstagramPreview: FC<{ + maximumCharacters?: number; +}> = (props) => { + const { value: topValue, integration } = useIntegration(); + const current = useLaunchStore((state) => state.current); + const mediaDir = useMediaDirectory(); + + const renderContent = topValue.map((p) => { + const newContent = stripHtmlValidation( + 'normal', + p.content.replace( + /<span.*?data-mention-id="([.\s\S]*?)"[.\s\S]*?>([.\s\S]*?)<\/span>/gi, + (match, match1, match2) => { + return `[[[${match2}]]]`; + } + ), + true + ); + + const { start, end } = textSlicer( + integration?.identifier || '', + props.maximumCharacters || 10000, + newContent + ); + + const finalValue = + `<strong class="text-[15px] font-[600]">${integration?.name} </strong>` + + newContent + .slice(start, end) + .replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">` + + newContent.slice(end).replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `</mark>`; + + return { text: finalValue, images: p.image }; + }); + return ( + <div className="py-[10px] flex flex-col px-[15px] w-full gap-[10px] bg-bgInstagram rounded-[12px]"> + <div className="flex gap-[10px] items-center"> + <div className="w-[36px] h-[36px]"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] w-[36px] h-[36px]" + /> + </div> + <div className="flex flex-col leading-[18px]"> + <div className="text-[15px] font-[600]">{integration?.name}</div> + </div> + </div> + {!!renderContent?.[0]?.images?.length && ( + <SliderComponent + className="h-[585px] rounded-[8px] overflow-hidden" + list={renderContent?.[0]?.images.map((image, index) => ( + <a + key={`image_${index}`} + className="flex-1" + href={mediaDir.set(image.path)} + target="_blank" + > + <VideoOrImage + autoplay={true} + src={mediaDir.set(image.path)} + /> + </a> + ))} + /> + )} + <div + className="text-[14px] font-[400] whitespace-pre-line" + dangerouslySetInnerHTML={{ + __html: renderContent?.[0]?.text, + }} + /> + <div className="py-[8px] text-textColor flex text-[14px] font-[700] gap-[10.5px]"> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="22" + height="20" + viewBox="0 0 22 20" + fill="none" + > + <path + d="M10.7232 18.2722C10.7232 18.2722 0.792969 12.7112 0.792969 5.95866C0.792969 4.76493 1.20656 3.60807 1.96337 2.68491C2.72018 1.76175 3.77346 1.12932 4.94401 0.895206C6.11455 0.661097 7.33006 0.839776 8.38371 1.40084C9.43737 1.96191 10.2641 2.87071 10.7232 3.97261V3.97261C11.1823 2.87071 12.0091 1.96191 13.0627 1.40084C14.1164 0.839776 15.3319 0.661097 16.5024 0.895206C17.673 1.12932 18.7263 1.76175 19.4831 2.68491C20.2399 3.60807 20.6535 4.76493 20.6535 5.95866C20.6535 12.7112 10.7232 18.2722 10.7232 18.2722Z" + stroke="currentColor" + strokeWidth="1.58884" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + <div>121</div> + </div> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="26" + height="26" + viewBox="0 0 26 26" + fill="none" + > + <path + d="M4.5067 17.576C3.3239 15.5805 2.91017 13.2218 3.34318 10.9428C3.7762 8.66377 5.02618 6.6212 6.85846 5.1985C8.69075 3.7758 10.9793 3.07083 13.2946 3.21592C15.6098 3.36102 17.7924 4.34621 19.4328 5.98653C21.0731 7.62686 22.0583 9.80951 22.2034 12.1247C22.3485 14.44 21.6435 16.7285 20.2208 18.5608C18.7981 20.3931 16.7555 21.6431 14.4765 22.0761C12.1975 22.5091 9.83884 22.0954 7.84327 20.9126V20.9126L4.54642 21.846C4.41135 21.8855 4.26814 21.888 4.13179 21.8531C3.99545 21.8182 3.871 21.7473 3.77149 21.6478C3.67197 21.5483 3.60106 21.4238 3.56619 21.2875C3.53131 21.1512 3.53375 21.0079 3.57326 20.8729L4.5067 17.576Z" + stroke="currentColor" + strokeWidth="1.58884" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + <div>32</div> + </div> + <div className="flex gap-[4px] items-center flex-1"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="26" + height="26" + viewBox="0 0 26 26" + fill="none" + > + <path + d="M20.884 3.56475L2.37397 8.77813C2.21641 8.82121 2.07595 8.91181 1.97174 9.0376C1.86752 9.16339 1.80462 9.31824 1.79159 9.48107C1.77857 9.6439 1.81605 9.80679 1.89894 9.94754C1.98183 10.0883 2.1061 10.2001 2.25481 10.2677L10.7551 14.2894C10.9216 14.3665 11.0553 14.5003 11.1325 14.6668L15.1542 23.1671C15.2218 23.3158 15.3336 23.44 15.4743 23.5229C15.6151 23.6058 15.778 23.6433 15.9408 23.6303C16.1036 23.6172 16.2585 23.5543 16.3843 23.4501C16.5101 23.3459 16.6007 23.2055 16.6437 23.0479L21.8571 4.53791C21.8966 4.40284 21.8991 4.25962 21.8642 4.12328C21.8293 3.98694 21.7584 3.86249 21.6589 3.76297C21.5594 3.66346 21.4349 3.59255 21.2986 3.55767C21.1622 3.5228 21.019 3.52524 20.884 3.56475V3.56475Z" + stroke="currentColor" + strokeWidth="1.58884" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M11.0117 14.4084L15.5002 9.91992" + stroke="currentColor" + strokeWidth="1.58884" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="26" + height="26" + viewBox="0 0 26 26" + fill="none" + > + <path + d="M19.0662 22.2443L12.7108 18.2722L6.35547 22.2443V4.76708C6.35547 4.55638 6.43917 4.35432 6.58815 4.20534C6.73713 4.05635 6.9392 3.97266 7.14989 3.97266H18.2718C18.4825 3.97266 18.6845 4.05635 18.8335 4.20534C18.9825 4.35432 19.0662 4.55638 19.0662 4.76708V22.2443Z" + stroke="currentColor" + strokeWidth="1.58884" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + {renderContent.length > 1 && ( + <> + {renderContent.slice(1).map((value, index) => ( + <div key={index} className="flex flex-col gap-[12px]"> + <div className="flex gap-[10px] leading-[17px]"> + <div className="h-[34px]"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] h-[34px] w-[34px]" + /> + </div> + <div className="flex flex-col gap-[6px] flex-1"> + <div className="flex gap-[4px] py-[8px]"> + <div + className="whitespace-pre-line text-[14px] font-[400] flex-1" + dangerouslySetInnerHTML={{ + __html: value.text, + }} + /> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M10 16.875C10 16.875 2.1875 12.5 2.1875 7.18751C2.1875 6.24836 2.51289 5.33821 3.1083 4.61193C3.70371 3.88564 4.53236 3.38808 5.45328 3.2039C6.37419 3.01971 7.33047 3.16029 8.15943 3.6017C8.98838 4.04311 9.63879 4.7581 10 5.62501V5.62501C10.3612 4.7581 11.0116 4.04311 11.8406 3.6017C12.6695 3.16029 13.6258 3.01971 14.5467 3.2039C15.4676 3.38808 16.2963 3.88564 16.8917 4.61193C17.4871 5.33821 17.8125 6.24836 17.8125 7.18751C17.8125 12.5 10 16.875 10 16.875Z" + stroke="currentColor" + strokeWidth="1.58884" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + <div className="flex font-[400] text-[12px] text-textLinkedin items-center"> + <div className="flex gap-[16px] flex-1"> + <div className="font-[700]">30m</div> + <div className="font-[700]">8 Likes</div> + <div className="font-[700]">Reply</div> + </div> + </div> + </div> + </div> + </div> + ))} + </> + )} + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.preview.tsx b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.preview.tsx new file mode 100644 index 00000000..4c9428ba --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.preview.tsx @@ -0,0 +1,513 @@ +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { textSlicer } from '@gitroom/helpers/utils/count.length'; +import { FC } from 'react'; +import { VideoOrImage } from '@gitroom/react/helpers/video.or.image'; + +const Icons = () => { + return ( + <svg + width={71} + height={22} + viewBox="0 0 71 22" + fill="none" + xmlns="http://www.w3.org/2000/svg" + className="text-bgLinkedin" + > + <rect + x={1.0625} + y={1.27539} + width={18.7109} + height={18.7109} + rx={9.35544} + stroke="currentColor" + strokeWidth={2.12624} + /> + <path + d="M10.4444 19.7508C15.682 19.7508 19.9279 15.6014 19.9279 10.4828C19.9279 5.36425 15.682 1.21484 10.4444 1.21484C5.20684 1.21484 0.960938 5.36425 0.960938 10.4828C0.960938 15.6014 5.20684 19.7508 10.4444 19.7508Z" + fill="#1485BD" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M15.7769 9.48947H15.0318C14.964 9.48947 14.8286 9.23791 14.4899 8.88043C13.9886 8.35083 13.4331 7.6756 13.0402 7.30488C12.0654 6.47806 11.2686 5.46989 10.6965 4.33913C10.3713 3.66389 10.3442 3.35938 9.68038 3.35938C9.41819 3.39246 9.17864 3.52159 9.01014 3.72067C8.84164 3.91975 8.75676 4.17392 8.77267 4.43181C8.77267 4.61717 8.86751 5.26593 8.90815 5.49101C9.19833 6.52618 9.65567 7.50947 10.2629 8.40379H10.9945H5.19604C5.03181 8.40143 4.86888 8.43263 4.71771 8.49541C4.56654 8.55818 4.43047 8.65114 4.31821 8.76832C4.20596 8.88551 4.12001 9.02432 4.06587 9.17587C4.01173 9.32742 3.9906 9.48835 4.00383 9.64835C4.02471 9.95855 4.16748 10.2489 4.40229 10.4586C4.63709 10.6683 4.94577 10.7812 5.26378 10.7737H5.48055C5.34176 10.7772 5.20503 10.8073 5.07816 10.8624C4.95129 10.9175 4.83678 10.9965 4.74116 11.0948C4.64554 11.1932 4.57069 11.309 4.52089 11.4357C4.47108 11.5623 4.44731 11.6973 4.45091 11.8329C4.45326 12.0997 4.55728 12.3561 4.74266 12.5519C4.92804 12.7478 5.18147 12.8691 5.45345 12.8921C5.28789 13.0159 5.16238 13.1837 5.09194 13.3755C5.0215 13.5672 5.00913 13.7747 5.05632 13.9731C5.10351 14.1716 5.20824 14.3525 5.35797 14.4942C5.50771 14.6359 5.69609 14.7325 5.90053 14.7722C5.77072 14.9924 5.72737 15.2513 5.7786 15.5004C5.84797 15.745 5.99897 15.9599 6.2076 16.111C6.41624 16.2621 6.67055 16.3408 6.93017 16.3345H10.0326C10.4391 16.3345 10.8441 16.2842 11.2384 16.1889L13.1622 15.6328H15.7363C17.1181 15.5798 17.4839 9.48947 15.7769 9.48947Z" + fill="#E6F7FF" + /> + <path + d="M9.66218 8.40402H5.16429C4.99306 8.40245 4.82349 8.43696 4.66712 8.50518C4.51076 8.5734 4.37127 8.67374 4.25814 8.79938C4.14501 8.92501 4.06091 9.07298 4.01154 9.23323C3.96218 9.39347 3.94872 9.56223 3.97208 9.72801C3.99629 10.0359 4.14049 10.3229 4.37495 10.5298C4.6094 10.7368 4.9162 10.8479 5.23203 10.8402H5.4488C5.31001 10.8436 5.17328 10.8737 5.04641 10.9288C4.91954 10.9839 4.80503 11.0629 4.70941 11.1613C4.61379 11.2596 4.53894 11.3754 4.48914 11.5021C4.43933 11.6287 4.41556 11.7637 4.41916 11.8994C4.42415 12.1667 4.52871 12.4231 4.71326 12.6205C4.89781 12.818 5.14969 12.9428 5.4217 12.9718C5.29214 13.0711 5.18712 13.1977 5.11442 13.3421C5.04172 13.4865 5.0032 13.645 5.00172 13.8059C5.0024 14.0543 5.09018 14.2949 5.25043 14.4876C5.41069 14.6804 5.63372 14.8136 5.88233 14.8651C5.75158 15.0898 5.70828 15.3531 5.7604 15.6065C5.83228 15.8487 5.98435 16.0606 6.19275 16.2092C6.40116 16.3577 6.65411 16.4345 6.91196 16.4274H10.0144C10.4209 16.4274 10.8259 16.3771 11.2202 16.2818L13.144 15.6595H15.7181C17.0728 15.6595 17.4386 9.51617 15.7181 9.51617C15.4699 9.52953 15.2211 9.52953 14.9729 9.51617C14.9052 9.51617 14.7697 9.26461 14.431 8.90714C13.9297 8.37754 13.3743 7.7023 12.9814 7.33158C12.0197 6.49714 11.2369 5.48461 10.6783 4.3526C10.5707 4.10277 10.4244 3.87057 10.2447 3.66412C10.0191 3.47868 9.72796 3.38726 9.43402 3.40955C9.14008 3.43183 8.86686 3.56604 8.67318 3.78328C8.53278 3.93167 8.41835 4.10165 8.33449 4.2864C8.27968 4.41424 8.23881 4.54738 8.21256 4.68359C8.17621 5.05687 8.20832 5.4335 8.30739 5.79575C8.3941 6.17706 8.51738 6.5491 8.67318 6.90791C8.92788 7.37528 9.22865 7.81881 9.56734 8.2319C9.60914 8.25707 9.6422 8.29399 9.66218 8.33782" + stroke="#004B7C" + strokeWidth={0.847356} + strokeLinecap="round" + strokeLinejoin="round" + /> + <rect + x={17.6484} + y={1.27539} + width={18.7109} + height={18.7109} + rx={9.35544} + stroke="currentColor" + strokeWidth={2.12624} + /> + <mask + id="mask0_2511_140139" + style={{ + maskType: 'alpha', + }} + maskUnits="userSpaceOnUse" + x={17} + y={1} + width={20} + height={19} + > + <path + d="M36.1844 10.5941C36.1844 5.47558 32.0349 1.32617 26.9164 1.32617C21.7978 1.32617 17.6484 5.47558 17.6484 10.5941C17.6484 15.7127 21.7978 19.8621 26.9164 19.8621C32.0349 19.8621 36.1844 15.7127 36.1844 10.5941Z" + fill="white" + /> + </mask> + <g mask="url(#mask0_2511_140139)"> + <path + d="M36.1844 10.5941C36.1844 5.47558 32.0349 1.32617 26.9164 1.32617C21.7978 1.32617 17.6484 5.47558 17.6484 10.5941C17.6484 15.7127 21.7978 19.8621 26.9164 19.8621C32.0349 19.8621 36.1844 15.7127 36.1844 10.5941Z" + fill="#D8D8D8" + /> + <path + d="M26.9242 19.864C32.0428 19.864 36.1922 15.7146 36.1922 10.5961C36.1922 5.47753 32.0428 1.32812 26.9242 1.32812C21.8057 1.32812 17.6562 5.47753 17.6562 10.5961C17.6562 15.7146 21.8057 19.864 26.9242 19.864Z" + fill="#6DAE4F" + /> + <path + d="M26.9279 1.3279C29.3859 1.3279 31.7432 2.30434 33.4813 4.04242C35.2194 5.7805 36.1959 8.13785 36.1959 10.5959C36.1959 13.0539 35.2194 15.4112 33.4813 17.1493C31.7432 18.8874 29.3859 19.8638 26.9279 19.8638C24.4699 19.8638 22.1125 18.8874 20.3745 17.1493C18.6364 15.4112 17.6599 13.0539 17.6599 10.5959C17.6599 8.13785 18.6364 5.7805 20.3745 4.04242C22.1125 2.30434 24.4699 1.3279 26.9279 1.3279ZM26.9279 0.00390625C24.1187 0.00390625 21.4246 1.11984 19.4382 3.10622C17.4519 5.09259 16.3359 7.7867 16.3359 10.5959C16.3359 13.405 17.4519 16.0991 19.4382 18.0855C21.4246 20.0719 24.1187 21.1878 26.9279 21.1878C29.7371 21.1878 32.4312 20.0719 34.4175 18.0855C36.4039 16.0991 37.5198 13.405 37.5198 10.5959C37.5198 7.7867 36.4039 5.09259 34.4175 3.10622C32.4312 1.11984 29.7371 0.00390625 26.9279 0.00390625V0.00390625Z" + fill="#1B1F23" + /> + <path + d="M32.3916 12.217C32.3675 11.1767 32.2073 10.144 31.915 9.14529C31.1136 8.50117 30.5044 7.64931 30.1541 6.68266C29.9422 5.98095 29.7966 5.72939 29.1743 5.71615C29.0477 5.72767 28.9246 5.76441 28.8123 5.82421C28.7001 5.88402 28.601 5.96568 28.5208 6.06438C28.4406 6.16308 28.381 6.27683 28.3454 6.39893C28.3098 6.52103 28.2991 6.64902 28.3137 6.77534C28.3217 7.0931 28.3481 7.41218 28.3931 7.72862C28.5745 8.59584 28.8301 9.44716 29.1611 10.2707L23.2031 5.74263C23.1037 5.64582 22.9851 5.57091 22.855 5.52273C22.7249 5.47455 22.5862 5.45417 22.4477 5.46291C22.3092 5.47164 22.1741 5.50929 22.0511 5.57343C21.9281 5.63757 21.8199 5.72678 21.7335 5.83531C21.6678 5.94514 21.6248 6.06707 21.6073 6.19384C21.5897 6.32061 21.5978 6.44962 21.6312 6.57317C21.6646 6.69673 21.7225 6.8123 21.8014 6.913C21.8804 7.0137 21.9789 7.09747 22.0909 7.1593L25.0699 9.41009L25.8511 10.1515L21.1244 6.5635C21.0297 6.46795 20.9155 6.39386 20.7897 6.34623C20.6638 6.29859 20.5292 6.27854 20.3949 6.28741C20.2607 6.29629 20.1299 6.33388 20.0114 6.39766C19.8929 6.46144 19.7895 6.54992 19.7081 6.65711C19.6268 6.7643 19.5694 6.88771 19.5398 7.01899C19.5103 7.15027 19.5093 7.28636 19.5368 7.41807C19.5644 7.54978 19.62 7.67403 19.6997 7.78242C19.7794 7.89082 19.8815 7.98083 19.999 8.04638L22.978 10.2972L24.7654 11.6212L20.8993 8.72162C20.8 8.62481 20.6814 8.5499 20.5513 8.50172C20.4212 8.45354 20.2824 8.43316 20.144 8.44189C20.0055 8.45063 19.8704 8.48828 19.7474 8.55242C19.6244 8.61656 19.5161 8.70577 19.4297 8.81429C19.0193 9.34389 19.3503 9.88673 19.7739 10.2177L22.7529 12.4553L24.249 13.5807L21.2701 11.3299C21.1783 11.23 21.0668 11.1502 20.9425 11.0956C20.8183 11.0411 20.6841 11.0129 20.5485 11.0129C20.4128 11.0129 20.2786 11.0411 20.1544 11.0956C20.0302 11.1502 19.9187 11.23 19.8269 11.3299C19.7456 11.4431 19.6891 11.5722 19.661 11.7088C19.633 11.8453 19.6339 11.9862 19.6639 12.1223C19.6939 12.2585 19.7522 12.3868 19.835 12.4989C19.9178 12.611 20.0233 12.7044 20.1447 12.773L25.573 16.745C26.3595 17.3368 28.3137 17.8479 29.1743 17.0998" + stroke="#165209" + strokeWidth={1.27103} + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M33.034 13.1164L32.4779 12.6266C32.4056 11.6521 32.1776 10.6955 31.8026 9.79321C31.0275 8.88392 30.4786 7.80414 30.2006 6.6421C29.9888 5.94038 29.8431 5.68882 29.2209 5.67558C29.0942 5.6871 28.9711 5.72384 28.8589 5.78365C28.7467 5.84345 28.6475 5.92511 28.5673 6.02381C28.4871 6.12252 28.4275 6.23627 28.3919 6.35837C28.3564 6.48047 28.3456 6.60845 28.3603 6.73478C28.3682 7.05254 28.3947 7.37162 28.4397 7.68805C28.592 8.52349 28.8369 9.3404 29.1679 10.1242L28.9163 9.97856L23.2099 5.59614C23.1105 5.49934 22.992 5.42443 22.8619 5.37625C22.7318 5.32807 22.593 5.30769 22.4545 5.31642C22.3161 5.32515 22.181 5.3628 22.058 5.42695C21.9349 5.49109 21.8267 5.5803 21.7403 5.68882C21.6633 5.80203 21.6112 5.93032 21.5875 6.06518C21.5637 6.20004 21.569 6.3384 21.6028 6.4711C21.6366 6.60379 21.6982 6.72779 21.7835 6.83487C21.8688 6.94196 21.976 7.02969 22.0978 7.09226L25.0106 9.32981L25.8579 10.005L21.1312 6.41702C21.0328 6.31945 20.9149 6.24359 20.7853 6.19431C20.6557 6.14504 20.5173 6.12345 20.3788 6.13093C20.2404 6.13842 20.105 6.17481 19.9815 6.23776C19.858 6.30072 19.749 6.38884 19.6616 6.49646C19.5861 6.60948 19.535 6.73697 19.5115 6.87082C19.4879 7.00466 19.4925 7.14195 19.5249 7.27394C19.5572 7.40593 19.6167 7.52974 19.6995 7.6375C19.7823 7.74526 19.8867 7.83462 20.0059 7.89989L22.9848 10.1507L24.7722 11.4747L20.9062 8.58837C20.8087 8.48888 20.6912 8.4113 20.5613 8.36083C20.4315 8.31035 20.2925 8.28813 20.1534 8.29565C20.0143 8.30317 19.8784 8.34025 19.7548 8.40442C19.6312 8.4686 19.5227 8.55839 19.4365 8.66781C19.359 8.78019 19.3062 8.90772 19.2815 9.042C19.2569 9.17627 19.2609 9.31425 19.2935 9.44684C19.326 9.57943 19.3862 9.70363 19.4702 9.81126C19.5542 9.91888 19.6601 10.0075 19.7808 10.0712L22.7598 12.3088L24.2559 13.4342L21.2769 11.1966C21.1874 11.093 21.0766 11.0098 20.9521 10.9528C20.8276 10.8958 20.6923 10.8663 20.5553 10.8663C20.4184 10.8663 20.283 10.8958 20.1585 10.9528C20.034 11.0098 19.9232 11.093 19.8337 11.1966C19.7527 11.3088 19.6963 11.4369 19.6684 11.5724C19.6404 11.7079 19.6415 11.8479 19.6715 11.983C19.7015 12.1181 19.7598 12.2453 19.8425 12.3562C19.9253 12.4671 20.0306 12.5593 20.1515 12.6266L25.5799 16.7442C25.9693 17.0372 26.4142 17.2482 26.8876 17.3642C27.3609 17.4803 27.8529 17.4991 28.3338 17.4194C29.0461 17.8325 29.6684 17.8325 30.2006 17.4194C30.9977 16.7985 30.7355 16.7428 31.326 15.9233C31.9943 15.0516 32.5669 14.1106 33.034 13.1164Z" + fill="#DCF0CB" + /> + <path + d="M32.3916 12.217C32.3675 11.1767 32.2073 10.144 31.915 9.14529C31.1136 8.50117 30.5044 7.64931 30.1541 6.68266C29.9422 5.98095 29.7966 5.72939 29.1743 5.71615C29.0477 5.72767 28.9246 5.76441 28.8123 5.82421C28.7001 5.88402 28.601 5.96568 28.5208 6.06438C28.4406 6.16308 28.381 6.27683 28.3454 6.39893C28.3098 6.52103 28.2991 6.64902 28.3137 6.77534C28.3217 7.0931 28.3481 7.41219 28.3931 7.72862C28.5745 8.59584 28.8301 9.44716 29.1611 10.2707L23.2031 5.74263C23.1037 5.64582 22.9851 5.57091 22.855 5.52273C22.7249 5.47455 22.5862 5.45417 22.4477 5.46291C22.3092 5.47164 22.1741 5.50929 22.0511 5.57343C21.9281 5.63757 21.8199 5.72678 21.7335 5.83531C21.6678 5.94514 21.6248 6.06707 21.6073 6.19384C21.5897 6.32061 21.5978 6.44962 21.6312 6.57317C21.6646 6.69673 21.7225 6.8123 21.8014 6.913C21.8804 7.0137 21.9789 7.09747 22.0909 7.1593L25.0699 9.41009L25.8511 10.1515L21.1244 6.5635C21.0297 6.46795 20.9155 6.39386 20.7897 6.34623C20.6638 6.29859 20.5292 6.27854 20.3949 6.28741C20.2607 6.29629 20.1299 6.33388 20.0114 6.39766C19.8929 6.46145 19.7895 6.54992 19.7081 6.65711C19.6268 6.7643 19.5694 6.88771 19.5398 7.01899C19.5103 7.15027 19.5093 7.28636 19.5368 7.41807C19.5644 7.54978 19.62 7.67403 19.6997 7.78242C19.7794 7.89082 19.8815 7.98083 19.999 8.04638L22.978 10.2972L24.7654 11.6212L20.8993 8.72162C20.8 8.62481 20.6814 8.5499 20.5513 8.50172C20.4212 8.45354 20.2824 8.43316 20.144 8.44189C20.0055 8.45063 19.8704 8.48828 19.7474 8.55242C19.6244 8.61656 19.5161 8.70577 19.4297 8.81429C19.0193 9.34389 19.3503 9.88673 19.7739 10.2177L22.7529 12.4553L24.249 13.5807L21.2701 11.3299C21.1783 11.23 21.0668 11.1502 20.9425 11.0956C20.8183 11.0411 20.6841 11.0129 20.5485 11.0129C20.4128 11.0129 20.2786 11.0411 20.1544 11.0956C20.0302 11.1502 19.9187 11.23 19.8269 11.3299C19.7456 11.4431 19.6891 11.5722 19.661 11.7088C19.633 11.8453 19.6339 11.9862 19.6639 12.1223C19.6939 12.2585 19.7522 12.3868 19.835 12.4989C19.9178 12.611 20.0233 12.7044 20.1447 12.773L25.573 16.745C26.3595 17.3368 28.2025 17.7274 29.1743 17.5659" + stroke="#165209" + strokeWidth={0.423678} + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M35.8765 15.0818C35.6087 14.7952 35.4015 14.4574 35.2675 14.0888C35.2675 13.7975 35.188 13.5195 35.1351 13.2282C35.0937 12.7201 34.9302 12.2296 34.6584 11.7983C33.891 10.8839 33.343 9.806 33.0564 8.64718C32.8446 7.94546 32.6989 7.6939 32.0766 7.68066C31.95 7.69218 31.8269 7.72892 31.7147 7.78873C31.6024 7.84853 31.5033 7.93019 31.4231 8.02889C31.3429 8.12759 31.2833 8.24134 31.2477 8.36345C31.2122 8.48555 31.2014 8.61353 31.216 8.73986C31.2253 9.05762 31.2518 9.37537 31.2955 9.69313C31.5338 10.9112 31.7854 11.9174 31.8118 11.9836L26.1319 7.73362C26.0335 7.63488 25.9152 7.55825 25.7849 7.50887C25.6546 7.45949 25.5152 7.43848 25.3761 7.44726C25.237 7.45603 25.1014 7.49438 24.9783 7.55974C24.8552 7.62511 24.7474 7.71598 24.6623 7.8263C24.596 7.93533 24.5524 8.05654 24.5338 8.18276C24.5153 8.30898 24.5223 8.43763 24.5544 8.56109C24.5865 8.68456 24.6431 8.80032 24.7207 8.90153C24.7984 9.00274 24.8956 9.08733 25.0065 9.15029L27.9855 11.4011L28.8858 12.1425L24.1194 8.5545C24.022 8.455 23.9044 8.37743 23.7746 8.32695C23.6448 8.27648 23.5057 8.25426 23.3666 8.26178C23.2276 8.26929 23.0917 8.30638 22.9681 8.37055C22.8445 8.43472 22.736 8.52452 22.6498 8.63394C22.5723 8.74632 22.5194 8.87385 22.4948 9.00812C22.4701 9.1424 22.4742 9.28038 22.5067 9.41297C22.5392 9.54556 22.5995 9.66976 22.6835 9.77738C22.7675 9.88501 22.8733 9.97361 22.994 10.0374L25.973 12.2882L27.7604 13.6122L23.8811 10.7126C23.782 10.6168 23.6638 10.5431 23.5343 10.4961C23.4047 10.4491 23.2667 10.43 23.1292 10.44C22.9918 10.45 22.858 10.4889 22.7366 10.5541C22.6152 10.6193 22.5089 10.7094 22.4247 10.8185C22.0143 11.3481 22.332 11.891 22.769 12.222L25.7479 14.4463L27.2308 15.5717L24.1989 13.3209C24.013 13.1594 23.7724 13.0749 23.5264 13.0848C23.2803 13.0946 23.0473 13.198 22.8749 13.3738C22.7938 13.4823 22.7361 13.6064 22.7055 13.7384C22.6748 13.8703 22.6718 14.0071 22.6967 14.1402C22.7216 14.2733 22.7738 14.3999 22.85 14.5118C22.9263 14.6237 23.0249 14.7186 23.1397 14.7905L28.4356 18.7625C28.8779 19.0935 29.3889 19.3199 29.9318 19.4245L30.6997 19.5436C31.1035 19.6032 31.4941 19.733 31.8516 19.9276C32.0987 20.0483 32.3822 20.0719 32.646 19.9938C33.5771 19.6654 34.3917 19.0717 34.9894 18.2859C35.5492 17.5121 35.8713 16.5919 35.9162 15.6379C35.9498 15.4521 35.9362 15.2609 35.8765 15.0818Z" + stroke="#165209" + strokeWidth={1.27103} + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M35.9319 15.0166L35.3361 14.5664C35.2702 13.591 35.042 12.6334 34.6609 11.7331C33.8911 10.8202 33.3429 9.74178 33.0589 8.58197C32.847 7.88025 32.7014 7.62869 32.0791 7.61545C31.9525 7.62697 31.8294 7.66371 31.7172 7.72351C31.6049 7.78332 31.5058 7.86498 31.4256 7.96368C31.3454 8.06238 31.2858 8.17613 31.2502 8.29823C31.2146 8.42033 31.2039 8.54832 31.2185 8.67464C31.2278 8.9924 31.2543 9.31016 31.298 9.62792C31.5363 10.8592 31.7878 11.8655 31.8143 11.9184L26.1344 7.66841C26.035 7.5716 25.9164 7.49669 25.7863 7.44851C25.6562 7.40034 25.5175 7.37996 25.379 7.38869C25.2405 7.39742 25.1054 7.43507 24.9824 7.49921C24.8594 7.56335 24.7512 7.65256 24.6648 7.76109C24.2543 8.30393 24.5853 8.83352 25.009 9.16452L27.988 11.4021L28.8883 12.1435L24.1219 8.48929C24.0225 8.39248 23.904 8.31757 23.7739 8.26939C23.6438 8.22121 23.505 8.20083 23.3665 8.20956C23.2281 8.2183 23.093 8.25595 22.97 8.32009C22.8469 8.38423 22.7387 8.47344 22.6523 8.58197C22.2418 9.11156 22.5728 9.6544 22.9965 9.9854L25.9755 12.2229L27.7629 13.5469L23.8836 10.6474C23.7854 10.5509 23.6679 10.4762 23.5389 10.4281C23.4098 10.3801 23.2721 10.3597 23.1347 10.3685C22.9973 10.3772 22.8633 10.4148 22.7413 10.4789C22.6194 10.5429 22.5124 10.6319 22.4272 10.7401C22.3475 10.8518 22.2929 10.9793 22.2671 11.1141C22.2412 11.2488 22.2448 11.3875 22.2774 11.5207C22.3101 11.654 22.3712 11.7786 22.4564 11.8861C22.5417 11.9936 22.6491 12.0814 22.7714 12.1435L25.7504 14.3811L27.2333 15.5065L24.2014 13.2689C24.0184 13.1011 23.7765 13.0125 23.5284 13.0224C23.2804 13.0324 23.0463 13.14 22.8774 13.3219C22.7849 13.4307 22.7177 13.5586 22.6805 13.6965C22.6434 13.8343 22.6372 13.9787 22.6626 14.1192C22.6879 14.2598 22.744 14.3929 22.827 14.5092C22.9099 14.6254 23.0175 14.7218 23.1422 14.7915L28.4381 18.7635C29.2411 19.2423 30.1518 19.511 31.0861 19.5446C31.4251 19.7518 31.7792 19.9333 32.1453 20.0875C33.2076 19.8872 34.1541 19.2909 34.7933 18.4192C35.4911 17.4176 35.8864 16.2365 35.9319 15.0166Z" + fill="#DDF6D1" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M35.8765 15.0818C35.6087 14.7952 35.4015 14.4574 35.2675 14.0888C35.2675 13.7975 35.188 13.5195 35.1351 13.2282C35.0937 12.7201 34.9302 12.2296 34.6584 11.7983C33.891 10.8839 33.343 9.806 33.0564 8.64718C32.8446 7.94546 32.6989 7.6939 32.0766 7.68066C31.95 7.69218 31.8269 7.72892 31.7147 7.78873C31.6024 7.84853 31.5033 7.93019 31.4231 8.02889C31.3429 8.12759 31.2833 8.24134 31.2477 8.36345C31.2122 8.48555 31.2014 8.61353 31.216 8.73986C31.2253 9.05762 31.2518 9.37537 31.2955 9.69313C31.5338 10.9112 31.7854 11.9174 31.8118 11.9836L26.1319 7.73362C26.0335 7.63488 25.9152 7.55825 25.7849 7.50887C25.6546 7.45949 25.5152 7.43848 25.3761 7.44726C25.237 7.45603 25.1014 7.49438 24.9783 7.55974C24.8552 7.62511 24.7474 7.71598 24.6623 7.8263C24.596 7.93533 24.5524 8.05654 24.5338 8.18276C24.5153 8.30898 24.5223 8.43763 24.5544 8.56109C24.5865 8.68456 24.6431 8.80032 24.7207 8.90153C24.7984 9.00274 24.8956 9.08733 25.0065 9.15029L27.9855 11.4011L28.8858 12.1425L24.1194 8.5545C24.022 8.455 23.9044 8.37743 23.7746 8.32695C23.6448 8.27648 23.5057 8.25426 23.3666 8.26178C23.2276 8.26929 23.0917 8.30638 22.9681 8.37055C22.8445 8.43472 22.736 8.52452 22.6498 8.63394C22.5723 8.74632 22.5194 8.87385 22.4948 9.00812C22.4701 9.1424 22.4742 9.28038 22.5067 9.41297C22.5392 9.54556 22.5995 9.66976 22.6835 9.77738C22.7675 9.88501 22.8733 9.97361 22.994 10.0374L25.973 12.2882L27.7604 13.6122L23.8811 10.7126C23.782 10.6168 23.6638 10.5431 23.5343 10.4961C23.4047 10.4491 23.2667 10.43 23.1292 10.44C22.9918 10.45 22.858 10.4889 22.7366 10.5541C22.6152 10.6193 22.5089 10.7094 22.4247 10.8185C22.0143 11.3481 22.332 11.891 22.769 12.222L25.7479 14.4463L27.2308 15.5717L24.1989 13.3209C24.013 13.1594 23.7724 13.0749 23.5264 13.0848C23.2803 13.0946 23.0473 13.198 22.8749 13.3738C22.7938 13.4823 22.7361 13.6064 22.7055 13.7384C22.6748 13.8703 22.6718 14.0071 22.6967 14.1402C22.7216 14.2733 22.7738 14.3999 22.85 14.5118C22.9263 14.6237 23.0249 14.7186 23.1397 14.7905L28.4356 18.7625C28.8779 19.0935 29.3889 19.3199 29.9318 19.4245L30.6997 19.5436C31.1035 19.6032 31.4941 19.733 31.8516 19.9276C32.0987 20.0483 32.3822 20.0719 32.646 19.9938C33.5771 19.6654 34.3917 19.0717 34.9894 18.2859C35.5492 17.5121 35.8713 16.5919 35.9162 15.6379C35.9498 15.4521 35.9362 15.2609 35.8765 15.0818Z" + stroke="#165209" + strokeWidth={0.423678} + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M26.3078 2.16684L25.4485 4.23227M28.0171 3.73578L27.0506 4.95385L28.0171 3.73578ZM23.6016 2.15625L23.6876 3.73578L23.6016 2.15625Z" + stroke="#165209" + strokeWidth={0.847356} + strokeLinecap="round" + strokeLinejoin="round" + /> + </g> + <rect + x={34.2305} + y={1.27539} + width={18.7109} + height={18.7109} + rx={9.35544} + stroke="currentColor" + strokeWidth={2.12624} + /> + <path + d="M43.6358 19.8621C48.8734 19.8621 53.1193 15.7127 53.1193 10.5941C53.1193 5.47558 48.8734 1.32617 43.6358 1.32617C38.3982 1.32617 34.1523 5.47558 34.1523 10.5941C34.1523 15.7127 38.3982 19.8621 43.6358 19.8621Z" + fill="#DF704D" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M43.2395 6.62305C42.9098 6.29999 42.5181 6.04368 42.0869 5.8688C41.6556 5.69391 41.1933 5.60389 40.7264 5.60389C40.2595 5.60389 39.7972 5.69391 39.3659 5.8688C38.9347 6.04368 38.543 6.29999 38.2133 6.62305C37.8794 6.94891 37.6146 7.33589 37.4339 7.76186C37.2532 8.18782 37.1602 8.64442 37.1602 9.10554C37.1602 9.56666 37.2532 10.0233 37.4339 10.4492C37.6146 10.8752 37.8794 11.2622 38.2133 11.588L43.6324 16.884L49.0516 11.588C49.3854 11.2622 49.6503 10.8752 49.831 10.4492C50.0117 10.0233 50.1047 9.56666 50.1047 9.10554C50.1047 8.64442 50.0117 8.18782 49.831 7.76186C49.6503 7.33589 49.3854 6.94891 49.0516 6.62305C48.724 6.29895 48.3337 6.04188 47.9034 5.86684C47.473 5.6918 47.0113 5.6023 46.5452 5.60358C46.0789 5.60081 45.6167 5.68963 45.1862 5.86476C44.7556 6.03988 44.3655 6.29775 44.0389 6.62305L43.6324 7.02025L43.2395 6.62305Z" + fill="#FFF3F0" + stroke="#77280C" + strokeWidth={0.847356} + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M43.2451 6.62256C42.9173 6.2963 42.5262 6.03715 42.0947 5.86025C41.6632 5.68335 41.1999 5.59224 40.732 5.59224C40.2641 5.59224 39.8008 5.68335 39.3693 5.86025C38.9379 6.03715 38.5467 6.2963 38.2189 6.62256C37.5651 7.27782 37.1992 8.15677 37.1992 9.07195C37.1992 9.98713 37.5651 10.8661 38.2189 11.5213L43.638 16.8173L49.0572 11.5213C49.7109 10.8661 50.0768 9.98713 50.0768 9.07195C50.0768 8.15677 49.7109 7.27782 49.0572 6.62256C48.7303 6.29666 48.3404 6.03753 47.9101 5.86024C47.4798 5.68295 47.0178 5.59103 46.5508 5.58984C46.0836 5.58955 45.6211 5.68079 45.1906 5.85817C44.7601 6.03555 44.3704 6.29547 44.0444 6.62256L43.638 6.95356L43.2451 6.62256Z" + stroke="#77280C" + strokeWidth={0.847356} + strokeLinecap="round" + strokeLinejoin="round" + /> + <rect + x={50.8164} + y={1.27539} + width={18.7109} + height={18.7109} + rx={9.35544} + stroke="currentColor" + strokeWidth={2.12624} + /> + <path + d="M60.1436 19.8621C65.3812 19.8621 69.6271 15.7127 69.6271 10.5941C69.6271 5.47558 65.3812 1.32617 60.1436 1.32617C54.9061 1.32617 50.6602 5.47558 50.6602 10.5941C50.6602 15.7127 54.9061 19.8621 60.1436 19.8621Z" + fill="#F5BB5C" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M61.2469 17.7454H59.0792C58.8852 17.7454 58.6991 17.6701 58.5619 17.536C58.4247 17.4019 58.3477 17.2201 58.3477 17.0304V15.2695H61.9785V17.0304C61.9785 17.2201 61.9014 17.4019 61.7642 17.536C61.627 17.6701 61.4409 17.7454 61.2469 17.7454Z" + fill="#FFE1B2" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M58.3634 15.6129V15.2687C58.3634 14.8186 58.2889 14.3724 58.1466 13.9447C57.9568 13.57 57.7152 13.2225 57.4286 12.912C56.9295 12.5112 56.5265 12.0082 56.2479 11.4386C55.9694 10.869 55.8221 10.2466 55.8164 9.61526C55.8164 8.48808 56.2746 7.40707 57.0902 6.61004C57.9057 5.813 59.0119 5.36523 60.1653 5.36523C61.3187 5.36523 62.4248 5.813 63.2404 6.61004C64.0559 7.40707 64.5141 8.48808 64.5141 9.61526C64.5025 10.2604 64.3475 10.8953 64.0597 11.476C63.772 12.0567 63.3584 12.5693 62.8477 12.9782C62.9426 12.912 62.7394 13.0576 62.6039 13.243C62.4705 13.435 62.3656 13.6445 62.2923 13.8653C62.1543 14.2935 62.0857 14.7403 62.0891 15.1893V15.5335" + fill="#FCF0DE" + /> + <path + d="M59.4061 6.33203C58.7742 6.43607 58.1941 6.73813 57.7533 7.19263C57.2911 7.63134 56.9708 8.19297 56.832 8.8079" + stroke="#1B1F23" + strokeWidth={1.69471} + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + fillRule="evenodd" + clipRule="evenodd" + d="M61.2469 17.7454H59.0792C58.8852 17.7454 58.6991 17.6701 58.5619 17.536C58.4247 17.4019 58.3477 17.2201 58.3477 17.0304V15.2695H61.9785V17.0304C61.9785 17.2201 61.9014 17.4019 61.7642 17.536C61.627 17.6701 61.4409 17.7454 61.2469 17.7454V17.7454Z" + stroke="#5D3B01" + strokeWidth={0.847356} + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M60.1336 1.98828V3.65651M58.3453 15.6122V15.2679C58.3489 14.8189 58.2803 14.3722 58.1421 13.9439C58.0538 13.722 57.9353 13.5127 57.7898 13.3217C57.645 13.1142 57.4769 12.9232 57.2886 12.7524C57.1078 12.5934 56.9402 12.4207 56.7873 12.236C56.16 11.4894 55.8155 10.5541 55.8118 9.58801C55.8118 8.46083 56.27 7.37982 57.0856 6.58279C57.9012 5.78576 59.0073 5.33799 60.1607 5.33799C61.3141 5.33799 62.4202 5.78576 63.2358 6.58279C64.0514 7.37982 64.5096 8.46083 64.5096 9.58801C64.4935 10.5542 64.1451 11.4869 63.5206 12.236C63.2903 12.514 63.0329 12.7656 62.7483 12.9907C62.6513 13.0768 62.5607 13.1697 62.4774 13.2687C62.3481 13.4612 62.2478 13.6707 62.1793 13.891C62.0413 14.3193 61.9728 14.766 61.9761 15.215V15.5592L58.3453 15.6122ZM55.5273 3.49234L56.3538 4.53962L55.5273 3.49234ZM64.7602 3.48704L63.9406 4.53962L64.7602 3.48704Z" + stroke="#5D3B01" + strokeWidth={0.847356} + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + ); +}; + +const LinkedinIconSmall = () => { + return ( + <svg + width="16" + height="16" + viewBox="0 0 16 16" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <rect width="16" height="16" rx="2.77787" fill="#E9A53F" /> + <g clipPath="url(#clip0_2511_140522)"> + <path + d="M3.33203 4.5751C3.33203 4.27479 3.43714 4.02704 3.64735 3.83186C3.85755 3.63666 4.13083 3.53906 4.46717 3.53906C4.7975 3.53906 5.06476 3.63515 5.26897 3.82735C5.47918 4.02555 5.58428 4.2838 5.58428 4.60213C5.58428 4.89041 5.48218 5.13065 5.27798 5.32285C5.06777 5.52104 4.79149 5.62014 4.44915 5.62014H4.44014C4.10981 5.62014 3.84254 5.52104 3.63834 5.32285C3.43413 5.12465 3.33203 4.8754 3.33203 4.5751ZM3.44915 12.458L3.44915 6.43996H5.44915L5.44915 12.458H3.44915ZM6.55726 12.458H8.55726V9.09762C8.55726 8.8874 8.58128 8.72524 8.62933 8.61113C8.71341 8.40693 8.84104 8.23425 9.01221 8.09312C9.18338 7.95197 9.39809 7.8814 9.65635 7.8814C10.329 7.8814 10.6654 8.33485 10.6654 9.24176V12.458H12.6654L12.6654 9.00753C12.6654 8.11864 12.4552 7.44447 12.0347 6.98501C11.6143 6.52555 11.0588 6.29582 10.3681 6.29582C9.59329 6.29582 8.98969 6.62915 8.55726 7.29582V7.31384H8.54825L8.55726 7.29582V6.43996H6.55726C6.56926 6.63215 6.57527 7.22975 6.57527 8.23276C6.57527 9.23575 6.56926 10.6442 6.55726 12.458Z" + fill="white" + /> + </g> + <defs> + <clipPath id="clip0_2511_140522"> + <rect + x="3.33203" + y="3.33203" + width="9.33333" + height="9.33333" + fill="white" + /> + </clipPath> + </defs> + </svg> + ); +}; +export const LinkedinPreview: FC<{ + maximumCharacters?: number; +}> = (props) => { + const { value: topValue, integration } = useIntegration(); + const current = useLaunchStore((state) => state.current); + const mediaDir = useMediaDirectory(); + + const renderContent = topValue.map((p) => { + const newContent = stripHtmlValidation( + 'normal', + p.content.replace( + /<span.*?data-mention-id="([.\s\S]*?)"[.\s\S]*?>([.\s\S]*?)<\/span>/gi, + (match, match1, match2) => { + return `[[[${match2}]]]`; + } + ), + true + ); + + const { start, end } = textSlicer( + integration?.identifier || '', + props.maximumCharacters || 10000, + newContent + ); + + const finalValue = + newContent + .slice(start, end) + .replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">` + + newContent.slice(end).replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `</mark>`; + + return { text: finalValue, images: p.image }; + }); + return ( + <div className="py-[15px] flex flex-col px-[15px] w-full gap-[20px] bg-bgLinkedin rounded-[12px]"> + <div className="flex gap-[8px]"> + <div className="w-[48px] h-[48px]"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] w-[48px] h-[48px]" + /> + </div> + <div className="flex flex-col leading-[16px]"> + <div className="text-[14px] font-[500]">{integration?.name}</div> + <div className="text-[12px] font-[400] text-[#A3A3A3]"> + 2,871 followers + </div> + <div className="text-[12px] font-[400] text-[#A3A3A3] flex gap-[4px] items-center"> + <span>30m •</span> + <span> + <svg + xmlns="http://www.w3.org/2000/svg" + width="10" + height="10" + viewBox="0 0 10 10" + fill="none" + > + <path + d="M5 10C4.30833 10 3.65833 9.86867 3.05 9.606C2.44167 9.34367 1.9125 8.9875 1.4625 8.5375C1.0125 8.0875 0.656333 7.55833 0.394 6.95C0.131333 6.34167 0 5.69167 0 5C0 4.30833 0.131333 3.65833 0.394 3.05C0.656333 2.44167 1.0125 1.9125 1.4625 1.4625C1.9125 1.0125 2.44167 0.656167 3.05 0.3935C3.65833 0.131167 4.30833 0 5 0C5.69167 0 6.34167 0.131167 6.95 0.3935C7.55833 0.656167 8.0875 1.0125 8.5375 1.4625C8.9875 1.9125 9.34367 2.44167 9.606 3.05C9.86867 3.65833 10 4.30833 10 5C10 5.69167 9.86867 6.34167 9.606 6.95C9.34367 7.55833 8.9875 8.0875 8.5375 8.5375C8.0875 8.9875 7.55833 9.34367 6.95 9.606C6.34167 9.86867 5.69167 10 5 10ZM4.5 8.975V8C4.225 8 3.98967 7.90217 3.794 7.7065C3.598 7.5105 3.5 7.275 3.5 7V6.5L1.1 4.1C1.075 4.25 1.052 4.4 1.031 4.55C1.01033 4.7 1 4.85 1 5C1 6.00833 1.33133 6.89167 1.994 7.65C2.65633 8.40833 3.49167 8.85 4.5 8.975ZM7.95 7.7C8.11667 7.51667 8.26667 7.31867 8.4 7.106C8.53333 6.89367 8.64383 6.67283 8.7315 6.4435C8.81883 6.2145 8.8855 5.97917 8.9315 5.7375C8.97717 5.49583 9 5.25 9 5C9 4.18333 8.773 3.4375 8.319 2.7625C7.86467 2.0875 7.25833 1.6 6.5 1.3V1.5C6.5 1.775 6.40217 2.01033 6.2065 2.206C6.0105 2.402 5.775 2.5 5.5 2.5H4.5V3.5C4.5 3.64167 4.45217 3.76033 4.3565 3.856C4.2605 3.952 4.14167 4 4 4H3V5H6C6.14167 5 6.2605 5.04783 6.3565 5.1435C6.45217 5.2395 6.5 5.35833 6.5 5.5V7H7C7.21667 7 7.4125 7.0645 7.5875 7.1935C7.7625 7.32283 7.88333 7.49167 7.95 7.7Z" + fill="currentColor" + /> + </svg> + </span> + </div> + </div> + </div> + <div + className="text-[14px] font-[400] whitespace-pre-line" + dangerouslySetInnerHTML={{ + __html: renderContent?.[0]?.text, + }} + /> + {!!renderContent?.[0]?.images?.length && ( + <div className="h-[280px] -mx-[15px] overflow-hidden flex"> + {renderContent?.[0]?.images.map((image, index) => ( + <a + key={`image_${index}`} + className="flex-1" + href={mediaDir.set(image.path)} + target="_blank" + > + <VideoOrImage autoplay={true} src={mediaDir.set(image.path)} /> + </a> + ))} + </div> + )} + <div className="flex text-textLinkedin text-[12px] font-[400] items-center"> + <div className="flex flex-1 gap-[11px] items-center"> + <Icons /> + <div className="">88</div> + </div> + <div className="gap-[9px] items-center flex"> + <div>4 Comments</div> + <div> + <div className="w-[3px] h-[3px] bg-[#565C65] rounded-full" /> + </div> + <div>8 Reposts</div> + </div> + </div> + <div className="pt-[8px] flex text-[14px] font-[700] px-[32px] justify-between border-t border-borderLinkedin text-textLinkedin"> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="21" + viewBox="0 0 20 21" + fill="none" + > + <path + d="M6.39692 20.41C5.39692 20.41 4.35692 20.02 3.75692 19.41C3.37692 19.03 3.13692 18.5 3.00692 17.79C2.67692 17.57 2.39692 17.28 2.17692 16.95C1.89692 16.53 1.71692 15.99 1.65692 15.29C0.916917 14.74 0.476917 14.06 0.366917 13.24C0.276917 12.56 0.406917 11.86 0.746917 11.15C0.336917 10.66 0.0969174 10.05 0.0169174 9.35C-0.163083 7.53 1.10692 6.16 3.17692 5.95C3.53692 5.91 3.89692 5.89 4.25692 5.89C4.65692 5.89 5.05692 5.91 5.45692 5.96C5.26692 5.39 5.14692 4.86 5.10692 4.4C4.97692 3.09 5.31692 1.98 6.11692 1.11C6.71692 0.45 7.63692 0 8.41692 0C8.54692 0 8.67692 0.01 8.78692 0.04C9.61692 0.22 10.2069 0.78 10.6269 1.79C10.8369 2.29 10.9269 2.69 11.0669 3.46C11.1669 4.05 11.2269 4.29 11.3169 4.57C11.6069 5.44 12.3869 6.36 13.3469 6.98C14.1069 7.47 14.9369 7.86 15.7969 8.14H17.9169C18.3569 8.14 18.7269 8.29 19.0069 8.56C19.3169 8.86 19.4669 9.28 19.4269 9.75V17.05C19.4269 17.53 19.2269 17.93 18.8769 18.18C18.6169 18.36 18.3069 18.45 17.9569 18.45H14.9069C12.2269 19.08 10.4069 19.54 9.54692 19.82C8.22692 20.24 7.62692 20.36 6.64692 20.43C6.56692 20.43 6.47692 20.43 6.39692 20.43V20.41ZM4.27692 7.68C3.97692 7.68 3.67692 7.69 3.36692 7.72C2.27692 7.83 1.72692 8.34 1.80692 9.16C1.85692 9.65 2.03692 9.99 2.39692 10.23C2.58692 10.37 2.71692 10.57 2.76692 10.8C2.80692 11.03 2.76692 11.27 2.63692 11.46C2.25692 12.03 2.09692 12.54 2.15692 12.99C2.20692 13.38 2.46692 13.7 2.97692 13.99C3.10692 14.07 3.21692 14.18 3.29692 14.31C3.37692 14.44 3.41692 14.59 3.42692 14.74C3.44692 15.49 3.58692 15.8 3.67692 15.92C3.82692 16.14 3.99692 16.29 4.21692 16.38C4.36692 16.44 4.48692 16.55 4.58692 16.68C4.67692 16.81 4.73692 16.97 4.75692 17.12C4.81692 17.79 4.97692 18.04 5.05692 18.12C5.31692 18.39 5.90692 18.59 6.41692 18.59H6.54692C7.37692 18.53 7.81692 18.44 9.02692 18.05C9.96692 17.75 11.8069 17.29 14.6469 16.62V9.55C13.7869 9.23 13.0469 8.86 12.3969 8.44C11.0969 7.6 10.0669 6.35 9.64692 5.09C9.50692 4.69 9.44692 4.38 9.32692 3.72C9.20692 3.01 9.13692 2.75 9.00692 2.43C8.76692 1.85 8.60692 1.78 8.43692 1.74C8.24692 1.74 7.78692 1.92 7.46692 2.26C7.00692 2.77 6.83692 3.36 6.91692 4.18C6.97692 4.84 7.25692 5.69 7.73692 6.72C7.80692 6.87 7.83692 7.05 7.81692 7.22C7.79692 7.39 7.72692 7.55 7.61692 7.68C7.50692 7.81 7.36692 7.91 7.19692 7.96C7.10692 7.99 7.01692 8 6.91692 8C6.83692 8 6.76692 8 6.68692 7.97C5.89692 7.76 5.08692 7.65 4.27692 7.65V7.68ZM17.6469 16.63V9.9H16.4169V16.63H17.6469Z" + fill="currentColor" + /> + </svg> + <div>Like</div> + </div> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="21" + height="19" + viewBox="0 0 21 19" + fill="none" + > + <path + d="M7 1H14C17.3137 1 20 3.68629 20 7C20 8.96497 19.0567 10.7096 17.5938 11.8057L17.5762 11.8184L17.5596 11.832L11.5 16.7891V13H7C3.68629 13 1 10.3137 1 7C1 3.68629 3.68629 1 7 1Z" + stroke="currentColor" + strokeWidth="2" + /> + </svg> + <div>Comments</div> + </div> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="25" + viewBox="0 0 18 25" + fill="none" + > + <g clip-path="url(#clip0_2511_140348)"> + <path + d="M14.69 4.17L10.2 0H7.56L11.08 3.27H3.61C1.62 3.27 0 4.89 0 6.88V15.94H1.8V6.88C1.8 5.88 2.61 5.07 3.61 5.07H11.08L7.58 8.32H10.22L14.69 4.17Z" + fill="currentColor" + /> + <path + d="M3.14062 20.1098L7.63062 24.2798H10.2706L6.75062 21.0098H14.2206C16.2106 21.0098 17.8306 19.3898 17.8306 17.3998V8.33984H16.0306V17.3998C16.0306 18.3998 15.2206 19.2098 14.2206 19.2098H6.75062L10.2506 15.9598H7.61063L3.14062 20.1098Z" + fill="currentColor" + /> + </g> + <defs> + <clipPath id="clip0_2511_140348"> + <rect width="17.83" height="24.28" fill="white" /> + </clipPath> + </defs> + </svg> + <div>Repost</div> + </div> + <div className="flex gap-[4px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="21" + height="21" + viewBox="0 0 21 21" + fill="none" + > + <path + d="M21 0L0 7.2L7.2 10.8L16.2 4.8L9.6 13.8L13.8 21L21 0Z" + fill="currentColor" + /> + </svg> + <div>Send</div> + </div> + </div> + {renderContent.length > 1 && ( + <> + {renderContent.slice(1).map((value, index) => ( + <div key={index} className="flex flex-col gap-[12px]"> + <div className="flex gap-[6px] leading-[17px]"> + <div className="h-[34px]"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] h-[34px] w-[34px]" + /> + </div> + <div className="flex flex-col gap-[12px]"> + <div className="flex flex-col"> + <div className="flex items-center gap-[6px]"> + <div className="text-[13px] font-[500]"> + {integration?.name} + </div> + <div> + <LinkedinIconSmall /> + </div> + <div className="text-[12px] font-[400]">• 1st</div> + </div> + <div className="text-[12px] font-[400] text-textLinkedin"> + Founder + </div> + </div> + <div + className="whitespace-pre-line text-[14px] font-[400]" + dangerouslySetInnerHTML={{ + __html: value.text, + }} + /> + <div className="flex gap-[6px] font-[400] text-[12px] text-textLinkedin items-center"> + <div className="font-[700]">Like</div> + <div>•</div> + <div> + <svg + width="21" + height="22" + viewBox="0 0 21 22" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <rect + x="1.0625" + y="1.27734" + width="18.7109" + height="18.7109" + rx="9.35544" + stroke="white" + strokeWidth="2.12624" + /> + <path + d="M10.4444 19.7547C15.682 19.7547 19.9279 15.6053 19.9279 10.4867C19.9279 5.36816 15.682 1.21875 10.4444 1.21875C5.20684 1.21875 0.960938 5.36816 0.960938 10.4867C0.960938 15.6053 5.20684 19.7547 10.4444 19.7547Z" + fill="#1485BD" + /> + <path + fill-rule="evenodd" + clip-rule="evenodd" + d="M15.7769 9.49337H15.0318C14.964 9.49337 14.8286 9.24182 14.4899 8.88434C13.9886 8.35474 13.4331 7.6795 13.0402 7.30878C12.0654 6.48197 11.2686 5.4738 10.6965 4.34304C10.3713 3.6678 10.3442 3.36328 9.68038 3.36328C9.41819 3.39637 9.17864 3.5255 9.01014 3.72458C8.84164 3.92366 8.75676 4.17783 8.77267 4.43572C8.77267 4.62108 8.86751 5.26983 8.90815 5.49491C9.19833 6.53009 9.65567 7.51338 10.2629 8.4077H10.9945H5.19604C5.03181 8.40533 4.86888 8.43654 4.71771 8.49931C4.56654 8.56209 4.43047 8.65505 4.31821 8.77223C4.20596 8.88941 4.12001 9.02823 4.06587 9.17978C4.01173 9.33132 3.9906 9.49226 4.00383 9.65225C4.02471 9.96246 4.16748 10.2528 4.40229 10.4625C4.63709 10.6722 4.94577 10.7852 5.26378 10.7776H5.48055C5.34176 10.7811 5.20503 10.8112 5.07816 10.8663C4.95129 10.9214 4.83678 11.0004 4.74116 11.0988C4.64554 11.1971 4.57069 11.3129 4.52089 11.4396C4.47108 11.5662 4.44731 11.7012 4.45091 11.8368C4.45326 12.1036 4.55728 12.36 4.74266 12.5558C4.92804 12.7517 5.18147 12.873 5.45345 12.896C5.28789 13.0198 5.16238 13.1876 5.09194 13.3794C5.0215 13.5711 5.00913 13.7786 5.05632 13.977C5.10351 14.1755 5.20824 14.3564 5.35797 14.4981C5.50771 14.6398 5.69609 14.7364 5.90053 14.7761C5.77072 14.9963 5.72737 15.2552 5.7786 15.5043C5.84797 15.7489 5.99897 15.9638 6.2076 16.1149C6.41624 16.266 6.67055 16.3447 6.93017 16.3384H10.0326C10.4391 16.3384 10.8441 16.2881 11.2384 16.1928L13.1622 15.6367H15.7363C17.1181 15.5837 17.4839 9.49337 15.7769 9.49337Z" + fill="#E6F7FF" + /> + <path + d="M9.66218 8.40793H5.16429C4.99306 8.40636 4.82349 8.44086 4.66712 8.50909C4.51076 8.57731 4.37127 8.67765 4.25814 8.80328C4.14501 8.92891 4.06091 9.07689 4.01154 9.23713C3.96218 9.39738 3.94872 9.56613 3.97208 9.73192C3.99629 10.0398 4.14049 10.3268 4.37495 10.5337C4.6094 10.7407 4.9162 10.8518 5.23203 10.8441H5.4488C5.31001 10.8475 5.17328 10.8776 5.04641 10.9327C4.91954 10.9878 4.80503 11.0668 4.70941 11.1652C4.61379 11.2635 4.53894 11.3794 4.48914 11.506C4.43933 11.6326 4.41556 11.7676 4.41916 11.9033C4.42415 12.1706 4.52871 12.427 4.71326 12.6245C4.89781 12.8219 5.14969 12.9468 5.4217 12.9757C5.29214 13.075 5.18712 13.2016 5.11442 13.346C5.04172 13.4904 5.0032 13.6489 5.00172 13.8098C5.0024 14.0582 5.09018 14.2988 5.25043 14.4915C5.41069 14.6843 5.63372 14.8175 5.88233 14.869C5.75158 15.0937 5.70828 15.357 5.7604 15.6105C5.83228 15.8526 5.98435 16.0645 6.19275 16.2131C6.40116 16.3616 6.65411 16.4384 6.91196 16.4313H10.0144C10.4209 16.4313 10.8259 16.381 11.2202 16.2857L13.144 15.6634H15.7181C17.0728 15.6634 17.4386 9.52008 15.7181 9.52008C15.4699 9.53344 15.2211 9.53344 14.9729 9.52008C14.9052 9.52008 14.7697 9.26852 14.431 8.91104C13.9297 8.38145 13.3743 7.70621 12.9814 7.33549C12.0197 6.50104 11.2369 5.48852 10.6783 4.3565C10.5707 4.10668 10.4244 3.87448 10.2447 3.66803C10.0191 3.48258 9.72796 3.39116 9.43402 3.41345C9.14008 3.43574 8.86686 3.56995 8.67318 3.78719C8.53278 3.93558 8.41835 4.10556 8.33449 4.2903C8.27968 4.41815 8.23881 4.55128 8.21256 4.6875C8.17621 5.06078 8.20832 5.43741 8.30739 5.79966C8.3941 6.18097 8.51738 6.55301 8.67318 6.91181C8.92788 7.37918 9.22865 7.82272 9.56734 8.23581C9.60914 8.26098 9.6422 8.29789 9.66218 8.34173" + stroke="#004B7C" + strokeWidth="0.847356" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div>19</div> + <div>|</div> + <div className="font-[700]">Reply</div> + <div>•</div> + <div>1 reply</div> + </div> + </div> + </div> + </div> + ))} + </> + )} + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx index b4b0e07d..a5d25386 100644 --- a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx @@ -8,6 +8,7 @@ import { Checkbox } from '@gitroom/react/form/checkbox'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import { LinkedinDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/linkedin.dto'; +import { LinkedinPreview } from '@gitroom/frontend/components/new-launch/providers/linkedin/linkedin.preview'; const LinkedInSettings = () => { const t = useT(); @@ -29,7 +30,7 @@ export default withProvider<LinkedinDto>({ postComment: PostComment.COMMENT, minimumCharacters: [], SettingsComponent: LinkedInSettings, - CustomPreviewComponent: undefined, + CustomPreviewComponent: LinkedinPreview, dto: LinkedinDto, checkValidity: async (posts, vals) => { const [firstPost, ...restPosts] = posts; diff --git a/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.preview.tsx b/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.preview.tsx new file mode 100644 index 00000000..97d4bd94 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.preview.tsx @@ -0,0 +1,160 @@ +import { FC } from 'react'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { textSlicer } from '@gitroom/helpers/utils/count.length'; +import { VideoOrImage } from '@gitroom/react/helpers/video.or.image'; +import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; + +export const PinterestPreview: FC<{ + maximumCharacters?: number; +}> = (props) => { + const { value: topValue, integration } = useIntegration(); + const mediaDir = useMediaDirectory(); + + const renderContent = topValue.map((p) => { + const newContent = stripHtmlValidation( + 'normal', + p.content.replace( + /<span.*?data-mention-id="([.\s\S]*?)"[.\s\S]*?>([.\s\S]*?)<\/span>/gi, + (match, match1, match2) => { + return `[[[${match2}]]]`; + } + ), + true + ); + + const { start, end } = textSlicer( + integration?.identifier || '', + props.maximumCharacters || 10000, + newContent + ); + + const finalValue = + newContent + .slice(start, end) + .replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">` + + newContent.slice(end).replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `</mark>`; + + return { text: finalValue, images: p.image }; + }); + + return ( + <div className="absolute left-0 top-0 gap-[10px] w-full h-full flex flex-col p-[16px] bg-bgYoutube"> + <div className="h-[40px] items-center flex"> + <div className="flex gap-[16px] flex-1 items-center"> + <div className="flex gap-[8px] items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="22" + height="20" + viewBox="0 0 22 20" + fill="none" + > + <path + fillRule="evenodd" + clipRule="evenodd" + d="M10.7432 2.88777C8.7438 0.550353 5.40975 -0.0784043 2.90469 2.06197C0.399644 4.20234 0.0469677 7.78093 2.0142 10.3124C3.64982 12.4171 8.59977 16.8561 10.2221 18.2928C10.4036 18.4535 10.4944 18.5339 10.6002 18.5655C10.6926 18.593 10.7937 18.593 10.8861 18.5655C10.9919 18.5339 11.0827 18.4535 11.2642 18.2928C12.8865 16.8561 17.8365 12.4171 19.4721 10.3124C21.4393 7.78093 21.1297 4.17982 18.5816 2.06197C16.0335 -0.0558897 12.7425 0.550353 10.7432 2.88777Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + <div>80</div> + </div> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="21" + height="19" + viewBox="0 0 21 19" + fill="none" + > + <path + d="M19.358 9.25C19.358 13.9444 15.5524 17.75 10.858 17.75C9.78124 17.75 8.75123 17.5498 7.80318 17.1845C7.62984 17.1178 7.54318 17.0844 7.47426 17.0685C7.40647 17.0529 7.3574 17.0463 7.28788 17.0437C7.21721 17.041 7.13967 17.049 6.98459 17.065L1.86356 17.5944C1.37532 17.6448 1.1312 17.6701 0.987197 17.5822C0.861771 17.5057 0.776342 17.3779 0.753601 17.2328C0.727493 17.0661 0.844148 16.8502 1.07746 16.4184L2.71312 13.3908C2.84782 13.1415 2.91517 13.0168 2.94568 12.8969C2.9758 12.7786 2.98309 12.6932 2.97345 12.5714C2.96369 12.4481 2.90959 12.2876 2.80139 11.9666C2.51387 11.1136 2.35802 10.2 2.35802 9.25C2.35802 4.55558 6.1636 0.75 10.858 0.75C15.5524 0.75 19.358 4.55558 19.358 9.25Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M18.75 9.75V13.95C18.75 15.6302 18.75 16.4702 18.423 17.112C18.1354 17.6765 17.6765 18.1354 17.112 18.423C16.4702 18.75 15.6302 18.75 13.95 18.75H5.55C3.86984 18.75 3.02976 18.75 2.38803 18.423C1.82354 18.1354 1.3646 17.6765 1.07698 17.112C0.75 16.4702 0.75 15.6302 0.75 13.95V9.75M13.75 4.75L9.75 0.75M9.75 0.75L5.75 4.75M9.75 0.75V12.75" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="4" + viewBox="0 0 18 4" + fill="none" + > + <path + d="M8.75 2.75C9.30228 2.75 9.75 2.30228 9.75 1.75C9.75 1.19772 9.30228 0.75 8.75 0.75C8.19772 0.75 7.75 1.19772 7.75 1.75C7.75 2.30228 8.19772 2.75 8.75 2.75Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M15.75 2.75C16.3023 2.75 16.75 2.30228 16.75 1.75C16.75 1.19772 16.3023 0.75 15.75 0.75C15.1977 0.75 14.75 1.19772 14.75 1.75C14.75 2.30228 15.1977 2.75 15.75 2.75Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + <path + d="M1.75 2.75C2.30228 2.75 2.75 2.30228 2.75 1.75C2.75 1.19772 2.30228 0.75 1.75 0.75C1.19772 0.75 0.75 1.19772 0.75 1.75C0.75 2.30228 1.19772 2.75 1.75 2.75Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + <div className="h-full flex rounded-[12px] text-[16px] font-[600] w-[100px] bg-[#E70024] text-white justify-center items-center"> + Save + </div> + </div> + <div + style={{ background: 'url(/no-video-youtube.png)' }} + className="!bg-cover w-full aspect-[calc(16/9)] rounded-[20px] overflow-hidden" + > + {!!renderContent?.[0]?.images?.[0]?.path && ( + <VideoOrImage + imageClassName="w-full aspect-[calc(16/9)]" + videoClassName="w-full aspect-[calc(16/9)] bg-black" + autoplay={true} + src={mediaDir.set(renderContent?.[0]?.images?.[0]?.path || '')} + /> + )} + </div> + <div + className="mt-[13px] whitespace-pre-line" + dangerouslySetInnerHTML={{ __html: renderContent?.[0]?.text || '' }} + ></div> + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx b/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx index e628eb83..02a3a359 100644 --- a/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx @@ -10,6 +10,7 @@ import { PinterestBoard } from '@gitroom/frontend/components/new-launch/provider import { PinterestSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/pinterest.dto'; import { Input } from '@gitroom/react/form/input'; import { ColorPicker } from '@gitroom/react/form/color.picker'; +import { PinterestPreview } from '@gitroom/frontend/components/new-launch/providers/pinterest/pinterest.preview'; const PinterestSettings: FC = () => { const { register, control } = useSettings(); return ( @@ -29,8 +30,9 @@ const PinterestSettings: FC = () => { export default withProvider({ postComment: PostComment.COMMENT, minimumCharacters: [], + comments: false, SettingsComponent: PinterestSettings, - CustomPreviewComponent: undefined, + CustomPreviewComponent: PinterestPreview, dto: PinterestSettingsDto, checkValidity: async ([firstItem, ...otherItems]) => { const isMp4 = firstItem?.find((item) => item.path.indexOf('mp4') > -1); @@ -46,9 +48,7 @@ export default withProvider({ if (isMp4 && firstItem.length > 2) { return 'If posting a video you can only have two media items'; } - if (otherItems.length) { - return 'Can only have one post'; - } + if ( firstItem.length > 1 && firstItem.every((p) => p.path.indexOf('mp4') == -1) diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 6afe1533..521eb8ae 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -200,7 +200,9 @@ export const ShowAllProviders = forwardRef((props, ref) => { )} </div> ) : ( - <GeneralPreviewComponent maximumCharacters={100000000} /> + <div className="border border-borderPreview rounded-[12px] shadow-previewShadow"> + <GeneralPreviewComponent maximumCharacters={100000000} /> + </div> )} </IntegrationContext.Provider> )} diff --git a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.preview.tsx b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.preview.tsx new file mode 100644 index 00000000..e0449dee --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.preview.tsx @@ -0,0 +1,183 @@ +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { textSlicer } from '@gitroom/helpers/utils/count.length'; +import { FC, ReactNode } from 'react'; +import { SliderComponent } from '@gitroom/frontend/components/third-parties/slider.component'; +import { VideoOrImage } from '@gitroom/react/helpers/video.or.image'; + +const TikTokItem: FC<{ icon: ReactNode; num: string }> = ({ icon, num }) => { + return ( + <div className="flex items-center flex-col"> + <div className="w-[29px] h-[29px] rounded-full bg-bgTiktokItem flex justify-center items-center text-bgTiktokItemIcon"> + {icon} + </div> + <div className="text-[8px] font-[700] text-bgTiktokItemIcon">{num}</div> + </div> + ); +}; +export const TiktokPreview: FC<{ + maximumCharacters?: number; +}> = (props) => { + const { value: topValue, integration } = useIntegration(); + const current = useLaunchStore((state) => state.current); + const mediaDir = useMediaDirectory(); + + const renderContent = topValue.map((p) => { + const newContent = stripHtmlValidation( + 'normal', + p.content.replace( + /<span.*?data-mention-id="([.\s\S]*?)"[.\s\S]*?>([.\s\S]*?)<\/span>/gi, + (match, match1, match2) => { + return `[[[${match2}]]]`; + } + ), + true + ); + + const { start, end } = textSlicer( + integration?.identifier || '', + props.maximumCharacters || 10000, + newContent + ); + + const finalValue = + newContent + .slice(start, end) + .replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">` + + newContent.slice(end).replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `</mark>`; + + return { text: finalValue, images: p.image }; + }); + return ( + <div className="p-[15px] absolute left-0 top-0 w-full h-full flex justify-center bg-newBgColorInner"> + <div className="relative"> + <SliderComponent + list={renderContent?.[0]?.images.map((image, index) => ( + <a + key={`image_${index}`} + className="flex-1" + href={mediaDir.set(image.path)} + target="_blank" + > + <VideoOrImage autoplay={true} src={mediaDir.set(image.path)} /> + </a> + ))} + className="h-full bg-black aspect-[calc(9/16)] rounded-[3px] overflow-hidden" + /> + <div className="absolute pointer-events-none w-full h-full start-0 top-0 px-[12px] py-[25px] justify-end items-start text-white flex flex-col"> + <div className="text-[14px] font-[500]">@{integration?.name}</div> + <div className="text-[13px] font-[400] whitespace-pre-line line-clamp-6 w-full" + dangerouslySetInnerHTML={{ __html: renderContent?.[0]?.text || '' }} + /> + </div> + </div> + <div className="flex flex-col justify-end gap-[10px] ml-[18px]"> + <div className="relative"> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full z-[2] w-[29px] h-[29px]" + /> + <div className="absolute left-[50%] -translate-x-[50%] bottom-0 translate-y-[50%] z-[1]"> + <svg + width="13" + height="13" + viewBox="0 0 13 13" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <circle cx="6.42665" cy="6.42665" r="6.42665" fill="#EA4359" /> + <path + d="M8.53954 5.78529H7.06717V4.31274C7.06717 3.95875 6.78003 3.67188 6.42627 3.67188C6.07226 3.67188 5.78538 3.95876 5.78538 4.31274V5.78529H4.31277C3.95876 5.78529 3.67188 6.07218 3.67188 6.42615C3.67188 6.78015 3.95878 7.06702 4.31277 7.06702H5.78538V8.53956C5.78538 8.89356 6.07228 9.18043 6.42627 9.18043C6.78027 9.18043 7.06717 8.89354 7.06717 8.53956V7.06702H8.53954C8.89355 7.06702 9.18043 6.78013 9.18043 6.42615C9.18043 6.07216 8.89353 5.78529 8.53954 5.78529Z" + fill="white" + /> + </svg> + </div> + </div> + <TikTokItem + num="1.3M" + icon={ + <svg + xmlns="http://www.w3.org/2000/svg" + width="14" + height="13" + viewBox="0 0 14 13" + fill="none" + > + <path + d="M6.73271 12.3432C6.54684 12.3432 6.36164 12.274 6.20938 12.1348C5.66959 11.6478 5.14626 11.1786 4.67368 10.7786C3.32385 9.57933 2.14275 8.55317 1.33269 7.54467C0.421822 6.41466 0 5.33697 0 4.17236C0 3.02468 0.371068 1.98152 1.06313 1.21694C1.75518 0.434621 2.7168 0 3.76278 0C4.53921 0 5.24773 0.260773 5.87189 0.747687C6.19221 1.00846 6.47958 1.30386 6.73268 1.66922C6.98577 1.30386 7.27247 1.00846 7.59346 0.747687C8.21762 0.243805 8.92615 0 9.70257 0C10.7486 0 11.6937 0.434621 12.4022 1.21694C13.0943 1.98159 13.4654 3.04235 13.4654 4.17236C13.4654 5.35467 13.0435 6.41466 12.1327 7.54467C11.3226 8.55313 10.1415 9.57853 8.79167 10.7786C8.33624 11.1786 7.79644 11.6478 7.25597 12.1348C7.10372 12.274 6.91852 12.3432 6.73264 12.3432H6.73271Z" + fill="currentColor" + /> + </svg> + } + /> + <TikTokItem + num="10.7M" + icon={ + <svg + xmlns="http://www.w3.org/2000/svg" + width="15" + height="14" + viewBox="0 0 15 14" + fill="none" + > + <path + d="M7.03906 0C10.9263 0.000164123 14.0771 2.70127 14.0771 6.0332C14.0771 6.9371 13.8434 7.79378 13.4277 8.56348C13.4272 8.56492 13.4274 8.56696 13.4268 8.56836C12.6717 10.1862 11.4147 11.5178 9.84277 12.3643L8.0918 13.3076C7.64292 13.5491 7.10605 13.1896 7.1582 12.6826L7.22168 12.0615C7.16098 12.0629 7.10014 12.0664 7.03906 12.0664C3.15189 12.0664 0.000323506 9.365 0 6.0332C0 2.70117 3.15169 0 7.03906 0ZM3.41895 5.22852C2.86382 5.22876 2.41406 5.67919 2.41406 6.23438C2.41423 6.78942 2.86392 7.23901 3.41895 7.23926C3.97418 7.23926 4.42464 6.78957 4.4248 6.23438C4.4248 5.67904 3.97428 5.22852 3.41895 5.22852ZM7.03711 5.22852C6.48177 5.22852 6.03125 5.67904 6.03125 6.23438C6.03143 6.78956 6.48188 7.23926 7.03711 7.23926C7.59219 7.23908 8.04181 6.78945 8.04199 6.23438C8.04199 5.67915 7.5923 5.22869 7.03711 5.22852ZM10.6582 5.22852C10.1029 5.22852 9.65234 5.67904 9.65234 6.23438C9.65251 6.78957 10.103 7.23926 10.6582 7.23926C11.2133 7.23914 11.6629 6.7895 11.6631 6.23438C11.6631 5.67911 11.2134 5.22864 10.6582 5.22852Z" + fill="currentColor" + /> + </svg> + } + /> + <TikTokItem + num="1.2M" + icon={ + <svg + xmlns="http://www.w3.org/2000/svg" + width="11" + height="12" + viewBox="0 0 11 12" + fill="none" + > + <path + d="M8.09766 0C9.37507 0.000192409 10.4129 1.04397 10.4189 2.31543V10.7666C10.4189 11.852 9.6448 12.3079 8.69727 11.7803L5.77051 10.1543C5.46465 9.98038 4.96034 9.98042 4.64844 10.1543L1.72168 11.7803C0.77405 12.3021 8.98228e-05 11.8461 0 10.7666V2.31543C0 1.04385 1.03786 0 2.31543 0H8.09766Z" + fill="currentColor" + /> + </svg> + } + /> + <TikTokItem + num="1.2M" + icon={ + <svg + xmlns="http://www.w3.org/2000/svg" + width="15" + height="12" + viewBox="0 0 15 12" + fill="none" + > + <path + d="M14.4733 6.3949L9.33173 11.5364C9.12172 11.7465 8.80634 11.8091 8.5316 11.6955C8.25755 11.5819 8.07853 11.314 8.07853 11.0173V8.82547C3.18623 8.99211 1.26785 10.7872 1.24864 10.8064H1.24795C1.0159 11.0323 0.662667 11.0791 0.378972 10.9221C0.0952775 10.7644 -0.0520695 10.4401 0.0167829 10.1234C0.0319314 10.0538 1.57984 3.43187 8.07847 2.96368V0.734176C8.07847 0.437402 8.2575 0.169555 8.53155 0.055929C8.80629 -0.0576845 9.12166 0.00497537 9.33167 0.214988L14.4732 5.35653C14.6109 5.49425 14.6887 5.68084 14.6887 5.87571C14.6887 6.07058 14.6109 6.25718 14.4732 6.39489L14.4733 6.3949Z" + fill="currentColor" + /> + </svg> + } + /> + <div> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full relative z-[2] w-[29px] h-[29px]" + /> + </div> + </div> + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx index 29c83f10..7268c2b8 100644 --- a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx @@ -2,11 +2,7 @@ import { FC, - ReactEventHandler, - useCallback, - useEffect, useMemo, - useState, } from 'react'; import { PostComment, @@ -15,75 +11,13 @@ import { import { TikTokDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/tiktok.dto'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import { Select } from '@gitroom/react/form/select'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; import { Checkbox } from '@gitroom/react/form/checkbox'; import clsx from 'clsx'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; import { Input } from '@gitroom/react/form/input'; +import { TiktokPreview } from '@gitroom/frontend/components/new-launch/providers/tiktok/tiktok.preview'; -const CheckTikTokValidity: FC<{ - picture: string; -}> = (props) => { - const { register } = useSettings(); - const t = useT(); - - const func = useCustomProviderFunction(); - const [maxVideoLength, setMaxVideoLength] = useState(0); - const [isValidVideo, setIsValidVideo] = useState<undefined | boolean>( - undefined - ); - const registerVideo = register('isValidVideo'); - const video = useMemo(() => { - return props.picture; - }, [props.picture]); - useEffect(() => { - loadStats(); - }, []); - const loadStats = useCallback(async () => { - const { maxDurationSeconds } = await func.get('maxVideoLength'); - // setMaxVideoLength(5); - setMaxVideoLength(maxDurationSeconds); - }, []); - const loadVideo: ReactEventHandler<HTMLVideoElement> = useCallback( - (e) => { - // @ts-ignore - setIsValidVideo(e.target.duration <= maxVideoLength); - registerVideo.onChange({ - target: { - name: 'isValidVideo', - // @ts-ignore - value: String(e.target.duration <= maxVideoLength), - }, - }); - }, - [maxVideoLength, registerVideo] - ); - if (!maxVideoLength || !video || video.indexOf('mp4') === -1) { - return null; - } - return ( - <> - {isValidVideo === false && ( - <div className="text-red-600 my-[20px]"> - {t( - 'video_length_is_invalid_must_be_up_to', - 'Video length is invalid, must be up to' - )} - {maxVideoLength} - {t('seconds', 'seconds')} - </div> - )} - <video - controls - onLoadedMetadata={loadVideo} - className="w-0 h-0 overflow-hidden pointer-events-none" - > - <source src={video} type="video/mp4" /> - </video> - </> - ); -}; const TikTokSettings: FC<{ values?: any; }> = (props) => { @@ -288,7 +222,7 @@ const TikTokSettings: FC<{ )} </div> </div> - <div className={clsx(!disclose && 'invisible', 'mt-[20px]')}> + <div className={clsx(!disclose && 'invisible h-0 overflow-hidden', 'mt-[20px]')}> <Checkbox variant="hollow" label={t('label_your_brand', 'Your brand')} @@ -364,13 +298,11 @@ export default withProvider({ postComment: PostComment.COMMENT, minimumCharacters: [], SettingsComponent: TikTokSettings, - CustomPreviewComponent: undefined, + comments: false, + CustomPreviewComponent: TiktokPreview, dto: TikTokDto, checkValidity: async (items) => { const [firstItems] = items; - if (items.length !== 1) { - return 'Should have one item'; - } if (firstItems.length === 0) { return 'No video / images selected'; } diff --git a/apps/frontend/src/components/new-launch/providers/youtube/youtube.preview.tsx b/apps/frontend/src/components/new-launch/providers/youtube/youtube.preview.tsx new file mode 100644 index 00000000..c942b468 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/youtube/youtube.preview.tsx @@ -0,0 +1,149 @@ +import { FC } from 'react'; +import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { textSlicer } from '@gitroom/helpers/utils/count.length'; +import { VideoOrImage } from '@gitroom/react/helpers/video.or.image'; + +export const YoutubePreview: FC<{ + maximumCharacters?: number; +}> = (props) => { + const { value: topValue, integration } = useIntegration(); + const current = useLaunchStore((state) => state.current); + const mediaDir = useMediaDirectory(); + + const renderContent = topValue.map((p) => { + const newContent = stripHtmlValidation( + 'normal', + p.content.replace( + /<span.*?data-mention-id="([.\s\S]*?)"[.\s\S]*?>([.\s\S]*?)<\/span>/gi, + (match, match1, match2) => { + return `[[[${match2}]]]`; + } + ), + true + ); + + const { start, end } = textSlicer( + integration?.identifier || '', + props.maximumCharacters || 10000, + newContent + ); + + const finalValue = + newContent + .slice(start, end) + .replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `<mark class="bg-red-500" data-tooltip-id="tooltip" data-tooltip-content="This text will be cropped">` + + newContent.slice(end).replace(/\[\[\[([.\s\S]*?)]]]/, (match, match1) => { + return `<span class="font-bold font-[arial]" style="color: #ae8afc">${match1}</span>`; + }) + + `</mark>`; + + return { text: finalValue, images: p.image }; + }); + + return ( + <div className="absolute left-0 top-0 gap-[12px] w-full h-full flex flex-col p-[16px] bg-bgYoutube"> + <div + style={{ background: 'url(/no-video-youtube.png)' }} + className="!bg-cover w-full aspect-[calc(16/9)] rounded-[4px] overflow-hidden" + > + {!!renderContent?.[0]?.images?.[0]?.path && ( + <VideoOrImage + imageClassName="w-full aspect-[calc(16/9)]" + videoClassName="w-full aspect-[calc(16/9)] bg-black" + autoplay={true} + src={mediaDir.set(renderContent?.[0]?.images?.[0]?.path || '')} + /> + )} + </div> + <div className="flex items-center"> + <div className="flex flex-1 gap-[17px] items-center"> + <div> + <img + src={integration?.picture || '/no-picture.jpg'} + alt="social" + className="rounded-full z-[2] w-[40px] h-[40px]" + /> + </div> + <div className="flex flex-col"> + <div className="text-[14px] font-[500]">{integration?.name}</div> + <div className="text-[10px] font-[400]">16.7M subscribers</div> + </div> + <div> + <div className="h-[32px] text-[12px] text-newBgColor font-[500] px-[14px] flex justify-center items-center bg-youtubeButton rounded-[16px]"> + Subscribe + </div> + </div> + </div> + <div className="gap-[4px] flex items-center text-youtubeSvg"> + <div className="bg-youtubeBgAction text-[13px] px-[9px] h-[32px] rounded-[16px] flex items-center"> + <svg + className="mr-[9px]" + xmlns="http://www.w3.org/2000/svg" + width="16" + height="15" + viewBox="0 0 16 15" + fill="none" + > + <path + d="M13.7503 6.1035H10.0621L11.3874 1.79617C11.6664 0.898087 10.934 0 9.92255 0C9.41684 0 8.92855 0.209263 8.59722 0.566754L3.48772 6.1035H0V14.8228H3.48772H4.35965H12.5819C13.5062 14.8228 14.3084 14.2386 14.4915 13.419L15.6598 8.18742C15.8953 7.10622 14.9797 6.1035 13.7503 6.1035ZM3.48772 13.9509H0.871929V6.97543H3.48772V13.9509ZM14.8054 7.99559L13.637 13.2272C13.5498 13.6457 13.1051 13.9509 12.5819 13.9509H4.35965V6.44356L9.24245 1.15967C9.40812 0.976561 9.66098 0.871929 9.92255 0.871929C10.1493 0.871929 10.3585 0.967842 10.4719 1.13351C10.5329 1.2207 10.6027 1.36021 10.5503 1.54331L9.22501 5.85065L8.87624 6.97543H10.0533H13.7416C14.0991 6.97543 14.4391 7.12366 14.6397 7.37652C14.753 7.50731 14.8664 7.72529 14.8054 7.99559Z" + fill="currentColor" + /> + </svg> + <div className="mr-[14px]">205K</div> + <div className="h-[20px] w-[1px] bg-[#A0A0A0] mr-[12px]" /> + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="15" + viewBox="0 0 16 15" + fill="none" + > + <path + d="M12.2092 0H11.3373H3.11498C2.18201 0 1.38856 0.584193 1.20545 1.40381L0.0370652 6.63538C-0.198356 7.71657 0.71717 8.71929 1.94659 8.71929H5.63485L4.30952 13.0266C4.0305 13.9247 4.76292 14.8228 5.77436 14.8228C6.28008 14.8228 6.76836 14.6135 7.09969 14.256L12.2092 8.71929H15.6969V0H12.2092ZM6.45446 13.6631C6.2888 13.8462 6.03594 13.9509 5.77436 13.9509C5.54766 13.9509 5.33839 13.855 5.22504 13.6893C5.16401 13.6021 5.09425 13.4626 5.14657 13.2795L6.4719 8.97215L6.82067 7.84736H5.63485H1.94659C1.5891 7.84736 1.24905 7.69913 1.0485 7.44628C0.943871 7.31549 0.830521 7.0975 0.891556 6.81849L2.05994 1.58691C2.14713 1.1771 2.59182 0.871929 3.11498 0.871929H11.3373V8.37924L6.45446 13.6631ZM14.825 7.84736H12.2092V0.871929H14.825V7.84736Z" + fill="currentColor" + /> + </svg> + </div> + <div className="h-[32px] w-[32px] rounded-full bg-youtubeBgAction flex justify-center items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="16" + viewBox="0 0 18 16" + fill="none" + > + <path + d="M11.3351 2.29317L16.2702 7.84736L11.3351 13.4016V10.4632V9.59122H10.4632C7.01031 9.59122 4.23758 10.4632 1.96184 12.2855C3.56619 8.73673 6.4174 6.70514 10.5852 6.09479L11.3351 5.98143V5.23158V2.29317ZM10.4632 0V5.23158C3.67954 6.21686 0.967841 10.7509 0 15.6947C2.42396 12.2332 5.61522 10.4632 10.4632 10.4632V15.6947L17.4386 7.84736L10.4632 0Z" + fill="currentColor" + /> + </svg> + </div> + <div className="h-[32px] w-[32px] rounded-full bg-youtubeBgAction flex justify-center items-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="14" + height="3" + viewBox="0 0 14 3" + fill="none" + > + <path + d="M2.61579 1.30789C2.61579 2.03159 2.03159 2.61579 1.30789 2.61579C0.584193 2.61579 0 2.03159 0 1.30789C0 0.584193 0.584193 0 1.30789 0C2.03159 0 2.61579 0.584193 2.61579 1.30789ZM6.53947 0C5.81577 0 5.23158 0.584193 5.23158 1.30789C5.23158 2.03159 5.81577 2.61579 6.53947 2.61579C7.26317 2.61579 7.84736 2.03159 7.84736 1.30789C7.84736 0.584193 7.26317 0 6.53947 0ZM11.771 0C11.0473 0 10.4632 0.584193 10.4632 1.30789C10.4632 2.03159 11.0473 2.61579 11.771 2.61579C12.4947 2.61579 13.0789 2.03159 13.0789 1.30789C13.0789 0.584193 12.4947 0 11.771 0Z" + fill="currentColor" + /> + </svg> + </div> + </div> + </div> + <div + className="bg-youtubeBgAction rounded-[12px] p-[12px] text-[12px] font-[400] whitespace-pre-line" + dangerouslySetInnerHTML={{ __html: renderContent?.[0]?.text }} + /> + </div> + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx b/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx index 03573774..99de4177 100644 --- a/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx @@ -11,6 +11,7 @@ import { Input } from '@gitroom/react/form/input'; import { MediumTags } from '@gitroom/frontend/components/new-launch/providers/medium/medium.tags'; import { MediaComponent } from '@gitroom/frontend/components/media/media.component'; import { Select } from '@gitroom/react/form/select'; +import { YoutubePreview } from '@gitroom/frontend/components/new-launch/providers/youtube/youtube.preview'; const type = [ { label: 'Public', @@ -81,15 +82,13 @@ const YoutubeSettings: FC = () => { }; export default withProvider({ postComment: PostComment.COMMENT, + comments: false, minimumCharacters: [], SettingsComponent: YoutubeSettings, - CustomPreviewComponent: undefined, + CustomPreviewComponent: YoutubePreview, dto: YoutubeSettingsDto, checkValidity: async (items) => { const [firstItems] = items; - if (items.length !== 1) { - return 'Should have one item'; - } if (items[0].length !== 1) { return 'You need one media'; } diff --git a/apps/frontend/src/components/new-launch/store.ts b/apps/frontend/src/components/new-launch/store.ts index d7b03718..143a590c 100644 --- a/apps/frontend/src/components/new-launch/store.ts +++ b/apps/frontend/src/components/new-launch/store.ts @@ -38,6 +38,7 @@ interface StoreState { tags: { label: string; value: string }[]; tab: 0 | 1; current: string; + comments: boolean | 'no-media'; locked: boolean; hide: boolean; setLocked: (locked: boolean) => void; @@ -125,12 +126,14 @@ interface StoreState { setLoaded?: (loaded: boolean) => void; setChars: (id: string, chars: number) => void; chars: Record<string, number>; + setComments: (comments: boolean | 'no-media') => void; } const initialState = { editor: undefined as undefined, loaded: true, dummy: false, + comments: true, activateExitButton: true, date: newDayjs(), postComment: PostComment.ALL, @@ -550,4 +553,8 @@ export const useLaunchStore = create<StoreState>()((set) => ({ [id]: chars, }, })), + setComments: (comments: boolean | 'no-media') => + set((state) => ({ + comments, + })), })); diff --git a/apps/frontend/src/components/third-parties/slider.component.tsx b/apps/frontend/src/components/third-parties/slider.component.tsx new file mode 100644 index 00000000..6194b7a6 --- /dev/null +++ b/apps/frontend/src/components/third-parties/slider.component.tsx @@ -0,0 +1,71 @@ +import { FC, ReactNode, useCallback, useState } from 'react'; +import clsx from 'clsx'; +import { + ChevronLeftIcon, + ChevronRightIcon, +} from '@gitroom/frontend/components/ui/icons'; + +export const SliderComponent: FC<{ + className: string; + list: ReactNode[]; +}> = ({ className, list }) => { + const [show, setShow] = useState(0); + + const goToPrevious = useCallback(() => { + setShow((prev) => (prev > 0 ? prev - 1 : prev)); + }, []); + + const goToNext = useCallback(() => { + setShow((prev) => (prev < list.length - 1 ? prev + 1 : prev)); + }, [list.length]); + + const canGoPrevious = show > 0; + const canGoNext = show < list.length - 1; + + return ( + <div className={clsx(className, 'relative')}> + {list[show]} + + {/* Left Arrow */} + {canGoPrevious && ( + <button + onClick={goToPrevious} + className="absolute top-[50%] start-[10px] -translate-y-[50%] flex items-center justify-center w-8 h-8 rounded-full bg-black/60 hover:bg-black/80 text-white transition-colors backdrop-blur-sm cursor-pointer" + aria-label="Previous slide" + > + <ChevronLeftIcon size={18} /> + </button> + )} + + {/* Right Arrow */} + {canGoNext && ( + <button + onClick={goToNext} + className="absolute top-[50%] end-[10px] -translate-y-[50%] flex items-center justify-center w-8 h-8 rounded-full bg-black/60 hover:bg-black/80 text-white transition-colors backdrop-blur-sm cursor-pointer" + aria-label="Next slide" + > + <ChevronRightIcon size={18} /> + </button> + )} + + {/* Pagination Dots */} + {list.length > 1 && ( + <div className="absolute bottom-[10px] left-[50%] -translate-x-[50%] flex gap-2"> + {list.map((_, index) => ( + <button + key={index} + onClick={() => setShow(index)} + className={clsx( + 'w-2 h-2 rounded-full transition-colors cursor-pointer', + index === show + ? 'bg-white' + : 'bg-transparent border border-white' + )} + aria-label={`Go to slide ${index + 1}`} + /> + ))} + </div> + )} + </div> + ); +}; diff --git a/apps/frontend/src/components/ui/is.scroll.hook.tsx b/apps/frontend/src/components/ui/is.scroll.hook.tsx new file mode 100644 index 00000000..41b80b47 --- /dev/null +++ b/apps/frontend/src/components/ui/is.scroll.hook.tsx @@ -0,0 +1,21 @@ +import { useState, useEffect } from 'react'; + +export function useHasScroll( + ref: React.RefObject<HTMLElement>, +) { + const [hasScroll, setHasScroll] = useState(false); + useEffect(() => { + const el = ref.current; + if (!el) return; + + const check = () => setHasScroll(el.scrollHeight > el.clientHeight); + check(); + + const observer = new MutationObserver(check); + observer.observe(el, { childList: true, subtree: true }); + + return () => observer.disconnect(); + }, [ref]); + + return hasScroll; +} diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index e4baea66..4eb7bb39 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -100,6 +100,19 @@ module.exports = { menuDotsHover: 'var(--new-menu-hover)', bigStrip: 'var(--new-big-strips)', popup: 'var(--popup-color)', + bgLinkedin: 'var(--linkedin-bg)', + bgFacebook: 'var(--facebook-bg)', + bgInstagram: 'var(--instagram-bg)', + bgTiktokItem: 'var(--tiktok-item-bg)', + bgTiktokItemIcon: 'var(--tiktok-item-icon-bg)', + bgYoutube: 'var(--youtube-bg)', + bgCommentFacebook: 'var(--facebook-bg-comment)', + textLinkedin: 'var(--linkedin-text)', + borderPreview: 'var(--border-preview)', + borderLinkedin: 'var(--linkedin-border)', + youtubeButton: 'var(--youtube-button)', + youtubeBgAction: 'var(--youtube-action-color)', + youtubeSvg: 'var(--youtube-svg-border)', }, gridTemplateColumns: { 13: 'repeat(13, minmax(0, 1fr));', @@ -129,6 +142,7 @@ module.exports = { yellowToast: '0px 0px 50px rgba(252, 186, 3, 0.3)', greenToast: '0px 0px 50px rgba(60, 124, 90, 0.3)', menu: 'var(--menu-shadow)', + previewShadow: 'var(--preview-box-shadow)', }, // that is actual animation keyframes: (theme) => ({ diff --git a/libraries/react-shared-libraries/src/helpers/video.or.image.tsx b/libraries/react-shared-libraries/src/helpers/video.or.image.tsx index acade786..7fa8149f 100644 --- a/libraries/react-shared-libraries/src/helpers/video.or.image.tsx +++ b/libraries/react-shared-libraries/src/helpers/video.or.image.tsx @@ -4,14 +4,16 @@ export const VideoOrImage: FC<{ src: string; autoplay: boolean; isContain?: boolean; + imageClassName?: string; + videoClassName?: string; }> = (props) => { - const { src, autoplay, isContain } = props; + const { src, autoplay, isContain, imageClassName, videoClassName } = props; if (src?.indexOf('mp4') > -1) { return ( <video src={src} autoPlay={autoplay} - className="w-full h-full" + className={clsx('w-full h-full', videoClassName)} muted={true} loop={true} /> @@ -21,7 +23,8 @@ export const VideoOrImage: FC<{ <img className={clsx( isContain ? 'object-contain' : 'object-cover', - 'w-full h-full' + 'w-full h-full', + imageClassName )} src={src} /> From 2cf0d632de384c36c9738f1b975953405988e932 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 27 Dec 2025 23:30:15 +0700 Subject: [PATCH 065/340] feat: editor bug fixes, some previews --- .../src/components/new-launch/finisher/thread.finisher.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/frontend/src/components/new-launch/finisher/thread.finisher.tsx b/apps/frontend/src/components/new-launch/finisher/thread.finisher.tsx index b15ec572..b6e95227 100644 --- a/apps/frontend/src/components/new-launch/finisher/thread.finisher.tsx +++ b/apps/frontend/src/components/new-launch/finisher/thread.finisher.tsx @@ -50,6 +50,7 @@ export const ThreadFinisher = () => { <div className="flex gap-[4px]"> <div className="flex-1 editor text-textColor"> <Editor + comments={true} chars={{}} selectedIntegration={[]} onChange={(val) => setValue('thread_finisher', val)} From 8c1191a093562e235c21e1ab1e3f4cd3738f3596 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 01:36:41 +0700 Subject: [PATCH 066/340] fix: full screen settings --- .../src/components/new-launch/manage.modal.tsx | 18 ++++++++++-------- .../providers/tiktok/tiktok.provider.tsx | 2 -- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 53923e43..1f381c40 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -206,7 +206,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { ); item.preview(); setLoading(false); - setShowSettings(true); + setShowSettings(false); return; } } @@ -341,7 +341,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { Create Post </div> <div className="flex-1 flex flex-col gap-[16px]"> - <div className="flex-1 relative"> + <div className={clsx("flex-1 relative", showSettings && 'hidden')}> <div id="social-content" className="gap-[32px] flex flex-col pr-[8px] pt-[20px] pl-[20px] absolute top-0 left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" @@ -378,10 +378,11 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { id="wrapper-settings" className={clsx( 'pb-[20px] px-[20px] select-none', - current === 'global' && 'hidden' + current === 'global' && 'hidden', + showSettings && 'flex-1 flex pt-[20px]' )} > - <div className="bg-newSettings flex flex-col rounded-[12px] gap-[12px]"> + <div className="bg-newSettings flex-1 flex flex-col rounded-[12px] gap-[12px] overflow-hidden"> <div onClick={() => setShowSettings(!showSettings)} className={clsx( @@ -403,12 +404,13 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { </div> </div> <div - id="social-settings" className={clsx( - !showSettings && 'hidden', - 'px-[12px] pb-[12px] text-[14px] text-textColor font-[500] max-h-[300px] overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor' + !showSettings ? 'hidden' : 'flex-1', + 'text-[14px] text-textColor font-[500] relative' )} - /> + > + <div id="social-settings" className="px-[12px] pb-[12px] absolute left-0 top-0 w-full h-full overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor" /> + </div> <style> {`#social-settings [data-id="${current}"] {display: block !important;}`} </style> diff --git a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx index 7268c2b8..d163ed1d 100644 --- a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx @@ -86,7 +86,6 @@ const TikTokSettings: FC<{ {isTitle && <Input label="Title" {...register('title')} maxLength={90} />} <Select label={t('label_who_can_see_this_video', 'Who can see this video?')} - hideErrors={true} disabled={isUploadMode} {...register('privacy_level', { value: 'PUBLIC_TO_EVERYONE', @@ -120,7 +119,6 @@ const TikTokSettings: FC<{ ))} </Select> <Select - hideErrors={true} label={t('label_auto_add_music', 'Auto add music')} {...register('autoAddMusic', { value: 'no', From 9c751fb7253dbb2789a997e5259204c48dc50fae Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 10:28:09 +0700 Subject: [PATCH 067/340] feat: provider checks protection --- .../providers/bluesky/bluesky.provider.tsx | 6 ++--- .../providers/dribbble/dribbble.provider.tsx | 8 +++--- .../new-launch/providers/gmb/gmb.provider.tsx | 10 +++---- .../instagram/instagram.collaborators.tsx | 18 ++++++------- .../providers/lemmy/lemmy.provider.tsx | 12 ++++----- .../providers/linkedin/linkedin.provider.tsx | 14 +++++----- .../pinterest/pinterest.provider.tsx | 26 +++++++++---------- .../providers/reddit/reddit.provider.tsx | 6 ++--- .../providers/threads/threads.provider.tsx | 10 +++---- .../providers/tiktok/tiktok.provider.tsx | 12 ++++----- .../providers/warpcast/warpcast.provider.tsx | 2 +- .../new-launch/providers/x/x.provider.tsx | 10 +++---- .../providers/youtube/youtube.provider.tsx | 6 ++--- 13 files changed, 70 insertions(+), 70 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/bluesky/bluesky.provider.tsx b/apps/frontend/src/components/new-launch/providers/bluesky/bluesky.provider.tsx index 8b2d7d0b..b3408970 100644 --- a/apps/frontend/src/components/new-launch/providers/bluesky/bluesky.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/bluesky/bluesky.provider.tsx @@ -18,14 +18,14 @@ export default withProvider({ dto: undefined, checkValidity: async (posts) => { if ( - posts.some( - (p) => p.some((a) => a.path.indexOf('mp4') > -1) && p.length > 1 + posts?.some( + (p) => p?.some((a) => (a?.path?.indexOf?.('mp4') ?? -1) > -1) && (p?.length ?? 0) > 1 ) ) { return 'You can only upload one video per post.'; } - if (posts.some((p) => p.length > 4)) { + if (posts?.some((p) => (p?.length ?? 0) > 4)) { return 'There can be maximum 4 pictures in a post.'; } return true; diff --git a/apps/frontend/src/components/new-launch/providers/dribbble/dribbble.provider.tsx b/apps/frontend/src/components/new-launch/providers/dribbble/dribbble.provider.tsx index ddc040c3..741aaa1f 100644 --- a/apps/frontend/src/components/new-launch/providers/dribbble/dribbble.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/dribbble/dribbble.provider.tsx @@ -24,9 +24,9 @@ export default withProvider({ SettingsComponent: DribbbleSettings, CustomPreviewComponent: undefined, dto: DribbbleDto, - checkValidity: async ([firstItem, ...otherItems]) => { - const isMp4 = firstItem?.find((item) => item.path.indexOf('mp4') > -1); - if (firstItem.length !== 1) { + checkValidity: async ([firstItem, ...otherItems] = []) => { + const isMp4 = firstItem?.find((item) => (item?.path?.indexOf?.('mp4') ?? -1) > -1); + if (firstItem?.length !== 1) { return 'Requires one item'; } if (isMp4) { @@ -41,7 +41,7 @@ export default withProvider({ // @ts-ignore resolve({ width: this.width, height: this.height }); }; - url.src = firstItem[0].path; + url.src = firstItem?.[0]?.path; }); if ( (details?.width === 400 && details?.height === 300) || diff --git a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx index edb05772..ba008a1a 100644 --- a/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/gmb/gmb.provider.tsx @@ -167,20 +167,20 @@ export default withProvider({ dto: GmbSettingsDto, checkValidity: async (items, settings: any) => { // GMB posts can have text only, or text with one image - if (items.length > 0 && items[0].length > 1) { + if ((items?.length ?? 0) > 0 && (items?.[0]?.length ?? 0) > 1) { return 'Google My Business posts can only have one image'; } // Check for video - GMB doesn't support video in local posts - if (items.length > 0 && items[0].length > 0) { - const media = items[0][0]; - if (media.path.indexOf('mp4') > -1) { + if ((items?.length ?? 0) > 0 && (items?.[0]?.length ?? 0) > 0) { + const media = items?.[0]?.[0]; + if ((media?.path?.indexOf?.('mp4') ?? -1) > -1) { return 'Google My Business posts do not support video attachments'; } } // Event posts require a title - if (settings.topicType === 'EVENT' && !settings.eventTitle) { + if (settings?.topicType === 'EVENT' && !settings?.eventTitle) { return 'Event posts require an event title'; } diff --git a/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx b/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx index 14c6e901..6ca7b088 100644 --- a/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx +++ b/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx @@ -60,18 +60,18 @@ export default withProvider<InstagramDto>({ SettingsComponent: InstagramCollaborators, CustomPreviewComponent: InstagramPreview, dto: InstagramDto, - checkValidity: async ([firstPost, ...otherPosts], settings) => { - if (!firstPost.length) { + checkValidity: async ([firstPost, ...otherPosts] = [], settings) => { + if (!firstPost?.length) { return 'Should have at least one media'; } - if (firstPost.length > 1 && settings.post_type === 'story') { + if ((firstPost?.length ?? 0) > 1 && settings?.post_type === 'story') { return 'Stories can only have one media'; } const checkVideosLength = await Promise.all( firstPost - .filter((f) => f.path.indexOf('mp4') > -1) - .flatMap((p) => p.path) - .map((p) => { + ?.filter((f) => (f?.path?.indexOf?.('mp4') ?? -1) > -1) + ?.flatMap((p) => p?.path) + ?.map((p) => { return new Promise<number>((res) => { const video = document.createElement('video'); video.preload = 'metadata'; @@ -80,13 +80,13 @@ export default withProvider<InstagramDto>({ res(video.duration); }); }); - }) + }) ?? [] ); for (const video of checkVideosLength) { - if (video > 60 && settings.post_type === 'story') { + if (video > 60 && settings?.post_type === 'story') { return 'Stories should be maximum 60 seconds'; } - if (video > 180 && settings.post_type === 'post') { + if (video > 180 && settings?.post_type === 'post') { return 'Reel should be maximum 180 seconds'; } } diff --git a/apps/frontend/src/components/new-launch/providers/lemmy/lemmy.provider.tsx b/apps/frontend/src/components/new-launch/providers/lemmy/lemmy.provider.tsx index ac8dd6de..74bbb294 100644 --- a/apps/frontend/src/components/new-launch/providers/lemmy/lemmy.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/lemmy/lemmy.provider.tsx @@ -73,13 +73,13 @@ export default withProvider({ CustomPreviewComponent: undefined, dto: LemmySettingsDto, checkValidity: async (items) => { - const [firstItems] = items; + const [firstItems] = items ?? []; if ( - firstItems.length && - firstItems[0].path.indexOf('png') === -1 && - firstItems[0].path.indexOf('jpg') === -1 && - firstItems[0].path.indexOf('jpef') === -1 && - firstItems[0].path.indexOf('gif') === -1 + firstItems?.length && + (firstItems?.[0]?.path?.indexOf?.('png') ?? -1) === -1 && + (firstItems?.[0]?.path?.indexOf?.('jpg') ?? -1) === -1 && + (firstItems?.[0]?.path?.indexOf?.('jpef') ?? -1) === -1 && + (firstItems?.[0]?.path?.indexOf?.('gif') ?? -1) === -1 ) { return 'You can set only one picture for a cover'; } diff --git a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx index a5d25386..d558ccb6 100644 --- a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx @@ -33,23 +33,23 @@ export default withProvider<LinkedinDto>({ CustomPreviewComponent: LinkedinPreview, dto: LinkedinDto, checkValidity: async (posts, vals) => { - const [firstPost, ...restPosts] = posts; + const [firstPost, ...restPosts] = posts ?? []; if ( - vals.post_as_images_carousel && - (firstPost.length < 2 || - firstPost.some((p) => p.path.indexOf('mp4') > -1)) + vals?.post_as_images_carousel && + ((firstPost?.length ?? 0) < 2 || + firstPost?.some((p) => (p?.path?.indexOf?.('mp4') ?? -1) > -1)) ) { return 'Carousel can only be created with 2 or more images and no videos.'; } if ( - firstPost.length > 1 && - firstPost.some((p) => p.path.indexOf('mp4') > -1) + (firstPost?.length ?? 0) > 1 && + firstPost?.some((p) => (p?.path?.indexOf?.('mp4') ?? -1) > -1) ) { return 'Can have maximum 1 media when selecting a video.'; } - if (restPosts.some((p) => p.length > 0)) { + if (restPosts?.some((p) => (p?.length ?? 0) > 0)) { return 'Comments can only contain text.'; } return true; diff --git a/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx b/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx index 02a3a359..db03ea64 100644 --- a/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/pinterest/pinterest.provider.tsx @@ -34,42 +34,42 @@ export default withProvider({ SettingsComponent: PinterestSettings, CustomPreviewComponent: PinterestPreview, dto: PinterestSettingsDto, - checkValidity: async ([firstItem, ...otherItems]) => { - const isMp4 = firstItem?.find((item) => item.path.indexOf('mp4') > -1); + checkValidity: async ([firstItem, ...otherItems] = []) => { + const isMp4 = firstItem?.find((item) => (item?.path?.indexOf?.('mp4') ?? -1) > -1); const isPicture = firstItem?.find( - (item) => item.path.indexOf('mp4') === -1 + (item) => (item?.path?.indexOf?.('mp4') ?? -1) === -1 ); - if (firstItem.length === 0) { + if ((firstItem?.length ?? 0) === 0) { return 'Requires at least one media'; } - if (isMp4 && firstItem.length !== 2 && !isPicture) { + if (isMp4 && firstItem?.length !== 2 && !isPicture) { return 'If posting a video you have to also include a cover image as second media'; } - if (isMp4 && firstItem.length > 2) { + if (isMp4 && (firstItem?.length ?? 0) > 2) { return 'If posting a video you can only have two media items'; } if ( - firstItem.length > 1 && - firstItem.every((p) => p.path.indexOf('mp4') == -1) + (firstItem?.length ?? 0) > 1 && + firstItem?.every((p) => (p?.path?.indexOf?.('mp4') ?? -1) == -1) ) { const loadAll: Array<{ width: number; height: number; }> = (await Promise.all( - firstItem.map((p) => { + firstItem?.map((p) => { return new Promise((resolve, reject) => { const url = new Image(); url.onload = function () { // @ts-ignore resolve({ width: this.width, height: this.height }); }; - url.src = p.path; + url.src = p?.path; }); - }) + }) ?? [] )) as any; - const checkAllTheSameWidthHeight = loadAll.every((p, i, arr) => { - return p.width === arr[0].width && p.height === arr[0].height; + const checkAllTheSameWidthHeight = loadAll?.every((p, i, arr) => { + return p?.width === arr?.[0]?.width && p?.height === arr?.[0]?.height; }); if (!checkAllTheSameWidthHeight) { return 'Requires all images to have the same width and height'; diff --git a/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx b/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx index 18ce5cae..51572ecc 100644 --- a/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/reddit/reddit.provider.tsx @@ -218,15 +218,15 @@ export default withProvider({ if ( settings?.subreddit?.some( (p: any, index: number) => - p?.value?.type === 'media' && posts[0].length !== 1 + p?.value?.type === 'media' && posts?.[0]?.length !== 1 ) ) { return 'When posting a media post, you must attached exactly one media file.'; } if ( - posts.some((p) => - p.some((a) => !a.thumbnail && a.path.indexOf('mp4') > -1) + posts?.some((p) => + p?.some((a) => !a?.thumbnail && (a?.path?.indexOf?.('mp4') ?? -1) > -1) ) ) { return 'You must attach a thumbnail to your video post.'; diff --git a/apps/frontend/src/components/new-launch/providers/threads/threads.provider.tsx b/apps/frontend/src/components/new-launch/providers/threads/threads.provider.tsx index 01ebc718..7b930d26 100644 --- a/apps/frontend/src/components/new-launch/providers/threads/threads.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/threads/threads.provider.tsx @@ -15,12 +15,12 @@ export default withProvider({ SettingsComponent: SettingsComponent, CustomPreviewComponent: undefined, dto: undefined, - checkValidity: async ([firstPost, ...otherPosts], settings) => { + checkValidity: async ([firstPost, ...otherPosts] = [], settings) => { const checkVideosLength = await Promise.all( firstPost - .filter((f) => f.path.indexOf('mp4') > -1) - .flatMap((p) => p.path) - .map((p) => { + ?.filter((f) => (f?.path?.indexOf?.('mp4') ?? -1) > -1) + ?.flatMap((p) => p?.path) + ?.map((p) => { return new Promise<number>((res) => { const video = document.createElement('video'); video.preload = 'metadata'; @@ -29,7 +29,7 @@ export default withProvider({ res(video.duration); }); }); - }) + }) ?? [] ); for (const video of checkVideosLength) { diff --git a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx index d163ed1d..5059223f 100644 --- a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx @@ -26,7 +26,7 @@ const TikTokSettings: FC<{ const t = useT(); const isTitle = useMemo(() => { - return value?.[0].image.some((p) => p.path.indexOf('mp4') === -1); + return value?.[0]?.image?.some((p) => (p?.path?.indexOf?.('mp4') ?? -1) === -1); }, [value]); const disclose = watch('disclose'); @@ -300,18 +300,18 @@ export default withProvider({ CustomPreviewComponent: TiktokPreview, dto: TikTokDto, checkValidity: async (items) => { - const [firstItems] = items; - if (firstItems.length === 0) { + const [firstItems] = items ?? []; + if ((firstItems?.length ?? 0) === 0) { return 'No video / images selected'; } if ( - firstItems.length > 1 && - firstItems?.some((p) => p?.path?.indexOf('mp4') > -1) + (firstItems?.length ?? 0) > 1 && + firstItems?.some((p) => (p?.path?.indexOf?.('mp4') ?? -1) > -1) ) { return 'Only pictures are supported when selecting multiple items'; } else if ( firstItems?.length !== 1 && - firstItems?.[0]?.path?.indexOf('mp4') > -1 + (firstItems?.[0]?.path?.indexOf?.('mp4') ?? -1) > -1 ) { return 'You need one media'; } diff --git a/apps/frontend/src/components/new-launch/providers/warpcast/warpcast.provider.tsx b/apps/frontend/src/components/new-launch/providers/warpcast/warpcast.provider.tsx index 1d3e25ef..efb372c5 100644 --- a/apps/frontend/src/components/new-launch/providers/warpcast/warpcast.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/warpcast/warpcast.provider.tsx @@ -65,7 +65,7 @@ export default withProvider({ dto: undefined, checkValidity: async (list) => { if ( - list.some((item) => item.some((field) => field.path.indexOf('mp4') > -1)) + list?.some((item) => item?.some((field) => (field?.path?.indexOf?.('mp4') ?? -1) > -1)) ) { return 'Can only accept images'; } diff --git a/apps/frontend/src/components/new-launch/providers/x/x.provider.tsx b/apps/frontend/src/components/new-launch/providers/x/x.provider.tsx index 2ddc7e71..da7f3083 100644 --- a/apps/frontend/src/components/new-launch/providers/x/x.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/x/x.provider.tsx @@ -80,18 +80,18 @@ export default withProvider({ const premium = additionalSettings?.find((p: any) => p?.title === 'Verified')?.value || false; - if (posts.some((p) => p.length > 4)) { + if (posts?.some((p) => (p?.length ?? 0) > 4)) { return 'There can be maximum 4 pictures in a post.'; } if ( - posts.some( - (p) => p.some((m) => m.path.indexOf('mp4') > -1) && p.length > 1 + posts?.some( + (p) => p?.some((m) => (m?.path?.indexOf?.('mp4') ?? -1) > -1) && (p?.length ?? 0) > 1 ) ) { return 'There can be maximum 1 video in a post.'; } - for (const load of posts.flatMap((p) => p.flatMap((a) => a.path))) { - if (load.indexOf('mp4') > -1) { + for (const load of posts?.flatMap((p) => p?.flatMap((a) => a?.path)) ?? []) { + if ((load?.indexOf?.('mp4') ?? -1) > -1) { const isValid = await checkVideoDuration(load, premium); if (!isValid) { return 'Video duration must be less than or equal to 140 seconds.'; diff --git a/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx b/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx index 99de4177..9509d1ef 100644 --- a/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/youtube/youtube.provider.tsx @@ -88,11 +88,11 @@ export default withProvider({ CustomPreviewComponent: YoutubePreview, dto: YoutubeSettingsDto, checkValidity: async (items) => { - const [firstItems] = items; - if (items[0].length !== 1) { + const [firstItems] = items ?? []; + if (items?.[0]?.length !== 1) { return 'You need one media'; } - if (firstItems[0].path.indexOf('mp4') === -1) { + if ((firstItems?.[0]?.path?.indexOf?.('mp4') ?? -1) === -1) { return 'Item must be a video'; } return true; From a188baa6f7a49832fbba773a6c30a6c9ad8f61cb Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 16:23:40 +0700 Subject: [PATCH 068/340] feat: calendar skeleton --- .../frontend/src/components/launches/calendar.context.tsx | 3 +++ apps/frontend/src/components/launches/calendar.tsx | 8 +++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx index 02725198..651634f5 100644 --- a/apps/frontend/src/components/launches/calendar.context.tsx +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -21,6 +21,7 @@ import weekOfYear from 'dayjs/plugin/weekOfYear'; import { extend } from 'dayjs'; import useCookie from 'react-use-cookie'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; +import { timer } from '@gitroom/helpers/utils/timer'; extend(isoWeek); extend(weekOfYear); @@ -28,6 +29,7 @@ export const CalendarContext = createContext({ startDate: newDayjs().startOf('isoWeek').format('YYYY-MM-DD'), endDate: newDayjs().endOf('isoWeek').format('YYYY-MM-DD'), customer: null as string | null, + loading: true, sets: [] as { name: string; id: string; content: string[] }[], signature: undefined as any, comments: [] as Array<{ @@ -251,6 +253,7 @@ export const CalendarWeekProvider: FC<{ reloadCalendarView: swr.mutate, ...filters, posts: isLoading ? [] : internalData, + loading: swr.isLoading, integrations, setFilters: setFiltersWrapper, changeDate, diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 2f72c92c..68694697 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -353,12 +353,12 @@ export const CalendarColumn: FC<{ const { integrations, posts, - trendings, changeDate, display, reloadCalendarView, sets, signature, + loading, } = useCalendar(); const toaster = useToaster(); const modal = useModals(); @@ -642,6 +642,7 @@ export const CalendarColumn: FC<{ className={clsx( 'flex flex-col w-full min-h-full relative', isBeforeNow && 'repeated-strip', + loading && 'animate-pulse', isBeforeNow ? 'cursor-not-allowed' : 'border border-newTextColor/5 rounded-[8px]' @@ -664,6 +665,11 @@ export const CalendarColumn: FC<{ isBeforeNow && postList.length === 0 && 'col-calendar' )} > + {loading && ( + <div className="h-full w-full p-[5px] animate-pulse absolute left-0 top-0 z-[50]"> + <div className="h-full w-full bg-newSettings rounded-[10px]" /> + </div> + )} {list.map((post) => ( <div key={post.id} From 5f945bd396a270cb0090b9061f9da1e943c5cc0f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 17:37:00 +0700 Subject: [PATCH 069/340] feat: color changes --- apps/frontend/src/app/colors.scss | 2 +- .../new-launch/picks.socials.component.tsx | 2 +- .../providers/instagram/instagram.preview.tsx | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/apps/frontend/src/app/colors.scss b/apps/frontend/src/app/colors.scss index 924db4a7..e7195808 100644 --- a/apps/frontend/src/app/colors.scss +++ b/apps/frontend/src/app/colors.scss @@ -84,7 +84,7 @@ --linkedin-border: #e9e5df; --linkedin-bg: #fff; --linkedin-text: #707070; - --facebook-bg: #f1f0f3; + --facebook-bg: #fff; --facebook-bg-comment: #f6f6f6; --instagram-bg: #fff; --tiktok-item-bg: #EEF1F0; diff --git a/apps/frontend/src/components/new-launch/picks.socials.component.tsx b/apps/frontend/src/components/new-launch/picks.socials.component.tsx index fabd5f30..25012393 100644 --- a/apps/frontend/src/components/new-launch/picks.socials.component.tsx +++ b/apps/frontend/src/components/new-launch/picks.socials.component.tsx @@ -56,7 +56,7 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ addOrRemoveSelectedIntegration(integration, {}); }} className={clsx( - 'cursor-pointer border-[1.5px] relative rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500', + 'cursor-pointer border-[2px] relative rounded-full flex justify-center items-center bg-fifth filter transition-all duration-500', selectedIntegrations.findIndex( (p) => p.integration.id === integration.id ) === -1 diff --git a/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx b/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx index edf87cbb..b14cc199 100644 --- a/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx +++ b/apps/frontend/src/components/new-launch/providers/instagram/instagram.preview.tsx @@ -61,7 +61,7 @@ export const InstagramPreview: FC<{ <div className="text-[15px] font-[600]">{integration?.name}</div> </div> </div> - {!!renderContent?.[0]?.images?.length && ( + {!!renderContent?.[0]?.images?.length ? ( <SliderComponent className="h-[585px] rounded-[8px] overflow-hidden" list={renderContent?.[0]?.images.map((image, index) => ( @@ -71,13 +71,15 @@ export const InstagramPreview: FC<{ href={mediaDir.set(image.path)} target="_blank" > - <VideoOrImage - autoplay={true} - src={mediaDir.set(image.path)} - /> + <VideoOrImage autoplay={true} src={mediaDir.set(image.path)} /> </a> ))} /> + ) : ( + <div + style={{ background: 'url(/no-video-youtube.png)' }} + className="!bg-cover w-full aspect-[calc(16/9)] rounded-[8px] overflow-hidden" + /> )} <div className="text-[14px] font-[400] whitespace-pre-line" From e0b496c4f94d455dc0325076e75da29301041c8d Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 17:53:45 +0700 Subject: [PATCH 070/340] feat: settings change --- .../components/new-launch/manage.modal.tsx | 30 +++++++++++++------ .../src/components/ui/icons/index.tsx | 10 ++++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 1f381c40..38ebfe33 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -99,9 +99,19 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const currentIntegration = integrations.find((p) => p.id === current)!; - return `${currentIntegration.name} (${capitalize( - currentIntegration.identifier.split('-').shift() - )})`; + return ( + <div className="flex items-center gap-[10px]"> + <div className="relative"> + <img + src={`/icons/platforms/${currentIntegration.identifier}.png`} + className="w-[20px] h-[20px] rounded-[4px]" + alt={currentIntegration.identifier} + /> + <SettingsIcon size={15} className="text-white absolute -end-[5px] -bottom-[5px]" /> + </div> + <div>{currentIntegration.name} Settings</div> + </div> + ); }, [current]); const changeCustomer = useCallback( @@ -341,7 +351,9 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { Create Post </div> <div className="flex-1 flex flex-col gap-[16px]"> - <div className={clsx("flex-1 relative", showSettings && 'hidden')}> + <div + className={clsx('flex-1 relative', showSettings && 'hidden')} + > <div id="social-content" className="gap-[32px] flex flex-col pr-[8px] pt-[20px] pl-[20px] absolute top-0 left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" @@ -390,11 +402,8 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { showSettings ? '!rounded-b-none' : '' )} > - <div className="flex"> - <SettingsIcon className="text-white" /> - </div> <div className="flex-1 text-[14px] font-[600] text-white"> - {currentIntegrationText} Settings + {currentIntegrationText} </div> <div> <ChevronDownIcon @@ -409,7 +418,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { 'text-[14px] text-textColor font-[500] relative' )} > - <div id="social-settings" className="px-[12px] pb-[12px] absolute left-0 top-0 w-full h-full overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor" /> + <div + id="social-settings" + className="px-[12px] pb-[12px] absolute left-0 top-0 w-full h-full overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor" + /> </div> <style> {`#social-settings [data-id="${current}"] {display: block !important;}`} diff --git a/apps/frontend/src/components/ui/icons/index.tsx b/apps/frontend/src/components/ui/icons/index.tsx index 78758ad4..b43f7cd8 100644 --- a/apps/frontend/src/components/ui/icons/index.tsx +++ b/apps/frontend/src/components/ui/icons/index.tsx @@ -23,18 +23,20 @@ export const SettingsIcon: FC<IconProps> = ({ {...props} > <path - d="M7.82888 16.1429L8.31591 17.2383C8.4607 17.5644 8.69698 17.8414 8.9961 18.0358C9.29522 18.2303 9.64434 18.3337 10.0011 18.3337C10.3579 18.3337 10.707 18.2303 11.0061 18.0358C11.3052 17.8414 11.5415 17.5644 11.6863 17.2383L12.1733 16.1429C12.3467 15.7542 12.6383 15.4302 13.0067 15.217C13.3773 15.0032 13.8061 14.9121 14.2317 14.9568L15.4233 15.0837C15.778 15.1212 16.136 15.055 16.4539 14.8931C16.7717 14.7312 17.0358 14.4806 17.2141 14.1716C17.3925 13.8628 17.4776 13.5089 17.4588 13.1527C17.4401 12.7966 17.3184 12.4535 17.1085 12.1651L16.403 11.1957C16.1517 10.8479 16.0175 10.4293 16.0196 10.0003C16.0195 9.57248 16.155 9.15562 16.4067 8.80959L17.1122 7.84014C17.3221 7.55179 17.4438 7.20872 17.4625 6.85255C17.4813 6.49639 17.3962 6.14244 17.2178 5.83366C17.0395 5.52469 16.7754 5.27407 16.4576 5.11218C16.1397 4.9503 15.7817 4.8841 15.427 4.92162L14.2354 5.04847C13.8098 5.09317 13.381 5.00209 13.0104 4.78829C12.6413 4.57387 12.3496 4.24812 12.177 3.85773L11.6863 2.76236C11.5415 2.4363 11.3052 2.15925 11.0061 1.96482C10.707 1.77039 10.3579 1.66693 10.0011 1.66699C9.64434 1.66693 9.29522 1.77039 8.9961 1.96482C8.69698 2.15925 8.4607 2.4363 8.31591 2.76236L7.82888 3.85773C7.65632 4.24812 7.3646 4.57387 6.99554 4.78829C6.62489 5.00209 6.1961 5.09317 5.77054 5.04847L4.57517 4.92162C4.22045 4.8841 3.86246 4.9503 3.5446 5.11218C3.22675 5.27407 2.96269 5.52469 2.78443 5.83366C2.60595 6.14244 2.52092 6.49639 2.53965 6.85255C2.55839 7.20872 2.68009 7.55179 2.88999 7.84014L3.59554 8.80959C3.84716 9.15562 3.98266 9.57248 3.98258 10.0003C3.98266 10.4282 3.84716 10.845 3.59554 11.1911L2.88999 12.1605C2.68009 12.4489 2.55839 12.7919 2.53965 13.1481C2.52092 13.5043 2.60595 13.8582 2.78443 14.167C2.96286 14.4758 3.22696 14.7263 3.54476 14.8882C3.86257 15.05 4.22047 15.1163 4.57517 15.079L5.76684 14.9522C6.1924 14.9075 6.62119 14.9986 6.99184 15.2124C7.36228 15.4262 7.65535 15.752 7.82888 16.1429Z" - stroke="currentColor" + d="M7.82888 16.1419L8.31591 17.2373C8.4607 17.5634 8.69698 17.8404 8.9961 18.0348C9.29522 18.2293 9.64434 18.3327 10.0011 18.3327C10.3579 18.3327 10.707 18.2293 11.0061 18.0348C11.3052 17.8404 11.5415 17.5634 11.6863 17.2373L12.1733 16.1419C12.3467 15.7533 12.6383 15.4292 13.0067 15.216C13.3773 15.0022 13.8061 14.9111 14.2317 14.9558L15.4233 15.0827C15.778 15.1202 16.136 15.054 16.4539 14.8921C16.7717 14.7302 17.0358 14.4796 17.2141 14.1706C17.3925 13.8619 17.4776 13.5079 17.4588 13.1518C17.4401 12.7956 17.3184 12.4525 17.1085 12.1642L16.403 11.1947C16.1517 10.847 16.0175 10.4284 16.0196 9.99935C16.0195 9.57151 16.155 9.15464 16.4067 8.80861L17.1122 7.83916C17.3221 7.55081 17.4438 7.20774 17.4625 6.85158C17.4813 6.49541 17.3962 6.14147 17.2178 5.83268C17.0395 5.52371 16.7754 5.27309 16.4576 5.1112C16.1397 4.94932 15.7817 4.88312 15.427 4.92065L14.2354 5.0475C13.8098 5.09219 13.381 5.00112 13.0104 4.78731C12.6413 4.57289 12.3496 4.24715 12.177 3.85676L11.6863 2.76139C11.5415 2.43532 11.3052 2.15828 11.0061 1.96385C10.707 1.76942 10.3579 1.66596 10.0011 1.66602C9.64434 1.66596 9.29522 1.76942 8.9961 1.96385C8.69698 2.15828 8.4607 2.43532 8.31591 2.76139L7.82888 3.85676C7.65632 4.24715 7.3646 4.57289 6.99554 4.78731C6.62489 5.00112 6.1961 5.09219 5.77054 5.0475L4.57517 4.92065C4.22045 4.88312 3.86246 4.94932 3.5446 5.1112C3.22675 5.27309 2.96269 5.52371 2.78443 5.83268C2.60595 6.14147 2.52092 6.49541 2.53965 6.85158C2.55839 7.20774 2.68009 7.55081 2.88999 7.83916L3.59554 8.80861C3.84716 9.15464 3.98266 9.57151 3.98258 9.99935C3.98266 10.4272 3.84716 10.8441 3.59554 11.1901L2.88999 12.1595C2.68009 12.4479 2.55839 12.791 2.53965 13.1471C2.52092 13.5033 2.60595 13.8572 2.78443 14.166C2.96286 14.4748 3.22696 14.7253 3.54476 14.8872C3.86257 15.049 4.22047 15.1153 4.57517 15.0781L5.76684 14.9512C6.1924 14.9065 6.62119 14.9976 6.99184 15.2114C7.36228 15.4252 7.65535 15.751 7.82888 16.1419Z" + stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" + style={{ fill: 'rgb(255, 255, 255)' }} /> <path - d="M9.99961 12.5003C11.3803 12.5003 12.4996 11.381 12.4996 10.0003C12.4996 8.61961 11.3803 7.50033 9.99961 7.50033C8.6189 7.50033 7.49961 8.61961 7.49961 10.0003C7.49961 11.381 8.6189 12.5003 9.99961 12.5003Z" - stroke="currentColor" + d="M9.99961 12.4993C11.3803 12.4993 12.4996 11.3801 12.4996 9.99935C12.4996 8.61864 11.3803 7.49935 9.99961 7.49935C8.6189 7.49935 7.49961 8.61864 7.49961 9.99935C7.49961 11.3801 8.6189 12.4993 9.99961 12.4993Z" + stroke="#612BD3" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" + style={{ fill: '#612BD3' }} /> </svg> ); From 3387b3c98911bad40813d0fdceb9dea380758ad3 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 18:12:18 +0700 Subject: [PATCH 071/340] feat: higher concurrency rate for facebook --- .../src/components/notifications/notification.component.tsx | 2 +- .../src/integrations/social/facebook.provider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/notifications/notification.component.tsx b/apps/frontend/src/components/notifications/notification.component.tsx index 642c9b1c..ca8ba299 100644 --- a/apps/frontend/src/components/notifications/notification.component.tsx +++ b/apps/frontend/src/components/notifications/notification.component.tsx @@ -49,7 +49,7 @@ export const NotificationOpenComponent = () => { return ( <div id="notification-popup" - className="opacity-0 animate-normalFadeDown mt-[10px] absolute w-[420px] min-h-[200px] top-[100%] end-0 bg-third text-textColor rounded-[16px] flex flex-col border border-tableBorder z-[300]" + className="opacity-0 animate-normalFadeDown mt-[10px] absolute w-[420px] min-h-[200px] top-[100%] end-0 bg-third text-textColor rounded-[16px] flex flex-col border border-tableBorder z-[600]" > <div className={`p-[16px] border-b border-tableBorder font-bold`} diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index a37e0e42..c9d02841 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -23,7 +23,7 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { 'pages_read_engagement', 'read_insights', ]; - override maxConcurrentJob = 3; // Facebook has reasonable rate limits + override maxConcurrentJob = 100; // Facebook has reasonable rate limits editor = 'normal' as const; maxLength() { return 63206; From 7a82798cdc38636304ff5d0b4b6655c985d6ae6a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 18:24:33 +0700 Subject: [PATCH 072/340] feat: fix throttler --- .../nestjs-libraries/src/throttler/throttler.provider.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/throttler/throttler.provider.ts b/libraries/nestjs-libraries/src/throttler/throttler.provider.ts index beb01763..8118aaa3 100644 --- a/libraries/nestjs-libraries/src/throttler/throttler.provider.ts +++ b/libraries/nestjs-libraries/src/throttler/throttler.provider.ts @@ -11,12 +11,14 @@ export class ThrottlerBehindProxyGuard extends ThrottlerGuard { return super.canActivate(context); } - return true; + return super.canActivate(context); } protected override async getTracker( req: Record<string, any> ): Promise<string> { - return req.org.id; + return ( + req.org.id + '_' + (req.url.indexOf('/posts') > -1 ? 'posts' : 'other') + ); } } From b9062423a433e60b466415463d3eb948439e1017 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 18:54:31 +0700 Subject: [PATCH 073/340] feat: fix linkedin pdf --- .../integrations/social/linkedin.provider.ts | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 4aa1c9d5..cf30d511 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -40,6 +40,16 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { maxLength() { return 3000; } + + public override handleErrors(body: string): + | { + type: 'refresh-token' | 'bad-body' | 'retry'; + value: string; + } + | undefined { + + return undefined; + } async refreshToken(refresh_token: string): Promise<AuthTokenDetails> { const { access_token: accessToken, @@ -380,17 +390,34 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { }) ); - // Use the dimensions of the first image for the PDF page size - // You could also use the largest dimensions if you prefer - const firstImageDimensions = imageData[0]; - const pageSize = [firstImageDimensions.width, firstImageDimensions.height]; + // Find the maximum dimensions across all images to use as page size + const maxWidth = Math.max(...imageData.map((data) => data.width)); + const maxHeight = Math.max(...imageData.map((data) => data.height)); + const pageSize = [maxWidth, maxHeight]; - // Convert images to PDF with exact image dimensions - const pdfStream = imageToPDF( - imageData.map((data) => data.buffer), - pageSize + // Resize all images to fit within the page size while maintaining aspect ratio + // and centering them on a white background + const resizedImageBuffers = await Promise.all( + imageData.map(async (data) => { + // If image already matches page size, return as-is but convert to JPEG for consistency + if (data.width === maxWidth && data.height === maxHeight) { + return await sharp(data.buffer).jpeg({ quality: 95 }).toBuffer(); + } + + // Resize image to fit within page dimensions, then extend with white background to fill page + return await sharp(data.buffer) + .resize(maxWidth, maxHeight, { + fit: 'contain', + background: { r: 255, g: 255, b: 255, alpha: 1 }, + }) + .jpeg({ quality: 95 }) + .toBuffer(); + }) ); + // Convert images to PDF with consistent page dimensions + const pdfStream = imageToPDF(resizedImageBuffers, pageSize); + // Convert stream to buffer const pdfBuffer = await this.streamToBuffer(pdfStream); From e5a4c558ddf79c15386b4b892c8746335f26225c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 28 Dec 2025 21:01:20 +0700 Subject: [PATCH 074/340] feat: fix throttle --- libraries/nestjs-libraries/src/throttler/throttler.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/throttler/throttler.provider.ts b/libraries/nestjs-libraries/src/throttler/throttler.provider.ts index 8118aaa3..312d0a79 100644 --- a/libraries/nestjs-libraries/src/throttler/throttler.provider.ts +++ b/libraries/nestjs-libraries/src/throttler/throttler.provider.ts @@ -11,7 +11,7 @@ export class ThrottlerBehindProxyGuard extends ThrottlerGuard { return super.canActivate(context); } - return super.canActivate(context); + return true; } protected override async getTracker( From 00caa7ddc4e5c3bd9185d30b71d6adf727d6aad0 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 29 Dec 2025 02:11:40 +0700 Subject: [PATCH 075/340] feat: moving to dub partners --- apps/frontend/src/app/(app)/layout.tsx | 6 ++-- apps/frontend/src/app/(extension)/layout.tsx | 2 +- .../billing/first.billing.component.tsx | 14 +++++--- .../billing/main.billing.component.tsx | 7 ++-- .../src/components/layout/dubAnalytics.tsx | 25 ++++++++++++++ .../src/components/layout/tolt.script.tsx | 17 ---------- .../src/dtos/billing/billing.subscribe.dto.ts | 2 +- .../src/services/stripe.service.ts | 34 +++++++++++-------- .../src/helpers/variable.context.tsx | 4 +-- package.json | 1 + pnpm-lock.yaml | 15 ++++++++ 11 files changed, 80 insertions(+), 47 deletions(-) create mode 100644 apps/frontend/src/components/layout/dubAnalytics.tsx delete mode 100644 apps/frontend/src/components/layout/tolt.script.tsx diff --git a/apps/frontend/src/app/(app)/layout.tsx b/apps/frontend/src/app/(app)/layout.tsx index cb980aeb..4a60fc05 100644 --- a/apps/frontend/src/app/(app)/layout.tsx +++ b/apps/frontend/src/app/(app)/layout.tsx @@ -13,7 +13,7 @@ import { VariableContextComponent } from '@gitroom/react/helpers/variable.contex import { Fragment } from 'react'; import { PHProvider } from '@gitroom/react/helpers/posthog'; import UtmSaver from '@gitroom/helpers/utils/utm.saver'; -import { ToltScript } from '@gitroom/frontend/components/layout/tolt.script'; +import { DubAnalytics } from '@gitroom/frontend/components/layout/dubAnalytics'; import { FacebookComponent } from '@gitroom/frontend/components/layout/facebook.component'; import { headers } from 'next/headers'; import { headerName } from '@gitroom/react/translation/i18n.config'; @@ -61,7 +61,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { oauthLogoUrl={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL!} oauthDisplayName={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME!} uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!} - tolt={process.env.NEXT_PUBLIC_TOLT!} + dub={!!process.env.STRIPE_PUBLISHABLE_KEY} facebookPixel={process.env.NEXT_PUBLIC_FACEBOOK_PIXEL!} telegramBotName={process.env.TELEGRAM_BOT_NAME!} neynarClientId={process.env.NEYNAR_CLIENT_ID!} @@ -82,7 +82,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { <SentryComponent> {/*<SetTimezone />*/} <HtmlComponent /> - <ToltScript /> + <DubAnalytics /> <FacebookComponent /> <Plausible domain={!!process.env.IS_GENERAL ? 'postiz.com' : 'gitroom.com'} diff --git a/apps/frontend/src/app/(extension)/layout.tsx b/apps/frontend/src/app/(extension)/layout.tsx index 89a192b4..4a02c5ea 100644 --- a/apps/frontend/src/app/(extension)/layout.tsx +++ b/apps/frontend/src/app/(extension)/layout.tsx @@ -41,7 +41,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { oauthLogoUrl={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL!} oauthDisplayName={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME!} uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!} - tolt={process.env.NEXT_PUBLIC_TOLT!} + dub={false} facebookPixel={process.env.NEXT_PUBLIC_FACEBOOK_PIXEL!} telegramBotName={process.env.TELEGRAM_BOT_NAME!} neynarClientId={process.env.NEYNAR_CLIENT_ID!} diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index a1bc4913..14316ae0 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -23,7 +23,7 @@ import { } from '@gitroom/frontend/components/billing/faq.component'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; -import { useTolt } from '@gitroom/frontend/components/layout/tolt.script'; +import { useDubClickId } from '@gitroom/frontend/components/layout/dubAnalytics'; const ModeComponent = dynamic( () => import('@gitroom/frontend/components/layout/mode.component'), @@ -45,12 +45,12 @@ const EmbeddedBilling = dynamic( export const FirstBillingComponent = () => { const { stripeClient } = useVariables(); const user = useUser(); + const dub = useDubClickId(); const [stripe, setStripe] = useState<null | Promise<Stripe>>(null); const [tier, setTier] = useState('STANDARD'); const [period, setPeriod] = useState('MONTHLY'); const fetch = useFetch(); const t = useT(); - const tolt = useTolt(); useEffect(() => { setStripe(loadStripe(stripeClient)); @@ -63,7 +63,7 @@ export const FirstBillingComponent = () => { body: JSON.stringify({ billing: tier, period: period, - tolt: tolt(), + ...(dub ? { dub } : {}), }), }) ).json(); @@ -156,7 +156,9 @@ export const FirstBillingComponent = () => { </div> <div className="flex px-[80px] tablet:px-[32px] mobile:!px-[16px] flex-1 flex-row tablet:flex-none tablet:flex-col-reverse"> <div className="flex-1 py-[40px] tablet:pt-[80px] flex flex-col pe-[40px] tablet:pe-0"> - <div className="block tablet:hidden"><JoinOver /></div> + <div className="block tablet:hidden"> + <JoinOver /> + </div> {!isLoading && data && stripe ? ( <> <EmbeddedBilling stripe={stripe} secret={data.client_secret} /> @@ -168,7 +170,9 @@ export const FirstBillingComponent = () => { </div> <div className="flex flex-col ps-[40px] tablet:!ps-[0] border-l border-newColColor py-[40px] mobile:!pt-[24px] tablet:border-none tablet:pb-0"> <div className="top-[20px] sticky"> - <div className="hidden tablet:block"><JoinOver /></div> + <div className="hidden tablet:block"> + <JoinOver /> + </div> <div className="flex mb-[24px] mobile:flex-col"> <div className="flex-1 text-[24px] font-[700]"> {t('billing_choose_plan', 'Choose a Plan')} diff --git a/apps/frontend/src/components/billing/main.billing.component.tsx b/apps/frontend/src/components/billing/main.billing.component.tsx index a52b6e63..aae4f120 100644 --- a/apps/frontend/src/components/billing/main.billing.component.tsx +++ b/apps/frontend/src/components/billing/main.billing.component.tsx @@ -18,17 +18,16 @@ import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useRouter, useSearchParams } from 'next/navigation'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { Textarea } from '@gitroom/react/form/textarea'; import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events'; import { useUtmUrl } from '@gitroom/helpers/utils/utm.saver'; -import { useTolt } from '@gitroom/frontend/components/layout/tolt.script'; import { useTrack } from '@gitroom/react/helpers/use.track'; import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum'; import { PurchaseCrypto } from '@gitroom/frontend/components/billing/purchase.crypto'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { FinishTrial } from '@gitroom/frontend/components/billing/finish.trial'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; +import { useDubClickId } from '@gitroom/frontend/components/layout/dubAnalytics'; export const Prorate: FC<{ period: 'MONTHLY' | 'YEARLY'; @@ -218,10 +217,10 @@ export const MainBillingComponent: FC<{ const fetch = useFetch(); const toast = useToaster(); const user = useUser(); + const dub = useDubClickId(); const modal = useModals(); const router = useRouter(); const utm = useUtmUrl(); - const tolt = useTolt(); const track = useTrack(); const t = useT(); const queryParams = useSearchParams(); @@ -387,7 +386,7 @@ export const MainBillingComponent: FC<{ period: monthlyOrYearly === 'on' ? 'YEARLY' : 'MONTHLY', utm, billing, - tolt: tolt(), + ...(dub ? { dub } : {}), }), }) ).json(); diff --git a/apps/frontend/src/components/layout/dubAnalytics.tsx b/apps/frontend/src/components/layout/dubAnalytics.tsx new file mode 100644 index 00000000..fdd6a2d1 --- /dev/null +++ b/apps/frontend/src/components/layout/dubAnalytics.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { useVariables } from '@gitroom/react/helpers/variable.context'; +import { Analytics as DubAnalyticsIn } from '@dub/analytics/react'; +import { getCookie } from 'react-use-cookie'; + +export const DubAnalytics = () => { + const { dub } = useVariables(); + if (!dub) return null; + return ( + <DubAnalyticsIn + domainsConfig={{ + refer: 'affiliate.postiz.com', + }} + /> + ); +}; + +export const useDubClickId = () => { + const { dub } = useVariables(); + if (!dub) return undefined; + + const dubCookie = getCookie('dub_partner_data', '{}'); + return JSON.parse(dubCookie)?.clickId || undefined; +}; diff --git a/apps/frontend/src/components/layout/tolt.script.tsx b/apps/frontend/src/components/layout/tolt.script.tsx deleted file mode 100644 index 4f647927..00000000 --- a/apps/frontend/src/components/layout/tolt.script.tsx +++ /dev/null @@ -1,17 +0,0 @@ -'use client'; - -import { useVariables } from '@gitroom/react/helpers/variable.context'; -import Script from 'next/script'; -export const useTolt = () => { - return () => { - // @ts-ignore - return window?.tolt_referral || ''; - }; -}; -export const ToltScript = () => { - const { tolt } = useVariables(); - if (!tolt) return null; - return ( - <Script async={true} src="https://cdn.tolt.io/tolt.js" data-tolt={tolt} /> - ); -}; diff --git a/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts b/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts index 642effae..0d4a3a91 100644 --- a/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts @@ -9,5 +9,5 @@ export class BillingSubscribeDto { utm: string; - tolt: string; + dub: string; } diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index f452cb31..41a1dae9 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -375,9 +375,19 @@ export class StripeService { // @ts-ignore apiVersion: '2025-03-31.basil', }); + + if (body.dub) { + await stripeCustom.customers.update(customer, { + metadata: { + dubCustomerExternalId: userId, + dubClickId: body.dub, + }, + }); + } const isUtm = body.utm ? `&utm_source=${body.utm}` : ''; // @ts-ignore const { client_secret } = await stripeCustom.checkout.sessions.create({ + // @ts-ignore ui_mode: 'custom', customer, return_url: @@ -394,13 +404,6 @@ export class StripeService { ud, }, }, - ...(body.tolt - ? { - metadata: { - tolt_referral: body.tolt, - }, - } - : {}), allow_promotion_codes: body.period === 'MONTHLY', line_items: [ { @@ -423,6 +426,16 @@ export class StripeService { allowTrial: boolean ) { const isUtm = body.utm ? `&utm_source=${body.utm}` : ''; + + if (body.dub) { + await stripe.customers.update(customer, { + metadata: { + dubCustomerExternalId: userId, + dubClickId: body.dub, + }, + }); + } + const { url } = await stripe.checkout.sessions.create({ customer, cancel_url: process.env['FRONTEND_URL'] + `/billing?cancel=true${isUtm}`, @@ -440,13 +453,6 @@ export class StripeService { ud, }, }, - ...(body.tolt - ? { - metadata: { - tolt_referral: body.tolt, - }, - } - : {}), allow_promotion_codes: body.period === 'MONTHLY', line_items: [ { diff --git a/libraries/react-shared-libraries/src/helpers/variable.context.tsx b/libraries/react-shared-libraries/src/helpers/variable.context.tsx index 817ca8fa..1b81f7c2 100644 --- a/libraries/react-shared-libraries/src/helpers/variable.context.tsx +++ b/libraries/react-shared-libraries/src/helpers/variable.context.tsx @@ -22,7 +22,7 @@ interface VariableContextInterface { disableImageCompression: boolean; disableXAnalytics: boolean; language: string; - tolt: string; + dub: boolean; transloadit: string[]; sentryDsn: string; } @@ -46,7 +46,7 @@ const VariableContext = createContext({ disableImageCompression: false, disableXAnalytics: false, language: '', - tolt: '', + dub: false, transloadit: [], sentryDsn: '', } as VariableContextInterface); diff --git a/package.json b/package.json index 0ad0d907..27219367 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@copilotkit/react-textarea": "1.10.6", "@copilotkit/react-ui": "1.10.6", "@copilotkit/runtime": "1.10.6", + "@dub/analytics": "^0.0.32", "@hookform/resolvers": "^3.3.4", "@langchain/community": "^0.3.40", "@langchain/core": "^0.3.44", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cee5dac1..8f900596 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -42,6 +42,9 @@ importers: '@copilotkit/runtime': specifier: 1.10.6 version: 1.10.6(c9e743140f0883eda50bf44e0628c06c) + '@dub/analytics': + specifier: ^0.0.32 + version: 0.0.32 '@hookform/resolvers': specifier: ^3.3.4 version: 3.10.0(react-hook-form@7.69.0(react@18.3.1)) @@ -1974,6 +1977,9 @@ packages: resolution: {integrity: sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==} engines: {node: '>= 6'} + '@dub/analytics@0.0.32': + resolution: {integrity: sha512-jmZrgbArOX08/kz+hi1s9ggt0k64WzRErt6jM6TfuqUHlXQYNNNRcefvKrmd9vwmbvNGR/GxZnYeyco6Zyw4fg==} + '@emnapi/core@1.7.1': resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} @@ -14998,6 +15004,9 @@ packages: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} + server-only@0.0.1: + resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -19140,6 +19149,10 @@ snapshots: tunnel-agent: 0.6.0 uuid: 8.3.2 + '@dub/analytics@0.0.32': + dependencies: + server-only: 0.0.1 + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 @@ -35803,6 +35816,8 @@ snapshots: transitivePeerDependencies: - supports-color + server-only@0.0.1: {} + set-blocking@2.0.0: {} set-function-length@1.2.2: From ca941aed85978ce5ee5c81b29af1ace8dd8a70c6 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 29 Dec 2025 02:59:17 +0700 Subject: [PATCH 076/340] fix: customer --- .../src/services/stripe.service.ts | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index 41a1dae9..c7832e2d 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -376,14 +376,22 @@ export class StripeService { apiVersion: '2025-03-31.basil', }); - if (body.dub) { + const user = await this._userService.getUserById(userId); + + try { await stripeCustom.customers.update(customer, { - metadata: { - dubCustomerExternalId: userId, - dubClickId: body.dub, - }, + email: user.email, + ...(body.dub + ? { + metadata: { + dubCustomerExternalId: userId, + dubClickId: body.dub, + }, + } + : {}), }); - } + } catch (err) {} + const isUtm = body.utm ? `&utm_source=${body.utm}` : ''; // @ts-ignore const { client_secret } = await stripeCustom.checkout.sessions.create({ From 6729de4563dba83a0dc165a721d900b3a0f4cc03 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 29 Dec 2025 09:31:13 +0700 Subject: [PATCH 077/340] fix: hot fix for modal creation --- .../components/new-launch/providers/high.order.provider.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx index ff658e2d..6c7c741f 100644 --- a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx @@ -15,13 +15,10 @@ import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; import { GeneralPreviewComponent } from '@gitroom/frontend/components/launches/general.preview.component'; import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration'; -import { Button } from '@gitroom/react/form/button'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; import { InternalChannels } from '@gitroom/frontend/components/launches/internal.channels'; -import { capitalize } from 'lodash'; -import clsx from 'clsx'; import { createPortal } from 'react-dom'; class Empty { @@ -317,7 +314,7 @@ export const withProvider = function <T extends object>(params: { !SettingsComponent && createPortal( <style>{`#wrapper-settings {display: none !important;} #social-empty {display: block !important;}`}</style>, - document.querySelector('#social-settings')! + document.querySelector('#social-settings') || document.createElement('div') )} </div> </FormProvider> From 0e5d6f8826330d009d46356c541ec4c4420dec27 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 29 Dec 2025 09:36:00 +0700 Subject: [PATCH 078/340] fix: hot fix for modal creation --- .../src/components/new-launch/providers/high.order.provider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx index 6c7c741f..5e9803d5 100644 --- a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx @@ -20,6 +20,7 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; import { InternalChannels } from '@gitroom/frontend/components/launches/internal.channels'; import { createPortal } from 'react-dom'; +import clsx from 'clsx'; class Empty { @IsOptional() From faef57738c658abcb44e425a32fec8f49adb1b77 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 29 Dec 2025 22:01:47 +0700 Subject: [PATCH 079/340] feat: generator --- .../modal/[style]/[platform]/page.tsx | 2 +- .../components/launches/layout.standalone.tsx | 2 +- .../src/components/media/media.component.tsx | 102 +++++++++--------- .../components/new-launch/manage.modal.tsx | 37 ++++--- .../standalone-modal/standalone.modal.tsx | 1 - 5 files changed, 74 insertions(+), 70 deletions(-) diff --git a/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx b/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx index b68643f5..c1f60c90 100644 --- a/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx +++ b/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx @@ -4,7 +4,7 @@ import { StandaloneModal } from '@gitroom/frontend/components/standalone-modal/s import { usePathname } from 'next/navigation'; export default async function Modal() { return ( - <div className="text-textColor"> + <div className="text-textColor h-screen w-screen"> <StandaloneModal /> </div> ); diff --git a/apps/frontend/src/components/launches/layout.standalone.tsx b/apps/frontend/src/components/launches/layout.standalone.tsx index 275bea5f..746a35de 100644 --- a/apps/frontend/src/components/launches/layout.standalone.tsx +++ b/apps/frontend/src/components/launches/layout.standalone.tsx @@ -15,7 +15,7 @@ export const AppLayout = ({ children }: { children: ReactNode }) => { }, [params]); return ( <div - className={`hideCopilot ${style} h-[100vh] !padding-[50px] w-full text-textColor flex flex-col !bg-none`} + className={`hideCopilot ${style} h-[100vh] w-full text-textColor flex flex-1 flex-col !bg-none`} > <style> {` diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 9f2c5731..a401156f 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -690,64 +690,62 @@ export const MultiMediaComponent: FC<{ </ReactSortable> )} </div> - {!dummy && ( - <div className="flex gap-[8px] px-[12px] border-t border-newColColor w-full b1 text-textColor"> - {!mediaNotAvailable && ( - <div className="flex py-[10px] b2 items-center gap-[4px]"> - <div - onClick={showModal} - className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" - > - <div className="flex gap-[8px] items-center"> - <div> - <InsertMediaIcon /> - </div> - <div className="text-[10px] font-[600] maxMedia:hidden block"> - {t('insert_media', 'Insert Media')} - </div> + <div className="flex gap-[8px] px-[12px] border-t border-newColColor w-full b1 text-textColor"> + {!mediaNotAvailable && ( + <div className="flex py-[10px] b2 items-center gap-[4px]"> + <div + onClick={showModal} + className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" + > + <div className="flex gap-[8px] items-center"> + <div> + <InsertMediaIcon /> + </div> + <div className="text-[10px] font-[600] maxMedia:hidden block"> + {t('insert_media', 'Insert Media')} </div> </div> - <div - onClick={designMedia} - className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" - > - <div className="flex gap-[5px] items-center"> - <div> - <DesignMediaIcon /> - </div> - <div className="text-[10px] font-[600] iconBreak:hidden block"> - {t('design_media', 'Design Media')} - </div> + </div> + <div + onClick={designMedia} + className="cursor-pointer h-[30px] rounded-[6px] justify-center items-center flex bg-newColColor px-[8px]" + > + <div className="flex gap-[5px] items-center"> + <div> + <DesignMediaIcon /> + </div> + <div className="text-[10px] font-[600] iconBreak:hidden block"> + {t('design_media', 'Design Media')} </div> </div> + </div> - <ThirdPartyMedia allData={allData} onChange={changeMedia} /> + <ThirdPartyMedia allData={allData} onChange={changeMedia} /> - {!!user?.tier?.ai && ( - <> - <AiImage value={text} onChange={changeMedia} /> - <AiVideo value={text} onChange={changeMedia} /> - </> - )} - </div> - )} - {!mediaNotAvailable && ( - <div className="text-newColColor h-full flex items-center"> - <VerticalDividerIcon /> - </div> - )} - {!!toolBar && ( - <div className="flex py-[10px] b2 items-center gap-[4px]"> - {toolBar} - </div> - )} - {information && ( - <div className="flex-1 justify-end flex py-[10px] b2 items-center gap-[4px]"> - {information} - </div> - )} - </div> - )} + {!!user?.tier?.ai && ( + <> + <AiImage value={text} onChange={changeMedia} /> + <AiVideo value={text} onChange={changeMedia} /> + </> + )} + </div> + )} + {!mediaNotAvailable && ( + <div className="text-newColColor h-full flex items-center"> + <VerticalDividerIcon /> + </div> + )} + {!!toolBar && ( + <div className="flex py-[10px] b2 items-center gap-[4px]"> + {toolBar} + </div> + )} + {information && ( + <div className="flex-1 justify-end flex py-[10px] b2 items-center gap-[4px]"> + {information} + </div> + )} + </div> </div> <div className="text-[12px] text-red-400">{error}</div> </> diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 38ebfe33..9bee0d8c 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -107,7 +107,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { className="w-[20px] h-[20px] rounded-[4px]" alt={currentIntegration.identifier} /> - <SettingsIcon size={15} className="text-white absolute -end-[5px] -bottom-[5px]" /> + <SettingsIcon + size={15} + className="text-white absolute -end-[5px] -bottom-[5px]" + /> </div> <div>{currentIntegration.name} Settings</div> </div> @@ -519,22 +522,26 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { ? t('schedule', 'Schedule') : t('update', 'Update')} </div> - <div className="flex justify-center items-center h-[20px] w-[20px] pt-[4px] arrow-change"> - <DropdownArrowSmallIcon className="group-hover:rotate-180 text-white" /> - </div> + {!dummy && ( + <div className="flex justify-center items-center h-[20px] w-[20px] pt-[4px] arrow-change"> + <DropdownArrowSmallIcon className="group-hover:rotate-180 text-white" /> + </div> + )} </button> - <button - onClick={schedule('now')} - disabled={ - selectedIntegrations.length === 0 || loading || locked - } - className="rounded-[8px] z-[300] disabled:cursor-not-allowed disabled:opacity-80 hidden group-hover:flex absolute bottom-[100%] -left-[12px] p-[12px] w-[206px] bg-newBgColorInner" - > - <div className="text-white rounded-[8px] bg-[#D82D7E] h-[44px] w-full flex justify-center items-center post-now"> - Post Now - </div> - </button> + {!dummy && ( + <button + onClick={schedule('now')} + disabled={ + selectedIntegrations.length === 0 || loading || locked + } + className="rounded-[8px] z-[300] disabled:cursor-not-allowed disabled:opacity-80 hidden group-hover:flex absolute bottom-[100%] -left-[12px] p-[12px] w-[206px] bg-newBgColorInner" + > + <div className="text-white rounded-[8px] bg-[#D82D7E] h-[44px] w-full flex justify-center items-center post-now"> + Post Now + </div> + </button> + )} </div> )} </div> diff --git a/apps/frontend/src/components/standalone-modal/standalone.modal.tsx b/apps/frontend/src/components/standalone-modal/standalone.modal.tsx index fe160dc2..469ca79c 100644 --- a/apps/frontend/src/components/standalone-modal/standalone.modal.tsx +++ b/apps/frontend/src/components/standalone-modal/standalone.modal.tsx @@ -53,7 +53,6 @@ export const StandaloneModal: FC = () => { '*' ); }} - padding="50px" mutate={() => {}} integrations={integrations} reopenModal={() => {}} From 5f4de7c987144b050d89734fb886fbe5118f2e42 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 29 Dec 2025 22:20:16 +0700 Subject: [PATCH 080/340] feat: fullscreen --- .../src/app/(extension)/modal/[style]/[platform]/page.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx b/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx index c1f60c90..e18e1d78 100644 --- a/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx +++ b/apps/frontend/src/app/(extension)/modal/[style]/[platform]/page.tsx @@ -1,11 +1,12 @@ 'use client'; import { StandaloneModal } from '@gitroom/frontend/components/standalone-modal/standalone.modal'; -import { usePathname } from 'next/navigation'; export default async function Modal() { return ( - <div className="text-textColor h-screen w-screen"> - <StandaloneModal /> + <div className="w-screen h-screen overflow-hidden bg-black"> + <div className="text-textColor h-[calc(100vh+80px)] w-[calc(100vw+80px)] -m-[40px]"> + <StandaloneModal /> + </div> </div> ); } From 44f190d466e3458a2df7dd4e6c622c7c9b1c85be Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Wed, 31 Dec 2025 13:03:41 +0700 Subject: [PATCH 081/340] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 866927af..635e3640 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,7 @@ Link: https://opencollective.com/postiz ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=gitroomhq/postiz-app&type=Date)](https://www.star-history.com/#gitroomhq/postiz-app&Date) +[![Star History Chart](https://api.star-history.com/svg?repos=gitroomhq/postiz-app&type=date&legend=top-left)](https://www.star-history.com/#gitroomhq/postiz-app&type=date&legend=top-left) ## License From c8c812e1ebc7c035abb0ab0519758c41deebcbb5 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 1 Jan 2026 14:55:41 +0700 Subject: [PATCH 082/340] feat: fix additional settings modal --- .../src/components/launches/menu/menu.tsx | 8 +------ .../components/launches/settings.modal.tsx | 24 +------------------ 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index b46fbb8a..2e0a09a9 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -255,13 +255,7 @@ export const Menu: FC<{ (integration) => integration.id === id ); modal.openModal({ - classNames: { - modal: 'w-[100%] max-w-[600px] bg-transparent text-textColor', - }, - size: '100%', - withCloseButton: false, - closeOnEscape: true, - closeOnClickOutside: true, + title: 'Additional Settings', children: ( <SettingsModal // @ts-ignore diff --git a/apps/frontend/src/components/launches/settings.modal.tsx b/apps/frontend/src/components/launches/settings.modal.tsx index c54e7333..210491f2 100644 --- a/apps/frontend/src/components/launches/settings.modal.tsx +++ b/apps/frontend/src/components/launches/settings.modal.tsx @@ -63,29 +63,7 @@ export const SettingsModal: FC<{ onClose(); }, [values, integration]); return ( - <div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative w-full"> - <TopTitle title={`Additional Settings`} /> - <button - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - onClick={() => modal.closeAll()} - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - + <div> <div className="mt-[16px]"> {values.map((setting: any, index: number) => ( <Element From b3d298daff40e10fd3af7def103b76513103e369 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 1 Jan 2026 15:09:23 +0700 Subject: [PATCH 083/340] fix: change vietnam flag --- apps/frontend/src/components/layout/language.component.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/frontend/src/components/layout/language.component.tsx b/apps/frontend/src/components/layout/language.component.tsx index 3d9bb432..3b6cc078 100644 --- a/apps/frontend/src/components/layout/language.component.tsx +++ b/apps/frontend/src/components/layout/language.component.tsx @@ -30,6 +30,7 @@ const getCountryCodeForFlag = (languageCode: string) => { if (languageCode === 'he') return 'IL'; if (languageCode === 'ja') return 'JP'; if (languageCode === 'ko') return 'KR'; + if (languageCode === 'vi') return 'VN'; // Check if language code itself is a valid country code try { From cd085e27cab868c82634d800b3876d41ef7e6ab9 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 1 Jan 2026 16:07:58 +0700 Subject: [PATCH 084/340] feat: more translations --- .../src/components/agents/agent.chat.tsx | 8 +- apps/frontend/src/components/agents/agent.tsx | 10 +- .../components/analytics/stars.and.forks.tsx | 4 +- .../analytics/stars.table.component.tsx | 14 +- .../src/components/auth/forgot-return.tsx | 8 +- apps/frontend/src/components/auth/forgot.tsx | 3 +- apps/frontend/src/components/auth/login.tsx | 6 +- .../auth/providers/farcaster.provider.tsx | 2 +- .../auth/providers/google.provider.tsx | 4 +- .../frontend/src/components/auth/register.tsx | 13 +- .../src/components/autopost/autopost.tsx | 41 +++--- .../launches/add.provider.component.tsx | 8 +- .../src/components/launches/bot.picture.tsx | 8 +- .../src/components/launches/calendar.tsx | 10 +- .../components/launches/customer.modal.tsx | 7 +- .../src/components/launches/filters.tsx | 2 +- .../launches/generator/generator.tsx | 12 +- .../launches/information.component.tsx | 10 +- .../launches/launches.component.tsx | 8 +- .../src/components/launches/menu/menu.tsx | 50 +++---- .../components/launches/repeat.component.tsx | 31 +++-- .../components/launches/select.customer.tsx | 6 +- .../components/launches/tags.component.tsx | 15 +- .../src/components/launches/time.table.tsx | 7 +- .../components/layout/settings.component.tsx | 2 +- .../src/components/media/media.component.tsx | 36 ++--- .../src/components/messages/messages.tsx | 6 +- .../src/components/new-launch/editor.tsx | 51 ++++--- .../components/new-launch/manage.modal.tsx | 82 +++++------ .../new-launch/mention.component.tsx | 5 +- .../components/new-launch/select.current.tsx | 4 +- .../src/components/onboarding/onboarding.tsx | 4 +- .../components/settings/teams.component.tsx | 27 ++-- .../src/components/webhooks/webhooks.tsx | 31 +++-- i18n.lock | 124 +++++++++++++++++ .../integrations/social/linkedin.provider.ts | 43 ++---- .../translation/locales/ar/translation.json | 126 ++++++++++++++++- .../translation/locales/bn/translation.json | 126 ++++++++++++++++- .../translation/locales/de/translation.json | 126 ++++++++++++++++- .../translation/locales/en/translation.json | 129 +++++++++++++++++- .../translation/locales/es/translation.json | 126 ++++++++++++++++- .../translation/locales/fr/translation.json | 126 ++++++++++++++++- .../translation/locales/he/translation.json | 126 ++++++++++++++++- .../translation/locales/it/translation.json | 126 ++++++++++++++++- .../translation/locales/ja/translation.json | 126 ++++++++++++++++- .../translation/locales/ko/translation.json | 126 ++++++++++++++++- .../translation/locales/pt/translation.json | 126 ++++++++++++++++- .../translation/locales/ru/translation.json | 126 ++++++++++++++++- .../translation/locales/tr/translation.json | 126 ++++++++++++++++- .../translation/locales/vi/translation.json | 126 ++++++++++++++++- .../translation/locales/zh/translation.json | 126 ++++++++++++++++- 51 files changed, 2295 insertions(+), 300 deletions(-) diff --git a/apps/frontend/src/components/agents/agent.chat.tsx b/apps/frontend/src/components/agents/agent.chat.tsx index 2f8aa0a6..e2bc282f 100644 --- a/apps/frontend/src/components/agents/agent.chat.tsx +++ b/apps/frontend/src/components/agents/agent.chat.tsx @@ -32,11 +32,13 @@ import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.m import dayjs from 'dayjs'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { ExistingDataContextProvider } from '@gitroom/frontend/components/launches/helpers/use.existing.data'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; export const AgentChat: FC = () => { const { backendUrl } = useVariables(); const params = useParams<{ id: string }>(); const { properties } = useContext(PropertiesContext); + const t = useT(); return ( <CopilotKit @@ -64,8 +66,8 @@ export const AgentChat: FC = () => { <CopilotChat className="w-full h-full" labels={{ - title: 'Your Assistant', - initial: `Hello, I am your Postiz agent 🙌🏻. + title: t('your_assistant', 'Your Assistant'), + initial: t('agent_welcome_message', `Hello, I am your Postiz agent 🙌🏻. I can schedule a post or multiple posts to multiple channels and generate pictures and videos. @@ -74,7 +76,7 @@ You can select the channels you want to use from the left menu. You can see your previous conversations from the right menu. You can also use me as an MCP Server, check Settings >> Public API -`, +`), }} UserMessage={Message} Input={NewInput} diff --git a/apps/frontend/src/components/agents/agent.tsx b/apps/frontend/src/components/agents/agent.tsx index 54da0f08..e4ca8b1f 100644 --- a/apps/frontend/src/components/agents/agent.tsx +++ b/apps/frontend/src/components/agents/agent.tsx @@ -21,6 +21,7 @@ import { MultiMediaComponent } from '@gitroom/frontend/components/media/media.co import { Integration } from '@prisma/client'; import Link from 'next/link'; import { useParams, usePathname, useRouter } from 'next/navigation'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; export const MediaPortal: FC<{ media: { path: string; id: string }[]; @@ -39,13 +40,14 @@ export const MediaPortal: FC<{ }) => void; }> = ({ media, setMedia, value }) => { const waitForClass = useWaitForClass('copilotKitMessages'); + const t = useT(); if (!waitForClass) return null; return ( <div className="pl-[14px] pr-[24px] whitespace-nowrap editor rm-bg"> <MultiMediaComponent allData={[{ content: value }]} text={value} - label="Attachments" + label={t('attachments', 'Attachments')} description="" value={media} dummy={false} @@ -62,6 +64,7 @@ export const AgentList: FC<{ onChange: (arr: any[]) => void }> = ({ onChange, }) => { const fetch = useFetch(); + const t = useT(); const [selected, setSelected] = useState([]); const load = useCallback(async () => { @@ -111,7 +114,7 @@ export const AgentList: FC<{ onChange: (arr: any[]) => void }> = ({ <div className="absolute top-0 start-0 w-full h-full p-[20px] overflow-auto scrollbar scrollbar-thumb-fifth scrollbar-track-newBgColor"> <div className="flex items-center"> <h2 className="group-[.sidebar]:hidden flex-1 text-[20px] font-[500] mb-[15px]"> - Select Channels + {t('select_channels', 'Select Channels')} </h2> <div onClick={() => setCollapseMenu(collapseMenu === '1' ? '0' : '1')} @@ -210,6 +213,7 @@ const Threads: FC = () => { const fetch = useFetch(); const router = useRouter(); const pathname = usePathname(); + const t = useT(); const threads = useCallback(async () => { return (await fetch('/copilot/list')).json(); }, []); @@ -247,7 +251,7 @@ const Threads: FC = () => { /> </svg> <div className="flex-1 text-start text-[16px] group-[.sidebar]:hidden"> - Start a new chat + {t('start_a_new_chat', 'Start a new chat')} </div> </Link> </div> diff --git a/apps/frontend/src/components/analytics/stars.and.forks.tsx b/apps/frontend/src/components/analytics/stars.and.forks.tsx index 97950bc0..3f7f067e 100644 --- a/apps/frontend/src/components/analytics/stars.and.forks.tsx +++ b/apps/frontend/src/components/analytics/stars.and.forks.tsx @@ -138,8 +138,8 @@ export const StarsAndForks: FC<StarsAndForksInterface> = (props) => { </div> <div className="text-[20px]"> {p === 0 - ? 'Last Github Trending' - : 'Next Predicted GitHub Trending'} + ? t('last_github_trending', 'Last Github Trending') + : t('next_predicted_github_trending', 'Next Predicted GitHub Trending')} </div> </div> <div className="flex items-center"> diff --git a/apps/frontend/src/components/analytics/stars.table.component.tsx b/apps/frontend/src/components/analytics/stars.table.component.tsx index ad805eee..476d6657 100644 --- a/apps/frontend/src/components/analytics/stars.table.component.tsx +++ b/apps/frontend/src/components/analytics/stars.table.component.tsx @@ -1,3 +1,5 @@ +'use client'; + import { FC, useCallback, @@ -205,22 +207,22 @@ export const StarsTableComponent = () => { <thead> <tr> <th> - <UpDown name="Repository" param="login" /> + <UpDown name={t('repository', 'Repository')} param="login" /> </th> <th> - <UpDown name="Date" param="date" /> + <UpDown name={t('date', 'Date')} param="date" /> </th> <th> - <UpDown name="Total Stars" param="totalStars" /> + <UpDown name={t('total_stars', 'Total Stars')} param="totalStars" /> </th> <th> - <UpDown name="Total Fork" param="totalForks" /> + <UpDown name={t('total_forks', 'Total Forks')} param="totalForks" /> </th> <th> - <UpDown name="Stars" param="stars" /> + <UpDown name={t('stars', 'Stars')} param="stars" /> </th> <th> - <UpDown name="Forks" param="forks" /> + <UpDown name={t('forks', 'Forks')} param="forks" /> </th> <th>{t('media', 'Media')}</th> </tr> diff --git a/apps/frontend/src/components/auth/forgot-return.tsx b/apps/frontend/src/components/auth/forgot-return.tsx index b6108ea0..4b79ab25 100644 --- a/apps/frontend/src/components/auth/forgot-return.tsx +++ b/apps/frontend/src/components/auth/forgot-return.tsx @@ -42,7 +42,7 @@ export function ForgotReturn({ token }: { token: string }) { if (!reset) { form.setError('password', { type: 'manual', - message: 'Your password reset link has expired. Please try again.', + message: t('password_reset_link_expired', 'Your password reset link has expired. Please try again.'), }); return false; } @@ -61,15 +61,17 @@ export function ForgotReturn({ token }: { token: string }) { <div className="space-y-4 text-textColor"> <Input label="New Password" + translationKey="label_new_password" {...form.register('password')} type="password" - placeholder="Password" + placeholder={t('label_password', 'Password')} /> <Input label="Repeat Password" + translationKey="label_repeat_password" {...form.register('repeatPassword')} type="password" - placeholder="Repeat Password" + placeholder={t('label_repeat_password', 'Repeat Password')} /> </div> <div className="text-center mt-6"> diff --git a/apps/frontend/src/components/auth/forgot.tsx b/apps/frontend/src/components/auth/forgot.tsx index d6f5110c..070404b0 100644 --- a/apps/frontend/src/components/auth/forgot.tsx +++ b/apps/frontend/src/components/auth/forgot.tsx @@ -49,9 +49,10 @@ export function Forgot() { <div className="space-y-4 text-textColor"> <Input label="Email" + translationKey="label_email" {...form.register('email')} type="email" - placeholder="Email Address" + placeholder={t('email_address', 'Email Address')} /> </div> <div className="text-center mt-6"> diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index 370f5f8a..cb194be0 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -82,7 +82,7 @@ export function Login() { <div className={`absolute z-[1] justify-center items-center w-full start-0 -top-[4px] flex`} > - <div className="px-[16px]">or</div> + <div className="px-[16px]">{t('or', 'or')}</div> </div> </div> <div className="flex flex-col gap-[12px]"> @@ -92,7 +92,7 @@ export function Login() { translationKey="label_email" {...form.register('email')} type="email" - placeholder="Email Address" + placeholder={t('email_address', 'Email Address')} /> <Input label="Password" @@ -100,7 +100,7 @@ export function Login() { {...form.register('password')} autoComplete="off" type="password" - placeholder="Password" + placeholder={t('label_password', 'Password')} /> </div> <div className="text-center mt-6"> diff --git a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx index 26e0d13f..d3ae3399 100644 --- a/apps/frontend/src/components/auth/providers/farcaster.provider.tsx +++ b/apps/frontend/src/components/auth/providers/farcaster.provider.tsx @@ -51,7 +51,7 @@ export const ButtonCaster: FC<{ </clipPath> </defs> </svg> - <div className="block xs:hidden">Farcaster</div> + <div className="block xs:hidden">{t('farcaster', 'Farcaster')}</div> </div> </NeynarAuthButton> </NeynarContextProvider> diff --git a/apps/frontend/src/components/auth/providers/google.provider.tsx b/apps/frontend/src/components/auth/providers/google.provider.tsx index 8f94299f..afa40666 100644 --- a/apps/frontend/src/components/auth/providers/google.provider.tsx +++ b/apps/frontend/src/components/auth/providers/google.provider.tsx @@ -1,3 +1,5 @@ +'use client'; + import { useCallback } from 'react'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -38,7 +40,7 @@ export const GoogleProvider = () => { /> </svg> </div> - <div className="block xs:hidden">Google</div> + <div className="block xs:hidden">{t('google', 'Google')}</div> </div> ); }; diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 467413fa..592c354e 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -151,7 +151,7 @@ export function RegisterAfter({ {t('sign_up', 'Sign Up')} </h1> </div> - <div className="text-[14px] mt-[32px] mb-[12px]">Continue With</div> + <div className="text-[14px] mt-[32px] mb-[12px]">{t('continue_with', 'Continue With')}</div> <div className="flex flex-col"> {!isAfterProvider && (!isGeneral ? ( @@ -174,7 +174,7 @@ export function RegisterAfter({ className={`absolute z-[1] justify-center items-center w-full start-0 -top-[4px] flex`} > <div className="px-[16px]"> - or + {t('or', 'or')} </div> </div> </div> @@ -185,25 +185,28 @@ export function RegisterAfter({ <> <Input label="Email" + translationKey="label_email" {...form.register('email')} type="email" - placeholder="Email Address" + placeholder={t('email_address', 'Email Address')} /> <Input label="Password" + translationKey="label_password" {...form.register('password')} autoComplete="off" type="password" - placeholder="Password" + placeholder={t('label_password', 'Password')} /> </> )} <Input label="Company" + translationKey="label_company" {...form.register('company')} autoComplete="off" type="text" - placeholder="Company" + placeholder={t('label_company', 'Company')} /> </div> <div className={clsx('text-[12px]')}> diff --git a/apps/frontend/src/components/autopost/autopost.tsx b/apps/frontend/src/components/autopost/autopost.tsx index bfda9118..e4f2ce13 100644 --- a/apps/frontend/src/components/autopost/autopost.tsx +++ b/apps/frontend/src/components/autopost/autopost.tsx @@ -1,9 +1,10 @@ +'use client'; + import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; import { Button } from '@gitroom/react/form/button'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { Input } from '@gitroom/react/form/input'; import { FormProvider, useForm } from 'react-hook-form'; import { array, boolean, object, string } from 'yup'; @@ -28,7 +29,7 @@ export const Autopost: FC = () => { const addWebhook = useCallback( (data?: any) => () => { modal.openModal({ - title: data ? 'Edit Autopost' : 'Add Autopost', + title: data ? t('edit_autopost', 'Edit Autopost') : t('add_autopost_title', 'Add Autopost'), withCloseButton: true, children: <AddOrEditWebhook data={data} reload={mutate} />, }); @@ -50,7 +51,7 @@ export const Autopost: FC = () => { method: 'DELETE', }); mutate(); - toaster.show('Webhook deleted successfully', 'success'); + toaster.show(t('webhook_deleted_successfully', 'Webhook deleted successfully'), 'success'); } }, [] @@ -142,33 +143,33 @@ const details = object().shape({ }) ), }); -const options = [ +const getOptions = (t: (key: string, fallback: string) => string) => [ { - label: 'All integrations', + label: t('all_integrations', 'All integrations'), value: 'all', }, { - label: 'Specific integrations', + label: t('specific_integrations', 'Specific integrations'), value: 'specific', }, ]; -const optionsChoose = [ +const getOptionsChoose = (t: (key: string, fallback: string) => string) => [ { - label: 'Yes', + label: t('yes', 'Yes'), value: true, }, { - label: 'No', + label: t('no', 'No'), value: false, }, ]; -const postImmediately = [ +const getPostImmediately = (t: (key: string, fallback: string) => string) => [ { - label: 'Post on the next available slot', + label: t('post_on_next_available_slot', 'Post on the next available slot'), value: true, }, { - label: 'Post Immediately', + label: t('post_immediately', 'Post Immediately'), value: false, }, ]; @@ -178,6 +179,10 @@ export const AddOrEditWebhook: FC<{ }> = (props) => { const { data, reload } = props; const fetch = useFetch(); + const t = useT(); + const options = getOptions(t); + const optionsChoose = getOptionsChoose(t); + const postImmediately = getPostImmediately(t); const [allIntegrations, setAllIntegrations] = useState( (JSON.parse(data?.integrations || '[]')?.length || 0) > 0 ? options[1] @@ -255,8 +260,8 @@ export const AddOrEditWebhook: FC<{ }); toast.show( data?.id - ? 'Autopost updated successfully' - : 'Autopost added successfully', + ? t('autopost_updated_successfully', 'Autopost updated successfully') + : t('autopost_added_successfully', 'Autopost added successfully'), 'success' ); modal.closeAll(); @@ -277,10 +282,10 @@ export const AddOrEditWebhook: FC<{ ).json(); if (!success) { setValid(''); - toast.show('Could not use this RSS feed', 'warning'); + toast.show(t('could_not_use_rss_feed', 'Could not use this RSS feed'), 'warning'); return; } - toast.show('RSS valid!', 'success'); + toast.show(t('rss_valid', 'RSS valid!'), 'success'); setValid(url); setLastUrl(newUrl); } catch (e: any) { @@ -288,8 +293,6 @@ export const AddOrEditWebhook: FC<{ } }, []); - const t = useT(); - return ( <FormProvider {...form}> <form onSubmit={form.handleSubmit(callBack)}> @@ -360,7 +363,7 @@ export const AddOrEditWebhook: FC<{ onChange={(e) => { form.setValue('content', e.target.value); }} - placeholder="Write your post..." + placeholder={t('write_your_post_placeholder', 'Write your post...')} autosuggestionsConfig={{ textareaPurpose: `Assist me in writing social media post`, chatApiConfigs: {}, diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 8273fc1b..ec03832a 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -114,7 +114,7 @@ export const ApiModal: FC<{ return; } methods.setError('api', { - message: 'Invalid API key', + message: t('invalid_api_key', 'Invalid API key'), }); }, []); @@ -343,7 +343,7 @@ export const AddProviderComponent: FC<{ await fetch(`/integrations/social/${identifier}`) ).json(); modal.openModal({ - title: 'Web3 provider', + title: t('web3_provider', 'Web3 provider'), withCloseButton: false, classNames: { modal: 'bg-transparent text-textColor', @@ -368,7 +368,7 @@ export const AddProviderComponent: FC<{ ) ).json(); if (err) { - toaster.show('Could not connect to the platform', 'warning'); + toaster.show(t('could_not_connect_to_platform', 'Could not connect to the platform'), 'warning'); return; } window.location.href = url; @@ -392,7 +392,7 @@ export const AddProviderComponent: FC<{ if (customFields) { modal.closeAll(); modal.openModal({ - title: 'Add Provider', + title: t('add_provider_title', 'Add Provider'), withCloseButton: false, classNames: { modal: 'bg-transparent text-textColor', diff --git a/apps/frontend/src/components/launches/bot.picture.tsx b/apps/frontend/src/components/launches/bot.picture.tsx index 53f2ac1e..38cbdc16 100644 --- a/apps/frontend/src/components/launches/bot.picture.tsx +++ b/apps/frontend/src/components/launches/bot.picture.tsx @@ -1,3 +1,5 @@ +'use client'; + import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import React, { FC, FormEventHandler, useCallback, useState } from 'react'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; @@ -33,7 +35,7 @@ export const BotPicture: FC<{ }), }); props.mutate(); - toast.show('Updated', 'success'); + toast.show(t('updated', 'Updated'), 'success'); modal.closeAll(); }, [nick, picture, props.mutate] @@ -45,7 +47,7 @@ export const BotPicture: FC<{ }, []); return ( <div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative w-full"> - <TopTitle title={`Change Bot Picture`} /> + <TopTitle title={t('change_bot_picture_title', 'Change Bot Picture')} /> <button className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" type="button" @@ -86,7 +88,7 @@ export const BotPicture: FC<{ value={nick} onChange={(e) => setNickname(e.target.value)} name="Nickname" - label="Nickname" + label={t('label_nickname', 'Nickname')} placeholder="" disableForm={true} /> diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 68694697..997218f8 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -921,12 +921,12 @@ const CalendarItem: FC<{ <div className="text-start"> {state === 'DRAFT' ? t('draft', 'Draft') + ': ' : ''} </div> - <div className="w-full relative"> - <div className="absolute top-0 start-0 w-full text-ellipsis break-words line-clamp-1 text-left"> - {stripHtmlValidation('none', post.content, false, true, false) || - 'no content'} + <div className="w-full relative"> + <div className="absolute top-0 start-0 w-full text-ellipsis break-words line-clamp-1 text-start"> + {stripHtmlValidation('none', post.content, false, true, false) || + t('no_content', 'no content')} + </div> </div> - </div> </div> </div> </div> diff --git a/apps/frontend/src/components/launches/customer.modal.tsx b/apps/frontend/src/components/launches/customer.modal.tsx index 53e47716..07b7b83a 100644 --- a/apps/frontend/src/components/launches/customer.modal.tsx +++ b/apps/frontend/src/components/launches/customer.modal.tsx @@ -1,4 +1,5 @@ -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; +'use client'; + import React, { FC, useCallback, useEffect, useState } from 'react'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; import { Integration } from '@prisma/client'; @@ -56,8 +57,8 @@ export const CustomerModal: FC<{ classNames={{ label: 'text-white', }} - label="Select Customer" - placeholder="Start typing..." + label={t('select_customer_label', 'Select Customer')} + placeholder={t('start_typing', 'Start typing...')} data={data?.map((p: any) => p.name) || []} /> </div> diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index 6980cbc4..43332f12 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -280,7 +280,7 @@ export const Filters = () => { onClick={setToday} className="hover:text-textItemFocused hover:bg-boxFocused py-[3px] px-[9px] flex justify-center items-center rounded-[8px] transition-all cursor-pointer text-[14px] bg-newBgColorInner border border-newTableBorder" > - Today + {t('today', 'Today')} </div> </div> </div> diff --git a/apps/frontend/src/components/launches/generator/generator.tsx b/apps/frontend/src/components/launches/generator/generator.tsx index 67ac5f8c..0bb0719c 100644 --- a/apps/frontend/src/components/launches/generator/generator.tsx +++ b/apps/frontend/src/components/launches/generator/generator.tsx @@ -1,3 +1,5 @@ +'use client'; + import React, { FC, useCallback, useMemo, useState } from 'react'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useRouter } from 'next/navigation'; @@ -19,7 +21,7 @@ import dayjs from 'dayjs'; import { Select } from '@gitroom/react/form/select'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; -import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component'; + const FirstStep: FC = (props) => { const { integrations, reloadCalendarView } = useCalendar(); const modal = useModals(); @@ -295,9 +297,9 @@ export const GeneratorComponent = () => { if (!user?.tier?.ai) { if ( await deleteDialog( - 'You need to upgrade to use this feature', - 'Move to billing', - 'Payment Required' + t('upgrade_required', 'You need to upgrade to use this feature'), + t('move_to_billing', 'Move to billing'), + t('payment_required', 'Payment Required') ) ) { router.push('/billing'); @@ -305,7 +307,7 @@ export const GeneratorComponent = () => { return; } modal.openModal({ - title: 'Generate Posts', + title: t('generate_posts', 'Generate Posts'), withCloseButton: false, classNames: { modal: 'bg-transparent text-textColor', diff --git a/apps/frontend/src/components/launches/information.component.tsx b/apps/frontend/src/components/launches/information.component.tsx index 07b50e5e..b65acfa7 100644 --- a/apps/frontend/src/components/launches/information.component.tsx +++ b/apps/frontend/src/components/launches/information.component.tsx @@ -1,9 +1,12 @@ +'use client'; + import React, { FC, Fragment, useMemo } from 'react'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; import clsx from 'clsx'; import Image from 'next/image'; import { capitalize } from 'lodash'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; const Valid: FC = () => { return ( @@ -57,6 +60,7 @@ export const InformationComponent: FC<{ totalAllowedChars: number; isPicture: boolean; }> = ({ totalChars, totalAllowedChars, chars, isPicture }) => { + const t = useT(); const { isGlobal, selectedIntegrations, internal } = useLaunchStore( useShallow((state) => ({ isGlobal: state.current === 'global', @@ -149,9 +153,7 @@ export const InformationComponent: FC<{ isGlobal && selectedIntegrations.length && 'mb-[12px]' )} > - Your post should have at least - <br /> - one character or one image. + {t('your_post_should_have_at_least_one_character_or_one_image', 'Your post should have at least one character or one image.')} </div> )} {isGlobal && ( @@ -191,7 +193,7 @@ export const InformationComponent: FC<{ )} > {isInternal?.[index] - ? 'Internal Edit' + ? t('internal_edit', 'Internal Edit') : `${totalChars}/${chars?.[p.integration.id] || 0}`} </div> </Fragment> diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index 5ddf682f..b8940a1e 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -7,7 +7,6 @@ import { groupBy, orderBy } from 'lodash'; import { CalendarWeekProvider } from '@gitroom/frontend/components/launches/calendar.context'; import { Filters } from '@gitroom/frontend/components/launches/filters'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import useSWR from 'swr'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; import clsx from 'clsx'; import { useUser } from '../layout/user.context'; @@ -234,6 +233,7 @@ export const MenuComponent: FC< collapsed, } = props; const user = useUser(); + const t = useT(); const [collected, drag, dragPreview] = useDrag(() => ({ type: 'menu', item: { @@ -247,7 +247,7 @@ export const MenuComponent: FC< {...(integration.refreshNeeded && { onClick: refreshChannel(integration), 'data-tooltip-id': 'tooltip', - 'data-tooltip-content': 'Channel disconnected, click to reconnect.', + 'data-tooltip-content': t('channel_disconnected_click_to_reconnect', 'Channel disconnected, click to reconnect.'), })} {...(collapsed ? { @@ -317,7 +317,7 @@ export const MenuComponent: FC< ? { 'data-tooltip-id': 'tooltip', 'data-tooltip-content': - 'This channel is disabled, please upgrade your plan to enable it.', + t('channel_disabled_upgrade_plan', 'This channel is disabled, please upgrade your plan to enable it.'), } : {})} role="Handle" @@ -469,7 +469,7 @@ export const LaunchesComponent = () => { fireEvents('channel_added'); window?.opener?.postMessage( { - msg: 'Channel added', + msg: t('channel_added', 'Channel added'), success: true, }, '*' diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index 2e0a09a9..647c9165 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -108,8 +108,8 @@ export const Menu: FC<{ const disableChannel = useCallback(async () => { if ( !(await deleteDialog( - 'Are you sure you want to disable this channel?', - 'Disable Channel' + t('are_you_sure_disable_channel', 'Are you sure you want to disable this channel?'), + t('disable_channel_title', 'Disable Channel') )) ) { return; @@ -120,15 +120,15 @@ export const Menu: FC<{ id, }), }); - toast.show('Channel Disabled', 'success'); + toast.show(t('channel_disabled', 'Channel Disabled'), 'success'); setShow(false); onChange(false); - }, []); + }, [t]); const deleteChannel = useCallback(async () => { if ( !(await deleteDialog( - 'Are you sure you want to delete this channel?', - 'Delete Channel' + t('are_you_sure_delete_channel', 'Are you sure you want to delete this channel?'), + t('delete_channel_title', 'Delete Channel') )) ) { return; @@ -141,15 +141,15 @@ export const Menu: FC<{ }); if (deleteIntegration.status === 406) { toast.show( - 'You have to delete all the posts associated with this channel before deleting it', + t('delete_posts_before_channel', 'You have to delete all the posts associated with this channel before deleting it'), 'warning' ); return; } - toast.show('Channel Deleted', 'success'); + toast.show(t('channel_deleted', 'Channel Deleted'), 'success'); setShow(false); onChange(true); - }, []); + }, [t]); const enableChannel = useCallback(async () => { await fetch('/integrations/enable', { @@ -158,10 +158,10 @@ export const Menu: FC<{ id, }), }); - toast.show('Channel Enabled', 'success'); + toast.show(t('channel_enabled', 'Channel Enabled'), 'success'); setShow(false); onChange(false); - }, []); + }, [t]); const editTimeTable = useCallback(() => { const findIntegration = integrations.find( @@ -172,20 +172,20 @@ export const Menu: FC<{ closeOnEscape: false, closeOnClickOutside: false, askClose: true, - title: 'Time Table Slots', + title: t('time_table_slots', 'Time Table Slots'), children: <TimeTable integration={findIntegration!} mutate={mutate} />, }); setShow(false); - }, [integrations]); + }, [integrations, t]); const copyChannelId = useCallback( (integration: Integrations) => async () => { setShow(false); const channelId = integration.id; copy(channelId); - toast.show(`Channel ID ${channelId} copied to clipboard`, 'success'); + toast.show(t('channel_id_copied', 'Channel ID copied to clipboard'), 'success'); }, - [] + [t] ); const createPost = useCallback( @@ -255,20 +255,20 @@ export const Menu: FC<{ (integration) => integration.id === id ); modal.openModal({ - title: 'Additional Settings', + title: t('additional_settings', 'Additional Settings'), children: ( <SettingsModal // @ts-ignore integration={findIntegration} onClose={() => { mutate(); - toast.show('Settings Updated', 'success'); + toast.show(t('settings_updated', 'Settings Updated'), 'success'); }} /> ), }); setShow(false); - }, [integrations]); + }, [integrations, t]); const addToCustomer = useCallback(() => { const findIntegration = integrations.find( (integration) => integration.id === id @@ -277,7 +277,7 @@ export const Menu: FC<{ classNames: { modal: 'md', }, - title: 'Move / Add to customer', + title: t('move_add_to_customer', 'Move / Add to customer'), withCloseButton: false, closeOnEscape: true, closeOnClickOutside: true, @@ -287,16 +287,16 @@ export const Menu: FC<{ integration={findIntegration} onClose={() => { mutate(); - toast.show('Customer Updated', 'success'); + toast.show(t('customer_updated', 'Customer Updated'), 'success'); }} /> ), }); setShow(false); - }, [integrations]); + }, [integrations, t]); const updateCredentials = useCallback(() => { modal.openModal({ - title: 'Custom URL', + title: t('custom_url', 'Custom URL'), withCloseButton: false, classNames: { modal: 'md', @@ -309,7 +309,7 @@ export const Menu: FC<{ /> ), }); - }, []); + }, [t]); return ( <div @@ -488,8 +488,8 @@ export const Menu: FC<{ <div className="text-[14px]"> {t('change_bot', 'Change Bot')} {[ - canChangeProfilePicture && 'Picture', - canChangeNickName && 'Nickname', + canChangeProfilePicture && t('picture', 'Picture'), + canChangeNickName && t('label_nickname', 'Nickname'), ] .filter((f) => f) .join(' / ')} diff --git a/apps/frontend/src/components/launches/repeat.component.tsx b/apps/frontend/src/components/launches/repeat.component.tsx index 9bc6e140..ad263bb0 100644 --- a/apps/frontend/src/components/launches/repeat.component.tsx +++ b/apps/frontend/src/components/launches/repeat.component.tsx @@ -1,3 +1,5 @@ +'use client'; + import { FC, useMemo, useState } from 'react'; import { Select } from '@gitroom/react/form/select'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -5,46 +7,46 @@ import { useClickOutside } from '@mantine/hooks'; import { isUSCitizen } from '@gitroom/frontend/components/launches/helpers/isuscitizen.utils'; import clsx from 'clsx'; import { RepeatIcon, DropdownArrowIcon } from '@gitroom/frontend/components/ui/icons'; -const list = [ +const getList = (t: (key: string, fallback: string) => string) => [ { value: 1, - label: 'Day', + label: t('day', 'Day'), }, { value: 2, - label: 'Two Days', + label: t('two_days', 'Two Days'), }, { value: 3, - label: 'Three Days', + label: t('three_days', 'Three Days'), }, { value: 4, - label: 'Four Days', + label: t('four_days', 'Four Days'), }, { value: 5, - label: 'Five Days', + label: t('five_days', 'Five Days'), }, { value: 6, - label: 'Six Days', + label: t('six_days', 'Six Days'), }, { value: 7, - label: 'Week', + label: t('week', 'Week'), }, { value: 14, - label: 'Two Weeks', + label: t('two_weeks', 'Two Weeks'), }, { value: 30, - label: 'Month', + label: t('month', 'Month'), }, { value: null, - label: 'Cancel', + label: t('cancel', 'Cancel'), }, ]; export const RepeatComponent: FC<{ @@ -53,6 +55,7 @@ export const RepeatComponent: FC<{ }> = (props) => { const { repeat } = props; const t = useT(); + const list = getList(t); const [isOpen, setIsOpen] = useState(false); const ref = useClickOutside(() => { @@ -67,7 +70,7 @@ export const RepeatComponent: FC<{ return ''; } return list.find((p) => p.value === repeat)?.label; - }, [repeat]); + }, [repeat, list]); return ( <div @@ -86,7 +89,7 @@ export const RepeatComponent: FC<{ </div> <div className="cursor-pointer"> {repeat - ? `Repeat Post Every ${everyLabel}` + ? `${t('repeat_post_every_label', 'Repeat Post Every')} ${everyLabel}` : t('repeat_post_every', 'Repeat Post Every...')} </div> <div className="cursor-pointer"> @@ -94,7 +97,7 @@ export const RepeatComponent: FC<{ </div> </div> {isOpen && ( - <div className="z-[300] absolute left-0 bottom-[100%] w-[240px] bg-newBgColorInner p-[12px] menu-shadow -translate-y-[10px] flex flex-col"> + <div className="z-[300] absolute start-0 bottom-[100%] w-[240px] bg-newBgColorInner p-[12px] menu-shadow -translate-y-[10px] flex flex-col"> {list.map((p) => ( <div onClick={() => { diff --git a/apps/frontend/src/components/launches/select.customer.tsx b/apps/frontend/src/components/launches/select.customer.tsx index f1ed526f..2b42acc3 100644 --- a/apps/frontend/src/components/launches/select.customer.tsx +++ b/apps/frontend/src/components/launches/select.customer.tsx @@ -1,3 +1,5 @@ +'use client'; + import { uniqBy } from 'lodash'; import React, { FC, useCallback, useMemo, useRef, useState } from 'react'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; @@ -53,7 +55,7 @@ export const SelectCustomer: FC<{ <div className="relative select-none z-[500]" ref={ref}> <div data-tooltip-id="tooltip" - data-tooltip-content="Select Customer" + data-tooltip-content={t('select_customer_tooltip', 'Select Customer')} onClick={openClose} className={clsx( 'relative z-[20] cursor-pointer h-[42px] rounded-[8px] pl-[16px] pr-[12px] gap-[8px] border flex items-center', @@ -73,7 +75,7 @@ export const SelectCustomer: FC<{ className="flex flex-col fixed pt-[12px] bg-newBgColorInner menu-shadow min-w-[250px]" > <div className="text-[14px] font-[600] px-[12px] mb-[5px]"> - Customers + {t('customers', 'Customers')} </div> {uniqBy(integrations, (u) => u?.customer?.name) .filter((f) => f.customer?.name) diff --git a/apps/frontend/src/components/launches/tags.component.tsx b/apps/frontend/src/components/launches/tags.component.tsx index 0679c4ea..1e0ff486 100644 --- a/apps/frontend/src/components/launches/tags.component.tsx +++ b/apps/frontend/src/components/launches/tags.component.tsx @@ -1,8 +1,9 @@ +'use client'; + import { FC, useCallback, useMemo, useState } from 'react'; import { ReactTags } from 'react-tag-autocomplete'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { Input } from '@gitroom/react/form/input'; import { ColorPicker } from '@gitroom/react/form/color.picker'; import { Button } from '@gitroom/react/form/button'; @@ -71,7 +72,7 @@ export const TagsComponentInner: FC<{ const addTag = useCallback(async () => { const val: string | undefined = await new Promise((resolve) => { modals.openModal({ - title: 'Add new tag', + title: t('add_new_tag', 'Add New Tag'), children: (close) => ( <ShowModal tag="" close={close} resolve={resolve} /> ), @@ -114,7 +115,7 @@ export const TagsComponentInner: FC<{ </div> <div className="cursor-pointer flex gap-[4px]"> {tagValue.length === 0 ? ( - 'Add New Tag' + t('add_new_tag', 'Add New Tag') ) : ( <> <div @@ -134,7 +135,7 @@ export const TagsComponentInner: FC<{ </div> </div> {isOpen && ( - <div className="z-[300] absolute left-0 bottom-[100%] w-[240px] bg-newBgColorInner p-[12px] menu-shadow -translate-y-[10px] flex flex-col"> + <div className="z-[300] absolute start-0 bottom-[100%] w-[240px] bg-newBgColorInner p-[12px] menu-shadow -translate-y-[10px] flex flex-col"> {(data?.tags || []).map((p: any) => ( <div onClick={() => { @@ -180,7 +181,7 @@ export const TagsComponentInner: FC<{ <div> <PlusIcon /> </div> - <div className="text-[13px] font-[600]">Add New Tag</div> + <div className="text-[13px] font-[600]">{t('add_new_tag', 'Add New Tag')}</div> </div> </div> )} @@ -431,13 +432,13 @@ const ShowModal: FC<{ <Input name="name" disableForm={true} - label="Name" + label={t('tag_name', 'Name')} value={tagName} onChange={(e) => setTagName(e.target.value)} /> <ColorPicker onChange={(e) => setColor(e.target.value)} - label="Tag Color" + label={t('label_tag_color', 'Tag Color')} name="color" value={color} enabled={true} diff --git a/apps/frontend/src/components/launches/time.table.tsx b/apps/frontend/src/components/launches/time.table.tsx index e7ba35de..ea2fca3b 100644 --- a/apps/frontend/src/components/launches/time.table.tsx +++ b/apps/frontend/src/components/launches/time.table.tsx @@ -1,4 +1,5 @@ -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; +'use client'; + import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; import dayjs from 'dayjs'; @@ -118,7 +119,7 @@ export const TimeTable: FC<{ <div className="mt-[16px] flex justify-center gap-[16px]"> <div className="w-[100px]"> <Select - label="Hour" + label={t('hour', 'Hour')} name="hour" disableForm={true} className="w-[100px] mt-[8px]" @@ -135,7 +136,7 @@ export const TimeTable: FC<{ </div> <div className="w-[100px]"> <Select - label="Minutes" + label={t('minutes', 'Minutes')} name="minutes" disableForm={true} className="w-[100px] mt-[8px]" diff --git a/apps/frontend/src/components/layout/settings.component.tsx b/apps/frontend/src/components/layout/settings.component.tsx index f0529caf..90c16e4e 100644 --- a/apps/frontend/src/components/layout/settings.component.tsx +++ b/apps/frontend/src/components/layout/settings.component.tsx @@ -76,7 +76,7 @@ export const SettingsPopup: FC<{ if (getRef) { return; } - toast.show('Profile updated'); + toast.show(t('profile_updated', 'Profile updated')); swr.mutate('/marketplace/account'); close(); }, []); diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index a401156f..74938f18 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -319,10 +319,10 @@ export const MediaBox: FC<{ className="cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]" > <PlusIcon size={14} /> - <div>Upload</div> + <div>{t('upload', 'Upload')}</div> </button> ); - }, []); + }, [t]); return ( <DropFiles className="flex flex-col flex-1" onDrop={dragAndDrop}> @@ -335,8 +335,8 @@ export const MediaBox: FC<{ > {!isLoading && !!data?.results?.length && ( <div className="flex-1 text-[14px] font-[600] whitespace-pre-line"> - Select or upload pictures (maximum 5 at a time).{'\n'} - You can also drag & drop pictures. + {t('select_or_upload_pictures_max_5', 'Select or upload pictures (maximum 5 at a time).')}{'\n'} + {t('you_can_drag_drop_pictures', 'You can also drag & drop pictures.')} </div> )} <input @@ -384,11 +384,11 @@ export const MediaBox: FC<{ <> <NoMediaIcon /> <div className="text-[20px] font-[600]"> - You don't have any media yet + {t('you_dont_have_any_media_yet', "You don't have any media yet")} </div> <div className="whitespace-pre-line text-newTextColor/[0.6] text-center"> - Select or upload pictures (maximum 5 at a time). {'\n'} - You can also drag & drop pictures. + {t('select_or_upload_pictures_max_5', 'Select or upload pictures (maximum 5 at a time).')} {'\n'} + {t('you_can_drag_drop_pictures', 'You can also drag & drop pictures.')} </div> <div className="forceChange">{btn}</div> </> @@ -474,7 +474,7 @@ export const MediaBox: FC<{ onClick={() => modals.closeCurrent()} className="cursor-pointer h-[52px] px-[20px] items-center justify-center border border-newTextColor/10 flex rounded-[10px]" > - Cancel + {t('cancel', 'Cancel')} </button> {!isLoading && !!data?.results?.length && ( <button @@ -542,6 +542,7 @@ export const MultiMediaComponent: FC<{ } = props; const user = useUser(); const modals = useModals(); + const t = useT(); useEffect(() => { if (value) { setCurrentMedia(value); @@ -576,7 +577,7 @@ export const MultiMediaComponent: FC<{ ); const showModal = useCallback(() => { modals.openModal({ - title: 'Media Library', + title: t('media_library', 'Media Library'), askClose: false, closeOnEscape: true, fullScreen: true, @@ -586,7 +587,7 @@ export const MultiMediaComponent: FC<{ <MediaBox setMedia={changeMedia} closeModal={close} /> ), }); - }, [changeMedia]); + }, [changeMedia, t]); const clearMedia = useCallback( (topIndex: number) => () => { @@ -606,16 +607,15 @@ export const MultiMediaComponent: FC<{ if (!!user?.tier?.ai && !dummy) { modals.openModal({ askClose: false, - title: 'Design Media', + title: t('design_media', 'Design Media'), size: '80%', children: (close) => ( <Polonto setMedia={changeMedia} closeModal={close} /> ), }); } - }, [changeMedia]); + }, [changeMedia, t]); - const t = useT(); return ( <> @@ -641,7 +641,7 @@ export const MultiMediaComponent: FC<{ <div onClick={async () => { modals.openModal({ - title: 'Media Settings', + title: t('media_settings', 'Media Settings'), children: (close) => ( <MediaComponentInner media={media as any} @@ -790,7 +790,7 @@ export const MediaComponent: FC<{ const showDesignModal = useCallback(() => { modals.openModal({ - title: 'Media Editor', + title: t('media_editor', 'Media Editor'), askClose: false, closeOnEscape: true, fullScreen: true, @@ -805,7 +805,7 @@ export const MediaComponent: FC<{ /> ), }); - }, []); + }, [t]); const changeMedia = useCallback((m: { path: string; id: string }[]) => { setCurrentMedia(m[0]); onChange({ @@ -817,7 +817,7 @@ export const MediaComponent: FC<{ }, []); const showModal = useCallback(() => { modals.openModal({ - title: 'Media Library', + title: t('media_library', 'Media Library'), askClose: false, closeOnEscape: true, fullScreen: true, @@ -827,7 +827,7 @@ export const MediaComponent: FC<{ <MediaBox setMedia={changeMedia} closeModal={close} type={type} /> ), }); - }, []); + }, [t]); const clearMedia = useCallback(() => { setCurrentMedia(undefined); onChange({ diff --git a/apps/frontend/src/components/messages/messages.tsx b/apps/frontend/src/components/messages/messages.tsx index d2c02895..3416c8ff 100644 --- a/apps/frontend/src/components/messages/messages.tsx +++ b/apps/frontend/src/components/messages/messages.tsx @@ -1,6 +1,5 @@ 'use client'; -import dayjs from 'dayjs'; export interface Root { id: string; buyerId: string; @@ -63,6 +62,7 @@ export const Message: FC<{ }> = (props) => { const { message, seller, buyer, scrollDown } = props; const user = useUser(); + const t = useT(); const amITheBuyerOrSeller = useMemo(() => { return user?.id === buyer?.id ? 'BUYER' : 'SELLER'; }, [buyer, user]); @@ -107,7 +107,7 @@ export const Message: FC<{ </div> <div className="flex-1 flex flex-col max-w-[534px] gap-[10px]"> <div className="flex gap-[10px] items-center"> - <div>{isMe ? 'Me' : person?.name}</div> + <div>{isMe ? t('me', 'Me') : person?.name}</div> <div className="w-[6px] h-[6px] bg-customColor34 rounded-full" /> <div className="text-[14px] text-inputText">{time}</div> </div> @@ -235,7 +235,7 @@ export const Messages = () => { )} </div> <div className="text-[20px] flex-1"> - {showFrom?.name || 'Noname'} + {showFrom?.name || t('noname', 'Noname')} </div> <div> <OrderTopActions /> diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 5f995411..2842b3d0 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -10,10 +10,8 @@ import React, { ClipboardEvent, forwardRef, useImperativeHandle, - Fragment, } from 'react'; import clsx from 'clsx'; -import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import EmojiPicker from 'emoji-picker-react'; import { Theme } from 'emoji-picker-react'; @@ -41,7 +39,6 @@ import { EditorContent, Extension, mergeAttributes, - Node, } from '@tiptap/react'; import Document from '@tiptap/extension-document'; import Bold from '@tiptap/extension-bold'; @@ -100,6 +97,7 @@ export const EditorWrapper: FC<{ totalPosts: number; value: string; }> = () => { + const t = useT(); const { setGlobalValueText, setInternalValueText, @@ -277,14 +275,14 @@ export const EditorWrapper: FC<{ const goBackToGlobal = useCallback(async () => { if ( await deleteDialog( - 'This action is irreversible. Are you sure you want to go back to global mode?', - 'Yes, go back to global mode' + t('are_you_sure_go_back_to_global_mode', 'This action is irreversible. Are you sure you want to go back to global mode?'), + t('yes_go_back_to_global_mode', 'Yes, go back to global mode') ) ) { setLoaded(false); addRemoveInternal(current); } - }, [addRemoveInternal, current]); + }, [addRemoveInternal, current, t]); const addValue = useCallback( (index: number) => () => { @@ -319,8 +317,8 @@ export const EditorWrapper: FC<{ (index: number) => async () => { if ( !(await deleteDialog( - 'Are you sure you want to delete this post?', - 'Yes, delete it!' + t('are_you_sure_delete_this_post', 'Are you sure you want to delete this post?'), + t('yes_delete_it', 'Yes, delete it!') )) ) { return; @@ -334,7 +332,7 @@ export const EditorWrapper: FC<{ deleteGlobalValue(index); setLoaded(false); }, - [current, global, internal] + [current, global, internal, t] ); if (!loaded || !loadedState) { @@ -360,7 +358,7 @@ export const EditorWrapper: FC<{ <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> </div> <div className="text-[14px] font-[600] text-white"> - You can't edit networks when creating a set + {t('cant_edit_networks_when_creating_set', "You can't edit networks when creating a set")} </div> </div> <div className="absolute w-full h-full left-0 top-0 bg-newBackdrop opacity-60 z-[100] rounded-[12px]" /> @@ -382,13 +380,11 @@ export const EditorWrapper: FC<{ <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> </div> <div className="text-[14px] font-[600] text-white"> - Click this button to exit global editing - <br /> - and customize the post for this channel + {t('click_to_exit_global_editing', 'Click this button to exit global editing and customize the post for this channel')} </div> <div> <div className="text-white rounded-[8px] h-[44px] px-[20px] bg-[#D82D7E] cursor-pointer flex justify-center items-center"> - Edit content + {t('edit_content', 'Edit content')} </div> </div> </div> @@ -453,7 +449,7 @@ export const EditorWrapper: FC<{ <div className="flex gap-[6px] items-center"> <div className="w-[8px] h-[8px] rounded-full bg-[#FC69FF]" /> <div className="text-[14px] font-[600]"> - Editing a Specific Network + {t('editing_a_specific_network', 'Editing a Specific Network')} </div> </div> <div className="flex gap-[6px] items-center"> @@ -461,7 +457,7 @@ export const EditorWrapper: FC<{ <ResetIcon /> </div> <div className="text-[13px] font-[600]"> - Back to global + {t('back_to_global', 'Back to global')} </div> </div> </div> @@ -483,7 +479,7 @@ export const EditorWrapper: FC<{ <TrashIcon onClick={deletePost(index)} data-tooltip-id="tooltip" - data-tooltip-content="Delete Post" + data-tooltip-content={t('delete_post_tooltip', 'Delete Post')} className="cursor-pointer text-[#FF3F3F]" /> )} @@ -626,14 +622,14 @@ export const Editor: FC<{ > <div className="relative cursor-text flex flex-1 flex-col"> <div {...getRootProps()} className="flex flex-1 flex-col"> - <div - className={clsx( - 'absolute left-0 top-0 w-full h-full bg-black/70 z-[300] transition-all items-center justify-center flex text-white text-sm', - !isDragActive ? 'pointer-events-none opacity-0' : 'opacity-100' - )} - > - Drop your files here to upload - </div> +<div + className={clsx( + 'absolute left-0 top-0 w-full h-full bg-black/70 z-[300] transition-all items-center justify-center flex text-white text-sm', + !isDragActive ? 'pointer-events-none opacity-0' : 'opacity-100' + )} + > + {t('drop_files_here_to_upload', 'Drop your files here to upload')} + </div> <div className="px-[10px] pt-[10px] bg-newBgColorInner rounded-t-[6px] relative z-[99]"> <OnlyEditor value={props.value} @@ -725,7 +721,7 @@ export const Editor: FC<{ )} <div data-tooltip-id="tooltip" - data-tooltip-content="Insert Emoji" + data-tooltip-content={t('insert_emoji', 'Insert Emoji')} className="select-none cursor-pointer rounded-[6px] w-[30px] h-[30px] bg-newColColor flex justify-center items-center" onClick={() => setEmojiPickerOpen(!emojiPickerOpen)} > @@ -781,6 +777,7 @@ export const OnlyEditor = forwardRef< paste?: (event: ClipboardEvent | File[]) => void; } >(({ editorType, value, onChange, paste }, ref) => { + const t = useT(); const fetch = useFetch(); const { internal } = useLaunchStore( @@ -831,7 +828,7 @@ export const OnlyEditor = forwardRef< BulletList, ListItem, Placeholder.configure({ - placeholder: 'Write something …', + placeholder: t('write_something', 'Write something …'), emptyEditorClass: 'is-editor-empty', }), ...(editorType === 'html' || editorType === 'markdown' diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 9bee0d8c..b118ab73 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -100,20 +100,20 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const currentIntegration = integrations.find((p) => p.id === current)!; return ( - <div className="flex items-center gap-[10px]"> - <div className="relative"> - <img - src={`/icons/platforms/${currentIntegration.identifier}.png`} - className="w-[20px] h-[20px] rounded-[4px]" - alt={currentIntegration.identifier} - /> - <SettingsIcon - size={15} - className="text-white absolute -end-[5px] -bottom-[5px]" - /> + <div className="flex items-center gap-[10px]"> + <div className="relative"> + <img + src={`/icons/platforms/${currentIntegration.identifier}.png`} + className="w-[20px] h-[20px] rounded-[4px]" + alt={currentIntegration.identifier} + /> + <SettingsIcon + size={15} + className="text-white absolute -end-[5px] -bottom-[5px]" + /> + </div> + <div>{currentIntegration.name} {t('channel_settings', 'Settings')}</div> </div> - <div>{currentIntegration.name} Settings</div> - </div> ); }, [current]); @@ -158,8 +158,8 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { setLoading(true); if ( !(await deleteDialog( - 'Are you sure you want to delete this post?', - 'Yes, delete it!' + t('are_you_sure_you_want_to_delete_post', 'Are you sure you want to delete this post?'), + t('yes_delete_it', 'Yes, delete it!') )) ) { setLoading(false); @@ -193,7 +193,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { toaster.show( '' + item.integration.name + - ' Your post should have at least one character or one image.', + ' ' + t('post_needs_content_or_image', 'Your post should have at least one character or one image.'), 'warning' ); setLoading(false); @@ -203,7 +203,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { for (const item of checkAllValid) { if (item.valid === false) { - toaster.show('Please fix your settings', 'warning'); + toaster.show(t('please_fix_your_settings', 'Please fix your settings'), 'warning'); item.fix(); setLoading(false); setShowSettings(true); @@ -240,7 +240,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { for (const item of sliceNeeded) { toaster.show( - `${item?.integration?.name} (${item?.integration?.identifier}) post is too long, please fix it`, + `${item?.integration?.name} (${item?.integration?.identifier}) ${t('post_is_too_long', 'post is too long, please fix it')}`, 'warning' ); item.preview(); @@ -265,8 +265,8 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const shortLink = !shortLinkUrl.ask ? false : await deleteDialog( - 'Do you want to shortlink the URLs? it will let you get statistics over clicks', - 'Yes, shortlink it!' + t('shortlink_urls_question', 'Do you want to shortlink the URLs? it will let you get statistics over clicks'), + t('yes_shortlink_it', 'Yes, shortlink it!') ); const group = existingData.group || makeId(10); @@ -327,8 +327,8 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { mutate(); toaster.show( !existingData.integration - ? 'Added successfully' - : 'Updated successfully' + ? t('added_successfully', 'Added successfully') + : t('updated_successfully', 'Updated successfully') ); } if (customClose) { @@ -349,9 +349,9 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { <div className="w-full h-full flex-1 p-[40px] flex relative"> <div className="flex flex-1 bg-newBgColorInner rounded-[20px] flex-col"> <div className="flex-1 flex"> - <div className="flex flex-col flex-1 border-r border-newBorder"> - <div className="bg-newBgColor h-[65px] rounded-tl-[20px] flex items-center px-[20px] text-[20px] font-[600]"> - Create Post + <div className="flex flex-col flex-1 border-e border-newBorder"> + <div className="bg-newBgColor h-[65px] rounded-s-[20px] !rounded-b-[0] flex items-center px-[20px] text-[20px] font-[600]"> + {t('create_post_title', 'Create Post')} </div> <div className="flex-1 flex flex-col gap-[16px]"> <div @@ -359,7 +359,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { > <div id="social-content" - className="gap-[32px] flex flex-col pr-[8px] pt-[20px] pl-[20px] absolute top-0 left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" + className="gap-[32px] flex flex-col pe-[8px] pt-[20px] ps-[20px] absolute top-0 left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" > <div className="flex w-full"> <div className="flex flex-1"> @@ -434,16 +434,16 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { </div> </div> <div className="w-[580px] flex flex-col"> - <div className="bg-newBgColor h-[65px] rounded-tr-[20px] flex items-center px-[20px] text-[20px] font-[600]"> - <div className="flex-1">Post Preview</div> + <div className="bg-newBgColor h-[65px] rounded-e-[20px] !rounded-b-[0] flex items-center px-[20px] text-[20px] font-[600]"> + <div className="flex-1">{t('post_preview', 'Post Preview')}</div> <div className="cursor-pointer"> <CloseIcon onClick={askClose} className="text-[#A3A3A3]" /> </div> </div> <div className="flex-1 relative"> <Scrollable - scrollClasses="!pr-[20px]" - className="absolute top-0 p-[20px] pr-[8px] left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" + scrollClasses="!pe-[20px]" + className="absolute top-0 p-[20px] pe-[8px] left-0 w-full h-full overflow-x-hidden overflow-y-scroll scrollbar scrollbar-thumb-newColColor scrollbar-track-newBgColorInner" > <ShowAllProviders ref={ref} /> </Scrollable> @@ -451,7 +451,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { </div> </div> <div className="select-none h-[84px] py-[20px] border-t border-newBorder flex items-center"> - <div className="flex-1 flex pl-[20px] gap-[8px]"> + <div className="flex-1 flex ps-[20px] gap-[8px]"> {!dummy && ( <TagsComponent name="tags" @@ -467,7 +467,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { <RepeatComponent repeat={repeater} onChange={setRepeater} /> )} </div> - <div className="pr-[20px] flex items-center justify-end gap-[8px]"> + <div className="pe-[20px] flex items-center justify-end gap-[8px]"> {existingData?.integration && ( <button onClick={deletePost} @@ -476,7 +476,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { <div> <TrashIcon /> </div> - <div>Delete Post</div> + <div>{t('delete_post', 'Delete Post')}</div> </button> )} <DatePicker onChange={setDate} date={date} /> @@ -488,12 +488,12 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { onClick={schedule('draft')} className="cursor-pointer disabled:cursor-not-allowed px-[20px] h-[44px] bg-btnSimple justify-center items-center flex rounded-[8px] text-[15px] font-[600]" > - Save as Draft + {t('save_as_draft', 'Save as Draft')} </button> )} {addEditSets && ( <button - className="text-white text-[15px] font-[600] min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] pl-[20px] pr-[16px]" + className="text-white text-[15px] font-[600] min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] ps-[20px] pe-[16px]" disabled={ selectedIntegrations.length === 0 || loading || locked } @@ -509,13 +509,13 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { selectedIntegrations.length === 0 || loading || locked } onClick={schedule('schedule')} - className="text-white min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] pl-[20px] pr-[16px]" + className="text-white min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] ps-[20px] pe-[16px]" > <div className="text-[15px] font-[600]"> {selectedIntegrations.length === 0 - ? 'Check the circles above' + ? t('check_circles_above', 'Check the circles above') : dummy - ? 'Create output' + ? t('create_output', 'Create output') : !existingData?.integration ? t('add_to_calendar', 'Add to calendar') : existingData?.posts?.[0]?.state === 'DRAFT' @@ -538,7 +538,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { className="rounded-[8px] z-[300] disabled:cursor-not-allowed disabled:opacity-80 hidden group-hover:flex absolute bottom-[100%] -left-[12px] p-[12px] w-[206px] bg-newBgColorInner" > <div className="text-white rounded-[8px] bg-[#D82D7E] h-[44px] w-full flex justify-center items-center post-now"> - Post Now + {t('post_now', 'Post Now')} </div> </button> )} @@ -562,8 +562,8 @@ Post content can be added using the addPostContentFor{num} function. After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} function. `} labels={{ - title: 'Your Assistant', - initial: 'Hi! I can help you to refine your social media posts.', + title: t('your_assistant', 'Your Assistant'), + initial: t('assistant_initial_message', 'Hi! I can help you to refine your social media posts.'), }} /> </div> diff --git a/apps/frontend/src/components/new-launch/mention.component.tsx b/apps/frontend/src/components/new-launch/mention.component.tsx index cd1e6252..2ff66a16 100644 --- a/apps/frontend/src/components/new-launch/mention.component.tsx +++ b/apps/frontend/src/components/new-launch/mention.component.tsx @@ -1,7 +1,8 @@ +'use client'; + import React, { FC, useEffect, useImperativeHandle, useState } from 'react'; import { computePosition, flip, shift } from '@floating-ui/dom'; import { posToDOMRect, ReactRenderer } from '@tiptap/react'; -import { timer } from '@gitroom/helpers/utils/timer'; // Debounce utility for TipTap const debounce = <T extends any[]>( @@ -93,7 +94,7 @@ const MentionList: FC = (props: any) => { ) : ( props?.items?.map((item: any, index: any) => ( <button - className={`flex gap-[10px] w-full p-2 text-left rounded hover:bg-gray-100 ${ + className={`flex gap-[10px] w-full p-2 text-start rounded hover:bg-gray-100 ${ index === selectedIndex ? 'bg-blue-100' : '' }`} key={item.id || index} diff --git a/apps/frontend/src/components/new-launch/select.current.tsx b/apps/frontend/src/components/new-launch/select.current.tsx index 4f0d9c49..f687643c 100644 --- a/apps/frontend/src/components/new-launch/select.current.tsx +++ b/apps/frontend/src/components/new-launch/select.current.tsx @@ -6,6 +6,7 @@ import clsx from 'clsx'; import Image from 'next/image'; import { useShallow } from 'zustand/react/shallow'; import { GlobalIcon } from '@gitroom/frontend/components/ui/icons'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; export function useHasScroll(ref: RefObject<HTMLElement>): boolean { const [hasHorizontalScroll, setHasHorizontalScroll] = useState(false); @@ -144,6 +145,7 @@ export const SelectCurrent: FC = () => { }; export const IsGlobal: FC<{ id: string }> = ({ id }) => { + const t = useT(); const { isInternal } = useLaunchStore( useShallow((state) => ({ @@ -158,7 +160,7 @@ export const IsGlobal: FC<{ id: string }> = ({ id }) => { return ( <div data-tooltip-id="tooltip" - data-tooltip-content="No longer in global mode" + data-tooltip-content={t('no_longer_global_mode', 'No longer in global mode')} className="w-[8px] h-[8px] bg-[#FC69FF] -top-[1px] -end-[3px] absolute rounded-full" /> ); diff --git a/apps/frontend/src/components/onboarding/onboarding.tsx b/apps/frontend/src/components/onboarding/onboarding.tsx index 30a0d9cc..d5b5a21c 100644 --- a/apps/frontend/src/components/onboarding/onboarding.tsx +++ b/apps/frontend/src/components/onboarding/onboarding.tsx @@ -7,12 +7,12 @@ import { Button } from '@gitroom/react/form/button'; import { ConnectChannels } from '@gitroom/frontend/components/onboarding/connect.channels'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component'; const Welcome: FC = () => { const { isGeneral } = useVariables(); const [step, setStep] = useState(1); const router = useRouter(); + const t = useT(); const goToLaunches = useCallback(() => { router.push('/launches'); @@ -24,7 +24,7 @@ const Welcome: FC = () => { <div> <ConnectChannels /> <div className="flex justify-end gap-[8px]"> - <Button onClick={goToLaunches}>Close</Button> + <Button onClick={goToLaunches}>{t('close', 'Close')}</Button> </div> </div> )} diff --git a/apps/frontend/src/components/settings/teams.component.tsx b/apps/frontend/src/components/settings/teams.component.tsx index 394ed17a..070ed9b2 100644 --- a/apps/frontend/src/components/settings/teams.component.tsx +++ b/apps/frontend/src/components/settings/teams.component.tsx @@ -1,3 +1,5 @@ +'use client'; + import { Button } from '@gitroom/react/form/button'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; @@ -5,7 +7,6 @@ import React, { useCallback, useMemo } from 'react'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { capitalize } from 'lodash'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { Input } from '@gitroom/react/form/input'; import { useForm, FormProvider, useWatch } from 'react-hook-form'; import { Select } from '@gitroom/react/form/select'; @@ -16,6 +17,7 @@ import { useToaster } from '@gitroom/react/toaster/toaster'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import copy from 'copy-to-clipboard'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; + const roles = [ { name: 'User', @@ -56,12 +58,12 @@ export const AddMember = () => { ).json(); if (values.sendEmail) { modals.closeAll(); - toast.show('Invitation link sent'); + toast.show(t('invitation_link_sent', 'Invitation link sent')); return; } copy(url); modals.closeAll(); - toast.show('Link copied to clipboard'); + toast.show(t('link_copied_to_clipboard', 'Link copied to clipboard')); }, [] ); @@ -96,7 +98,7 @@ export const AddMember = () => { </div> </div> <Button type="submit" className="mt-[18px]"> - {sendEmail ? 'Send Invitation Link' : 'Copy Link'} + {sendEmail ? t('send_invitation_link', 'Send Invitation Link') : t('copy_link', 'Copy Link')} </Button> </div> </form> @@ -107,6 +109,7 @@ export const TeamsComponent = () => { const fetch = useFetch(); const user = useUser(); const modals = useModals(); + const t = useT(); const myLevel = user?.role === 'USER' ? 0 : user?.role === 'ADMIN' ? 1 : 2; const getLevel = useCallback( (role: 'USER' | 'ADMIN' | 'SUPERADMIN') => @@ -128,11 +131,11 @@ export const TeamsComponent = () => { classNames: { modal: 'bg-transparent text-textColor', }, - title: 'Add Team Member', + title: t('top_title_add_member', 'Add Member'), withCloseButton: true, children: <AddMember />, }); - }, []); + }, [t]); const { data, mutate } = useSWR('/api/teams', loadTeam, { revalidateOnFocus: false, revalidateOnReconnect: false, @@ -147,7 +150,7 @@ export const TeamsComponent = () => { async () => { if ( !(await deleteDialog( - 'Are you sure you want to remove this team member?' + t('are_you_sure_remove_team_member', 'Are you sure you want to remove this team member?') )) ) { return; @@ -157,11 +160,9 @@ export const TeamsComponent = () => { }); await mutate(); }, - [] + [t] ); - const t = useT(); - return ( <div className="flex flex-col"> <h3 className="text-[20px]">{t('team_members', 'Team Members')}</h3> @@ -180,10 +181,10 @@ export const TeamsComponent = () => { </div> <div className="flex-1"> {p.role === 'USER' - ? 'User' + ? t('user', 'User') : p.role === 'ADMIN' - ? 'Admin' - : 'Super Admin'} + ? t('admin', 'Admin') + : t('super_admin', 'Super Admin')} </div> {+myLevel > +getLevel(p.role) ? ( <div className="flex-1 flex justify-end"> diff --git a/apps/frontend/src/components/webhooks/webhooks.tsx b/apps/frontend/src/components/webhooks/webhooks.tsx index 89245a23..194dbb25 100644 --- a/apps/frontend/src/components/webhooks/webhooks.tsx +++ b/apps/frontend/src/components/webhooks/webhooks.tsx @@ -1,10 +1,11 @@ -import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; +'use client'; + +import React, { FC, Fragment, useCallback, useState } from 'react'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import useSWR from 'swr'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { Button } from '@gitroom/react/form/button'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { Input } from '@gitroom/react/form/input'; import { FormProvider, useForm } from 'react-hook-form'; import { array, object, string } from 'yup'; @@ -15,11 +16,13 @@ import { useToaster } from '@gitroom/react/toaster/toaster'; import clsx from 'clsx'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; + export const Webhooks: FC = () => { const fetch = useFetch(); const user = useUser(); const modal = useModals(); const toaster = useToaster(); + const t = useT(); const list = useCallback(async () => { return (await fetch('/webhooks')).json(); }, []); @@ -27,12 +30,12 @@ export const Webhooks: FC = () => { const addWebhook = useCallback( (data?: any) => () => { modal.openModal({ - title: data ? 'Update webhook' : 'Add webhook', + title: data ? t('update_webhook', 'Update webhook') : t('add_webhook', 'Add webhook'), withCloseButton: true, children: <AddOrEditWebhook data={data} reload={mutate} />, }); }, - [] + [t] ); const deleteHook = useCallback( (data: any) => async () => { @@ -49,14 +52,12 @@ export const Webhooks: FC = () => { method: 'DELETE', }); mutate(); - toaster.show('Webhook deleted successfully', 'success'); + toaster.show(t('webhook_deleted_successfully', 'Webhook deleted successfully'), 'success'); } }, [] ); - const t = useT(); - return ( <div className="flex flex-col"> <h3 className="text-[20px]"> @@ -116,13 +117,13 @@ const details = object().shape({ url: string().url().required(), integrations: array(), }); -const options = [ +const getWebhookOptions = (t: (key: string, fallback: string) => string) => [ { - label: 'All integrations', + label: t('all_integrations', 'All integrations'), value: 'all', }, { - label: 'Specific integrations', + label: t('specific_integrations', 'Specific integrations'), value: 'specific', }, ]; @@ -132,6 +133,8 @@ export const AddOrEditWebhook: FC<{ }> = (props) => { const { data, reload } = props; const fetch = useFetch(); + const t = useT(); + const options = getWebhookOptions(t); const [allIntegrations, setAllIntegrations] = useState( (data?.integrations?.length || 0) > 0 ? options[1] : options[0] ); @@ -184,8 +187,8 @@ export const AddOrEditWebhook: FC<{ }); toast.show( data?.id - ? 'Webhook updated successfully' - : 'Webhook added successfully', + ? t('webhook_updated_successfully', 'Webhook updated successfully') + : t('webhook_added_successfully', 'Webhook added successfully'), 'success' ); modal.closeAll(); @@ -195,7 +198,7 @@ export const AddOrEditWebhook: FC<{ ); const sendTest = useCallback(async () => { const url = form.getValues('url'); - toast.show('Webhook send', 'success'); + toast.show(t('webhook_sent', 'Webhook send'), 'success'); try { await fetch(`/webhooks/send?url=${encodeURIComponent(url)}`, { method: 'POST', @@ -238,8 +241,6 @@ export const AddOrEditWebhook: FC<{ } }, []); - const t = useT(); - return ( <FormProvider {...form}> <form onSubmit={form.handleSubmit(callBack)}> diff --git a/i18n.lock b/i18n.lock index 5eb7039d..a7b7dcb3 100644 --- a/i18n.lock +++ b/i18n.lock @@ -540,3 +540,127 @@ checksums: billing_pay_0_start_trial: 28e72154e6cce7541e707b35f3a67309 billing_pay_now: 50cb14454e1b2df4a2f83bf1ac799819 billing_per_month: 6293d01c3d13f6938d47285122bd1a48 + select_channels: 3aab82dc90ccafe55059c32c5a33c222 + start_a_new_chat: 65b9067206fa4e4929d48ce242d879a6 + your_assistant: bd6422d1c2a2ba461e1da169b082bf7b + agent_welcome_message: 21c75c6cdb64a5e2d4d9ac6a9b04b7ad + last_github_trending: 893235fc69e91abe15960108dcd77d7b + next_predicted_github_trending: 2e718a5c1acdad0c058440d0a70a0ea6 + repository: 28c2debe822a225389ff8710934abff4 + date: 56f41c5d30a76295bb087b20b7bee4c3 + total_stars: 28e8ab506d02f06489ba8cee96da6083 + total_forks: 79df39bca889feadf97b7963560574c6 + continue_with: 6f825c6ccb2974ef5289b0aa84f9aff7 + email_address: 0ee22bbbe989a0c61a18023407d12dc2 + email_already_exists: ad25f0ca7a16c0d37e566a9c090a0fd2 + google: 6cc462fb53d90d404f48d5a6695c1de1 + farcaster: b09af25fcb497663e8594b9fc514b969 + edit_autopost: 2cde36144cddda0acc069e6913e095f9 + add_autopost_title: d018d5d12c9f41c0b610da4b101d77e9 + webhook_deleted_successfully: fcefd247ec76a372002d2cffac3c5b0f + all_integrations: 7605d915dd24362c1cb9ec769dee3888 + specific_integrations: fb3040ce68e4f41a8ac5e9a53d69a1e5 + post_on_next_available_slot: abb1a1cd887b1e36f7b72e249b0e3e2e + post_immediately: ea58c7d158b37d944a894f90ac098eca + could_not_use_rss_feed: 9884cf220241440a4d635e5d39aa1c69 + rss_valid: 592dc400803f044bb77eb92e8f241acb + autopost_updated_successfully: eb9b6518aa38dd4011435fbd8358d570 + autopost_added_successfully: c83af96759f9baa9059984501ea33f73 + write_your_post_placeholder: 0f4d124dbef4685590d5a95af3b5df64 + select_or_upload_pictures_max_5: af063197b75e970f03303afcce5bde63 + you_can_drag_drop_pictures: d41ac7a74ca936f4b388af721830f9d9 + you_dont_have_any_media_yet: 59257169efa001e5dc0a7bf67867f77e + media_library: 2b05965bcc34712a764496206514c087 + media_settings: 6f1f271ae8845b19f961e54206dde3bf + media_editor: de25325f8f2dd32ada6fde72e657ab59 + close: 2c2e22f8424a1031de89063bd0022e16 + me: e0cdc99c6c88fe486dbd78d9d366b788 + noname: 90d26318cd738264fac16f6e59905d49 + password_reset_link_expired: fce5f6a7e85fdc08ef0e6fe8d04cac87 + invalid_api_key: cb0881df33e293f5f28e6b8677d24cc6 + could_not_connect_to_platform: f778f0b7c8e3583a010e748f0bd68a7a + web3_provider: d748b2fb275a0cc1263030377543762c + add_provider_title: edbf2eb94542bde00e1da93999e667a1 + profile_updated: 4a55423a795e43ff4bd1a8b3956de4b9 + sets: a8e00731555775ef06d2d8933898c457 + invitation_link_sent: 3d67ff64ec205998378cf7c8f124799f + send_invitation_link: 8eccac7afd966c2bf358b9e04fb02ee8 + copy_link: 57a37acfe6d7ed71d00fbbc8079fbb35 + are_you_sure_remove_team_member: fdd24783fdaefbfcd8150d51d088cc9d + admin: 90eb20f1400db82ab874744e47836dc6 + super_admin: 6f8651907beb1156db8c3bb5a7d5d791 + update_webhook: 7c761650a21097d9738cdef4a382a9d4 + add_webhook: f8fa42d8c95936075e05ad238fd03467 + webhook_updated_successfully: d67dc42810694fd1344a9a9545aca1e7 + webhook_added_successfully: 2d8e8d7a158ea8e4b65e67900363527b + webhook_sent: 6ced91337118dc0efbf07668ae843380 + today: 142173f9752e18e92109623a3ee68cad + channel_disconnected_click_to_reconnect: 404a3150eb00bf6dff15dab467ddeb3c + channel_disabled_upgrade_plan: 6ef222177df5034474096ad41375fd28 + channel_added: 2f77e6b11433d4dc3f44dc8ae1140999 + are_you_sure_disable_channel: 737398b5c5c3ca14b14eb3ba178f1781 + disable_channel_title: a2d862c419b9fff01f72481499431341 + channel_disabled: 67dbc2759aad3c5d3f67c09d7241645a + are_you_sure_delete_channel: ffd1e325a87cb148609263fe1b86effb + delete_channel_title: e179ddd5c85d4ad9bfc067107473fc04 + delete_posts_before_channel: 4248eca31cde87c71d03f3f787324288 + channel_deleted: b8aa2e7bd6ada3f5d51f5c6b3dca4d1d + channel_enabled: 26f6c39e1247322f9e63420a2703dbfd + time_table_slots: ba2e44486ebe947322acc78d3bc05a3a + channel_id_copied: 58bf6a74adf570a5daf233ce496149de + settings_updated: 260e10a61200b581616d766f3af5ae52 + customer_updated: ab17bcd115b14b72bb2e89f2b48883a4 + custom_url: f1e4e1c6a386d212abe94b937cf5e812 + picture: 14818ef364a0ecc8b738bdb23c46b3c3 + upgrade_required: 460dbea5061a1aba2080ffdecdf9544c + move_to_billing: e8fe4bf664cfb029e3e5ccb14cd4f86f + payment_required: 3acec87fe94c0bdb92b35e3081f45061 + no_content: 1d2af798c895527a7f8a4c72c2571fdd + select_customer_tooltip: 1cd2f6f08560b7ebcc9285e845557673 + customers: c31a0dfd0d92496ba6f5673781db24ef + hour: ec3113f22fc51d01f0c615c5496f8f87 + minutes: c5442938e948e2e3937fb812cf68869c + updated: 8aa8ff2dc2977ca4b269e80a513100b4 + change_bot_picture_title: 6ff9b68f0d29f9477a300b9042be0d4d + select_customer_label: 1cd2f6f08560b7ebcc9285e845557673 + start_typing: b350b435f6d1cacadcaee99c862f2a19 + choose_set_or_continue: 06611847f691e5374844289a4ed61059 + continue_without_set: 96b7adb0defee5f96a9f8cd89150710c + select_set: 6723a7be7156d364ceaea2aa709f8d61 + channel_settings: 8df6777277469c1fd88cc18dde2f1cc3 + post_needs_content_or_image: e7a0d63d2931ca540ceea25c6d4df7ec + please_fix_your_settings: 41c970bc73bb775e8b1882dfdbaad569 + shortlink_urls_question: 5308003901830e666ff9915b4855a9c9 + yes_shortlink_it: b784b6f9ce6d5eec451de7f3c7f1e01d + added_successfully: 5a1557f26138d3f80b6449d4570d47a9 + updated_successfully: fb4e4de59015fd30331e0f56c63918c5 + create_post_title: 06549e1cb8bbba2645f5ded68259684d + post_preview: fe8a07b5e03233de2e8dbedb205bd824 + check_circles_above: 18f66bbfa1607a87dff3b30a01639ad5 + create_output: 2c2908912452a1510fd67f9151c6f7f4 + assistant_initial_message: ac5658f3e6f50edf9f2e1ec331fc0bef + no_longer_global_mode: 166b528448bb464bfb7aff18c8eb6d0e + two_days: c0422dbabbbd18a7806dc2fa46a78ac6 + three_days: 543b7c228c228eca588939eba3071af2 + four_days: 29132e6b3311d11143ea8cf9fbffef2a + five_days: 87d5f0cb4569d52bd952007ae0a55ac2 + six_days: 988ffdb8c75a33301ae2aa598c911838 + two_weeks: 2535083efa302c3dde7d06cce2455b74 + repeat_post_every_label: f3e3fd4d95a0eeaf1eab0219278ae812 + add_new_tag: 911360b396ef6db7a24eb53134f0f7d2 + tag_name: 9368b5a047572b6051f334af5aa76819 + post_is_too_long: 7784c6ecca2c7e58665e83e219aff80b + your_post_should_have_at_least_one_character_or_one_image: e7a0d63d2931ca540ceea25c6d4df7ec + internal_edit: 04ed5d507cfed7c4f348df338c106809 + are_you_sure_go_back_to_global_mode: e7e0913ac6a8bdecd5a9349ad84d2f3c + yes_go_back_to_global_mode: 50484cd2f72d76bd6a02d5ac956ba466 + are_you_sure_delete_this_post: 25e67cb71ee64bf67af09ee20257655f + cant_edit_networks_when_creating_set: 10c389a15c85e82b69f3202bcd8fc527 + click_to_exit_global_editing: fec20b458bec4dcabf3efe65d1e5777b + edit_content: 13b729cc6459ecdc3ea84d476c6dcddd + editing_a_specific_network: 127d93025c413d0074553b55388c8656 + back_to_global: db0b2554e15d8a53ce09aed3085a7374 + delete_post_tooltip: bcf081392c76d2f2a8522944ddd36b78 + drop_files_here_to_upload: a7971302d02a19a461c519cffedfa5e5 + insert_emoji: 6e2ab0e239c0ee87d385b1cd17185e00 + write_something: bc9257cede62880c411001e61310ca43 diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index cf30d511..4aa1c9d5 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -40,16 +40,6 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { maxLength() { return 3000; } - - public override handleErrors(body: string): - | { - type: 'refresh-token' | 'bad-body' | 'retry'; - value: string; - } - | undefined { - - return undefined; - } async refreshToken(refresh_token: string): Promise<AuthTokenDetails> { const { access_token: accessToken, @@ -390,34 +380,17 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { }) ); - // Find the maximum dimensions across all images to use as page size - const maxWidth = Math.max(...imageData.map((data) => data.width)); - const maxHeight = Math.max(...imageData.map((data) => data.height)); - const pageSize = [maxWidth, maxHeight]; + // Use the dimensions of the first image for the PDF page size + // You could also use the largest dimensions if you prefer + const firstImageDimensions = imageData[0]; + const pageSize = [firstImageDimensions.width, firstImageDimensions.height]; - // Resize all images to fit within the page size while maintaining aspect ratio - // and centering them on a white background - const resizedImageBuffers = await Promise.all( - imageData.map(async (data) => { - // If image already matches page size, return as-is but convert to JPEG for consistency - if (data.width === maxWidth && data.height === maxHeight) { - return await sharp(data.buffer).jpeg({ quality: 95 }).toBuffer(); - } - - // Resize image to fit within page dimensions, then extend with white background to fill page - return await sharp(data.buffer) - .resize(maxWidth, maxHeight, { - fit: 'contain', - background: { r: 255, g: 255, b: 255, alpha: 1 }, - }) - .jpeg({ quality: 95 }) - .toBuffer(); - }) + // Convert images to PDF with exact image dimensions + const pdfStream = imageToPDF( + imageData.map((data) => data.buffer), + pageSize ); - // Convert images to PDF with consistent page dimensions - const pdfStream = imageToPDF(resizedImageBuffers, pageSize); - // Convert stream to buffer const pdfBuffer = await this.streamToBuffer(pdfStream); diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index ff17cde6..54d69655 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "يمكنك الإلغاء في أي وقت.", "billing_pay_0_start_trial": "ادفع 0 دولار اليوم - ابدأ تجربتك المجانية!", "billing_pay_now": "ادفع الآن", - "billing_per_month": "/ شهريًا" + "billing_per_month": "/ شهريًا", + "select_channels": "اختر القنوات", + "start_a_new_chat": "ابدأ محادثة جديدة", + "your_assistant": "مساعدك", + "agent_welcome_message": "مرحبًا، أنا وكيل Postiz الخاص بك 🙌🏻.\n\nيمكنني جدولة منشور أو عدة منشورات لعدة قنوات، وتوليد الصور والفيديوهات.\n\nيمكنك اختيار القنوات التي تريد استخدامها من القائمة اليسرى.\n\nيمكنك رؤية محادثاتك السابقة من القائمة اليمنى.\n\nيمكنك أيضًا استخدامي كخادم MCP، تحقق من الإعدادات >> واجهة برمجة التطبيقات العامة (Public API)", + "last_github_trending": "آخر الترندات على Github", + "next_predicted_github_trending": "الترند المتوقع القادم على Github", + "repository": "المستودع", + "date": "التاريخ", + "total_stars": "إجمالي النجوم", + "total_forks": "إجمالي التفرعات", + "continue_with": "المتابعة باستخدام", + "email_address": "عنوان البريد الإلكتروني", + "email_already_exists": "البريد الإلكتروني موجود بالفعل", + "google": "جوجل", + "farcaster": "فاركاستر", + "edit_autopost": "تعديل النشر التلقائي", + "add_autopost_title": "إضافة نشر تلقائي", + "webhook_deleted_successfully": "تم حذف الويب هوك بنجاح", + "all_integrations": "جميع التكاملات", + "specific_integrations": "تكاملات محددة", + "post_on_next_available_slot": "النشر في أول وقت متاح", + "post_immediately": "انشر فورًا", + "could_not_use_rss_feed": "تعذر استخدام موجز RSS هذا", + "rss_valid": "موجز RSS صالح!", + "autopost_updated_successfully": "تم تحديث النشر التلقائي بنجاح", + "autopost_added_successfully": "تمت إضافة النشر التلقائي بنجاح", + "write_your_post_placeholder": "اكتب منشورك...", + "select_or_upload_pictures_max_5": "اختر أو حمّل صورًا (بحد أقصى 5 في المرة الواحدة).", + "you_can_drag_drop_pictures": "يمكنك أيضًا سحب وإفلات الصور.", + "you_dont_have_any_media_yet": "ليس لديك أي وسائط بعد", + "media_library": "مكتبة الوسائط", + "media_settings": "إعدادات الوسائط", + "media_editor": "محرر الوسائط", + "close": "إغلاق", + "me": "أنا", + "noname": "بدون اسم", + "password_reset_link_expired": "انتهت صلاحية رابط إعادة تعيين كلمة المرور. يرجى المحاولة مرة أخرى.", + "invalid_api_key": "مفتاح API غير صالح", + "could_not_connect_to_platform": "تعذر الاتصال بالمنصة", + "web3_provider": "مزود Web3", + "add_provider_title": "إضافة مزود", + "profile_updated": "تم تحديث الملف الشخصي", + "sets": "مجموعات", + "invitation_link_sent": "تم إرسال رابط الدعوة", + "send_invitation_link": "إرسال رابط الدعوة", + "copy_link": "نسخ الرابط", + "are_you_sure_remove_team_member": "هل أنت متأكد أنك تريد إزالة هذا العضو من الفريق؟", + "admin": "مشرف", + "super_admin": "مشرف عام", + "update_webhook": "تحديث Webhook", + "add_webhook": "إضافة ويب هوك", + "webhook_updated_successfully": "تم تحديث الويب هوك بنجاح", + "webhook_added_successfully": "تمت إضافة الويب هوك بنجاح", + "webhook_sent": "تم إرسال الويب هوك", + "today": "اليوم", + "channel_disconnected_click_to_reconnect": "تم فصل القناة، انقر لإعادة الاتصال.", + "channel_disabled_upgrade_plan": "تم تعطيل هذه القناة، يرجى ترقية خطتك لتفعيلها.", + "channel_added": "تمت إضافة القناة", + "are_you_sure_disable_channel": "هل أنت متأكد أنك تريد تعطيل هذه القناة؟", + "disable_channel_title": "تعطيل القناة", + "channel_disabled": "تم تعطيل القناة", + "are_you_sure_delete_channel": "هل أنت متأكد أنك تريد حذف هذه القناة؟", + "delete_channel_title": "حذف القناة", + "delete_posts_before_channel": "يجب عليك حذف جميع المنشورات المرتبطة بهذه القناة قبل حذفها", + "channel_deleted": "تم حذف القناة", + "channel_enabled": "تم تفعيل القناة", + "time_table_slots": "فترات الجدول الزمني", + "channel_id_copied": "تم نسخ معرف القناة إلى الحافظة", + "settings_updated": "تم تحديث الإعدادات", + "customer_updated": "تم تحديث العميل", + "custom_url": "رابط مخصص", + "picture": "صورة", + "upgrade_required": "تحتاج إلى الترقية لاستخدام هذه الميزة", + "move_to_billing": "الانتقال إلى الفوترة", + "payment_required": "الدفع مطلوب", + "no_content": "لا يوجد محتوى", + "select_customer_tooltip": "اختر العميل", + "customers": "العملاء", + "hour": "ساعة", + "minutes": "دقائق", + "updated": "تم التحديث", + "change_bot_picture_title": "تغيير صورة البوت", + "select_customer_label": "اختر العميل", + "start_typing": "ابدأ الكتابة...", + "choose_set_or_continue": "اختر مجموعة أو تابع بدون واحدة", + "continue_without_set": "المتابعة بدون مجموعة", + "select_set": "اختر مجموعة", + "channel_settings": "الإعدادات", + "post_needs_content_or_image": "يجب أن يحتوي منشورك على حرف واحد على الأقل أو صورة واحدة.", + "please_fix_your_settings": "يرجى تصحيح إعداداتك", + "shortlink_urls_question": "هل تريد تقصير الروابط؟ سيسمح لك ذلك بالحصول على إحصائيات حول النقرات", + "yes_shortlink_it": "نعم، قصّر الرابط!", + "added_successfully": "تمت الإضافة بنجاح", + "updated_successfully": "تم التحديث بنجاح", + "create_post_title": "إنشاء منشور", + "post_preview": "معاينة المنشور", + "check_circles_above": "تحقق من الدوائر أعلاه", + "create_output": "إنشاء المخرجات", + "assistant_initial_message": "مرحبًا! يمكنني مساعدتك في تحسين منشوراتك على وسائل التواصل الاجتماعي.", + "no_longer_global_mode": "لم تعد في الوضع العالمي", + "two_days": "يومان", + "three_days": "ثلاثة أيام", + "four_days": "أربعة أيام", + "five_days": "خمسة أيام", + "six_days": "ستة أيام", + "two_weeks": "أسبوعان", + "repeat_post_every_label": "تكرار النشر كل", + "add_new_tag": "إضافة وسم جديد", + "tag_name": "الاسم", + "post_is_too_long": "المنشور طويل جدًا، يرجى تعديله", + "your_post_should_have_at_least_one_character_or_one_image": "يجب أن يحتوي منشورك على حرف واحد على الأقل أو صورة واحدة.", + "internal_edit": "تعديل داخلي", + "are_you_sure_go_back_to_global_mode": "هذا الإجراء لا يمكن التراجع عنه. هل أنت متأكد أنك تريد العودة إلى الوضع العام؟", + "yes_go_back_to_global_mode": "نعم، العودة إلى الوضع العام", + "are_you_sure_delete_this_post": "هل أنت متأكد أنك تريد حذف هذا المنشور؟", + "cant_edit_networks_when_creating_set": "لا يمكنك تعديل الشبكات أثناء إنشاء مجموعة", + "click_to_exit_global_editing": "انقر على هذا الزر للخروج من التحرير العام وتخصيص المنشور لهذه القناة", + "edit_content": "تعديل المحتوى", + "editing_a_specific_network": "تعديل شبكة محددة", + "back_to_global": "العودة إلى الوضع العام", + "delete_post_tooltip": "حذف المنشور", + "drop_files_here_to_upload": "أسقط ملفاتك هنا للتحميل", + "insert_emoji": "إدراج رمز تعبيري", + "write_something": "اكتب شيئًا …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index 5bde1d21..b57d2b4d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "যেকোনো সময় বাতিল করুন।", "billing_pay_0_start_trial": "আজ $0 দিন - আপনার ফ্রি ট্রায়াল শুরু করুন!", "billing_pay_now": "এখনই পেমেন্ট করুন", - "billing_per_month": "/ মাস" + "billing_per_month": "/ মাস", + "select_channels": "চ্যানেল নির্বাচন করুন", + "start_a_new_chat": "নতুন চ্যাট শুরু করুন", + "your_assistant": "আপনার সহকারী", + "agent_welcome_message": "হ্যালো, আমি আপনার Postiz এজেন্ট 🙌🏻।\n\nআমি এক বা একাধিক চ্যানেলে পোস্ট নির্ধারণ করতে পারি এবং ছবি ও ভিডিও তৈরি করতে পারি।\n\nআপনি বাম মেনু থেকে আপনার পছন্দের চ্যানেলগুলো নির্বাচন করতে পারেন।\n\nডান মেনু থেকে আপনি আপনার পূর্ববর্তী কথোপকথন দেখতে পারবেন।\n\nআপনি আমাকে MCP সার্ভার হিসেবেও ব্যবহার করতে পারেন, সেটিংস >> পাবলিক API দেখুন।", + "last_github_trending": "সর্বশেষ গিটহাব ট্রেন্ডিং", + "next_predicted_github_trending": "পরবর্তী অনুমানকৃত গিটহাব ট্রেন্ডিং", + "repository": "রিপোজিটরি", + "date": "তারিখ", + "total_stars": "মোট স্টার", + "total_forks": "মোট ফর্ক", + "continue_with": "চালিয়ে যান", + "email_address": "ইমেইল ঠিকানা", + "email_already_exists": "ইমেইল ইতিমধ্যে বিদ্যমান", + "google": "গুগল", + "farcaster": "ফারকাস্টার", + "edit_autopost": "অটোপোস্ট সম্পাদনা করুন", + "add_autopost_title": "অটোপোস্ট যোগ করুন", + "webhook_deleted_successfully": "ওয়েবহুক সফলভাবে মুছে ফেলা হয়েছে", + "all_integrations": "সব ইন্টিগ্রেশন", + "specific_integrations": "নির্দিষ্ট ইন্টিগ্রেশন", + "post_on_next_available_slot": "পরবর্তী উপলব্ধ স্লটে পোস্ট করুন", + "post_immediately": "সঙ্গে সঙ্গে পোস্ট করুন", + "could_not_use_rss_feed": "এই RSS ফিড ব্যবহার করা যায়নি", + "rss_valid": "RSS বৈধ!", + "autopost_updated_successfully": "অটোপোস্ট সফলভাবে আপডেট হয়েছে", + "autopost_added_successfully": "অটোপোস্ট সফলভাবে যোগ হয়েছে", + "write_your_post_placeholder": "আপনার পোস্ট লিখুন...", + "select_or_upload_pictures_max_5": "ছবি নির্বাচন করুন বা আপলোড করুন (একসাথে সর্বাধিক ৫টি)।", + "you_can_drag_drop_pictures": "আপনি চাইলে ছবি ড্র্যাগ ও ড্রপও করতে পারেন।", + "you_dont_have_any_media_yet": "আপনার কাছে এখনও কোনো মিডিয়া নেই", + "media_library": "মিডিয়া লাইব্রেরি", + "media_settings": "মিডিয়া সেটিংস", + "media_editor": "মিডিয়া এডিটর", + "close": "বন্ধ করুন", + "me": "আমি", + "noname": "নামহীন", + "password_reset_link_expired": "আপনার পাসওয়ার্ড রিসেট লিঙ্কের মেয়াদ শেষ হয়েছে। অনুগ্রহ করে আবার চেষ্টা করুন।", + "invalid_api_key": "অবৈধ API কী", + "could_not_connect_to_platform": "প্ল্যাটফর্মে সংযোগ করা যায়নি", + "web3_provider": "Web3 প্রদানকারী", + "add_provider_title": "প্রদানকারী যোগ করুন", + "profile_updated": "প্রোফাইল আপডেট হয়েছে", + "sets": "সেট", + "invitation_link_sent": "আমন্ত্রণ লিঙ্ক পাঠানো হয়েছে", + "send_invitation_link": "আমন্ত্রণ লিঙ্ক পাঠান", + "copy_link": "লিঙ্ক কপি করুন", + "are_you_sure_remove_team_member": "আপনি কি নিশ্চিত এই টিম সদস্যকে সরাতে চান?", + "admin": "অ্যাডমিন", + "super_admin": "সুপার অ্যাডমিন", + "update_webhook": "ওয়েবহুক আপডেট করুন", + "add_webhook": "ওয়েবহুক যোগ করুন", + "webhook_updated_successfully": "ওয়েবহুক সফলভাবে আপডেট হয়েছে", + "webhook_added_successfully": "ওয়েবহুক সফলভাবে যোগ হয়েছে", + "webhook_sent": "ওয়েবহুক পাঠানো হয়েছে", + "today": "আজ", + "channel_disconnected_click_to_reconnect": "চ্যানেল সংযোগ বিচ্ছিন্ন হয়েছে, পুনরায় সংযোগ করতে ক্লিক করুন।", + "channel_disabled_upgrade_plan": "এই চ্যানেলটি নিষ্ক্রিয়, এটি সক্রিয় করতে আপনার প্ল্যান আপগ্রেড করুন।", + "channel_added": "চ্যানেল যোগ হয়েছে", + "are_you_sure_disable_channel": "আপনি কি নিশ্চিত যে আপনি এই চ্যানেলটি নিষ্ক্রিয় করতে চান?", + "disable_channel_title": "চ্যানেল নিষ্ক্রিয় করুন", + "channel_disabled": "চ্যানেল নিষ্ক্রিয় হয়েছে", + "are_you_sure_delete_channel": "আপনি কি নিশ্চিত যে আপনি এই চ্যানেলটি মুছে ফেলতে চান?", + "delete_channel_title": "চ্যানেল মুছে ফেলুন", + "delete_posts_before_channel": "এই চ্যানেলটি মুছে ফেলার আগে আপনাকে এর সাথে যুক্ত সব পোস্ট মুছে ফেলতে হবে", + "channel_deleted": "চ্যানেল মুছে ফেলা হয়েছে", + "channel_enabled": "চ্যানেল সক্রিয় হয়েছে", + "time_table_slots": "সময়সূচির স্লট", + "channel_id_copied": "চ্যানেল আইডি ক্লিপবোর্ডে কপি হয়েছে", + "settings_updated": "সেটিংস আপডেট হয়েছে", + "customer_updated": "গ্রাহক আপডেট হয়েছে", + "custom_url": "কাস্টম URL", + "picture": "ছবি", + "upgrade_required": "এই ফিচারটি ব্যবহার করতে আপনাকে আপগ্রেড করতে হবে", + "move_to_billing": "বিলিং-এ যান", + "payment_required": "পেমেন্ট প্রয়োজন", + "no_content": "কোনো বিষয়বস্তু নেই", + "select_customer_tooltip": "গ্রাহক নির্বাচন করুন", + "customers": "গ্রাহকগণ", + "hour": "ঘণ্টা", + "minutes": "মিনিট", + "updated": "আপডেট হয়েছে", + "change_bot_picture_title": "বটের ছবি পরিবর্তন করুন", + "select_customer_label": "গ্রাহক নির্বাচন করুন", + "start_typing": "টাইপ করা শুরু করুন...", + "choose_set_or_continue": "একটি সেট নির্বাচন করুন অথবা সেট ছাড়াই চালিয়ে যান", + "continue_without_set": "সেট ছাড়াই চালিয়ে যান", + "select_set": "একটি সেট নির্বাচন করুন", + "channel_settings": "সেটিংস", + "post_needs_content_or_image": "আপনার পোস্টে অন্তত একটি অক্ষর বা একটি ছবি থাকতে হবে।", + "please_fix_your_settings": "অনুগ্রহ করে আপনার সেটিংস ঠিক করুন", + "shortlink_urls_question": "আপনি কি URL গুলো শর্টলিংক করতে চান? এতে আপনি ক্লিকের পরিসংখ্যান পেতে পারবেন", + "yes_shortlink_it": "হ্যাঁ, শর্টলিংক করুন!", + "added_successfully": "সফলভাবে যোগ হয়েছে", + "updated_successfully": "সফলভাবে আপডেট হয়েছে", + "create_post_title": "পোস্ট তৈরি করুন", + "post_preview": "পোস্টের প্রিভিউ", + "check_circles_above": "উপরে বৃত্তগুলো চেক করুন", + "create_output": "আউটপুট তৈরি করুন", + "assistant_initial_message": "হাই! আমি আপনাকে আপনার সোশ্যাল মিডিয়া পোস্টগুলো আরও সুন্দর করতে সাহায্য করতে পারি।", + "no_longer_global_mode": "আর গ্লোবাল মোডে নেই", + "two_days": "দুই দিন", + "three_days": "তিন দিন", + "four_days": "চার দিন", + "five_days": "পাঁচ দিন", + "six_days": "ছয় দিন", + "two_weeks": "দুই সপ্তাহ", + "repeat_post_every_label": "প্রতি কতদিন পর পোস্ট পুনরাবৃত্তি হবে", + "add_new_tag": "নতুন ট্যাগ যোগ করুন", + "tag_name": "নাম", + "post_is_too_long": "পোস্টটি খুব বড়, অনুগ্রহ করে ঠিক করুন", + "your_post_should_have_at_least_one_character_or_one_image": "আপনার পোস্টে অন্তত একটি অক্ষর বা একটি ছবি থাকতে হবে।", + "internal_edit": "অভ্যন্তরীণ সম্পাদনা", + "are_you_sure_go_back_to_global_mode": "এই কাজটি অপরিবর্তনীয়। আপনি কি নিশ্চিত যে আপনি গ্লোবাল মোডে ফিরে যেতে চান?", + "yes_go_back_to_global_mode": "হ্যাঁ, গ্লোবাল মোডে ফিরে যান", + "are_you_sure_delete_this_post": "আপনি কি নিশ্চিত যে আপনি এই পোস্টটি মুছে ফেলতে চান?", + "cant_edit_networks_when_creating_set": "সেট তৈরি করার সময় আপনি নেটওয়ার্ক সম্পাদনা করতে পারবেন না", + "click_to_exit_global_editing": "গ্লোবাল সম্পাদনা থেকে বেরিয়ে এই চ্যানেলের জন্য পোস্টটি কাস্টমাইজ করতে এই বোতামে ক্লিক করুন", + "edit_content": "বিষয়বস্তু সম্পাদনা করুন", + "editing_a_specific_network": "নির্দিষ্ট নেটওয়ার্ক সম্পাদনা", + "back_to_global": "গ্লোবালে ফিরে যান", + "delete_post_tooltip": "পোস্ট মুছুন", + "drop_files_here_to_upload": "আপলোড করতে আপনার ফাইলগুলো এখানে ছেড়ে দিন", + "insert_emoji": "ইমোজি যুক্ত করুন", + "write_something": "কিছু লিখুন …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index ba42367d..15e28284 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Jederzeit kündbar.", "billing_pay_0_start_trial": "Heute 0 $ zahlen – Starten Sie Ihre kostenlose Testphase!", "billing_pay_now": "Jetzt bezahlen", - "billing_per_month": "/ Monat" + "billing_per_month": "/ Monat", + "select_channels": "Kanäle auswählen", + "start_a_new_chat": "Neuen Chat starten", + "your_assistant": "Ihr Assistent", + "agent_welcome_message": "Hallo, ich bin Ihr Postiz-Agent 🙌🏻.\n\nIch kann einen oder mehrere Beiträge für mehrere Kanäle planen und Bilder sowie Videos generieren.\n\nSie können die gewünschten Kanäle im linken Menü auswählen.\n\nIhre bisherigen Unterhaltungen finden Sie im rechten Menü.\n\nSie können mich auch als MCP-Server verwenden, siehe Einstellungen >> Öffentliche API.", + "last_github_trending": "Letzte Github-Trends", + "next_predicted_github_trending": "Nächste vorhergesagte Github-Trends", + "repository": "Repository", + "date": "Datum", + "total_stars": "Gesamtanzahl Sterne", + "total_forks": "Gesamtanzahl Forks", + "continue_with": "Weiter mit", + "email_address": "E-Mail-Adresse", + "email_already_exists": "E-Mail existiert bereits", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Autopost bearbeiten", + "add_autopost_title": "Autopost hinzufügen", + "webhook_deleted_successfully": "Webhook erfolgreich gelöscht", + "all_integrations": "Alle Integrationen", + "specific_integrations": "Spezifische Integrationen", + "post_on_next_available_slot": "Im nächsten verfügbaren Zeitfenster posten", + "post_immediately": "Sofort posten", + "could_not_use_rss_feed": "Dieser RSS-Feed konnte nicht verwendet werden", + "rss_valid": "RSS gültig!", + "autopost_updated_successfully": "Autopost erfolgreich aktualisiert", + "autopost_added_successfully": "Autopost erfolgreich hinzugefügt", + "write_your_post_placeholder": "Schreibe deinen Beitrag...", + "select_or_upload_pictures_max_5": "Wähle Bilder aus oder lade sie hoch (maximal 5 auf einmal).", + "you_can_drag_drop_pictures": "Du kannst Bilder auch per Drag & Drop hinzufügen.", + "you_dont_have_any_media_yet": "Du hast noch keine Medien", + "media_library": "Medienbibliothek", + "media_settings": "Medieneinstellungen", + "media_editor": "Medieneditor", + "close": "Schließen", + "me": "Ich", + "noname": "Kein Name", + "password_reset_link_expired": "Dein Passwort-Zurücksetzungslink ist abgelaufen. Bitte versuche es erneut.", + "invalid_api_key": "Ungültiger API-Schlüssel", + "could_not_connect_to_platform": "Verbindung zur Plattform konnte nicht hergestellt werden", + "web3_provider": "Web3-Anbieter", + "add_provider_title": "Anbieter hinzufügen", + "profile_updated": "Profil aktualisiert", + "sets": "Sets", + "invitation_link_sent": "Einladungslink gesendet", + "send_invitation_link": "Einladungslink senden", + "copy_link": "Link kopieren", + "are_you_sure_remove_team_member": "Bist du sicher, dass du dieses Teammitglied entfernen möchtest?", + "admin": "Admin", + "super_admin": "Super-Admin", + "update_webhook": "Webhook aktualisieren", + "add_webhook": "Webhook hinzufügen", + "webhook_updated_successfully": "Webhook erfolgreich aktualisiert", + "webhook_added_successfully": "Webhook erfolgreich hinzugefügt", + "webhook_sent": "Webhook gesendet", + "today": "Heute", + "channel_disconnected_click_to_reconnect": "Kanal getrennt, klicken Sie zum Wiederverbinden.", + "channel_disabled_upgrade_plan": "Dieser Kanal ist deaktiviert, bitte aktualisieren Sie Ihren Plan, um ihn zu aktivieren.", + "channel_added": "Kanal hinzugefügt", + "are_you_sure_disable_channel": "Sind Sie sicher, dass Sie diesen Kanal deaktivieren möchten?", + "disable_channel_title": "Kanal deaktivieren", + "channel_disabled": "Kanal deaktiviert", + "are_you_sure_delete_channel": "Sind Sie sicher, dass Sie diesen Kanal löschen möchten?", + "delete_channel_title": "Kanal löschen", + "delete_posts_before_channel": "Sie müssen alle mit diesem Kanal verbundenen Beiträge löschen, bevor Sie ihn löschen können", + "channel_deleted": "Kanal gelöscht", + "channel_enabled": "Kanal aktiviert", + "time_table_slots": "Zeitplan-Slots", + "channel_id_copied": "Kanal-ID in die Zwischenablage kopiert", + "settings_updated": "Einstellungen aktualisiert", + "customer_updated": "Kunde aktualisiert", + "custom_url": "Benutzerdefinierte URL", + "picture": "Bild", + "upgrade_required": "Sie müssen ein Upgrade durchführen, um diese Funktion zu nutzen", + "move_to_billing": "Zur Abrechnung wechseln", + "payment_required": "Zahlung erforderlich", + "no_content": "kein Inhalt", + "select_customer_tooltip": "Kunden auswählen", + "customers": "Kunden", + "hour": "Stunde", + "minutes": "Minuten", + "updated": "Aktualisiert", + "change_bot_picture_title": "Bot-Bild ändern", + "select_customer_label": "Kunden auswählen", + "start_typing": "Tippen Sie hier...", + "choose_set_or_continue": "Wählen Sie ein Set oder fahren Sie ohne fort", + "continue_without_set": "Ohne Set fortfahren", + "select_set": "Set auswählen", + "channel_settings": "Einstellungen", + "post_needs_content_or_image": "Ihr Beitrag sollte mindestens ein Zeichen oder ein Bild enthalten.", + "please_fix_your_settings": "Bitte korrigieren Sie Ihre Einstellungen", + "shortlink_urls_question": "Möchten Sie die URLs kürzen? So erhalten Sie Statistiken über Klicks.", + "yes_shortlink_it": "Ja, bitte kürzen!", + "added_successfully": "Erfolgreich hinzugefügt", + "updated_successfully": "Erfolgreich aktualisiert", + "create_post_title": "Beitrag erstellen", + "post_preview": "Beitragsvorschau", + "check_circles_above": "Überprüfen Sie die Kreise oben", + "create_output": "Ausgabe erstellen", + "assistant_initial_message": "Hallo! Ich kann Ihnen helfen, Ihre Social-Media-Beiträge zu optimieren.", + "no_longer_global_mode": "Nicht mehr im globalen Modus", + "two_days": "Zwei Tage", + "three_days": "Drei Tage", + "four_days": "Vier Tage", + "five_days": "Fünf Tage", + "six_days": "Sechs Tage", + "two_weeks": "Zwei Wochen", + "repeat_post_every_label": "Beitrag wiederholen alle", + "add_new_tag": "Neues Schlagwort hinzufügen", + "tag_name": "Name", + "post_is_too_long": "Beitrag ist zu lang, bitte korrigieren", + "your_post_should_have_at_least_one_character_or_one_image": "Dein Beitrag sollte mindestens ein Zeichen oder ein Bild enthalten.", + "internal_edit": "Interne Bearbeitung", + "are_you_sure_go_back_to_global_mode": "Diese Aktion ist unwiderruflich. Bist du sicher, dass du in den globalen Modus zurückkehren möchtest?", + "yes_go_back_to_global_mode": "Ja, zum globalen Modus zurückkehren", + "are_you_sure_delete_this_post": "Bist du sicher, dass du diesen Beitrag löschen möchtest?", + "cant_edit_networks_when_creating_set": "Du kannst Netzwerke beim Erstellen eines Sets nicht bearbeiten.", + "click_to_exit_global_editing": "Klicke auf diese Schaltfläche, um die globale Bearbeitung zu beenden und den Beitrag für diesen Kanal anzupassen.", + "edit_content": "Inhalt bearbeiten", + "editing_a_specific_network": "Bearbeitung eines bestimmten Netzwerks", + "back_to_global": "Zurück zum globalen Modus", + "delete_post_tooltip": "Beitrag löschen", + "drop_files_here_to_upload": "Dateien hierher ziehen, um sie hochzuladen", + "insert_emoji": "Emoji einfügen", + "write_something": "Schreibe etwas …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index f7822cac..4e86bbd7 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -535,5 +535,132 @@ "billing_cancel_anytime_short": "Cancel anytime.", "billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!", "billing_pay_now": "Pay Now", - "billing_per_month": "/ month" + "billing_per_month": "/ month", + "select_channels": "Select Channels", + "start_a_new_chat": "Start a new chat", + "your_assistant": "Your Assistant", + "agent_welcome_message": "Hello, I am your Postiz agent 🙌🏻.\n\nI can schedule a post or multiple posts to multiple channels and generate pictures and videos.\n\nYou can select the channels you want to use from the left menu.\n\nYou can see your previous conversations from the right menu.\n\nYou can also use me as an MCP Server, check Settings >> Public API", + "last_github_trending": "Last Github Trending", + "next_predicted_github_trending": "Next Predicted GitHub Trending", + "repository": "Repository", + "date": "Date", + "total_stars": "Total Stars", + "total_forks": "Total Forks", + "continue_with": "Continue With", + "email_address": "Email Address", + "label_company": "Company", + "email_already_exists": "Email already exists", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Edit Autopost", + "add_autopost_title": "Add Autopost", + "webhook_deleted_successfully": "Webhook deleted successfully", + "all_integrations": "All integrations", + "specific_integrations": "Specific integrations", + "post_on_next_available_slot": "Post on the next available slot", + "post_immediately": "Post Immediately", + "could_not_use_rss_feed": "Could not use this RSS feed", + "rss_valid": "RSS valid!", + "autopost_updated_successfully": "Autopost updated successfully", + "autopost_added_successfully": "Autopost added successfully", + "write_your_post_placeholder": "Write your post...", + "select_or_upload_pictures_max_5": "Select or upload pictures (maximum 5 at a time).", + "you_can_drag_drop_pictures": "You can also drag & drop pictures.", + "you_dont_have_any_media_yet": "You don't have any media yet", + "media_library": "Media Library", + "media_settings": "Media Settings", + "media_editor": "Media Editor", + "close": "Close", + "me": "Me", + "noname": "Noname", + "password_reset_link_expired": "Your password reset link has expired. Please try again.", + "invalid_api_key": "Invalid API key", + "could_not_connect_to_platform": "Could not connect to the platform", + "web3_provider": "Web3 provider", + "add_provider_title": "Add Provider", + "profile_updated": "Profile updated", + "sets": "Sets", + "email_address": "Email Address", + "invitation_link_sent": "Invitation link sent", + "send_invitation_link": "Send Invitation Link", + "copy_link": "Copy Link", + "are_you_sure_remove_team_member": "Are you sure you want to remove this team member?", + "admin": "Admin", + "super_admin": "Super Admin", + "update_webhook": "Update webhook", + "add_webhook": "Add webhook", + "webhook_updated_successfully": "Webhook updated successfully", + "webhook_added_successfully": "Webhook added successfully", + "webhook_sent": "Webhook send", + "today": "Today", + "channel_disconnected_click_to_reconnect": "Channel disconnected, click to reconnect.", + "channel_disabled_upgrade_plan": "This channel is disabled, please upgrade your plan to enable it.", + "channel_added": "Channel added", + "are_you_sure_disable_channel": "Are you sure you want to disable this channel?", + "disable_channel_title": "Disable Channel", + "channel_disabled": "Channel Disabled", + "are_you_sure_delete_channel": "Are you sure you want to delete this channel?", + "delete_channel_title": "Delete Channel", + "delete_posts_before_channel": "You have to delete all the posts associated with this channel before deleting it", + "channel_deleted": "Channel Deleted", + "channel_enabled": "Channel Enabled", + "time_table_slots": "Time Table Slots", + "channel_id_copied": "Channel ID copied to clipboard", + "settings_updated": "Settings Updated", + "customer_updated": "Customer Updated", + "custom_url": "Custom URL", + "picture": "Picture", + "upgrade_required": "You need to upgrade to use this feature", + "move_to_billing": "Move to billing", + "payment_required": "Payment Required", + "no_content": "no content", + "select_customer_tooltip": "Select Customer", + "customers": "Customers", + "hour": "Hour", + "minutes": "Minutes", + "updated": "Updated", + "change_bot_picture_title": "Change Bot Picture", + "select_customer_label": "Select Customer", + "start_typing": "Start typing...", + "choose_set_or_continue": "Choose a set or continue without one", + "continue_without_set": "Continue without set", + "select_set": "Select a Set", + "channel_settings": "Settings", + "post_needs_content_or_image": "Your post should have at least one character or one image.", + "please_fix_your_settings": "Please fix your settings", + "shortlink_urls_question": "Do you want to shortlink the URLs? it will let you get statistics over clicks", + "yes_shortlink_it": "Yes, shortlink it!", + "added_successfully": "Added successfully", + "updated_successfully": "Updated successfully", + "create_post_title": "Create Post", + "post_preview": "Post Preview", + "check_circles_above": "Check the circles above", + "create_output": "Create output", + "assistant_initial_message": "Hi! I can help you to refine your social media posts.", + "no_longer_global_mode": "No longer in global mode", + "two_days": "Two Days", + "three_days": "Three Days", + "four_days": "Four Days", + "five_days": "Five Days", + "six_days": "Six Days", + "two_weeks": "Two Weeks", + "repeat_post_every_label": "Repeat Post Every", + "add_new_tag": "Add New Tag", + "tag_name": "Name", + "post_is_too_long": "post is too long, please fix it", + "your_post_should_have_at_least_one_character_or_one_image": "Your post should have at least one character or one image.", + "internal_edit": "Internal Edit", + "are_you_sure_go_back_to_global_mode": "This action is irreversible. Are you sure you want to go back to global mode?", + "yes_go_back_to_global_mode": "Yes, go back to global mode", + "are_you_sure_delete_this_post": "Are you sure you want to delete this post?", + "yes_delete_it": "Yes, delete it!", + "cant_edit_networks_when_creating_set": "You can't edit networks when creating a set", + "click_to_exit_global_editing": "Click this button to exit global editing and customize the post for this channel", + "edit_content": "Edit content", + "editing_a_specific_network": "Editing a Specific Network", + "back_to_global": "Back to global", + "delete_post_tooltip": "Delete Post", + "drop_files_here_to_upload": "Drop your files here to upload", + "insert_emoji": "Insert Emoji", + "write_something": "Write something …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index 88a130b6..c9b6afe6 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Cancela en cualquier momento.", "billing_pay_0_start_trial": "¡Paga $0 hoy - Comienza tu prueba gratis!", "billing_pay_now": "Pagar ahora", - "billing_per_month": "/ mes" + "billing_per_month": "/ mes", + "select_channels": "Seleccionar canales", + "start_a_new_chat": "Iniciar un nuevo chat", + "your_assistant": "Tu Asistente", + "agent_welcome_message": "Hola, soy tu agente de Postiz 🙌🏻.\n\nPuedo programar una publicación o varias publicaciones en múltiples canales y generar imágenes y videos.\n\nPuedes seleccionar los canales que deseas usar desde el menú de la izquierda.\n\nPuedes ver tus conversaciones anteriores desde el menú de la derecha.\n\nTambién puedes usarme como un servidor MCP, revisa Configuración >> API Pública", + "last_github_trending": "Últimas tendencias de Github", + "next_predicted_github_trending": "Próximas tendencias previstas de GitHub", + "repository": "Repositorio", + "date": "Fecha", + "total_stars": "Estrellas totales", + "total_forks": "Forks totales", + "continue_with": "Continuar con", + "email_address": "Dirección de correo electrónico", + "email_already_exists": "El correo electrónico ya existe", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Editar autopublicación", + "add_autopost_title": "Agregar autopublicación", + "webhook_deleted_successfully": "Webhook eliminado correctamente", + "all_integrations": "Todas las integraciones", + "specific_integrations": "Integraciones específicas", + "post_on_next_available_slot": "Publicar en el siguiente espacio disponible", + "post_immediately": "Publicar inmediatamente", + "could_not_use_rss_feed": "No se pudo usar este feed RSS", + "rss_valid": "¡RSS válido!", + "autopost_updated_successfully": "Autopublicación actualizada correctamente", + "autopost_added_successfully": "Autopublicación añadida con éxito", + "write_your_post_placeholder": "Escribe tu publicación...", + "select_or_upload_pictures_max_5": "Selecciona o sube imágenes (máximo 5 a la vez).", + "you_can_drag_drop_pictures": "También puedes arrastrar y soltar imágenes.", + "you_dont_have_any_media_yet": "Aún no tienes ningún medio", + "media_library": "Biblioteca de medios", + "media_settings": "Configuración de medios", + "media_editor": "Editor de medios", + "close": "Cerrar", + "me": "Yo", + "noname": "Sin nombre", + "password_reset_link_expired": "Tu enlace para restablecer la contraseña ha expirado. Por favor, inténtalo de nuevo.", + "invalid_api_key": "Clave API inválida", + "could_not_connect_to_platform": "No se pudo conectar a la plataforma", + "web3_provider": "Proveedor Web3", + "add_provider_title": "Agregar proveedor", + "profile_updated": "Perfil actualizado", + "sets": "Conjuntos", + "invitation_link_sent": "Enlace de invitación enviado", + "send_invitation_link": "Enviar enlace de invitación", + "copy_link": "Copiar enlace", + "are_you_sure_remove_team_member": "¿Estás seguro de que deseas eliminar a este miembro del equipo?", + "admin": "Administrador", + "super_admin": "Súper administrador", + "update_webhook": "Actualizar webhook", + "add_webhook": "Agregar webhook", + "webhook_updated_successfully": "Webhook actualizado correctamente", + "webhook_added_successfully": "Webhook agregado correctamente", + "webhook_sent": "Webhook enviado", + "today": "Hoy", + "channel_disconnected_click_to_reconnect": "Canal desconectado, haz clic para reconectar.", + "channel_disabled_upgrade_plan": "Este canal está deshabilitado, por favor actualiza tu plan para habilitarlo.", + "channel_added": "Canal agregado", + "are_you_sure_disable_channel": "¿Estás seguro de que deseas deshabilitar este canal?", + "disable_channel_title": "Deshabilitar canal", + "channel_disabled": "Canal deshabilitado", + "are_you_sure_delete_channel": "¿Estás seguro de que deseas eliminar este canal?", + "delete_channel_title": "Eliminar canal", + "delete_posts_before_channel": "Debes eliminar todas las publicaciones asociadas a este canal antes de eliminarlo", + "channel_deleted": "Canal eliminado", + "channel_enabled": "Canal habilitado", + "time_table_slots": "Intervalos de horario", + "channel_id_copied": "ID del canal copiado al portapapeles", + "settings_updated": "Configuración actualizada", + "customer_updated": "Cliente actualizado", + "custom_url": "URL personalizada", + "picture": "Imagen", + "upgrade_required": "Necesitas actualizar para usar esta función", + "move_to_billing": "Ir a facturación", + "payment_required": "Pago requerido", + "no_content": "sin contenido", + "select_customer_tooltip": "Seleccionar cliente", + "customers": "Clientes", + "hour": "Hora", + "minutes": "Minutos", + "updated": "Actualizado", + "change_bot_picture_title": "Cambiar imagen del bot", + "select_customer_label": "Seleccionar cliente", + "start_typing": "Empieza a escribir...", + "choose_set_or_continue": "Elige un conjunto o continúa sin uno", + "continue_without_set": "Continuar sin conjunto", + "select_set": "Seleccionar un conjunto", + "channel_settings": "Configuración", + "post_needs_content_or_image": "Tu publicación debe tener al menos un carácter o una imagen.", + "please_fix_your_settings": "Por favor, corrige tu configuración", + "shortlink_urls_question": "¿Quieres acortar las URLs? Esto te permitirá obtener estadísticas sobre los clics", + "yes_shortlink_it": "¡Sí, acórtala!", + "added_successfully": "Agregado exitosamente", + "updated_successfully": "Actualizado exitosamente", + "create_post_title": "Crear publicación", + "post_preview": "Vista previa de la publicación", + "check_circles_above": "Revisa los círculos de arriba", + "create_output": "Crear resultado", + "assistant_initial_message": "¡Hola! Puedo ayudarte a mejorar tus publicaciones en redes sociales.", + "no_longer_global_mode": "Ya no estás en modo global", + "two_days": "Dos días", + "three_days": "Tres días", + "four_days": "Cuatro días", + "five_days": "Cinco días", + "six_days": "Seis días", + "two_weeks": "Dos semanas", + "repeat_post_every_label": "Repetir publicación cada", + "add_new_tag": "Agregar nueva etiqueta", + "tag_name": "Nombre", + "post_is_too_long": "la publicación es demasiado larga, por favor arréglala", + "your_post_should_have_at_least_one_character_or_one_image": "Tu publicación debe tener al menos un carácter o una imagen.", + "internal_edit": "Edición interna", + "are_you_sure_go_back_to_global_mode": "Esta acción es irreversible. ¿Estás seguro de que quieres volver al modo global?", + "yes_go_back_to_global_mode": "Sí, volver al modo global", + "are_you_sure_delete_this_post": "¿Estás seguro de que quieres eliminar esta publicación?", + "cant_edit_networks_when_creating_set": "No puedes editar redes al crear un conjunto", + "click_to_exit_global_editing": "Haz clic en este botón para salir de la edición global y personalizar la publicación para este canal", + "edit_content": "Editar contenido", + "editing_a_specific_network": "Editando una red específica", + "back_to_global": "Volver a global", + "delete_post_tooltip": "Eliminar publicación", + "drop_files_here_to_upload": "Suelta tus archivos aquí para subirlos", + "insert_emoji": "Insertar emoji", + "write_something": "Escribe algo…" } diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index 80d40742..5fa04566 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Annulez à tout moment.", "billing_pay_0_start_trial": "Payez 0 $ aujourd'hui - Commencez votre essai gratuit !", "billing_pay_now": "Payer maintenant", - "billing_per_month": "/ mois" + "billing_per_month": "/ mois", + "select_channels": "Sélectionner les canaux", + "start_a_new_chat": "Démarrer une nouvelle conversation", + "your_assistant": "Votre assistant", + "agent_welcome_message": "Bonjour, je suis votre agent Postiz 🙌🏻.\n\nJe peux programmer une ou plusieurs publications sur plusieurs canaux et générer des images et des vidéos.\n\nVous pouvez sélectionner les canaux que vous souhaitez utiliser dans le menu de gauche.\n\nVous pouvez voir vos conversations précédentes dans le menu de droite.\n\nVous pouvez également m'utiliser comme serveur MCP, consultez Paramètres >> API publique", + "last_github_trending": "Dernières tendances GitHub", + "next_predicted_github_trending": "Prochaines tendances GitHub prévues", + "repository": "Dépôt", + "date": "Date", + "total_stars": "Nombre total d'étoiles", + "total_forks": "Nombre total de forks", + "continue_with": "Continuer avec", + "email_address": "Adresse e-mail", + "email_already_exists": "L'adresse e-mail existe déjà", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Modifier l'autopost", + "add_autopost_title": "Ajouter un autopost", + "webhook_deleted_successfully": "Webhook supprimé avec succès", + "all_integrations": "Toutes les intégrations", + "specific_integrations": "Intégrations spécifiques", + "post_on_next_available_slot": "Publier au prochain créneau disponible", + "post_immediately": "Publier immédiatement", + "could_not_use_rss_feed": "Impossible d'utiliser ce flux RSS", + "rss_valid": "RSS valide !", + "autopost_updated_successfully": "Autopost mis à jour avec succès", + "autopost_added_successfully": "Publication automatique ajoutée avec succès", + "write_your_post_placeholder": "Écrivez votre publication...", + "select_or_upload_pictures_max_5": "Sélectionnez ou téléversez des images (maximum 5 à la fois).", + "you_can_drag_drop_pictures": "Vous pouvez aussi glisser-déposer des images.", + "you_dont_have_any_media_yet": "Vous n'avez pas encore de média", + "media_library": "Bibliothèque de médias", + "media_settings": "Paramètres des médias", + "media_editor": "Éditeur de médias", + "close": "Fermer", + "me": "Moi", + "noname": "Sans nom", + "password_reset_link_expired": "Votre lien de réinitialisation du mot de passe a expiré. Veuillez réessayer.", + "invalid_api_key": "Clé API invalide", + "could_not_connect_to_platform": "Impossible de se connecter à la plateforme", + "web3_provider": "Fournisseur Web3", + "add_provider_title": "Ajouter un fournisseur", + "profile_updated": "Profil mis à jour", + "sets": "Ensembles", + "invitation_link_sent": "Lien d'invitation envoyé", + "send_invitation_link": "Envoyer le lien d'invitation", + "copy_link": "Copier le lien", + "are_you_sure_remove_team_member": "Êtes-vous sûr de vouloir retirer ce membre de l'équipe ?", + "admin": "Admin", + "super_admin": "Super admin", + "update_webhook": "Mettre à jour le webhook", + "add_webhook": "Ajouter un webhook", + "webhook_updated_successfully": "Webhook mis à jour avec succès", + "webhook_added_successfully": "Webhook ajouté avec succès", + "webhook_sent": "Webhook envoyé", + "today": "Aujourd'hui", + "channel_disconnected_click_to_reconnect": "Canal déconnecté, cliquez pour reconnecter.", + "channel_disabled_upgrade_plan": "Ce canal est désactivé, veuillez mettre à niveau votre abonnement pour l'activer.", + "channel_added": "Canal ajouté", + "are_you_sure_disable_channel": "Êtes-vous sûr de vouloir désactiver ce canal ?", + "disable_channel_title": "Désactiver le canal", + "channel_disabled": "Canal désactivé", + "are_you_sure_delete_channel": "Êtes-vous sûr de vouloir supprimer ce canal ?", + "delete_channel_title": "Supprimer le canal", + "delete_posts_before_channel": "Vous devez supprimer tous les messages associés à ce canal avant de le supprimer", + "channel_deleted": "Canal supprimé", + "channel_enabled": "Canal activé", + "time_table_slots": "Créneaux horaires", + "channel_id_copied": "ID du canal copié dans le presse-papiers", + "settings_updated": "Paramètres mis à jour", + "customer_updated": "Client mis à jour", + "custom_url": "URL personnalisée", + "picture": "Image", + "upgrade_required": "Vous devez mettre à niveau pour utiliser cette fonctionnalité", + "move_to_billing": "Aller à la facturation", + "payment_required": "Paiement requis", + "no_content": "aucun contenu", + "select_customer_tooltip": "Sélectionner un client", + "customers": "Clients", + "hour": "Heure", + "minutes": "Minutes", + "updated": "Mis à jour", + "change_bot_picture_title": "Changer l'image du bot", + "select_customer_label": "Sélectionner un client", + "start_typing": "Commencez à taper...", + "choose_set_or_continue": "Choisissez un ensemble ou continuez sans", + "continue_without_set": "Continuer sans ensemble", + "select_set": "Sélectionner un ensemble", + "channel_settings": "Paramètres", + "post_needs_content_or_image": "Votre publication doit contenir au moins un caractère ou une image.", + "please_fix_your_settings": "Veuillez corriger vos paramètres", + "shortlink_urls_question": "Voulez-vous raccourcir les URL ? Cela vous permettra d'obtenir des statistiques sur les clics.", + "yes_shortlink_it": "Oui, raccourcissez-les !", + "added_successfully": "Ajouté avec succès", + "updated_successfully": "Mis à jour avec succès", + "create_post_title": "Créer une publication", + "post_preview": "Aperçu de la publication", + "check_circles_above": "Cochez les cercles ci-dessus", + "create_output": "Créer la sortie", + "assistant_initial_message": "Bonjour ! Je peux vous aider à améliorer vos publications sur les réseaux sociaux.", + "no_longer_global_mode": "N'est plus en mode global", + "two_days": "Deux jours", + "three_days": "Trois jours", + "four_days": "Quatre jours", + "five_days": "Cinq jours", + "six_days": "Six jours", + "two_weeks": "Deux semaines", + "repeat_post_every_label": "Répéter la publication tous les", + "add_new_tag": "Ajouter un nouveau tag", + "tag_name": "Nom", + "post_is_too_long": "le message est trop long, veuillez le corriger", + "your_post_should_have_at_least_one_character_or_one_image": "Votre publication doit contenir au moins un caractère ou une image.", + "internal_edit": "Modification interne", + "are_you_sure_go_back_to_global_mode": "Cette action est irréversible. Êtes-vous sûr de vouloir revenir au mode global ?", + "yes_go_back_to_global_mode": "Oui, revenir au mode global", + "are_you_sure_delete_this_post": "Êtes-vous sûr de vouloir supprimer cette publication ?", + "cant_edit_networks_when_creating_set": "Vous ne pouvez pas modifier les réseaux lors de la création d’un ensemble", + "click_to_exit_global_editing": "Cliquez sur ce bouton pour quitter l’édition globale et personnaliser la publication pour ce canal", + "edit_content": "Modifier le contenu", + "editing_a_specific_network": "Modification d’un réseau spécifique", + "back_to_global": "Retour au global", + "delete_post_tooltip": "Supprimer la publication", + "drop_files_here_to_upload": "Déposez vos fichiers ici pour les télécharger", + "insert_emoji": "Insérer un emoji", + "write_something": "Écrivez quelque chose…" } diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index c6728b11..6456456c 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "ניתן לבטל בכל עת.", "billing_pay_0_start_trial": "שלם 0$ היום - התחל את תקופת הניסיון שלך!", "billing_pay_now": "שלם עכשיו", - "billing_per_month": "/ חודש" + "billing_per_month": "/ חודש", + "select_channels": "בחר ערוצים", + "start_a_new_chat": "התחל שיחה חדשה", + "your_assistant": "העוזר שלך", + "agent_welcome_message": "שלום, אני הסוכן של Postiz שלך 🙌🏻.\n\nאני יכול לתזמן פוסט או מספר פוסטים למספר ערוצים וליצור תמונות וסרטונים.\n\nאתה יכול לבחור את הערוצים שברצונך להשתמש בהם מהתפריט השמאלי.\n\nאתה יכול לראות את השיחות הקודמות שלך מהתפריט הימני.\n\nאתה יכול גם להשתמש בי כשרת MCP, בדוק הגדרות >> API ציבורי", + "last_github_trending": "הטרנדים האחרונים ב-GitHub", + "next_predicted_github_trending": "הטרנד הבא הצפוי ב-GitHub", + "repository": "מאגר", + "date": "תאריך", + "total_stars": "סך כל הכוכבים", + "total_forks": "סך כל הפורקים", + "continue_with": "המשך עם", + "email_address": "כתובת אימייל", + "email_already_exists": "האימייל כבר קיים", + "google": "גוגל", + "farcaster": "Farcaster", + "edit_autopost": "ערוך פרסום אוטומטי", + "add_autopost_title": "הוסף פרסום אוטומטי", + "webhook_deleted_successfully": "ה-Webhook נמחק בהצלחה", + "all_integrations": "כל האינטגרציות", + "specific_integrations": "אינטגרציות מסוימות", + "post_on_next_available_slot": "פרסם במועד הפנוי הבא", + "post_immediately": "פרסם מיד", + "could_not_use_rss_feed": "לא ניתן היה להשתמש ב-RSS זה", + "rss_valid": "ה-RSS תקין!", + "autopost_updated_successfully": "הפרסום האוטומטי עודכן בהצלחה", + "autopost_added_successfully": "הפוסט האוטומטי נוסף בהצלחה", + "write_your_post_placeholder": "כתוב את הפוסט שלך...", + "select_or_upload_pictures_max_5": "בחר או העלה תמונות (מקסימום 5 בכל פעם).", + "you_can_drag_drop_pictures": "ניתן גם לגרור ולשחרר תמונות.", + "you_dont_have_any_media_yet": "עדיין אין לך מדיה", + "media_library": "ספריית מדיה", + "media_settings": "הגדרות מדיה", + "media_editor": "עורך מדיה", + "close": "סגור", + "me": "אני", + "noname": "ללא שם", + "password_reset_link_expired": "קישור איפוס הסיסמה שלך פג תוקף. נסה שוב.", + "invalid_api_key": "מפתח API לא תקין", + "could_not_connect_to_platform": "לא ניתן להתחבר לפלטפורמה", + "web3_provider": "ספק Web3", + "add_provider_title": "הוסף ספק", + "profile_updated": "הפרופיל עודכן", + "sets": "סטים", + "invitation_link_sent": "קישור ההזמנה נשלח", + "send_invitation_link": "שלח קישור הזמנה", + "copy_link": "העתק קישור", + "are_you_sure_remove_team_member": "האם אתה בטוח שברצונך להסיר את חבר הצוות הזה?", + "admin": "מנהל", + "super_admin": "מנהל ראשי", + "update_webhook": "עדכן Webhook", + "add_webhook": "הוסף וובהוק", + "webhook_updated_successfully": "הוובהוק עודכן בהצלחה", + "webhook_added_successfully": "הוובהוק נוסף בהצלחה", + "webhook_sent": "וובהוק נשלח", + "today": "היום", + "channel_disconnected_click_to_reconnect": "הערוץ מנותק, לחץ כדי להתחבר מחדש.", + "channel_disabled_upgrade_plan": "הערוץ הזה מושבת, אנא שדרג את התוכנית שלך כדי להפעיל אותו.", + "channel_added": "הערוץ נוסף", + "are_you_sure_disable_channel": "האם אתה בטוח שברצונך להשבית את הערוץ הזה?", + "disable_channel_title": "השבת ערוץ", + "channel_disabled": "הערוץ הושבת", + "are_you_sure_delete_channel": "האם אתה בטוח שברצונך למחוק את הערוץ הזה?", + "delete_channel_title": "מחק ערוץ", + "delete_posts_before_channel": "עליך למחוק את כל הפוסטים המשויכים לערוץ הזה לפני מחיקתו", + "channel_deleted": "הערוץ נמחק", + "channel_enabled": "הערוץ הופעל", + "time_table_slots": "משבצות לוח זמנים", + "channel_id_copied": "מזהה הערוץ הועתק ללוח", + "settings_updated": "ההגדרות עודכנו", + "customer_updated": "הלקוח עודכן", + "custom_url": "כתובת URL מותאמת אישית", + "picture": "תמונה", + "upgrade_required": "עליך לשדרג כדי להשתמש בתכונה זו", + "move_to_billing": "עבור לחיוב", + "payment_required": "נדרש תשלום", + "no_content": "אין תוכן", + "select_customer_tooltip": "בחר לקוח", + "customers": "לקוחות", + "hour": "שעה", + "minutes": "דקות", + "updated": "עודכן", + "change_bot_picture_title": "שנה תמונת בוט", + "select_customer_label": "בחר לקוח", + "start_typing": "התחל להקליד...", + "choose_set_or_continue": "בחר סט או המשך בלעדיו", + "continue_without_set": "המשך ללא סט", + "select_set": "בחר סט", + "channel_settings": "הגדרות", + "post_needs_content_or_image": "הפוסט שלך צריך לכלול לפחות תו אחד או תמונה אחת.", + "please_fix_your_settings": "אנא תקן את ההגדרות שלך", + "shortlink_urls_question": "האם ברצונך לקצר את הקישורים? זה יאפשר לך לקבל סטטיסטיקות על הקלקות", + "yes_shortlink_it": "כן, קצר את זה!", + "added_successfully": "נוסף בהצלחה", + "updated_successfully": "עודכן בהצלחה", + "create_post_title": "צור פוסט", + "post_preview": "תצוגה מקדימה של הפוסט", + "check_circles_above": "סמן את העיגולים למעלה", + "create_output": "צור פלט", + "assistant_initial_message": "היי! אני יכול לעזור לך לשפר את הפוסטים שלך ברשתות החברתיות.", + "no_longer_global_mode": "לא במצב גלובלי יותר", + "two_days": "יומיים", + "three_days": "שלושה ימים", + "four_days": "ארבעה ימים", + "five_days": "חמישה ימים", + "six_days": "שישה ימים", + "two_weeks": "שבועיים", + "repeat_post_every_label": "חזור על הפוסט כל", + "add_new_tag": "הוסף תגית חדשה", + "tag_name": "שם", + "post_is_too_long": "הפוסט ארוך מדי, אנא תקן אותו", + "your_post_should_have_at_least_one_character_or_one_image": "הפוסט שלך צריך לכלול לפחות תו אחד או תמונה אחת.", + "internal_edit": "עריכה פנימית", + "are_you_sure_go_back_to_global_mode": "פעולה זו אינה ניתנת לביטול. האם אתה בטוח שברצונך לחזור למצב גלובלי?", + "yes_go_back_to_global_mode": "כן, חזור למצב גלובלי", + "are_you_sure_delete_this_post": "האם אתה בטוח שברצונך למחוק את הפוסט הזה?", + "cant_edit_networks_when_creating_set": "לא ניתן לערוך רשתות בעת יצירת סט", + "click_to_exit_global_editing": "לחץ על כפתור זה כדי לצאת מעריכה גלובלית ולהתאים את הפוסט לערוץ זה", + "edit_content": "ערוך תוכן", + "editing_a_specific_network": "עריכה של רשת מסוימת", + "back_to_global": "חזרה לגלובלי", + "delete_post_tooltip": "מחק פוסט", + "drop_files_here_to_upload": "גרור קבצים לכאן להעלאה", + "insert_emoji": "הוסף אימוג'י", + "write_something": "כתוב משהו…" } diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index d56bd2be..63357a4d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Annulla in qualsiasi momento.", "billing_pay_0_start_trial": "Paga 0€ oggi - Inizia la tua prova gratuita!", "billing_pay_now": "Paga ora", - "billing_per_month": "/ mese" + "billing_per_month": "/ mese", + "select_channels": "Seleziona canali", + "start_a_new_chat": "Inizia una nuova chat", + "your_assistant": "Il tuo assistente", + "agent_welcome_message": "Ciao, sono il tuo agente Postiz 🙌🏻.\n\nPosso programmare uno o più post su più canali e generare immagini e video.\n\nPuoi selezionare i canali che vuoi usare dal menu a sinistra.\n\nPuoi vedere le tue conversazioni precedenti dal menu a destra.\n\nPuoi anche usarmi come server MCP, vai su Impostazioni >> API pubblica", + "last_github_trending": "Ultime tendenze su Github", + "next_predicted_github_trending": "Prossime tendenze previste su GitHub", + "repository": "Repository", + "date": "Data", + "total_stars": "Stelle totali", + "total_forks": "Fork totali", + "continue_with": "Continua con", + "email_address": "Indirizzo email", + "email_already_exists": "L'email esiste già", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Modifica autopost", + "add_autopost_title": "Aggiungi autopost", + "webhook_deleted_successfully": "Webhook eliminato con successo", + "all_integrations": "Tutte le integrazioni", + "specific_integrations": "Integrazioni specifiche", + "post_on_next_available_slot": "Pubblica nel prossimo slot disponibile", + "post_immediately": "Pubblica immediatamente", + "could_not_use_rss_feed": "Impossibile utilizzare questo feed RSS", + "rss_valid": "RSS valido!", + "autopost_updated_successfully": "Autopost aggiornato con successo", + "autopost_added_successfully": "Autopost aggiunto con successo", + "write_your_post_placeholder": "Scrivi il tuo post...", + "select_or_upload_pictures_max_5": "Seleziona o carica immagini (massimo 5 alla volta).", + "you_can_drag_drop_pictures": "Puoi anche trascinare e rilasciare le immagini.", + "you_dont_have_any_media_yet": "Non hai ancora nessun media", + "media_library": "Libreria media", + "media_settings": "Impostazioni media", + "media_editor": "Editor media", + "close": "Chiudi", + "me": "Io", + "noname": "Senza nome", + "password_reset_link_expired": "Il link per il reset della password è scaduto. Per favore riprova.", + "invalid_api_key": "API key non valida", + "could_not_connect_to_platform": "Impossibile connettersi alla piattaforma", + "web3_provider": "Provider Web3", + "add_provider_title": "Aggiungi provider", + "profile_updated": "Profilo aggiornato", + "sets": "Set", + "invitation_link_sent": "Link di invito inviato", + "send_invitation_link": "Invia link di invito", + "copy_link": "Copia link", + "are_you_sure_remove_team_member": "Sei sicuro di voler rimuovere questo membro del team?", + "admin": "Admin", + "super_admin": "Super Admin", + "update_webhook": "Aggiorna webhook", + "add_webhook": "Aggiungi webhook", + "webhook_updated_successfully": "Webhook aggiornato con successo", + "webhook_added_successfully": "Webhook aggiunto con successo", + "webhook_sent": "Webhook inviato", + "today": "Oggi", + "channel_disconnected_click_to_reconnect": "Canale disconnesso, clicca per riconnettere.", + "channel_disabled_upgrade_plan": "Questo canale è disabilitato, aggiorna il tuo piano per abilitarlo.", + "channel_added": "Canale aggiunto", + "are_you_sure_disable_channel": "Sei sicuro di voler disabilitare questo canale?", + "disable_channel_title": "Disabilita Canale", + "channel_disabled": "Canale Disabilitato", + "are_you_sure_delete_channel": "Sei sicuro di voler eliminare questo canale?", + "delete_channel_title": "Elimina Canale", + "delete_posts_before_channel": "Devi eliminare tutti i post associati a questo canale prima di eliminarlo", + "channel_deleted": "Canale Eliminato", + "channel_enabled": "Canale Abilitato", + "time_table_slots": "Fasce orarie", + "channel_id_copied": "ID canale copiato negli appunti", + "settings_updated": "Impostazioni aggiornate", + "customer_updated": "Cliente aggiornato", + "custom_url": "URL personalizzato", + "picture": "Immagine", + "upgrade_required": "Devi eseguire l'upgrade per utilizzare questa funzione", + "move_to_billing": "Vai alla fatturazione", + "payment_required": "Pagamento richiesto", + "no_content": "nessun contenuto", + "select_customer_tooltip": "Seleziona cliente", + "customers": "Clienti", + "hour": "Ora", + "minutes": "Minuti", + "updated": "Aggiornato", + "change_bot_picture_title": "Cambia immagine del bot", + "select_customer_label": "Seleziona cliente", + "start_typing": "Inizia a digitare...", + "choose_set_or_continue": "Scegli un set o continua senza", + "continue_without_set": "Continua senza set", + "select_set": "Seleziona un set", + "channel_settings": "Impostazioni", + "post_needs_content_or_image": "Il tuo post deve contenere almeno un carattere o un'immagine.", + "please_fix_your_settings": "Per favore, correggi le tue impostazioni", + "shortlink_urls_question": "Vuoi accorciare gli URL? Ti permetterà di ottenere statistiche sui clic", + "yes_shortlink_it": "Sì, accorcialo!", + "added_successfully": "Aggiunto con successo", + "updated_successfully": "Aggiornato con successo", + "create_post_title": "Crea post", + "post_preview": "Anteprima post", + "check_circles_above": "Controlla i cerchi sopra", + "create_output": "Crea output", + "assistant_initial_message": "Ciao! Posso aiutarti a perfezionare i tuoi post sui social media.", + "no_longer_global_mode": "Non più in modalità globale", + "two_days": "Due giorni", + "three_days": "Tre giorni", + "four_days": "Quattro giorni", + "five_days": "Cinque giorni", + "six_days": "Sei giorni", + "two_weeks": "Due settimane", + "repeat_post_every_label": "Ripeti post ogni", + "add_new_tag": "Aggiungi nuovo tag", + "tag_name": "Nome", + "post_is_too_long": "il post è troppo lungo, per favore correggilo", + "your_post_should_have_at_least_one_character_or_one_image": "Il tuo post deve contenere almeno un carattere o un'immagine.", + "internal_edit": "Modifica interna", + "are_you_sure_go_back_to_global_mode": "Questa azione è irreversibile. Sei sicuro di voler tornare alla modalità globale?", + "yes_go_back_to_global_mode": "Sì, torna alla modalità globale", + "are_you_sure_delete_this_post": "Sei sicuro di voler eliminare questo post?", + "cant_edit_networks_when_creating_set": "Non puoi modificare le reti durante la creazione di un set", + "click_to_exit_global_editing": "Clicca su questo pulsante per uscire dalla modifica globale e personalizzare il post per questo canale", + "edit_content": "Modifica contenuto", + "editing_a_specific_network": "Modifica di una rete specifica", + "back_to_global": "Torna alla modalità globale", + "delete_post_tooltip": "Elimina post", + "drop_files_here_to_upload": "Trascina qui i tuoi file per caricarli", + "insert_emoji": "Inserisci emoji", + "write_something": "Scrivi qualcosa…" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index 1a66090a..3d157ec0 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "いつでもキャンセル可能。", "billing_pay_0_start_trial": "本日のお支払いは0円 - 無料トライアルを始めましょう!", "billing_pay_now": "今すぐ支払う", - "billing_per_month": "/月" + "billing_per_month": "/月", + "select_channels": "チャンネルを選択", + "start_a_new_chat": "新しいチャットを開始", + "your_assistant": "あなたのアシスタント", + "agent_welcome_message": "こんにちは、私はあなたのPostizエージェントです🙌🏻。\n\n複数のチャンネルに投稿や複数の投稿をスケジュールしたり、画像や動画を生成したりできます。\n\n左側のメニューから使用したいチャンネルを選択できます。\n\n右側のメニューから過去の会話を見ることができます。\n\nまた、私をMCPサーバーとしても利用できます。設定 >> パブリックAPI をご確認ください。", + "last_github_trending": "最新のGitHubトレンド", + "next_predicted_github_trending": "次に予測されるGitHubトレンド", + "repository": "リポジトリ", + "date": "日付", + "total_stars": "総スター数", + "total_forks": "総フォーク数", + "continue_with": "続ける", + "email_address": "メールアドレス", + "email_already_exists": "メールアドレスは既に存在します", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "自動投稿を編集", + "add_autopost_title": "自動投稿を追加", + "webhook_deleted_successfully": "Webhookが正常に削除されました", + "all_integrations": "すべての連携", + "specific_integrations": "特定の連携", + "post_on_next_available_slot": "次の利用可能なスロットで投稿", + "post_immediately": "すぐに投稿", + "could_not_use_rss_feed": "このRSSフィードは使用できませんでした", + "rss_valid": "RSSは有効です!", + "autopost_updated_successfully": "自動投稿が正常に更新されました", + "autopost_added_successfully": "自動投稿が正常に追加されました", + "write_your_post_placeholder": "投稿内容を入力してください...", + "select_or_upload_pictures_max_5": "画像を選択またはアップロード(最大5枚まで同時に可能)", + "you_can_drag_drop_pictures": "画像をドラッグ&ドロップすることもできます。", + "you_dont_have_any_media_yet": "まだメディアがありません", + "media_library": "メディアライブラリ", + "media_settings": "メディア設定", + "media_editor": "メディアエディター", + "close": "閉じる", + "me": "自分", + "noname": "名前なし", + "password_reset_link_expired": "パスワードリセットリンクの有効期限が切れています。もう一度お試しください。", + "invalid_api_key": "無効なAPIキーです", + "could_not_connect_to_platform": "プラットフォームに接続できませんでした", + "web3_provider": "Web3プロバイダー", + "add_provider_title": "プロバイダーを追加", + "profile_updated": "プロフィールが更新されました", + "sets": "セット", + "invitation_link_sent": "招待リンクが送信されました", + "send_invitation_link": "招待リンクを送信", + "copy_link": "リンクをコピー", + "are_you_sure_remove_team_member": "このチームメンバーを削除してもよろしいですか?", + "admin": "管理者", + "super_admin": "スーパー管理者", + "update_webhook": "Webhookを更新", + "add_webhook": "Webhookを追加", + "webhook_updated_successfully": "Webhookが正常に更新されました", + "webhook_added_successfully": "Webhookが正常に追加されました", + "webhook_sent": "Webhook送信", + "today": "今日", + "channel_disconnected_click_to_reconnect": "チャンネルが切断されました。再接続するにはクリックしてください。", + "channel_disabled_upgrade_plan": "このチャンネルは無効になっています。有効にするにはプランをアップグレードしてください。", + "channel_added": "チャンネルが追加されました", + "are_you_sure_disable_channel": "本当にこのチャンネルを無効にしますか?", + "disable_channel_title": "チャンネルを無効化", + "channel_disabled": "チャンネルが無効になりました", + "are_you_sure_delete_channel": "本当にこのチャンネルを削除しますか?", + "delete_channel_title": "チャンネルを削除", + "delete_posts_before_channel": "このチャンネルを削除する前に、関連するすべての投稿を削除する必要があります", + "channel_deleted": "チャンネルが削除されました", + "channel_enabled": "チャンネルが有効になりました", + "time_table_slots": "タイムテーブル枠", + "channel_id_copied": "チャンネルIDがクリップボードにコピーされました", + "settings_updated": "設定が更新されました", + "customer_updated": "顧客情報が更新されました", + "custom_url": "カスタムURL", + "picture": "画像", + "upgrade_required": "この機能を利用するにはアップグレードが必要です", + "move_to_billing": "請求画面へ移動", + "payment_required": "お支払いが必要です", + "no_content": "内容なし", + "select_customer_tooltip": "顧客を選択", + "customers": "顧客", + "hour": "時間", + "minutes": "分", + "updated": "更新済み", + "change_bot_picture_title": "ボットの画像を変更", + "select_customer_label": "顧客を選択", + "start_typing": "入力を開始...", + "choose_set_or_continue": "セットを選択するか、セットなしで続行してください", + "continue_without_set": "セットなしで続行", + "select_set": "セットを選択", + "channel_settings": "設定", + "post_needs_content_or_image": "投稿には少なくとも1文字または1つの画像が必要です。", + "please_fix_your_settings": "設定を修正してください", + "shortlink_urls_question": "URLを短縮リンクにしますか?クリック数の統計を取得できます。", + "yes_shortlink_it": "はい、短縮リンクにします!", + "added_successfully": "追加に成功しました", + "updated_successfully": "更新に成功しました", + "create_post_title": "投稿を作成", + "post_preview": "投稿プレビュー", + "check_circles_above": "上の円を確認してください", + "create_output": "出力を作成", + "assistant_initial_message": "こんにちは!あなたのSNS投稿をブラッシュアップするお手伝いができます。", + "no_longer_global_mode": "グローバルモードを終了しました", + "two_days": "2日間", + "three_days": "3日間", + "four_days": "4日間", + "five_days": "5日間", + "six_days": "6日間", + "two_weeks": "2週間", + "repeat_post_every_label": "投稿を繰り返す間隔", + "add_new_tag": "新しいタグを追加", + "tag_name": "名前", + "post_is_too_long": "投稿が長すぎます。修正してください", + "your_post_should_have_at_least_one_character_or_one_image": "投稿には少なくとも1文字または1枚の画像が必要です。", + "internal_edit": "内部編集", + "are_you_sure_go_back_to_global_mode": "この操作は元に戻せません。本当にグローバルモードに戻りますか?", + "yes_go_back_to_global_mode": "はい、グローバルモードに戻ります", + "are_you_sure_delete_this_post": "本当にこの投稿を削除しますか?", + "cant_edit_networks_when_creating_set": "セット作成中はネットワークを編集できません", + "click_to_exit_global_editing": "このボタンをクリックしてグローバル編集を終了し、このチャンネル用に投稿をカスタマイズします", + "edit_content": "内容を編集", + "editing_a_specific_network": "特定のネットワークを編集中", + "back_to_global": "グローバルに戻る", + "delete_post_tooltip": "投稿を削除", + "drop_files_here_to_upload": "ここにファイルをドロップしてアップロード", + "insert_emoji": "絵文字を挿入", + "write_something": "何かを書いてください…" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index 9fb6557d..7212ad82 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "언제든지 취소 가능합니다.", "billing_pay_0_start_trial": "오늘 $0 결제 - 무료 체험을 시작하세요!", "billing_pay_now": "지금 결제", - "billing_per_month": "/월" + "billing_per_month": "/월", + "select_channels": "채널 선택", + "start_a_new_chat": "새 채팅 시작", + "your_assistant": "당신의 어시스턴트", + "agent_welcome_message": "안녕하세요, 저는 Postiz 에이전트입니다 🙌🏻.\n\n저는 하나 또는 여러 개의 게시물을 여러 채널에 예약 게시할 수 있고, 사진과 동영상을 생성할 수 있습니다.\n\n왼쪽 메뉴에서 사용하고 싶은 채널을 선택할 수 있습니다.\n\n오른쪽 메뉴에서 이전 대화를 볼 수 있습니다.\n\n또한 저를 MCP 서버로 사용할 수 있으며, 설정 >> 공개 API에서 확인할 수 있습니다.", + "last_github_trending": "최근 깃허브 트렌딩", + "next_predicted_github_trending": "다음 예측 깃허브 트렌딩", + "repository": "저장소", + "date": "날짜", + "total_stars": "총 별점", + "total_forks": "총 포크", + "continue_with": "다음으로 계속", + "email_address": "이메일 주소", + "email_already_exists": "이미 존재하는 이메일입니다", + "google": "구글", + "farcaster": "파캐스터", + "edit_autopost": "자동 게시 수정", + "add_autopost_title": "자동 게시 추가", + "webhook_deleted_successfully": "웹훅이 성공적으로 삭제되었습니다", + "all_integrations": "모든 통합", + "specific_integrations": "특정 통합", + "post_on_next_available_slot": "다음 사용 가능한 시간에 게시", + "post_immediately": "즉시 게시", + "could_not_use_rss_feed": "이 RSS 피드를 사용할 수 없습니다", + "rss_valid": "RSS가 유효합니다!", + "autopost_updated_successfully": "자동 게시가 성공적으로 업데이트되었습니다", + "autopost_added_successfully": "자동 게시가 성공적으로 추가되었습니다", + "write_your_post_placeholder": "게시글을 작성하세요...", + "select_or_upload_pictures_max_5": "사진을 선택하거나 업로드하세요 (최대 5장까지 한 번에 가능합니다).", + "you_can_drag_drop_pictures": "사진을 드래그 앤 드롭하여 추가할 수도 있습니다.", + "you_dont_have_any_media_yet": "아직 미디어가 없습니다", + "media_library": "미디어 라이브러리", + "media_settings": "미디어 설정", + "media_editor": "미디어 편집기", + "close": "닫기", + "me": "나", + "noname": "이름 없음", + "password_reset_link_expired": "비밀번호 재설정 링크가 만료되었습니다. 다시 시도해 주세요.", + "invalid_api_key": "잘못된 API 키입니다", + "could_not_connect_to_platform": "플랫폼에 연결할 수 없습니다", + "web3_provider": "Web3 제공자", + "add_provider_title": "제공자 추가", + "profile_updated": "프로필이 업데이트되었습니다", + "sets": "세트", + "invitation_link_sent": "초대 링크가 전송되었습니다", + "send_invitation_link": "초대 링크 보내기", + "copy_link": "링크 복사", + "are_you_sure_remove_team_member": "이 팀 멤버를 삭제하시겠습니까?", + "admin": "관리자", + "super_admin": "슈퍼 관리자", + "update_webhook": "웹훅 업데이트", + "add_webhook": "웹훅 추가", + "webhook_updated_successfully": "웹훅이 성공적으로 업데이트되었습니다", + "webhook_added_successfully": "웹훅이 성공적으로 추가되었습니다", + "webhook_sent": "웹훅 전송됨", + "today": "오늘", + "channel_disconnected_click_to_reconnect": "채널이 연결 해제되었습니다. 다시 연결하려면 클릭하세요.", + "channel_disabled_upgrade_plan": "이 채널은 비활성화되어 있습니다. 활성화하려면 요금제를 업그레이드하세요.", + "channel_added": "채널이 추가되었습니다", + "are_you_sure_disable_channel": "이 채널을 비활성화하시겠습니까?", + "disable_channel_title": "채널 비활성화", + "channel_disabled": "채널이 비활성화되었습니다", + "are_you_sure_delete_channel": "이 채널을 삭제하시겠습니까?", + "delete_channel_title": "채널 삭제", + "delete_posts_before_channel": "이 채널을 삭제하기 전에 관련된 모든 게시물을 삭제해야 합니다", + "channel_deleted": "채널이 삭제되었습니다", + "channel_enabled": "채널이 활성화되었습니다", + "time_table_slots": "시간표 슬롯", + "channel_id_copied": "채널 ID가 클립보드에 복사되었습니다", + "settings_updated": "설정이 업데이트되었습니다", + "customer_updated": "고객 정보가 업데이트되었습니다", + "custom_url": "맞춤 URL", + "picture": "사진", + "upgrade_required": "이 기능을 사용하려면 업그레이드가 필요합니다", + "move_to_billing": "결제 페이지로 이동", + "payment_required": "결제가 필요합니다", + "no_content": "내용 없음", + "select_customer_tooltip": "고객 선택", + "customers": "고객", + "hour": "시간", + "minutes": "분", + "updated": "업데이트됨", + "change_bot_picture_title": "봇 사진 변경", + "select_customer_label": "고객 선택", + "start_typing": "입력 시작...", + "choose_set_or_continue": "세트를 선택하거나 세트 없이 계속하세요", + "continue_without_set": "세트 없이 계속하기", + "select_set": "세트 선택", + "channel_settings": "설정", + "post_needs_content_or_image": "게시물에는 최소 한 글자 또는 이미지가 있어야 합니다.", + "please_fix_your_settings": "설정을 수정해 주세요", + "shortlink_urls_question": "URL을 단축하시겠습니까? 클릭 통계를 확인할 수 있습니다.", + "yes_shortlink_it": "네, 단축하세요!", + "added_successfully": "성공적으로 추가되었습니다", + "updated_successfully": "성공적으로 업데이트되었습니다", + "create_post_title": "게시물 작성", + "post_preview": "게시물 미리보기", + "check_circles_above": "위의 원을 확인하세요", + "create_output": "결과 생성", + "assistant_initial_message": "안녕하세요! 소셜 미디어 게시물을 다듬는 데 도와드릴 수 있습니다.", + "no_longer_global_mode": "글로벌 모드가 해제되었습니다", + "two_days": "이틀", + "three_days": "삼일", + "four_days": "나흘", + "five_days": "닷새", + "six_days": "엿새", + "two_weeks": "2주", + "repeat_post_every_label": "게시 반복 주기", + "add_new_tag": "새 태그 추가", + "tag_name": "이름", + "post_is_too_long": "게시글이 너무 깁니다. 수정해 주세요.", + "your_post_should_have_at_least_one_character_or_one_image": "게시물에는 최소한 한 글자 또는 한 이미지를 포함해야 합니다.", + "internal_edit": "내부 편집", + "are_you_sure_go_back_to_global_mode": "이 작업은 되돌릴 수 없습니다. 정말로 글로벌 모드로 돌아가시겠습니까?", + "yes_go_back_to_global_mode": "네, 글로벌 모드로 돌아갑니다", + "are_you_sure_delete_this_post": "이 게시물을 삭제하시겠습니까?", + "cant_edit_networks_when_creating_set": "세트를 생성할 때는 네트워크를 편집할 수 없습니다.", + "click_to_exit_global_editing": "이 버튼을 클릭하면 글로벌 편집을 종료하고 이 채널에 맞게 게시물을 맞춤 설정할 수 있습니다.", + "edit_content": "내용 편집", + "editing_a_specific_network": "특정 네트워크 편집 중", + "back_to_global": "글로벌로 돌아가기", + "delete_post_tooltip": "게시물 삭제", + "drop_files_here_to_upload": "여기에 파일을 끌어다 놓아 업로드하세요", + "insert_emoji": "이모지 삽입", + "write_something": "무엇인가를 작성하세요 …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index c0807c34..3906d742 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Cancele a qualquer momento.", "billing_pay_0_start_trial": "Pague R$0 hoje - Comece seu teste gratuito!", "billing_pay_now": "Pagar agora", - "billing_per_month": "/ mês" + "billing_per_month": "/ mês", + "select_channels": "Selecionar canais", + "start_a_new_chat": "Iniciar um novo chat", + "your_assistant": "Seu Assistente", + "agent_welcome_message": "Olá, sou seu agente Postiz 🙌🏻.\n\nPosso agendar uma publicação ou várias publicações para múltiplos canais e gerar imagens e vídeos.\n\nVocê pode selecionar os canais que deseja usar no menu à esquerda.\n\nVocê pode ver suas conversas anteriores no menu à direita.\n\nVocê também pode me usar como um Servidor MCP, confira Configurações >> API Pública", + "last_github_trending": "Últimas tendências do Github", + "next_predicted_github_trending": "Próxima tendência prevista do GitHub", + "repository": "Repositório", + "date": "Data", + "total_stars": "Total de estrelas", + "total_forks": "Total de forks", + "continue_with": "Continuar com", + "email_address": "Endereço de e-mail", + "email_already_exists": "O e-mail já existe", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Editar autopost", + "add_autopost_title": "Adicionar autopost", + "webhook_deleted_successfully": "Webhook excluído com sucesso", + "all_integrations": "Todas as integrações", + "specific_integrations": "Integrações específicas", + "post_on_next_available_slot": "Publicar no próximo horário disponível", + "post_immediately": "Publicar imediatamente", + "could_not_use_rss_feed": "Não foi possível usar este feed RSS", + "rss_valid": "RSS válido!", + "autopost_updated_successfully": "Autopost atualizado com sucesso", + "autopost_added_successfully": "Autopost adicionado com sucesso", + "write_your_post_placeholder": "Escreva sua postagem...", + "select_or_upload_pictures_max_5": "Selecione ou envie fotos (máximo de 5 por vez).", + "you_can_drag_drop_pictures": "Você também pode arrastar e soltar fotos.", + "you_dont_have_any_media_yet": "Você ainda não tem nenhuma mídia", + "media_library": "Biblioteca de Mídia", + "media_settings": "Configurações de Mídia", + "media_editor": "Editor de Mídia", + "close": "Fechar", + "me": "Eu", + "noname": "Sem nome", + "password_reset_link_expired": "Seu link de redefinição de senha expirou. Por favor, tente novamente.", + "invalid_api_key": "Chave de API inválida", + "could_not_connect_to_platform": "Não foi possível conectar à plataforma", + "web3_provider": "Provedor Web3", + "add_provider_title": "Adicionar Provedor", + "profile_updated": "Perfil atualizado", + "sets": "Conjuntos", + "invitation_link_sent": "Link de convite enviado", + "send_invitation_link": "Enviar link de convite", + "copy_link": "Copiar link", + "are_you_sure_remove_team_member": "Tem certeza de que deseja remover este membro da equipe?", + "admin": "Administrador", + "super_admin": "Super Administrador", + "update_webhook": "Atualizar webhook", + "add_webhook": "Adicionar webhook", + "webhook_updated_successfully": "Webhook atualizado com sucesso", + "webhook_added_successfully": "Webhook adicionado com sucesso", + "webhook_sent": "Webhook enviado", + "today": "Hoje", + "channel_disconnected_click_to_reconnect": "Canal desconectado, clique para reconectar.", + "channel_disabled_upgrade_plan": "Este canal está desativado, por favor faça upgrade do seu plano para habilitá-lo.", + "channel_added": "Canal adicionado", + "are_you_sure_disable_channel": "Tem certeza de que deseja desativar este canal?", + "disable_channel_title": "Desativar Canal", + "channel_disabled": "Canal Desativado", + "are_you_sure_delete_channel": "Tem certeza de que deseja excluir este canal?", + "delete_channel_title": "Excluir Canal", + "delete_posts_before_channel": "Você precisa excluir todas as postagens associadas a este canal antes de excluí-lo", + "channel_deleted": "Canal Excluído", + "channel_enabled": "Canal Ativado", + "time_table_slots": "Horários da Tabela", + "channel_id_copied": "ID do canal copiado para a área de transferência", + "settings_updated": "Configurações Atualizadas", + "customer_updated": "Cliente Atualizado", + "custom_url": "URL personalizada", + "picture": "Imagem", + "upgrade_required": "Você precisa fazer upgrade para usar este recurso", + "move_to_billing": "Ir para cobrança", + "payment_required": "Pagamento Necessário", + "no_content": "sem conteúdo", + "select_customer_tooltip": "Selecionar cliente", + "customers": "Clientes", + "hour": "Hora", + "minutes": "Minutos", + "updated": "Atualizado", + "change_bot_picture_title": "Alterar foto do bot", + "select_customer_label": "Selecionar cliente", + "start_typing": "Comece a digitar...", + "choose_set_or_continue": "Escolha um conjunto ou continue sem um", + "continue_without_set": "Continuar sem conjunto", + "select_set": "Selecionar um conjunto", + "channel_settings": "Configurações", + "post_needs_content_or_image": "Sua publicação deve ter pelo menos um caractere ou uma imagem.", + "please_fix_your_settings": "Por favor, corrija suas configurações", + "shortlink_urls_question": "Você quer encurtar as URLs? Isso permitirá obter estatísticas sobre os cliques", + "yes_shortlink_it": "Sim, encurte!", + "added_successfully": "Adicionado com sucesso", + "updated_successfully": "Atualizado com sucesso", + "create_post_title": "Criar publicação", + "post_preview": "Prévia da publicação", + "check_circles_above": "Verifique os círculos acima", + "create_output": "Criar saída", + "assistant_initial_message": "Olá! Posso ajudar você a aprimorar suas postagens nas redes sociais.", + "no_longer_global_mode": "Não está mais no modo global", + "two_days": "Dois dias", + "three_days": "Três dias", + "four_days": "Quatro dias", + "five_days": "Cinco dias", + "six_days": "Seis dias", + "two_weeks": "Duas semanas", + "repeat_post_every_label": "Repetir postagem a cada", + "add_new_tag": "Adicionar nova tag", + "tag_name": "Nome", + "post_is_too_long": "a postagem está muito longa, por favor corrija", + "your_post_should_have_at_least_one_character_or_one_image": "Sua postagem deve ter pelo menos um caractere ou uma imagem.", + "internal_edit": "Edição interna", + "are_you_sure_go_back_to_global_mode": "Esta ação é irreversível. Tem certeza de que deseja voltar para o modo global?", + "yes_go_back_to_global_mode": "Sim, voltar para o modo global", + "are_you_sure_delete_this_post": "Tem certeza de que deseja excluir esta postagem?", + "cant_edit_networks_when_creating_set": "Você não pode editar redes ao criar um conjunto", + "click_to_exit_global_editing": "Clique neste botão para sair da edição global e personalizar a postagem para este canal", + "edit_content": "Editar conteúdo", + "editing_a_specific_network": "Editando uma Rede Específica", + "back_to_global": "Voltar para o global", + "delete_post_tooltip": "Excluir postagem", + "drop_files_here_to_upload": "Solte seus arquivos aqui para fazer upload", + "insert_emoji": "Inserir emoji", + "write_something": "Escreva algo…" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index 82343202..b03b3aad 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Отменить можно в любое время.", "billing_pay_0_start_trial": "Заплатите $0 сегодня — начните бесплатный пробный период!", "billing_pay_now": "Оплатить сейчас", - "billing_per_month": "/ месяц" + "billing_per_month": "/ месяц", + "select_channels": "Выберите каналы", + "start_a_new_chat": "Начать новый чат", + "your_assistant": "Ваш помощник", + "agent_welcome_message": "Здравствуйте, я ваш агент Postiz 🙌🏻.\n\nЯ могу запланировать публикацию одного или нескольких постов в нескольких каналах, а также сгенерировать изображения и видео.\n\nВы можете выбрать нужные каналы в левом меню.\n\nВаши предыдущие разговоры доступны в правом меню.\n\nТакже вы можете использовать меня как MCP Server, проверьте Настройки >> Публичный API", + "last_github_trending": "Последние тренды на Github", + "next_predicted_github_trending": "Следующий прогнозируемый тренд на GitHub", + "repository": "Репозиторий", + "date": "Дата", + "total_stars": "Всего звёзд", + "total_forks": "Всего форков", + "continue_with": "Продолжить с", + "email_address": "Адрес электронной почты", + "email_already_exists": "Электронная почта уже существует", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Редактировать автопост", + "add_autopost_title": "Добавить автопост", + "webhook_deleted_successfully": "Вебхук успешно удалён", + "all_integrations": "Все интеграции", + "specific_integrations": "Конкретные интеграции", + "post_on_next_available_slot": "Опубликовать в следующем доступном слоте", + "post_immediately": "Опубликовать немедленно", + "could_not_use_rss_feed": "Не удалось использовать этот RSS-канал", + "rss_valid": "RSS-канал действителен!", + "autopost_updated_successfully": "Автопост успешно обновлён", + "autopost_added_successfully": "Автопост успешно добавлен", + "write_your_post_placeholder": "Напишите свой пост...", + "select_or_upload_pictures_max_5": "Выберите или загрузите изображения (максимум 5 за раз).", + "you_can_drag_drop_pictures": "Вы также можете перетащить изображения.", + "you_dont_have_any_media_yet": "У вас ещё нет медиафайлов", + "media_library": "Медиатека", + "media_settings": "Настройки медиа", + "media_editor": "Редактор медиа", + "close": "Закрыть", + "me": "Я", + "noname": "Без имени", + "password_reset_link_expired": "Ссылка для сброса пароля истекла. Пожалуйста, попробуйте снова.", + "invalid_api_key": "Недействительный API-ключ", + "could_not_connect_to_platform": "Не удалось подключиться к платформе", + "web3_provider": "Web3-провайдер", + "add_provider_title": "Добавить провайдера", + "profile_updated": "Профиль обновлён", + "sets": "Наборы", + "invitation_link_sent": "Ссылка-приглашение отправлена", + "send_invitation_link": "Отправить ссылку-приглашение", + "copy_link": "Скопировать ссылку", + "are_you_sure_remove_team_member": "Вы уверены, что хотите удалить этого участника команды?", + "admin": "Администратор", + "super_admin": "Супер администратор", + "update_webhook": "Обновить вебхук", + "add_webhook": "Добавить вебхук", + "webhook_updated_successfully": "Вебхук успешно обновлён", + "webhook_added_successfully": "Вебхук успешно добавлен", + "webhook_sent": "Вебхук отправлен", + "today": "Сегодня", + "channel_disconnected_click_to_reconnect": "Канал отключён, нажмите для повторного подключения.", + "channel_disabled_upgrade_plan": "Этот канал отключён, пожалуйста, обновите ваш тариф для его активации.", + "channel_added": "Канал добавлен", + "are_you_sure_disable_channel": "Вы уверены, что хотите отключить этот канал?", + "disable_channel_title": "Отключить канал", + "channel_disabled": "Канал отключён", + "are_you_sure_delete_channel": "Вы уверены, что хотите удалить этот канал?", + "delete_channel_title": "Удалить канал", + "delete_posts_before_channel": "Вам нужно удалить все публикации, связанные с этим каналом, прежде чем его удалить", + "channel_deleted": "Канал удалён", + "channel_enabled": "Канал включён", + "time_table_slots": "Слоты расписания", + "channel_id_copied": "ID канала скопирован в буфер обмена", + "settings_updated": "Настройки обновлены", + "customer_updated": "Клиент обновлён", + "custom_url": "Пользовательский URL", + "picture": "Изображение", + "upgrade_required": "Вам необходимо обновить тариф для использования этой функции", + "move_to_billing": "Перейти к оплате", + "payment_required": "Требуется оплата", + "no_content": "нет содержимого", + "select_customer_tooltip": "Выберите клиента", + "customers": "Клиенты", + "hour": "Час", + "minutes": "Минуты", + "updated": "Обновлено", + "change_bot_picture_title": "Изменить изображение бота", + "select_customer_label": "Выберите клиента", + "start_typing": "Начните вводить...", + "choose_set_or_continue": "Выберите набор или продолжите без него", + "continue_without_set": "Продолжить без набора", + "select_set": "Выберите набор", + "channel_settings": "Настройки", + "post_needs_content_or_image": "Ваш пост должен содержать хотя бы один символ или одно изображение.", + "please_fix_your_settings": "Пожалуйста, исправьте ваши настройки", + "shortlink_urls_question": "Хотите сократить ссылки? Это позволит получать статистику по кликам.", + "yes_shortlink_it": "Да, сократить!", + "added_successfully": "Успешно добавлено", + "updated_successfully": "Успешно обновлено", + "create_post_title": "Создать пост", + "post_preview": "Предпросмотр поста", + "check_circles_above": "Проверьте круги выше", + "create_output": "Создать результат", + "assistant_initial_message": "Привет! Я могу помочь вам улучшить ваши посты для социальных сетей.", + "no_longer_global_mode": "Глобальный режим отключён", + "two_days": "Два дня", + "three_days": "Три дня", + "four_days": "Четыре дня", + "five_days": "Пять дней", + "six_days": "Шесть дней", + "two_weeks": "Две недели", + "repeat_post_every_label": "Повторять публикацию каждые", + "add_new_tag": "Добавить новый тег", + "tag_name": "Название", + "post_is_too_long": "пост слишком длинный, пожалуйста, исправьте его", + "your_post_should_have_at_least_one_character_or_one_image": "Ваша публикация должна содержать хотя бы один символ или одно изображение.", + "internal_edit": "Внутреннее редактирование", + "are_you_sure_go_back_to_global_mode": "Это действие необратимо. Вы уверены, что хотите вернуться в глобальный режим?", + "yes_go_back_to_global_mode": "Да, вернуться в глобальный режим", + "are_you_sure_delete_this_post": "Вы уверены, что хотите удалить эту публикацию?", + "cant_edit_networks_when_creating_set": "Вы не можете редактировать сети при создании набора", + "click_to_exit_global_editing": "Нажмите эту кнопку, чтобы выйти из глобального редактирования и настроить публикацию для этого канала", + "edit_content": "Редактировать содержимое", + "editing_a_specific_network": "Редактирование определённой сети", + "back_to_global": "Вернуться к глобальному", + "delete_post_tooltip": "Удалить публикацию", + "drop_files_here_to_upload": "Перетащите файлы сюда для загрузки", + "insert_emoji": "Вставить эмодзи", + "write_something": "Напишите что-нибудь …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index 2c47213e..f831d6ce 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "İstediğiniz zaman iptal edin.", "billing_pay_0_start_trial": "Bugün 0$ ödeyin - Ücretsiz denemenizi başlatın!", "billing_pay_now": "Şimdi Öde", - "billing_per_month": "/ ay" + "billing_per_month": "/ ay", + "select_channels": "Kanalları Seç", + "start_a_new_chat": "Yeni bir sohbet başlat", + "your_assistant": "Asistanınız", + "agent_welcome_message": "Merhaba, ben Postiz ajanınızım 🙌🏻.\n\nBir veya birden fazla gönderiyi birden fazla kanala zamanlayabilir, resim ve video oluşturabilirim.\n\nKullanmak istediğiniz kanalları sol menüden seçebilirsiniz.\n\nÖnceki konuşmalarınızı sağ menüden görebilirsiniz.\n\nAyrıca beni bir MCP Sunucusu olarak da kullanabilirsiniz, Ayarlar >> Genel API bölümünü kontrol edin.", + "last_github_trending": "Son Github Trendleri", + "next_predicted_github_trending": "Sonraki Tahmini Github Trendleri", + "repository": "Depo", + "date": "Tarih", + "total_stars": "Toplam Yıldız", + "total_forks": "Toplam Çatallanma", + "continue_with": "Şununla Devam Et", + "email_address": "E-posta Adresi", + "email_already_exists": "E-posta zaten mevcut", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Otomatik Gönderiyi Düzenle", + "add_autopost_title": "Otomatik Gönderi Ekle", + "webhook_deleted_successfully": "Webhook başarıyla silindi", + "all_integrations": "Tüm entegrasyonlar", + "specific_integrations": "Belirli entegrasyonlar", + "post_on_next_available_slot": "Bir sonraki uygun zamanda paylaş", + "post_immediately": "Hemen Paylaş", + "could_not_use_rss_feed": "Bu RSS beslemesi kullanılamadı", + "rss_valid": "RSS geçerli!", + "autopost_updated_successfully": "Otomatik gönderi başarıyla güncellendi", + "autopost_added_successfully": "Otomatik gönderi başarıyla eklendi", + "write_your_post_placeholder": "Gönderinizi yazın...", + "select_or_upload_pictures_max_5": "Resim seçin veya yükleyin (aynı anda en fazla 5).", + "you_can_drag_drop_pictures": "Ayrıca resimleri sürükleyip bırakabilirsiniz.", + "you_dont_have_any_media_yet": "Henüz herhangi bir medyanız yok", + "media_library": "Medya Kütüphanesi", + "media_settings": "Medya Ayarları", + "media_editor": "Medya Editörü", + "close": "Kapat", + "me": "Ben", + "noname": "İsimsiz", + "password_reset_link_expired": "Şifre sıfırlama bağlantınızın süresi doldu. Lütfen tekrar deneyin.", + "invalid_api_key": "Geçersiz API anahtarı", + "could_not_connect_to_platform": "Platforma bağlanılamadı", + "web3_provider": "Web3 sağlayıcı", + "add_provider_title": "Sağlayıcı Ekle", + "profile_updated": "Profil güncellendi", + "sets": "Setler", + "invitation_link_sent": "Davet bağlantısı gönderildi", + "send_invitation_link": "Davet Bağlantısı Gönder", + "copy_link": "Bağlantıyı Kopyala", + "are_you_sure_remove_team_member": "Bu ekip üyesini kaldırmak istediğinizden emin misiniz?", + "admin": "Yönetici", + "super_admin": "Süper Yönetici", + "update_webhook": "Webhook'u güncelle", + "add_webhook": "Webhook ekle", + "webhook_updated_successfully": "Webhook başarıyla güncellendi", + "webhook_added_successfully": "Webhook başarıyla eklendi", + "webhook_sent": "Webhook gönderildi", + "today": "Bugün", + "channel_disconnected_click_to_reconnect": "Kanal bağlantısı kesildi, yeniden bağlanmak için tıklayın.", + "channel_disabled_upgrade_plan": "Bu kanal devre dışı, etkinleştirmek için lütfen planınızı yükseltin.", + "channel_added": "Kanal eklendi", + "are_you_sure_disable_channel": "Bu kanalı devre dışı bırakmak istediğinizden emin misiniz?", + "disable_channel_title": "Kanalı Devre Dışı Bırak", + "channel_disabled": "Kanal Devre Dışı", + "are_you_sure_delete_channel": "Bu kanalı silmek istediğinizden emin misiniz?", + "delete_channel_title": "Kanalı Sil", + "delete_posts_before_channel": "Bu kanalı silmeden önce bu kanala ait tüm gönderileri silmelisiniz", + "channel_deleted": "Kanal Silindi", + "channel_enabled": "Kanal Etkinleştirildi", + "time_table_slots": "Zaman Tablosu Dilimleri", + "channel_id_copied": "Kanal kimliği panoya kopyalandı", + "settings_updated": "Ayarlar Güncellendi", + "customer_updated": "Müşteri Güncellendi", + "custom_url": "Özel URL", + "picture": "Resim", + "upgrade_required": "Bu özelliği kullanmak için yükseltme yapmanız gerekiyor", + "move_to_billing": "Faturalandırmaya geç", + "payment_required": "Ödeme Gerekli", + "no_content": "içerik yok", + "select_customer_tooltip": "Müşteri Seç", + "customers": "Müşteriler", + "hour": "Saat", + "minutes": "Dakika", + "updated": "Güncellendi", + "change_bot_picture_title": "Bot Resmini Değiştir", + "select_customer_label": "Müşteri Seç", + "start_typing": "Yazmaya başlayın...", + "choose_set_or_continue": "Bir set seçin veya setsiz devam edin", + "continue_without_set": "Setsiz devam et", + "select_set": "Bir Set Seçin", + "channel_settings": "Ayarlar", + "post_needs_content_or_image": "Gönderinizde en az bir karakter veya bir görsel olmalıdır.", + "please_fix_your_settings": "Lütfen ayarlarınızı düzeltin", + "shortlink_urls_question": "URL'leri kısaltmak ister misiniz? Bu, tıklamalar üzerinde istatistik almanızı sağlar.", + "yes_shortlink_it": "Evet, kısalt!", + "added_successfully": "Başarıyla eklendi", + "updated_successfully": "Başarıyla güncellendi", + "create_post_title": "Gönderi Oluştur", + "post_preview": "Gönderi Önizlemesi", + "check_circles_above": "Yukarıdaki daireleri kontrol edin", + "create_output": "Çıktı oluştur", + "assistant_initial_message": "Merhaba! Sosyal medya gönderilerinizi düzenlemenize yardımcı olabilirim.", + "no_longer_global_mode": "Artık global modda değil", + "two_days": "İki Gün", + "three_days": "Üç Gün", + "four_days": "Dört Gün", + "five_days": "Beş Gün", + "six_days": "Altı Gün", + "two_weeks": "İki Hafta", + "repeat_post_every_label": "Gönderiyi Her Tekrarla", + "add_new_tag": "Yeni Etiket Ekle", + "tag_name": "Adı", + "post_is_too_long": "gönderi çok uzun, lütfen düzeltin", + "your_post_should_have_at_least_one_character_or_one_image": "Gönderinizde en az bir karakter veya bir görsel olmalıdır.", + "internal_edit": "Dahili Düzenleme", + "are_you_sure_go_back_to_global_mode": "Bu işlem geri alınamaz. Global moda dönmek istediğinizden emin misiniz?", + "yes_go_back_to_global_mode": "Evet, global moda dön", + "are_you_sure_delete_this_post": "Bu gönderiyi silmek istediğinizden emin misiniz?", + "cant_edit_networks_when_creating_set": "Bir set oluştururken ağları düzenleyemezsiniz", + "click_to_exit_global_editing": "Global düzenlemeden çıkmak ve bu kanal için gönderiyi özelleştirmek için bu butona tıklayın", + "edit_content": "İçeriği düzenle", + "editing_a_specific_network": "Belirli Bir Ağı Düzenliyorsunuz", + "back_to_global": "Globale dön", + "delete_post_tooltip": "Gönderiyi Sil", + "drop_files_here_to_upload": "Yüklemek için dosyalarınızı buraya bırakın", + "insert_emoji": "Emoji Ekle", + "write_something": "Bir şeyler yazın …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 02005756..05aa3f23 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "Hủy bất cứ lúc nào.", "billing_pay_0_start_trial": "Thanh toán $0 hôm nay - Bắt đầu dùng thử miễn phí!", "billing_pay_now": "Thanh toán ngay", - "billing_per_month": "/ tháng" + "billing_per_month": "/ tháng", + "select_channels": "Chọn kênh", + "start_a_new_chat": "Bắt đầu cuộc trò chuyện mới", + "your_assistant": "Trợ lý của bạn", + "agent_welcome_message": "Xin chào, tôi là trợ lý Postiz của bạn 🙌🏻.\n\nTôi có thể lên lịch đăng một hoặc nhiều bài lên nhiều kênh khác nhau và tạo hình ảnh, video.\n\nBạn có thể chọn các kênh muốn sử dụng từ menu bên trái.\n\nBạn có thể xem các cuộc trò chuyện trước đó từ menu bên phải.\n\nBạn cũng có thể sử dụng tôi như một MCP Server, kiểm tra Cài đặt >> Public API", + "last_github_trending": "Xu hướng Github gần đây", + "next_predicted_github_trending": "Dự đoán xu hướng Github tiếp theo", + "repository": "Kho lưu trữ", + "date": "Ngày", + "total_stars": "Tổng số sao", + "total_forks": "Tổng số nhánh", + "continue_with": "Tiếp tục với", + "email_address": "Địa chỉ email", + "email_already_exists": "Email đã tồn tại", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Chỉnh sửa tự động đăng", + "add_autopost_title": "Thêm tự động đăng", + "webhook_deleted_successfully": "Đã xóa webhook thành công", + "all_integrations": "Tất cả tích hợp", + "specific_integrations": "Tích hợp cụ thể", + "post_on_next_available_slot": "Đăng vào khung giờ tiếp theo có sẵn", + "post_immediately": "Đăng ngay lập tức", + "could_not_use_rss_feed": "Không thể sử dụng nguồn cấp RSS này", + "rss_valid": "RSS hợp lệ!", + "autopost_updated_successfully": "Cập nhật tự động đăng thành công", + "autopost_added_successfully": "Tự động đăng bài đã được thêm thành công", + "write_your_post_placeholder": "Viết bài đăng của bạn...", + "select_or_upload_pictures_max_5": "Chọn hoặc tải lên hình ảnh (tối đa 5 hình cùng lúc).", + "you_can_drag_drop_pictures": "Bạn cũng có thể kéo và thả hình ảnh.", + "you_dont_have_any_media_yet": "Bạn chưa có phương tiện nào", + "media_library": "Thư viện phương tiện", + "media_settings": "Cài đặt phương tiện", + "media_editor": "Trình chỉnh sửa phương tiện", + "close": "Đóng", + "me": "Tôi", + "noname": "Không tên", + "password_reset_link_expired": "Liên kết đặt lại mật khẩu của bạn đã hết hạn. Vui lòng thử lại.", + "invalid_api_key": "API key không hợp lệ", + "could_not_connect_to_platform": "Không thể kết nối với nền tảng", + "web3_provider": "Nhà cung cấp Web3", + "add_provider_title": "Thêm nhà cung cấp", + "profile_updated": "Cập nhật hồ sơ thành công", + "sets": "Bộ", + "invitation_link_sent": "Đã gửi liên kết mời", + "send_invitation_link": "Gửi liên kết mời", + "copy_link": "Sao chép liên kết", + "are_you_sure_remove_team_member": "Bạn có chắc chắn muốn xóa thành viên này khỏi nhóm không?", + "admin": "Quản trị viên", + "super_admin": "Siêu quản trị viên", + "update_webhook": "Cập nhật webhook", + "add_webhook": "Thêm webhook", + "webhook_updated_successfully": "Cập nhật webhook thành công", + "webhook_added_successfully": "Thêm webhook thành công", + "webhook_sent": "Đã gửi webhook", + "today": "Hôm nay", + "channel_disconnected_click_to_reconnect": "Kênh đã bị ngắt kết nối, nhấn để kết nối lại.", + "channel_disabled_upgrade_plan": "Kênh này đã bị vô hiệu hóa, vui lòng nâng cấp gói của bạn để kích hoạt.", + "channel_added": "Đã thêm kênh", + "are_you_sure_disable_channel": "Bạn có chắc chắn muốn vô hiệu hóa kênh này không?", + "disable_channel_title": "Vô hiệu hóa kênh", + "channel_disabled": "Kênh đã bị vô hiệu hóa", + "are_you_sure_delete_channel": "Bạn có chắc chắn muốn xóa kênh này không?", + "delete_channel_title": "Xóa kênh", + "delete_posts_before_channel": "Bạn phải xóa tất cả các bài đăng liên kết với kênh này trước khi xóa nó", + "channel_deleted": "Đã xóa kênh", + "channel_enabled": "Kênh đã được kích hoạt", + "time_table_slots": "Khung giờ thời gian biểu", + "channel_id_copied": "Đã sao chép ID kênh vào bộ nhớ tạm", + "settings_updated": "Đã cập nhật cài đặt", + "customer_updated": "Đã cập nhật khách hàng", + "custom_url": "URL tùy chỉnh", + "picture": "Hình ảnh", + "upgrade_required": "Bạn cần nâng cấp để sử dụng tính năng này", + "move_to_billing": "Chuyển đến thanh toán", + "payment_required": "Yêu cầu thanh toán", + "no_content": "không có nội dung", + "select_customer_tooltip": "Chọn khách hàng", + "customers": "Khách hàng", + "hour": "Giờ", + "minutes": "Phút", + "updated": "Đã cập nhật", + "change_bot_picture_title": "Thay đổi ảnh Bot", + "select_customer_label": "Chọn khách hàng", + "start_typing": "Bắt đầu nhập...", + "choose_set_or_continue": "Chọn một bộ hoặc tiếp tục mà không có bộ nào", + "continue_without_set": "Tiếp tục mà không có bộ", + "select_set": "Chọn một bộ", + "channel_settings": "Cài đặt", + "post_needs_content_or_image": "Bài đăng của bạn cần có ít nhất một ký tự hoặc một hình ảnh.", + "please_fix_your_settings": "Vui lòng sửa cài đặt của bạn", + "shortlink_urls_question": "Bạn có muốn rút gọn các URL không? Điều này sẽ giúp bạn thống kê số lần nhấp chuột", + "yes_shortlink_it": "Vâng, hãy rút gọn!", + "added_successfully": "Thêm thành công", + "updated_successfully": "Cập nhật thành công", + "create_post_title": "Tạo bài đăng", + "post_preview": "Xem trước bài đăng", + "check_circles_above": "Kiểm tra các vòng tròn phía trên", + "create_output": "Tạo kết quả", + "assistant_initial_message": "Chào bạn! Tôi có thể giúp bạn chỉnh sửa các bài đăng trên mạng xã hội.", + "no_longer_global_mode": "Không còn ở chế độ toàn cục", + "two_days": "Hai ngày", + "three_days": "Ba ngày", + "four_days": "Bốn ngày", + "five_days": "Năm ngày", + "six_days": "Sáu ngày", + "two_weeks": "Hai tuần", + "repeat_post_every_label": "Lặp lại bài đăng mỗi", + "add_new_tag": "Thêm thẻ mới", + "tag_name": "Tên", + "post_is_too_long": "bài đăng quá dài, vui lòng chỉnh sửa", + "your_post_should_have_at_least_one_character_or_one_image": "Bài đăng của bạn phải có ít nhất một ký tự hoặc một hình ảnh.", + "internal_edit": "Chỉnh sửa nội bộ", + "are_you_sure_go_back_to_global_mode": "Hành động này không thể hoàn tác. Bạn có chắc chắn muốn quay lại chế độ toàn cục không?", + "yes_go_back_to_global_mode": "Vâng, quay lại chế độ toàn cục", + "are_you_sure_delete_this_post": "Bạn có chắc chắn muốn xóa bài đăng này không?", + "cant_edit_networks_when_creating_set": "Bạn không thể chỉnh sửa các mạng khi đang tạo một bộ", + "click_to_exit_global_editing": "Nhấn vào nút này để thoát chỉnh sửa toàn cục và tùy chỉnh bài đăng cho kênh này", + "edit_content": "Chỉnh sửa nội dung", + "editing_a_specific_network": "Đang chỉnh sửa một mạng cụ thể", + "back_to_global": "Quay lại toàn cục", + "delete_post_tooltip": "Xóa bài đăng", + "drop_files_here_to_upload": "Kéo thả tệp vào đây để tải lên", + "insert_emoji": "Chèn biểu tượng cảm xúc", + "write_something": "Viết gì đó …" } diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index 805ec576..ba3e416c 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -535,5 +535,129 @@ "billing_cancel_anytime_short": "随时取消。", "billing_pay_0_start_trial": "今日支付$0 - 开始您的免费试用!", "billing_pay_now": "立即支付", - "billing_per_month": "每月" + "billing_per_month": "每月", + "select_channels": "选择频道", + "start_a_new_chat": "开始新聊天", + "your_assistant": "你的助手", + "agent_welcome_message": "你好,我是你的 Postiz 代理 🙌🏻。\n\n我可以为多个频道安排一条或多条帖子,并生成图片和视频。\n\n你可以从左侧菜单中选择你想要使用的频道。\n\n你可以从右侧菜单中查看你之前的对话。\n\n你也可以将我用作 MCP 服务器,查看设置 >> 公共 API。", + "last_github_trending": "最新 Github 趋势", + "next_predicted_github_trending": "下一个预测的 Github 趋势", + "repository": "仓库", + "date": "日期", + "total_stars": "总星标数", + "total_forks": "总分支数", + "continue_with": "继续使用", + "email_address": "电子邮件地址", + "email_already_exists": "电子邮件已存在", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "编辑自动发布", + "add_autopost_title": "添加自动发布", + "webhook_deleted_successfully": "Webhook 删除成功", + "all_integrations": "所有集成", + "specific_integrations": "特定集成", + "post_on_next_available_slot": "在下一个可用时间发布", + "post_immediately": "立即发布", + "could_not_use_rss_feed": "无法使用此 RSS 源", + "rss_valid": "RSS 有效!", + "autopost_updated_successfully": "自动发布更新成功", + "autopost_added_successfully": "自动发布添加成功", + "write_your_post_placeholder": "写下你的帖子...", + "select_or_upload_pictures_max_5": "选择或上传图片(每次最多5张)。", + "you_can_drag_drop_pictures": "你也可以拖放图片。", + "you_dont_have_any_media_yet": "你还没有任何媒体", + "media_library": "媒体库", + "media_settings": "媒体设置", + "media_editor": "媒体编辑器", + "close": "关闭", + "me": "我", + "noname": "无名", + "password_reset_link_expired": "您的密码重置链接已过期,请重试。", + "invalid_api_key": "无效的API密钥", + "could_not_connect_to_platform": "无法连接到平台", + "web3_provider": "Web3提供商", + "add_provider_title": "添加提供商", + "profile_updated": "个人资料已更新", + "sets": "集合", + "invitation_link_sent": "邀请链接已发送", + "send_invitation_link": "发送邀请链接", + "copy_link": "复制链接", + "are_you_sure_remove_team_member": "您确定要移除此团队成员吗?", + "admin": "管理员", + "super_admin": "超级管理员", + "update_webhook": "更新Webhook", + "add_webhook": "添加 webhook", + "webhook_updated_successfully": "Webhook 更新成功", + "webhook_added_successfully": "Webhook 添加成功", + "webhook_sent": "Webhook 已发送", + "today": "今天", + "channel_disconnected_click_to_reconnect": "频道已断开连接,点击重新连接。", + "channel_disabled_upgrade_plan": "该频道已被禁用,请升级您的套餐以启用。", + "channel_added": "频道已添加", + "are_you_sure_disable_channel": "您确定要禁用此频道吗?", + "disable_channel_title": "禁用频道", + "channel_disabled": "频道已禁用", + "are_you_sure_delete_channel": "您确定要删除此频道吗?", + "delete_channel_title": "删除频道", + "delete_posts_before_channel": "您需要先删除与此频道相关的所有帖子,然后才能删除该频道", + "channel_deleted": "频道已删除", + "channel_enabled": "频道已启用", + "time_table_slots": "时间表时段", + "channel_id_copied": "频道 ID 已复制到剪贴板", + "settings_updated": "设置已更新", + "customer_updated": "客户已更新", + "custom_url": "自定义 URL", + "picture": "图片", + "upgrade_required": "您需要升级才能使用此功能", + "move_to_billing": "前往结算", + "payment_required": "需要付款", + "no_content": "无内容", + "select_customer_tooltip": "选择客户", + "customers": "客户", + "hour": "小时", + "minutes": "分钟", + "updated": "已更新", + "change_bot_picture_title": "更换机器人头像", + "select_customer_label": "选择客户", + "start_typing": "开始输入...", + "choose_set_or_continue": "选择一个集合或继续不选择", + "continue_without_set": "不选择集合继续", + "select_set": "选择集合", + "channel_settings": "设置", + "post_needs_content_or_image": "您的帖子至少需要一个字符或一张图片。", + "please_fix_your_settings": "请修正您的设置", + "shortlink_urls_question": "您想要对网址进行短链处理吗?这样可以统计点击次数。", + "yes_shortlink_it": "是的,短链处理!", + "added_successfully": "添加成功", + "updated_successfully": "更新成功", + "create_post_title": "创建帖子", + "post_preview": "帖子预览", + "check_circles_above": "请检查上方的圆圈", + "create_output": "生成内容", + "assistant_initial_message": "您好!我可以帮助您优化社交媒体帖子。", + "no_longer_global_mode": "已退出全局模式", + "two_days": "两天", + "three_days": "三天", + "four_days": "四天", + "five_days": "五天", + "six_days": "六天", + "two_weeks": "两周", + "repeat_post_every_label": "每隔...重复发布", + "add_new_tag": "添加新标签", + "tag_name": "名称", + "post_is_too_long": "帖子太长,请修改", + "your_post_should_have_at_least_one_character_or_one_image": "您的帖子至少应包含一个字符或一张图片。", + "internal_edit": "内部编辑", + "are_you_sure_go_back_to_global_mode": "此操作不可逆。您确定要返回全局模式吗?", + "yes_go_back_to_global_mode": "是的,返回全局模式", + "are_you_sure_delete_this_post": "您确定要删除此帖子吗?", + "cant_edit_networks_when_creating_set": "创建集合时无法编辑网络", + "click_to_exit_global_editing": "点击此按钮退出全局编辑,并为该频道自定义帖子", + "edit_content": "编辑内容", + "editing_a_specific_network": "正在编辑特定网络", + "back_to_global": "返回全局", + "delete_post_tooltip": "删除帖子", + "drop_files_here_to_upload": "将文件拖到此处以上传", + "insert_emoji": "插入表情", + "write_something": "写点什么…" } From 9e0eff7e5a6f3161a825a11e633bbe65e930096c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 3 Jan 2026 11:00:06 +0700 Subject: [PATCH 085/340] feat: payload wizard --- .../src/components/media/media.component.tsx | 33 +++- .../public-api/public.component.tsx | 176 ++++++++++-------- 2 files changed, 122 insertions(+), 87 deletions(-) diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 74938f18..7d7605dd 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -257,9 +257,8 @@ export const MediaBox: FC<{ const addToUpload = useCallback(async (e: ChangeEvent<HTMLInputElement>) => { const files = Array.from(e.target.files).slice(0, 5); - for (const file of files) { - uppy.addFile(file); - } + // @ts-ignore + uppy.addFiles(files); }, []); const dragAndDrop = useCallback( @@ -335,8 +334,15 @@ export const MediaBox: FC<{ > {!isLoading && !!data?.results?.length && ( <div className="flex-1 text-[14px] font-[600] whitespace-pre-line"> - {t('select_or_upload_pictures_max_5', 'Select or upload pictures (maximum 5 at a time).')}{'\n'} - {t('you_can_drag_drop_pictures', 'You can also drag & drop pictures.')} + {t( + 'select_or_upload_pictures_max_5', + 'Select or upload pictures (maximum 5 at a time).' + )} + {'\n'} + {t( + 'you_can_drag_drop_pictures', + 'You can also drag & drop pictures.' + )} </div> )} <input @@ -384,11 +390,21 @@ export const MediaBox: FC<{ <> <NoMediaIcon /> <div className="text-[20px] font-[600]"> - {t('you_dont_have_any_media_yet', "You don't have any media yet")} + {t( + 'you_dont_have_any_media_yet', + "You don't have any media yet" + )} </div> <div className="whitespace-pre-line text-newTextColor/[0.6] text-center"> - {t('select_or_upload_pictures_max_5', 'Select or upload pictures (maximum 5 at a time).')} {'\n'} - {t('you_can_drag_drop_pictures', 'You can also drag & drop pictures.')} + {t( + 'select_or_upload_pictures_max_5', + 'Select or upload pictures (maximum 5 at a time).' + )}{' '} + {'\n'} + {t( + 'you_can_drag_drop_pictures', + 'You can also drag & drop pictures.' + )} </div> <div className="forceChange">{btn}</div> </> @@ -616,7 +632,6 @@ export const MultiMediaComponent: FC<{ } }, [changeMedia, t]); - return ( <> <div className="b1 flex flex-col gap-[8px] rounded-bl-[8px] select-none w-full"> diff --git a/apps/frontend/src/components/public-api/public.component.tsx b/apps/frontend/src/components/public-api/public.component.tsx index 99728f96..570342bc 100644 --- a/apps/frontend/src/components/public-api/public.component.tsx +++ b/apps/frontend/src/components/public-api/public.component.tsx @@ -9,7 +9,7 @@ import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; export const PublicComponent = () => { const user = useUser(); - const { backendUrl } = useVariables(); + const { backendUrl, frontEndUrl } = useVariables(); const toaster = useToaster(); const [reveal, setReveal] = useState(false); const [reveal2, setReveal2] = useState(false); @@ -28,91 +28,111 @@ export const PublicComponent = () => { return null; } return ( - <div className="flex flex-col"> - <h3 className="text-[20px]">{t('public_api', 'Public API')}</h3> - <div className="text-customColor18 mt-[4px]"> - {t( - 'use_postiz_api_to_integrate_with_your_tools', - 'Use Postiz API to integrate with your tools.' - )} - <br /> - <a - className="underline hover:font-bold hover:underline" - href="https://docs.postiz.com/public-api" - target="_blank" - > + <div className="flex flex-col gap-[20px]"> + <div className="flex flex-col"> + <h3 className="text-[20px]">{t('public_api', 'Public API')}</h3> + <div className="text-customColor18 mt-[4px]"> {t( - 'read_how_to_use_it_over_the_documentation', - 'Read how to use it over the documentation.' - )} - </a> - <a - className="underline hover:font-bold hover:underline" - href="https://www.npmjs.com/package/n8n-nodes-postiz" - target="_blank" - ><br /> - {t( - 'check_n8n', - 'Check out our N8N custom node for Postiz.' - )} - </a> - </div> - <div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]"> - <div className="flex items-center"> - {reveal ? ( - user.publicApi - ) : ( - <> - <div className="blur-sm">{user.publicApi.slice(0, -5)}</div> - <div>{user.publicApi.slice(-5)}</div> - </> + 'use_postiz_api_to_integrate_with_your_tools', + 'Use Postiz API to integrate with your tools.' )} + <br /> + <a + className="underline hover:font-bold hover:underline" + href="https://docs.postiz.com/public-api" + target="_blank" + > + {t( + 'read_how_to_use_it_over_the_documentation', + 'Read how to use it over the documentation.' + )} + </a> + <a + className="underline hover:font-bold hover:underline" + href="https://www.npmjs.com/package/n8n-nodes-postiz" + target="_blank" + > + <br /> + {t('check_n8n', 'Check out our N8N custom node for Postiz.')} + </a> </div> - <div> - {!reveal ? ( - <Button onClick={() => setReveal(true)}> - {t('reveal', 'Reveal')} - </Button> - ) : ( - <Button onClick={copyToClipboard}> - {t('copy_key', 'Copy Key')} - </Button> - )} + <div className="flex flex-col"> + <div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]"> + <div className="flex items-center"> + {reveal ? ( + user.publicApi + ) : ( + <> + <div className="blur-sm">{user.publicApi.slice(0, -5)}</div> + <div>{user.publicApi.slice(-5)}</div> + </> + )} + </div> + <div> + {!reveal ? ( + <Button onClick={() => setReveal(true)}> + {t('reveal', 'Reveal')} + </Button> + ) : ( + <Button onClick={copyToClipboard}> + {t('copy_key', 'Copy Key')} + </Button> + )} + </div> + </div> </div> </div> - <h3 className="text-[20px]">{t('mcp', 'MCP')}</h3> - <div className="text-customColor18 mt-[4px]"> - {t( - 'connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster', - 'Connect Postiz MCP server to your client (Http streaming) to schedule your posts faster.' - )} - </div> - <div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]"> - <div className="flex items-center"> - {reveal2 ? ( - `${backendUrl}/mcp/` + user.publicApi - ) : ( - <> - <div className="blur-sm"> - {(`${backendUrl}/mcp/` + user.publicApi).slice(0, -5)} - </div> - <div> - {(`${backendUrl}/mcp/` + user.publicApi).slice(-5)} - </div> - </> + <div className="flex flex-col"> + <h3 className="text-[20px]">{t('mcp', 'MCP')}</h3> + <div className="text-customColor18 mt-[4px]"> + {t( + 'connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster', + 'Connect Postiz MCP server to your client (Http streaming) to schedule your posts faster.' )} </div> - <div> - {!reveal2 ? ( - <Button onClick={() => setReveal2(true)}> - {t('reveal', 'Reveal')} - </Button> - ) : ( - <Button onClick={copyToClipboard2}> - {t('copy_key', 'Copy Key')} - </Button> - )} + <div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]"> + <div className="flex items-center"> + {reveal2 ? ( + `${backendUrl}/mcp/` + user.publicApi + ) : ( + <> + <div className="blur-sm"> + {(`${backendUrl}/mcp/` + user.publicApi).slice(0, -5)} + </div> + <div>{(`${backendUrl}/mcp/` + user.publicApi).slice(-5)}</div> + </> + )} + </div> + <div> + {!reveal2 ? ( + <Button onClick={() => setReveal2(true)}> + {t('reveal', 'Reveal')} + </Button> + ) : ( + <Button onClick={copyToClipboard2}> + {t('copy_key', 'Copy Key')} + </Button> + )} + </div> + </div> + </div> + + <div className="flex flex-col"> + <h3 className="text-[20px]">Building your Postiz payload</h3> + <div className="text-customColor18 mt-[4px] whitespace-pre-line"> + Sending a POST request to <strong className="text-textColor">/posts</strong> might feel a bit overwhelming as many + platforms have different requirements.{'\n'} + We have created an easy way to build your Postiz payload to schedule + posts. {'\n'} + You can use the Postiz wizard, and schedule a post with our UI, after + you added all your text and settings, the wizard will generate the + payload for you.{'\n'} + </div> + <div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]"> + <Button onClick={() => window.open(`${frontEndUrl}/modal/dark/all`, '_blank')}> + Open the payload wizard + </Button> </div> </div> </div> From d8a6215155050224bbaf644fc6037cae947d0d8a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 11:35:15 +0700 Subject: [PATCH 086/340] feat: before split --- apps/backend/src/app.module.ts | 4 + apps/orchestrator/.gitignore | 8 + apps/orchestrator/.swcrc | 38 ++ apps/orchestrator/nest-cli.json | 20 + apps/orchestrator/package.json | 14 + .../src/activities/post.activity.ts | 249 ++++++++++ apps/orchestrator/src/app.module.ts | 22 + apps/orchestrator/src/main.ts | 15 + apps/orchestrator/src/workflows/index.ts | 1 + .../src/workflows/post.workflow.ts | 331 +++++++++++++ apps/orchestrator/tsconfig.build.json | 23 + apps/orchestrator/tsconfig.json | 11 + apps/workers/src/app/plugs.controller.ts | 46 -- apps/workers/src/app/posts.controller.ts | 22 - .../helpers/src/utils/concurrency.service.ts | 6 +- .../integrations/integration.service.ts | 75 +-- .../database/prisma/posts/posts.repository.ts | 26 + .../database/prisma/posts/posts.service.ts | 454 ++++++------------ .../src/integrations/social.abstract.ts | 67 +-- .../integrations/social/linkedin.provider.ts | 15 +- .../social/social.integrations.interface.ts | 19 +- .../integrations/social/tiktok.provider.ts | 1 - .../src/temporal/temporal.module.ts | 28 ++ .../src/temporal/temporal.register.ts | 50 ++ .../src/temporal/temporal.search.attribute.ts | 14 + package.json | 10 +- pnpm-lock.yaml | 424 ++++++++++++++++ tsconfig.base.json | 1 + 28 files changed, 1518 insertions(+), 476 deletions(-) create mode 100644 apps/orchestrator/.gitignore create mode 100644 apps/orchestrator/.swcrc create mode 100644 apps/orchestrator/nest-cli.json create mode 100644 apps/orchestrator/package.json create mode 100644 apps/orchestrator/src/activities/post.activity.ts create mode 100644 apps/orchestrator/src/app.module.ts create mode 100644 apps/orchestrator/src/main.ts create mode 100644 apps/orchestrator/src/workflows/index.ts create mode 100644 apps/orchestrator/src/workflows/post.workflow.ts create mode 100644 apps/orchestrator/tsconfig.build.json create mode 100644 apps/orchestrator/tsconfig.json delete mode 100644 apps/workers/src/app/plugs.controller.ts create mode 100644 libraries/nestjs-libraries/src/temporal/temporal.module.ts create mode 100644 libraries/nestjs-libraries/src/temporal/temporal.register.ts create mode 100644 libraries/nestjs-libraries/src/temporal/temporal.search.attribute.ts diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index fb6d7bd4..d4f800d4 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -13,6 +13,8 @@ import { VideoModule } from '@gitroom/nestjs-libraries/videos/video.module'; import { SentryModule } from '@sentry/nestjs/setup'; import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception'; import { ChatModule } from '@gitroom/nestjs-libraries/chat/chat.module'; +import { getTemporalModule } from '@gitroom/nestjs-libraries/temporal/temporal.module'; +import { TemporalRegisterMissingSearchAttributesModule } from '@gitroom/nestjs-libraries/temporal/temporal.register'; @Global() @Module({ @@ -26,6 +28,8 @@ import { ChatModule } from '@gitroom/nestjs-libraries/chat/chat.module'; ThirdPartyModule, VideoModule, ChatModule, + getTemporalModule(false), + TemporalRegisterMissingSearchAttributesModule, ThrottlerModule.forRoot([ { ttl: 3600000, diff --git a/apps/orchestrator/.gitignore b/apps/orchestrator/.gitignore new file mode 100644 index 00000000..0dff6fb6 --- /dev/null +++ b/apps/orchestrator/.gitignore @@ -0,0 +1,8 @@ +dist/ +node_modules/ +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + diff --git a/apps/orchestrator/.swcrc b/apps/orchestrator/.swcrc new file mode 100644 index 00000000..7d41ef14 --- /dev/null +++ b/apps/orchestrator/.swcrc @@ -0,0 +1,38 @@ +{ + "jsc": { + "parser": { + "syntax": "typescript", + "tsx": false, + "decorators": true, + "dynamicImport": true + }, + "target": "es2020", + "baseUrl": "/Users/nevodavid/Projects/gitroom", + "paths": { + "@gitroom/backend/*": ["apps/backend/src/*"], + "@gitroom/cron/*": ["apps/cron/src/*"], + "@gitroom/frontend/*": ["apps/frontend/src/*"], + "@gitroom/helpers/*": ["libraries/helpers/src/*"], + "@gitroom/nestjs-libraries/*": ["libraries/nestjs-libraries/src/*"], + "@gitroom/react/*": ["libraries/react-shared-libraries/src/*"], + "@gitroom/plugins/*": ["libraries/plugins/src/*"], + "@gitroom/workers/*": ["apps/workers/src/*"], + "@gitroom/extension/*": ["apps/extension/src/*"] + }, + "keepClassNames": true, + "transform": { + "legacyDecorator": true, + "decoratorMetadata": true + }, + "loose": true + }, + "module": { + "type": "commonjs", + "strict": false, + "strictMode": true, + "lazy": false, + "noInterop": false + }, + "sourceMaps": true, + "minify": false +} \ No newline at end of file diff --git a/apps/orchestrator/nest-cli.json b/apps/orchestrator/nest-cli.json new file mode 100644 index 00000000..aef73dc1 --- /dev/null +++ b/apps/orchestrator/nest-cli.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://json.schemastore.org/nest-cli", + "collection": "@nestjs/schematics", + "monorepo": false, + "sourceRoot": "src", + "entryFile": "../../dist/orchestrator/apps/orchestrator/src/main", + "language": "ts", + "generateOptions": { + "spec": false + }, + "compilerOptions": { + "manualRestart": true, + "tsConfigPath": "./tsconfig.build.json", + "webpack": false, + "deleteOutDir": true, + "assets": [], + "watchAssets": false, + "plugins": [] + } +} diff --git a/apps/orchestrator/package.json b/apps/orchestrator/package.json new file mode 100644 index 00000000..daa427f2 --- /dev/null +++ b/apps/orchestrator/package.json @@ -0,0 +1,14 @@ +{ + "name": "postiz-orchestrator", + "version": "1.0.0", + "description": "", + "scripts": { + "dev": "dotenv -e ../../.env -- nest start --watch --entryFile=./apps/orchestrator/src/main", + "build": "cross-env NODE_ENV=production nest build", + "start": "dotenv -e ../../.env -- node --experimental-require-module ./dist/apps/orchestrator/src/main.js", + "pm2": "pm2 start pnpm --name orchestrator -- start" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts new file mode 100644 index 00000000..88fdcef0 --- /dev/null +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -0,0 +1,249 @@ +import { Injectable } from '@nestjs/common'; +import { Activity, ActivityMethod } from 'nestjs-temporal-core'; +import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; +import { + NotificationService, + NotificationType, +} from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; +import { Integration, Post, State } from '@prisma/client'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; +import { timer } from '@gitroom/helpers/utils/timer'; +import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; +import { WebhooksService } from '@gitroom/nestjs-libraries/database/prisma/webhooks/webhooks.service'; + +@Injectable() +@Activity() +export class PostActivity { + constructor( + private _postService: PostsService, + private _notificationService: NotificationService, + private _integrationManager: IntegrationManager, + private _integrationService: IntegrationService, + private _refreshIntegrationService: RefreshIntegrationService, + private _webhookService: WebhooksService + ) {} + + @ActivityMethod() + async updatePost(id: string, postId: string, releaseURL: string) { + return this._postService.updatePost(id, postId, releaseURL); + } + + @ActivityMethod() + async getPostsList(orgId: string, postId: string) { + return this._postService.getPostsRecursively(postId, true, orgId); + } + + @ActivityMethod() + async isCommentable(integration: Integration) { + const getIntegration = this._integrationManager.getSocialIntegration( + integration.providerIdentifier + ); + + return !!getIntegration.comment; + } + + @ActivityMethod() + async postComment( + postId: string, + lastPostId: string | undefined, + integration: Integration, + posts: Post[] + ) { + const getIntegration = this._integrationManager.getSocialIntegration( + integration.providerIdentifier + ); + + const newPosts = await this._postService.updateTags( + integration.organizationId, + posts + ); + + return getIntegration.comment( + integration.internalId, + postId, + lastPostId, + integration.token, + await Promise.all( + (newPosts || []).map(async (p) => ({ + id: p.id, + message: stripHtmlValidation( + getIntegration.editor, + p.content, + true, + false, + !/<\/?[a-z][\s\S]*>/i.test(p.content), + getIntegration.mentionFormat + ), + settings: JSON.parse(p.settings || '{}'), + media: await this._postService.updateMedia( + p.id, + JSON.parse(p.image || '[]'), + getIntegration?.convertToJPEG || false + ), + })) + ), + integration + ); + } + + @ActivityMethod() + async postSocial(integration: Integration, posts: Post[]) { + const getIntegration = this._integrationManager.getSocialIntegration( + integration.providerIdentifier + ); + + const newPosts = await this._postService.updateTags( + integration.organizationId, + posts + ); + + return getIntegration.post( + integration.internalId, + integration.token, + await Promise.all( + (newPosts || []).map(async (p) => ({ + id: p.id, + message: stripHtmlValidation( + getIntegration.editor, + p.content, + true, + false, + !/<\/?[a-z][\s\S]*>/i.test(p.content), + getIntegration.mentionFormat + ), + settings: JSON.parse(p.settings || '{}'), + media: await this._postService.updateMedia( + p.id, + JSON.parse(p.image || '[]'), + getIntegration?.convertToJPEG || false + ), + })) + ), + integration + ); + } + + @ActivityMethod() + async inAppNotification( + orgId: string, + subject: string, + message: string, + sendEmail = false, + digest = false, + type: NotificationType = 'success' + ) { + return this._notificationService.inAppNotification( + orgId, + subject, + message, + sendEmail, + digest, + type + ); + } + + @ActivityMethod() + async globalPlugs(integration: Integration) { + return this._postService.checkPlugs( + integration.organizationId, + integration.providerIdentifier, + integration.id + ); + } + + @ActivityMethod() + async changeState(id: string, state: State, err?: any, body?: any) { + return this._postService.changeState(id, state, err, body); + } + + @ActivityMethod() + async internalPlugs(integration: Integration, settings: any) { + return this._postService.checkInternalPlug( + integration, + integration.organizationId, + integration.id, + settings + ); + } + + @ActivityMethod() + async sendWebhooks(postId: string, orgId: string, integrationId: string) { + const webhooks = (await this._webhookService.getWebhooks(orgId)).filter( + (f) => { + return ( + f.integrations.length === 0 || + f.integrations.some((i) => i.integration.id === integrationId) + ); + } + ); + + const post = await this._postService.getPostByForWebhookId(postId); + return Promise.all( + webhooks.map(async (webhook) => { + try { + await fetch(webhook.url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(post), + }); + } catch (e) { + /**empty**/ + } + }) + ); + } + @ActivityMethod() + async processPlug(data: { + plugId: string; + postId: string; + delay: number; + totalRuns: number; + currentRun: number; + }) { + return this._integrationService.processPlugs(data); + } + + @ActivityMethod() + async processInternalPlug(data: { + post: string; + originalIntegration: string; + integration: string; + plugName: string; + orgId: string; + delay: number; + information: any; + }) { + return this._integrationService.processInternalPlug(data); + } + + @ActivityMethod() + async refreshToken( + integration: Integration + ): Promise<false | AuthTokenDetails> { + const getIntegration = this._integrationManager.getSocialIntegration( + integration.providerIdentifier + ); + + try { + const refresh = await this._refreshIntegrationService.refresh( + integration + ); + if (!refresh) { + return false; + } + + if (getIntegration.refreshWait) { + await timer(10000); + } + + return refresh; + } catch (err) { + return false; + } + } +} diff --git a/apps/orchestrator/src/app.module.ts b/apps/orchestrator/src/app.module.ts new file mode 100644 index 00000000..cf5115cb --- /dev/null +++ b/apps/orchestrator/src/app.module.ts @@ -0,0 +1,22 @@ +import { Module } from '@nestjs/common'; +import { PostActivity } from '@gitroom/orchestrator/activities/post.activity'; +import { getTemporalModule } from '@gitroom/nestjs-libraries/temporal/temporal.module'; +import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; +import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; + +const activities = [ + PostActivity, +]; +@Module({ + imports: [ + BullMqModule, + DatabaseModule, + getTemporalModule(true, require.resolve('./workflows'), activities), + ], + controllers: [], + providers: [...activities], + get exports() { + return [...this.providers, ...this.imports]; + }, +}) +export class AppModule {} diff --git a/apps/orchestrator/src/main.ts b/apps/orchestrator/src/main.ts new file mode 100644 index 00000000..03bf80a2 --- /dev/null +++ b/apps/orchestrator/src/main.ts @@ -0,0 +1,15 @@ +import 'source-map-support/register'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +dayjs.extend(utc); + +import { NestFactory } from '@nestjs/core'; +import { AppModule } from '@gitroom/orchestrator/app.module'; + +async function bootstrap() { + // some comment again + const app = await NestFactory.createApplicationContext(AppModule); + app.enableShutdownHooks(); +} + +bootstrap(); diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts new file mode 100644 index 00000000..a64cfc6e --- /dev/null +++ b/apps/orchestrator/src/workflows/index.ts @@ -0,0 +1 @@ +export * from './post.workflow'; diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts new file mode 100644 index 00000000..aef3e4a1 --- /dev/null +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -0,0 +1,331 @@ +import { PostActivity } from '@gitroom/orchestrator/activities/post.activity'; +import { + ActivityFailure, + ApplicationFailure, + startChild, + proxyActivities, + sleep, +} from '@temporalio/workflow'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { capitalize, sortBy } from 'lodash'; +import { PostResponse } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { TypedSearchAttributes } from '@temporalio/common'; +import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; + +const { + getPostsList, + inAppNotification, + postSocial, + postComment, + refreshToken, + internalPlugs, + changeState, + globalPlugs, + updatePost, + processInternalPlug, + processPlug, + sendWebhooks, + isCommentable, +} = proxyActivities<PostActivity>({ + startToCloseTimeout: '10 minute', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, +}); + +export async function postWorkflow({ + postId, + organizationId, + postNow = false, +}: { + postId: string; + organizationId: string; + postNow?: boolean; +}) { + const startTime = new Date(); + // get all the posts and comments to post + const postsList = await getPostsList(organizationId, postId); + const [post] = postsList; + + // in case doesn't exists for some reason, fail it + if (!post) { + return; + } + + // if it's a repeatable post, we should ignore this + if (!postNow) { + await sleep(dayjs(post.publishDate).diff(dayjs(), 'millisecond')); + } + + // if refresh is needed from last time, let's inform the user + if (post.integration?.refreshNeeded) { + await inAppNotification( + post.organizationId, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name} because you need to reconnect it. Please enable it and try again.`, + true, + false, + 'info' + ); + return; + } + + // if it's disabled, inform the user + if (post.integration?.disabled) { + await inAppNotification( + post.organizationId, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name} because it's disabled. Please enable it and try again.`, + true, + false, + 'info' + ); + return; + } + + // Do we need to post comment for this social? + const toComment = + postsList.length === 1 ? false : await isCommentable(post.integration); + + // list of all the saved results + const postsResults: PostResponse[] = []; + + // iterate over the posts + for (let i = 0; i < postsList.length; i++) { + // this is a small trick to repeat an action in case of token refresh + while (true) { + try { + // first post the main post + if (i === 0) { + postsResults.push( + ...(await postSocial(post.integration as Integration, [ + postsList[i], + ])) + ); + + // then post the comments if any + } else { + if (!toComment) { + break; + } + postsResults.push( + ...(await postComment( + postsResults[0].postId, + postsResults.length === 1 ? undefined : postsResults[i - 1].id, + post.integration, + [postsList[i]] + )) + ); + } + + // mark post as successful + await updatePost( + postsList[i].id, + postsResults[i].postId, + postsResults[i].releaseURL + ); + + // break the current while to move to the next post + break; + } catch (err) { + // if token refresh is needed, do it and repeat + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'refresh_token' + ) { + const refresh = await refreshToken(post.integration); + if (!refresh) { + return false; + } + + post.integration.token = refresh.accessToken; + continue; + } + + // for other errors, change state and inform the user if needed + await changeState(postsList[0].id, 'ERROR', err, postsList); + + // specific case for bad body errors + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'bad_body' + ) { + await inAppNotification( + post.organizationId, + `Error posting on ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `An error occurred while posting on ${ + post.integration?.providerIdentifier + }${err?.message ? `: ${err?.message}` : ``}`, + true, + false, + 'fail' + ); + return false; + } + + return false; + } + } + } + + // send notification on a sucessful post + await inAppNotification( + post.integration.organizationId, + `Your post has been published on ${capitalize( + post.integration.providerIdentifier + )}`, + `Your post has been published on ${capitalize( + post.integration.providerIdentifier + )} at ${postsResults[0].releaseURL}`, + true, + true + ); + + // send webhooks for the post + await sendWebhooks( + postsResults[0].postId, + post.organizationId, + post.integration.id + ); + + // load internal plugs like repost by other users + const internalPlugsList = await internalPlugs( + post.integration, + JSON.parse(post.settings) + ); + + // load global plugs, like repost a post if it gets to a certain number of likes + const globalPlugsList = (await globalPlugs(post.integration)).reduce( + (all, current) => { + for (let i = 1; i <= current.totalRuns; i++) { + all.push({ + ...current, + delay: current.delay * i, + }); + } + + return all; + }, + [] + ); + + // Check if the post is repeatable + const repeatPost = !post.intervalInDays + ? [] + : [ + { + type: 'repeat-post', + delay: + post.intervalInDays * 24 * 60 * 60 * 1000 - + (new Date().getTime() - startTime.getTime()), + }, + ]; + + // Sort all the actions by delay, so we can process them in order + const list = sortBy( + [...internalPlugsList, ...globalPlugsList, ...repeatPost], + 'delay' + ); + + // process all the plugs in order, we are using while because in some cases we need to remove items from the list + while (list.length > 0) { + // get the next to process + const todo = list.shift(); + + // wait for the delay + await sleep(todo.delay); + + // process internal plug + if (todo.type === 'internal-plug') { + while (true) { + try { + await processInternalPlug({ ...todo, post: postsResults[0].postId }); + } catch (err) { + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'refresh_token' + ) { + const refresh = await refreshToken(post.integration); + if (!refresh) { + return false; + } + + post.integration.token = refresh.accessToken; + continue; + } + } + break; + } + } + + // process global plug + if (todo.type === 'global') { + while (true) { + try { + const process = await processPlug({ + ...todo, + postId: postsResults[0].postId, + }); + if (process) { + const toDelete = list + .reduce((all, current, index) => { + if (current.plugId === todo.plugId) { + all.push(index); + } + + return all; + }, []) + .reverse(); + + for (const index of toDelete) { + list.splice(index, 1); + } + } + } catch (err) { + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'refresh_token' + ) { + const refresh = await refreshToken(post.integration); + if (!refresh) { + return false; + } + + post.integration.token = refresh.accessToken; + continue; + } + } + break; + } + } + + // process repeat post in a new workflow, this is important so the other plugs can keep running + if (todo.type === 'repeat-post') { + await startChild(postWorkflow, { + parentClosePolicy: 'ABANDON', + args: [ + { + postId, + organizationId, + postNow: true, + }, + ], + workflowId: `post_${post.id}_${makeId(10)}`, + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: postId, + }, + ]), + }); + } + } +} diff --git a/apps/orchestrator/tsconfig.build.json b/apps/orchestrator/tsconfig.build.json new file mode 100644 index 00000000..bf14cec5 --- /dev/null +++ b/apps/orchestrator/tsconfig.build.json @@ -0,0 +1,23 @@ +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"], + "compilerOptions": { + "module": "CommonJS", + "resolveJsonModule": true, + "declaration": true, + "removeComments": true, + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "allowSyntheticDefaultImports": true, + "target": "ES2021", + "sourceMap": true, + "incremental": true, + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false, + "strictBindCallApply": false, + "forceConsistentCasingInFileNames": false, + "noFallthroughCasesInSwitch": false, + "outDir": "./dist" + } +} diff --git a/apps/orchestrator/tsconfig.json b/apps/orchestrator/tsconfig.json new file mode 100644 index 00000000..d2cc80fd --- /dev/null +++ b/apps/orchestrator/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "module": "commonjs", + "declaration": true, + "removeComments": true, + "allowSyntheticDefaultImports": true, + "target": "es2017", + "sourceMap": true + } +} diff --git a/apps/workers/src/app/plugs.controller.ts b/apps/workers/src/app/plugs.controller.ts deleted file mode 100644 index a7d1134b..00000000 --- a/apps/workers/src/app/plugs.controller.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { EventPattern, Transport } from '@nestjs/microservices'; -import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; - -@Controller() -export class PlugsController { - constructor(private _integrationService: IntegrationService) {} - - @EventPattern('plugs', Transport.REDIS) - async plug(data: { - plugId: string; - postId: string; - delay: number; - totalRuns: number; - currentRun: number; - }) { - try { - return await this._integrationService.processPlugs(data); - } catch (err) { - console.log( - "Unhandled error, let's avoid crashing the plug worker", - err - ); - } - } - - @EventPattern('internal-plugs', Transport.REDIS) - async internalPlug(data: { - post: string; - originalIntegration: string; - integration: string; - plugName: string; - orgId: string; - delay: number; - information: any; - }) { - try { - return await this._integrationService.processInternalPlug(data); - } catch (err) { - console.log( - "Unhandled error, let's avoid crashing the internal plugs worker", - err - ); - } - } -} diff --git a/apps/workers/src/app/posts.controller.ts b/apps/workers/src/app/posts.controller.ts index f41b77fc..2bfbaeb8 100644 --- a/apps/workers/src/app/posts.controller.ts +++ b/apps/workers/src/app/posts.controller.ts @@ -12,28 +12,6 @@ export class PostsController { private _autopostsService: AutopostService ) {} - @EventPattern('post', Transport.REDIS) - async post(data: { id: string }) { - console.log('processing', data); - try { - return await this._postsService.post(data.id); - } catch (err) { - console.log("Unhandled error, let's avoid crashing the post worker", err); - } - } - - @EventPattern('submit', Transport.REDIS) - async payout(data: { id: string; releaseURL: string }) { - try { - return await this._postsService.payout(data.id, data.releaseURL); - } catch (err) { - console.log( - "Unhandled error, let's avoid crashing the submit worker", - err - ); - } - } - @EventPattern('sendDigestEmail', Transport.REDIS) async sendDigestEmail(data: { subject: string; org: string; since: string }) { try { diff --git a/libraries/helpers/src/utils/concurrency.service.ts b/libraries/helpers/src/utils/concurrency.service.ts index 4f241493..bd2c02c1 100644 --- a/libraries/helpers/src/utils/concurrency.service.ts +++ b/libraries/helpers/src/utils/concurrency.service.ts @@ -1,6 +1,5 @@ import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; import Bottleneck from 'bottleneck'; -import { timer } from '@gitroom/helpers/utils/timer'; import { BadBody } from '@gitroom/nestjs-libraries/integrations/social.abstract'; const connection = new Bottleneck.IORedisConnection({ @@ -35,11 +34,12 @@ export const concurrency = async <T>( async () => { try { return await func(); - } catch (err) {} + } catch (err) { + console.log(err); + } } ); } catch (err) { - console.log(err); throw new BadBody( identifier, JSON.stringify({}), diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 5ab3c3c4..1ad9c831 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -1,4 +1,10 @@ -import { forwardRef, HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; +import { + forwardRef, + HttpException, + HttpStatus, + Inject, + Injectable, +} from '@nestjs/common'; import { IntegrationRepository } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.repository'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { @@ -442,40 +448,15 @@ export class IntegrationService { getIntegration.providerIdentifier ); - if ( - dayjs(getIntegration?.tokenExpiration).isBefore(dayjs()) || - forceRefresh - ) { - const data = await this._refreshIntegrationService.refresh( - getIntegration - ); - if (!data) { - return; - } - const { accessToken } = data; + // @ts-ignore + await getSocialIntegration?.[getAllInternalPlugs.methodName]?.( + getIntegration, + originalIntegration, + data.post, + data.information + ); - getIntegration.token = accessToken; - - if (getSocialIntegration.refreshWait) { - await timer(10000); - } - } - - try { - // @ts-ignore - await getSocialIntegration?.[getAllInternalPlugs.methodName]?.( - getIntegration, - originalIntegration, - data.post, - data.information - ); - } catch (err) { - if (err instanceof RefreshToken) { - return this.processInternalPlug(data, true); - } - - return; - } + return; } async processPlugs(data: { @@ -487,19 +468,13 @@ export class IntegrationService { }) { const getPlugById = await this._integrationRepository.getPlug(data.plugId); if (!getPlugById) { - return; + return true; } const integration = this._integrationManager.getSocialIntegration( getPlugById.integration.providerIdentifier ); - const findPlug = this._integrationManager - .getAllPlugs() - .find( - (p) => p.identifier === getPlugById.integration.providerIdentifier - )!; - // @ts-ignore const process = await integration[getPlugById.plugFunction]( getPlugById.integration, @@ -511,26 +486,14 @@ export class IntegrationService { ); if (process) { - return; + return true; } if (data.totalRuns === data.currentRun) { - return; + return true; } - this._workerServiceProducer.emit('plugs', { - id: 'plug_' + data.postId + '_' + findPlug.identifier, - options: { - delay: data.delay, - }, - payload: { - plugId: data.plugId, - postId: data.postId, - delay: data.delay, - totalRuns: data.totalRuns, - currentRun: data.currentRun + 1, - }, - }); + return false; } async createOrUpdatePlug( diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 96fccaff..5dba914f 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -694,6 +694,32 @@ export class PostsRepository { }); } + async getPostByForWebhookId(postId: string) { + return this._post.model.post.findMany({ + where: { + id: postId, + deletedAt: null, + parentPostId: null, + }, + select: { + id: true, + content: true, + publishDate: true, + releaseURL: true, + state: true, + integration: { + select: { + id: true, + name: true, + providerIdentifier: true, + picture: true, + type: true, + }, + }, + }, + }); + } + async getPostsSince(orgId: string, since: string) { return this._post.model.post.findMany({ where: { diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index ee82d18a..d20c7085 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -8,38 +8,29 @@ import { PostsRepository } from '@gitroom/nestjs-libraries/database/prisma/posts import { CreatePostDto } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto'; import dayjs from 'dayjs'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; -import { Integration, Post, Media, From } from '@prisma/client'; +import { Integration, Post, Media, From, State } from '@prisma/client'; import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; -import { capitalize, shuffle, uniq } from 'lodash'; +import { shuffle } from 'lodash'; import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service'; import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generator/create.generated.posts.dto'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; -import { - BadBody, - RefreshToken, -} from '@gitroom/nestjs-libraries/integrations/social.abstract'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; -import { timer } from '@gitroom/helpers/utils/timer'; -import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; import utc from 'dayjs/plugin/utc'; import { MediaService } from '@gitroom/nestjs-libraries/database/prisma/media/media.service'; import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service'; -import { WebhooksService } from '@gitroom/nestjs-libraries/database/prisma/webhooks/webhooks.service'; import { CreateTagDto } from '@gitroom/nestjs-libraries/dtos/posts/create.tag.dto'; import axios from 'axios'; import sharp from 'sharp'; import { UploadFactory } from '@gitroom/nestjs-libraries/upload/upload.factory'; import { Readable } from 'stream'; import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service'; -import { plainToInstance } from 'class-transformer'; -import { validate } from 'class-validator'; -import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; dayjs.extend(utc); import * as Sentry from '@sentry/nestjs'; -import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; +import { TemporalService } from 'nestjs-temporal-core'; +import { TypedSearchAttributes } from '@temporalio/common'; +import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; type PostWithConditionals = Post & { integration?: Integration; @@ -51,7 +42,6 @@ export class PostsService { private storage = UploadFactory.createStorage(); constructor( private _postRepository: PostsRepository, - private _workerServiceProducer: BullMqClient, private _integrationManager: IntegrationManager, private _notificationService: NotificationService, private _messagesService: MessagesService, @@ -59,9 +49,8 @@ export class PostsService { private _integrationService: IntegrationService, private _mediaService: MediaService, private _shortLinkService: ShortLinkService, - private _webhookService: WebhooksService, private openaiService: OpenaiService, - private _refreshIntegrationService: RefreshIntegrationService + private _temporalService: TemporalService ) {} checkPending15minutesBack() { @@ -71,6 +60,10 @@ export class PostsService { return this._postRepository.searchForMissingThreeHoursPosts(); } + updatePost(id: string, postId: string, releaseURL: string) { + return this._postRepository.updatePost(id, postId, releaseURL); + } + async getStatistics(orgId: string, id: string) { const getPost = await this.getPostsRecursively(id, true, orgId, true); const content = getPost.map((p) => p.content); @@ -292,102 +285,7 @@ export class PostsService { return this._postRepository.getOldPosts(orgId, date); } - async post(id: string) { - const allPosts = await this.getPostsRecursively(id, true); - const [firstPost, ...morePosts] = allPosts; - if (!firstPost) { - return; - } - - if (firstPost.integration?.refreshNeeded) { - await this._notificationService.inAppNotification( - firstPost.organizationId, - `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, - `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name} because you need to reconnect it. Please enable it and try again.`, - true, - false, - 'info' - ); - return; - } - - if (firstPost.integration?.disabled) { - await this._notificationService.inAppNotification( - firstPost.organizationId, - `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, - `We couldn't post to ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name} because it's disabled. Please enable it and try again.`, - true, - false, - 'info' - ); - return; - } - - try { - const finalPost = await this.postSocial(firstPost.integration!, [ - firstPost, - ...morePosts, - ]); - - if (firstPost?.intervalInDays) { - this._workerServiceProducer.emit('post', { - id, - options: { - delay: firstPost.intervalInDays * 86400000, - }, - payload: { - id: id, - }, - }); - } - - if (!finalPost?.postId || !finalPost?.releaseURL) { - await this._postRepository.changeState(firstPost.id, 'ERROR'); - await this._notificationService.inAppNotification( - firstPost.organizationId, - `Error posting on ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, - `An error occurred while posting on ${firstPost.integration?.providerIdentifier}`, - true, - false, - 'fail' - ); - - return; - } - } catch (err: any) { - await this._postRepository.changeState( - firstPost.id, - 'ERROR', - err, - allPosts - ); - if (err instanceof BadBody) { - await this._notificationService.inAppNotification( - firstPost.organizationId, - `Error posting on ${firstPost.integration?.providerIdentifier} for ${firstPost?.integration?.name}`, - `An error occurred while posting on ${ - firstPost.integration?.providerIdentifier - }${err?.message ? `: ${err?.message}` : ``}`, - true, - false, - 'fail' - ); - - console.error( - '[Error] posting on', - firstPost.integration?.providerIdentifier, - err.identifier, - err.json, - err.body, - err - ); - } - - return; - } - } - - private async updateTags(orgId: string, post: Post[]): Promise<Post[]> { + public async updateTags(orgId: string, post: Post[]): Promise<Post[]> { const plainText = JSON.stringify(post); const extract = Array.from( plainText.match(/\(post:[a-zA-Z0-9-_]+\)/g) || [] @@ -411,127 +309,7 @@ export class PostsService { return this.updateTags(orgId, JSON.parse(newPlainText) as Post[]); } - private async postSocial( - integration: Integration, - posts: Post[], - forceRefresh = false - ): Promise<Partial<{ postId: string; releaseURL: string }> | undefined> { - const getIntegration = this._integrationManager.getSocialIntegration( - integration.providerIdentifier - ); - - if (!getIntegration) { - return {}; - } - - if (dayjs(integration?.tokenExpiration).isBefore(dayjs()) || forceRefresh) { - const data = await this._refreshIntegrationService.refresh(integration); - - if (!data) { - return undefined; - } - - integration.token = data.accessToken; - - if (getIntegration.refreshWait) { - await timer(10000); - } - } - - const newPosts = await this.updateTags(integration.organizationId, posts); - - try { - const publishedPosts = await getIntegration.post( - integration.internalId, - integration.token, - await Promise.all( - (newPosts || []).map(async (p) => ({ - id: p.id, - message: stripHtmlValidation( - getIntegration.editor, - p.content, - true, - false, - !/<\/?[a-z][\s\S]*>/i.test(p.content), - getIntegration.mentionFormat - ), - settings: JSON.parse(p.settings || '{}'), - media: await this.updateMedia( - p.id, - JSON.parse(p.image || '[]'), - getIntegration?.convertToJPEG || false - ), - })) - ), - integration - ); - - for (const post of publishedPosts) { - try { - await this._postRepository.updatePost( - post.id, - post.postId, - post.releaseURL - ); - } catch (err) {} - } - - try { - await this._notificationService.inAppNotification( - integration.organizationId, - `Your post has been published on ${capitalize( - integration.providerIdentifier - )}`, - `Your post has been published on ${capitalize( - integration.providerIdentifier - )} at ${publishedPosts[0].releaseURL}`, - true, - true - ); - - await this._webhookService.digestWebhooks( - integration.organizationId, - dayjs(newPosts[0].publishDate).format('YYYY-MM-DDTHH:mm:00') - ); - - await this.checkPlugs( - integration.organizationId, - getIntegration.identifier, - integration.id, - publishedPosts[0].postId - ); - - await this.checkInternalPlug( - integration, - integration.organizationId, - publishedPosts[0].postId, - JSON.parse(newPosts[0].settings || '{}') - ); - } catch (err) {} - - return { - postId: publishedPosts[0].postId, - releaseURL: publishedPosts[0].releaseURL, - }; - } catch (err) { - if (err instanceof RefreshToken) { - return this.postSocial(integration, posts, true); - } - - if (err instanceof BadBody) { - throw err; - } - - throw new BadBody( - integration.providerIdentifier, - JSON.stringify(err), - {} as any, - '' - ); - } - } - - private async checkInternalPlug( + public async checkInternalPlug( integration: Integration, orgId: string, id: string, @@ -542,7 +320,7 @@ export class PostsService { }); if (plugs.length === 0) { - return; + return []; } const parsePlugs = plugs.reduce((all, [key, value]) => { @@ -559,32 +337,24 @@ export class PostsService { active: boolean; }[] = Object.values(parsePlugs); - for (const trigger of list || []) { - for (const int of trigger?.integrations || []) { - this._workerServiceProducer.emit('internal-plugs', { - id: 'plug_' + id + '_' + trigger.name + '_' + int.id, - options: { - delay: +trigger.delay, - }, - payload: { - post: id, - originalIntegration: integration.id, - integration: int.id, - plugName: trigger.name, - orgId: orgId, - delay: +trigger.delay, - information: trigger, - }, - }); - } - } + return (list || []).flatMap((trigger) => { + return (trigger?.integrations || []).flatMap((int) => ({ + type: 'internal-plug', + post: id, + originalIntegration: integration.id, + integration: int.id, + plugName: trigger.name, + orgId: orgId, + delay: +trigger.delay, + information: trigger, + })); + }); } - private async checkPlugs( + public async checkPlugs( orgId: string, providerName: string, - integrationId: string, - postId: string + integrationId: string ) { const loadAllPlugs = this._integrationManager.getAllPlugs(); const getPlugs = await this._integrationService.getPlugs( @@ -594,35 +364,51 @@ export class PostsService { const currentPlug = loadAllPlugs.find((p) => p.identifier === providerName); - for (const plug of getPlugs) { - const runPlug = currentPlug?.plugs?.find( - (p: any) => p.methodName === plug.plugFunction - )!; - if (!runPlug) { - continue; - } - - this._workerServiceProducer.emit('plugs', { - id: 'plug_' + postId + '_' + runPlug.identifier, - options: { - delay: runPlug.runEveryMilliseconds, - }, - payload: { + return getPlugs + .filter((plug) => { + return currentPlug?.plugs?.some( + (p: any) => p.methodName === plug.plugFunction + ); + }) + .map((plug) => { + const runPlug = currentPlug?.plugs?.find( + (p: any) => p.methodName === plug.plugFunction + )!; + return { + type: 'global', plugId: plug.id, - postId, delay: runPlug.runEveryMilliseconds, totalRuns: runPlug.totalRuns, - currentRun: 1, - }, + }; }); - } } async deletePost(orgId: string, group: string) { const post = await this._postRepository.deletePost(orgId, group); + if (post?.id) { - await this._workerServiceProducer.delete('post', post.id); - return { id: post.id }; + try { + const workflows = this._temporalService.client + .getRawClient() + ?.workflow.list({ + query: `WorkflowType="postWorkflow" AND postId="${post.id}" AND ExecutionStatus="Running"`, + }); + + for await (const executionInfo of workflows) { + try { + const workflow = + await this._temporalService.client.getWorkflowHandle( + executionInfo.workflowId + ); + if ( + workflow && + (await workflow.describe()).status.name !== 'TERMINATED' + ) { + await workflow.terminate(); + } + } catch (err) {} + } + } catch (err) {} } return { error: true }; @@ -632,6 +418,9 @@ export class PostsService { return this._postRepository.countPostsFromDay(orgId, date); } + getPostByForWebhookId(id: string) { + return this._postRepository.getPostByForWebhookId(id); + } async createPost(orgId: string, body: CreatePostDto): Promise<any[]> { const postList = []; for (const post of body.posts) { @@ -661,32 +450,42 @@ export class PostsService { return [] as any[]; } - await this._workerServiceProducer.delete( - 'post', - previousPost ? previousPost : posts?.[0]?.id - ); + try { + const workflows = this._temporalService.client + .getRawClient() + ?.workflow.list({ + query: `WorkflowType="postWorkflow" AND postId="${posts[0].id}" AND ExecutionStatus="Running"`, + }); - if ( - body.type === 'now' || - (body.type === 'schedule' && dayjs(body.date).isAfter(dayjs())) - ) { - this._workerServiceProducer.emit('post', { - id: posts[0].id, - options: { - delay: - body.type === 'now' - ? 0 - : dayjs(posts[0].publishDate).diff(dayjs(), 'millisecond'), - }, - payload: { - id: posts[0].id, - delay: - body.type === 'now' - ? 0 - : dayjs(posts[0].publishDate).diff(dayjs(), 'millisecond'), - }, + for await (const executionInfo of workflows) { + try { + const workflow = + await this._temporalService.client.getWorkflowHandle( + executionInfo.workflowId + ); + if ( + workflow && + (await workflow.describe()).status.name !== 'TERMINATED' + ) { + await workflow.terminate(); + } + } catch (err) {} + } + } catch (err) {} + + await this._temporalService.client + .getRawClient() + ?.workflow.start('postWorkflow', { + workflowId: `post_${posts[0].id}`, + taskQueue: 'main', + args: [{ postId: posts[0].id, organizationId: orgId }], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: posts[0].id, + }, + ]), }); - } Sentry.metrics.count('post_created', 1); postList.push({ @@ -702,24 +501,51 @@ export class PostsService { return this.openaiService.separatePosts(content, len); } + async changeState(id: string, state: State, err?: any, body?: any) { + return this._postRepository.changeState(id, state, err, body); + } + async changeDate(orgId: string, id: string, date: string) { const getPostById = await this._postRepository.getPostById(id, orgId); + const newDate = await this._postRepository.changeDate(orgId, id, date); - await this._workerServiceProducer.delete('post', id); - if (getPostById?.state !== 'DRAFT') { - this._workerServiceProducer.emit('post', { - id: id, - options: { - delay: dayjs(date).diff(dayjs(), 'millisecond'), - }, - payload: { - id: id, - delay: dayjs(date).diff(dayjs(), 'millisecond'), - }, + try { + const workflows = this._temporalService.client + .getRawClient() + ?.workflow.list({ + query: `WorkflowType="postWorkflow" AND postId="${getPostById.id}" AND ExecutionStatus="Running"`, + }); + + for await (const executionInfo of workflows) { + try { + const workflow = await this._temporalService.client.getWorkflowHandle( + executionInfo.workflowId + ); + if ( + workflow && + (await workflow.describe()).status.name !== 'TERMINATED' + ) { + await workflow.terminate(); + } + } catch (err) {} + } + } catch (err) {} + + await this._temporalService.client + .getRawClient() + ?.workflow.start('postWorkflow', { + workflowId: `post_${getPostById.id}`, + taskQueue: 'main', + args: [{ postId: getPostById.id, organizationId: orgId }], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: getPostById.id, + }, + ]), }); - } - return this._postRepository.changeDate(orgId, id, date); + return newDate; } async payout(id: string, url: string) { diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 94bb51b0..22fe3a18 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -1,22 +1,30 @@ import { timer } from '@gitroom/helpers/utils/timer'; import { concurrency } from '@gitroom/helpers/utils/concurrency.service'; import { Integration } from '@prisma/client'; +import { ApplicationFailure } from '@temporalio/activity'; -export class RefreshToken { - constructor( - public identifier: string, - public json: string, - public body: BodyInit, - public message = '' - ) {} +export class RefreshToken extends ApplicationFailure { + constructor(identifier: string, json: string, body: BodyInit, message = '') { + super(message, 'refresh_token', true, [ + { + identifier, + json, + body, + }, + ]); + } } -export class BadBody { - constructor( - public identifier: string, - public json: string, - public body: BodyInit, - public message = '' - ) {} + +export class BadBody extends ApplicationFailure { + constructor(identifier: string, json: string, body: BodyInit, message = '') { + super(message, 'bad_body', true, [ + { + identifier, + json, + body, + }, + ]); + } } export class NotEnoughScopes { @@ -58,7 +66,6 @@ export abstract class SocialAbstract { try { return await func(); } catch (err) { - console.log(err); const handle = this.handleErrors(JSON.stringify(err)); return { err: true, ...(handle || {}) }; } @@ -109,34 +116,36 @@ export abstract class SocialAbstract { json.includes('Rate limit') ) { await timer(5000); - return this.fetch(url, options, identifier, totalRetries + 1, ignoreConcurrency); + return this.fetch( + url, + options, + identifier, + totalRetries + 1, + ignoreConcurrency + ); } const handleError = this.handleErrors(json || '{}'); if (handleError?.type === 'retry') { await timer(5000); - return this.fetch(url, options, identifier, totalRetries + 1, ignoreConcurrency); + return this.fetch( + url, + options, + identifier, + totalRetries + 1, + ignoreConcurrency + ); } if ( request.status === 401 && (handleError?.type === 'refresh-token' || !handleError) ) { - throw new RefreshToken( - identifier, - json, - options.body!, - handleError?.value - ); + throw new RefreshToken(identifier, json, options.body!, handleError?.value); } - throw new BadBody( - identifier, - json, - options.body!, - handleError?.value || '' - ); + throw new BadBody(identifier, json, options.body!, handleError?.value || ''); } checkScopes(required: string[], got: string | string[]) { diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 4aa1c9d5..0e6f9872 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -554,6 +554,17 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { isPdf ); + console.log({ + method: 'POST', + headers: { + 'LinkedIn-Version': '202501', + 'X-Restli-Protocol-Version': '2.0.0', + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify(postPayload), + }); + const response = await this.fetch('https://api.linkedin.com/rest/posts', { method: 'POST', headers: { @@ -723,7 +734,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202504', + 'LinkedIn-Version': '202511', Authorization: `Bearer ${integration.token}`, }, }); @@ -739,7 +750,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202504', + 'LinkedIn-Version': '202511', Authorization: `Bearer ${token}`, }, } diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 3653b97e..8ade3a81 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -77,6 +77,15 @@ export interface ISocialMediaIntegration { postDetails: PostDetails[], integration: Integration ): Promise<PostResponse[]>; // Schedules a new post + + comment?( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]>; // Schedules a new post } export type PostResponse = { @@ -143,8 +152,14 @@ export interface SocialProvider url: string ) => Promise<{ client_id: string; client_secret: string }>; mention?: ( - token: string, data: { query: string }, id: string, integration: Integration - ) => Promise<{ id: string; label: string; image: string, doNotCache?: boolean }[] | {none: true}>; + token: string, + data: { query: string }, + id: string, + integration: Integration + ) => Promise< + | { id: string; label: string; image: string; doNotCache?: boolean }[] + | { none: true } + >; mentionFormat?(idOrHandle: string, name: string): string; fetchPageInformation?( accessToken: string, diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 8682d1aa..bf4f7264 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -13,7 +13,6 @@ import { TikTokDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settin import { timer } from '@gitroom/helpers/utils/timer'; import { Integration } from '@prisma/client'; import { Rules } from '@gitroom/nestjs-libraries/chat/rules.description.decorator'; -import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; @Rules( 'TikTok can have one video or one picture or multiple pictures, it cannot be without an attachment' diff --git a/libraries/nestjs-libraries/src/temporal/temporal.module.ts b/libraries/nestjs-libraries/src/temporal/temporal.module.ts new file mode 100644 index 00000000..0e0225fb --- /dev/null +++ b/libraries/nestjs-libraries/src/temporal/temporal.module.ts @@ -0,0 +1,28 @@ +import { TemporalModule } from 'nestjs-temporal-core'; + +export const getTemporalModule = ( + isWorkers: boolean, + path?: string, + activityClasses?: any[], +) => { + return TemporalModule.register({ + isGlobal: true, + connection: { + address: process.env.TEMPORAL_ADDRESS || 'localhost:7233', + namespace: process.env.TEMPORAL_NAMESPACE || 'default', + }, + taskQueue: 'main', + ...(isWorkers + ? { + worker: { + workflowsPath: path!, + activityClasses: activityClasses!, + autoStart: true, + workerOptions: { + maxConcurrentActivityTaskExecutions: 24, + }, + }, + } + : {}), + }); +}; diff --git a/libraries/nestjs-libraries/src/temporal/temporal.register.ts b/libraries/nestjs-libraries/src/temporal/temporal.register.ts new file mode 100644 index 00000000..e8090aad --- /dev/null +++ b/libraries/nestjs-libraries/src/temporal/temporal.register.ts @@ -0,0 +1,50 @@ +import { + Global, + Injectable, + Module, + OnModuleInit, +} from '@nestjs/common'; +import { TemporalService } from 'nestjs-temporal-core'; +import { Connection } from '@temporalio/client'; + +@Injectable() +export class TemporalRegister implements OnModuleInit { + constructor(private _client: TemporalService) {} + + async onModuleInit(): Promise<void> { + const connection = this._client?.client?.getRawClient() + ?.connection as Connection; + + const { customAttributes } = + await connection.operatorService.listSearchAttributes({ + namespace: process.env.TEMPORAL_NAMESPACE || 'default', + }); + + const neededAttribute = ['organizationId', 'postId']; + const missingAttributes = neededAttribute.filter( + (attr) => !customAttributes[attr], + ); + + if (missingAttributes.length > 0) { + await connection.operatorService.addSearchAttributes({ + namespace: process.env.TEMPORAL_NAMESPACE || 'default', + searchAttributes: missingAttributes.reduce((all, current) => { + // @ts-ignore + all[current] = 1; + return all; + }, {}), + }); + } + } +} + +@Global() +@Module({ + imports: [], + controllers: [], + providers: [TemporalRegister], + get exports() { + return this.providers; + }, +}) +export class TemporalRegisterMissingSearchAttributesModule {} diff --git a/libraries/nestjs-libraries/src/temporal/temporal.search.attribute.ts b/libraries/nestjs-libraries/src/temporal/temporal.search.attribute.ts new file mode 100644 index 00000000..f037efd1 --- /dev/null +++ b/libraries/nestjs-libraries/src/temporal/temporal.search.attribute.ts @@ -0,0 +1,14 @@ +import { + defineSearchAttributeKey, + SearchAttributeType, +} from '@temporalio/common'; + +export const organizationId = defineSearchAttributeKey( + 'organizationId', + SearchAttributeType.TEXT +); + +export const postId = defineSearchAttributeKey( + 'postId', + SearchAttributeType.TEXT +); diff --git a/package.json b/package.json index 27219367..f7881301 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ }, "packageManager": "pnpm@10.6.1", "scripts": { - "dev": "pnpm run --filter ./apps/extension --filter ./apps/cron --filter ./apps/workers --filter ./apps/backend --filter ./apps/frontend --parallel dev", + "dev": "pnpm run --filter ./apps/extension --filter ./apps/cron --filter ./apps/orchestrator --filter ./apps/backend --filter ./apps/frontend --parallel dev", "pm2": "pnpm run pm2-run", "publish-sdk": "pnpm run --filter ./apps/sdk publish", "pm2-run": "pm2 delete all || true && pnpm run prisma-db-push && pnpm run --parallel pm2 && pm2 logs", @@ -20,11 +20,13 @@ "build:backend": "rm -rf apps/backend/dist && pnpm --filter ./apps/backend run build", "build:frontend": "rm -rf apps/frontend/dist && pnpm --filter ./apps/frontend run build", "build:workers": "rm -rf apps/workers/dist && pnpm --filter ./apps/workers run build", + "build:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run build", "build:cron": "rm -rf apps/cron/dist && pnpm --filter ./apps/cron run build", "build:extension": "rm -rf apps/extension/dist && pnpm --filter ./apps/extension run build", "dev:backend": "rm -rf apps/backend/dist && pnpm --filter ./apps/backend run dev", "dev:frontend": "rm -rf apps/frontend/dist && pnpm --filter ./apps/frontend run dev", "dev:workers": "rm -rf apps/workers/dist && pnpm --filter ./apps/workers run dev", + "dev:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run dev", "dev:cron": "rm -rf apps/cron/dist && pnpm --filter ./apps/cron run dev", "start:prod:backend": "pnpm --filter ./apps/backend run start", "start:prod:frontend": "pnpm --filter ./apps/frontend run start", @@ -92,6 +94,11 @@ "@swc/helpers": "0.5.13", "@sweetalert2/theme-dark": "^5.0.16", "@tailwindcss/postcss": "^4.1.7", + "@temporalio/activity": "^1.14.0", + "@temporalio/client": "^1.14.0", + "@temporalio/common": "^1.14.0", + "@temporalio/worker": "^1.14.0", + "@temporalio/workflow": "^1.14.0", "@tiptap/extension-bold": "^3.0.6", "@tiptap/extension-document": "^3.0.6", "@tiptap/extension-heading": "^3.0.7", @@ -181,6 +188,7 @@ "music-metadata": "^7.14.0", "nestjs-command": "^3.1.4", "nestjs-real-ip": "^3.0.1", + "nestjs-temporal-core": "^3.2.0", "next": "14.2.35", "next-plausible": "^3.12.0", "node-fetch": "^3.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8f900596..07c334b9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -156,6 +156,21 @@ importers: '@tailwindcss/postcss': specifier: ^4.1.7 version: 4.1.18 + '@temporalio/activity': + specifier: ^1.14.0 + version: 1.14.0 + '@temporalio/client': + specifier: ^1.14.0 + version: 1.14.0 + '@temporalio/common': + specifier: ^1.14.0 + version: 1.14.0 + '@temporalio/worker': + specifier: ^1.14.0 + version: 1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12) + '@temporalio/workflow': + specifier: ^1.14.0 + version: 1.14.0 '@tiptap/extension-bold': specifier: ^3.0.6 version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) @@ -423,6 +438,9 @@ importers: nestjs-real-ip: specifier: ^3.0.1 version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)) + nestjs-temporal-core: + specifier: ^3.2.0 + version: 3.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@temporalio/client@1.14.0)(@temporalio/common@1.14.0)(@temporalio/worker@1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12))(@temporalio/workflow@1.14.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) next: specifier: 14.2.35 version: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) @@ -792,6 +810,8 @@ importers: apps/frontend: {} + apps/orchestrator: {} + apps/sdk: dependencies: node-fetch: @@ -2787,6 +2807,42 @@ packages: '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/buffers@1.2.1': + resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/codegen@1.0.0': + resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.21.0': + resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pointer@1.0.2': + resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.9.0': + resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} @@ -6954,6 +7010,38 @@ packages: '@tanstack/virtual-core@3.13.13': resolution: {integrity: sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA==} + '@temporalio/activity@1.14.0': + resolution: {integrity: sha512-ayGqfjqW8R1nhow54Y3A5ezoVwFr4SbB8VHaQA3seDFOB+6TyOVSlulYqGgFMxl/FXBkRa/VEswEDqS/xQq7aQ==} + engines: {node: '>= 18.0.0'} + + '@temporalio/client@1.14.0': + resolution: {integrity: sha512-kjzJ+7M2kHj32cTTSQT5WOjEIOxY0TNV5g6Sw9PzWmKWdtIZig+d7qUIA3VjDe/TieNozxjR2wNAX5sKzYFANA==} + engines: {node: '>= 18.0.0'} + + '@temporalio/common@1.14.0': + resolution: {integrity: sha512-jVmurBdFHdqw/wIehzVJikS8MhavL630p88TJ64P5PH0nP8S5V8R5vhkmHZ7n0sMRO+A0QFyWYyvnccu6MQZvw==} + engines: {node: '>= 18.0.0'} + + '@temporalio/core-bridge@1.14.0': + resolution: {integrity: sha512-62WRbESKVtCx1FafbikQB90EwKNF+mEAaOJKifUIU4lQnk9wlZPRfrf6pwyqr+Uqi7uZhD2YqHXWUNVYbmQU7w==} + engines: {node: '>= 18.0.0'} + + '@temporalio/nexus@1.14.0': + resolution: {integrity: sha512-0tgf+EBuz5vgYUukaYUzVHKr27XNQejXXO1i0x8+4sjR5zN6euNKraHfRzrDWRSm3nTZ6199rCTbR+CPrqaC/g==} + engines: {node: '>= 18.0.0'} + + '@temporalio/proto@1.14.0': + resolution: {integrity: sha512-duYVjt3x6SkuFzJr+5NlklEgookPqW065qdcvogmdfVjrgiwz4W/07AN3+fL4ufmqt1//0SyF6nyqv9RNADYNA==} + engines: {node: '>= 18.0.0'} + + '@temporalio/worker@1.14.0': + resolution: {integrity: sha512-wo5rgPSt83aT1hLYmh/0X4yOx/6uRbIvBa9LXqGo7s9s1GJkUyJpAahRt8aMoLm4qPsiZtu1gtU5KcASOmgqtg==} + engines: {node: '>= 18.0.0'} + + '@temporalio/workflow@1.14.0': + resolution: {integrity: sha512-hxUqCZTkdSwgy5nc/O1DIpYH0Z77cM57RfJvhK4ELmkkb1jh/Q4dshDannH1qQ1zYT0IKRBHSW7m1aMy1+dgDA==} + engines: {node: '>= 18.0.0'} + '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} @@ -8306,6 +8394,12 @@ packages: peerDependencies: acorn: ^8 + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -8932,6 +9026,10 @@ packages: capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} + cargo-cp-artifact@0.1.9: + resolution: {integrity: sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==} + hasBin: true + caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -9965,6 +10063,9 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + es-object-atoms@1.1.1: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} @@ -10776,6 +10877,12 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} + glob-to-regex.js@1.2.0: + resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} @@ -11029,6 +11136,10 @@ packages: header-case@2.0.4: resolution: {integrity: sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==} + heap-js@2.7.1: + resolution: {integrity: sha512-EQfezRg0NCZGNlhlDR3Evrw1FVL2G3LhU7EgPoxufQKruNBSYA8MiRPHeWbU+36o+Fhel0wMwM+sLEiBAlNLJA==} + engines: {node: '>=10.0.0'} + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -11194,6 +11305,10 @@ packages: humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + i18n-iso-countries@7.14.0: resolution: {integrity: sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==} engines: {node: '>= 12'} @@ -11623,6 +11738,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + iso-3166-1@2.1.1: resolution: {integrity: sha512-RZxXf8cw5Y8LyHZIwIRvKw8sWTIHh2/txBT+ehO0QroesVfnz3JNFFX4i/OC/Yuv2bDIVYrHna5PMvjtpefq5w==} @@ -12611,6 +12730,9 @@ packages: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} + memfs@4.51.1: + resolution: {integrity: sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==} + memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} @@ -13007,6 +13129,10 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@3.0.0-canary.1: + resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==} + engines: {node: '>=12.13'} + msgpackr-extract@3.0.3: resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} hasBin: true @@ -13095,6 +13221,19 @@ packages: peerDependencies: '@nestjs/common': '>=8' + nestjs-temporal-core@3.2.0: + resolution: {integrity: sha512-EIf1PZKBj9YR2ln8eDqBliGqo9IpWmetPbSbsO+X00s1qwewWNGGw7VX2Fq4Bw8/WPkbWeHgUHXwphkuOIIyaQ==} + engines: {node: '>=16.0.0', npm: '>=8.0.0'} + peerDependencies: + '@nestjs/common': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@temporalio/client': ^1.12.0 || ^1.13.0 + '@temporalio/common': ^1.12.0 || ^1.13.0 + '@temporalio/worker': ^1.12.0 || ^1.13.0 + '@temporalio/workflow': ^1.12.0 || ^1.13.0 + reflect-metadata: ^0.2.2 + rxjs: ^7.8.0 + netmask@2.0.2: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} @@ -13124,6 +13263,10 @@ packages: sass: optional: true + nexus-rpc@0.0.1: + resolution: {integrity: sha512-hAWn8Hh2eewpB5McXR5EW81R3pR/ziuGhKCF3wFyUVCklanPqrIgMNr7jKCbzXeNVad0nUDfWpFRqh2u+zxQtw==} + engines: {node: '>= 18.0.0'} + nice-grpc-client-middleware-retry@3.1.13: resolution: {integrity: sha512-Q9I/wm5lYkDTveKFirrTHBkBY137yavXZ4xQDXTPIycUp7aLXD8xPTHFhqtAFWUw05aS91uffZZRgdv3HS0y/g==} @@ -14068,6 +14211,10 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + proto3-json-serializer@2.0.2: + resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} + engines: {node: '>=14.0.0'} + protobufjs@7.5.4: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} @@ -15187,6 +15334,12 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} + source-map-loader@4.0.2: + resolution: {integrity: sha512-oYwAqCuL0OZhBoSgmdrLa7mv9MjommVMiQIWgcztf+eS4+8BfcUee6nenFnDhKOhzAVnk5gpZdfnz1iiBv+5sg==} + engines: {node: '>= 14.15.0'} + peerDependencies: + webpack: ^5.72.1 + source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -15504,6 +15657,12 @@ packages: swagger-ui-dist@5.17.14: resolution: {integrity: sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==} + swc-loader@0.2.6: + resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==} + peerDependencies: + '@swc/core': ^1.2.147 + webpack: '>=2' + sweetalert2@11.4.8: resolution: {integrity: sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==} @@ -15597,6 +15756,12 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + thingies@2.5.0: + resolution: {integrity: sha512-s+2Bwztg6PhWUD7XMfeYm5qliDdSiZm7M7n8KjTkIsm3l/2lgVRc2/Gx/v+ZX8lT4FMA+i8aQvhcWylldc+ZNw==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + thread-stream@0.15.2: resolution: {integrity: sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA==} @@ -15745,6 +15910,12 @@ packages: resolution: {integrity: sha512-FvhKs0EBiQufK29irGLM/4aMIrfU5S/TiHB3h+DcO2hjRnVVM2WC278UQJCrNO4L/REE8IKWx/mQzQW2MrrLsg==} engines: {node: '>= 10.0.0'} + tree-dump@1.1.0: + resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -16071,6 +16242,9 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unionfs@4.6.0: + resolution: {integrity: sha512-fJAy3gTHjFi5S3TP5EGdjs/OUMFFvI/ady3T8qVuZfkv8Qi8prV/Q8BuFEgODJslhZTT2z2qdD2lGdee9qjEnA==} + unist-util-filter@5.0.1: resolution: {integrity: sha512-pHx7D4Zt6+TsfwylH9+lYhBhzyhEnCXs/lbq/Hstxno5z4gVdyc2WEW0asfjGKPyG4pEKrnBv5hdkO6+aRnQJw==} @@ -16560,6 +16734,16 @@ packages: webpack-virtual-modules@0.5.0: resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + webpack@5.104.1: + resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + webpack@5.87.0: resolution: {integrity: sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==} engines: {node: '>=10.13.0'} @@ -16632,6 +16816,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@4.0.0: + resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} + engines: {node: ^16.13.0 || >=18.0.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -19954,6 +20143,42 @@ snapshots: '@jsdevtools/ono@7.1.3': {} + '@jsonjoy.com/base64@1.1.2(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) + '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) + tslib: 2.8.1 + '@juggle/resize-observer@3.4.0': {} '@keystonehq/alias-sampling@0.1.2': {} @@ -25035,6 +25260,86 @@ snapshots: '@tanstack/virtual-core@3.13.13': {} + '@temporalio/activity@1.14.0': + dependencies: + '@temporalio/client': 1.14.0 + '@temporalio/common': 1.14.0 + abort-controller: 3.0.0 + + '@temporalio/client@1.14.0': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@temporalio/common': 1.14.0 + '@temporalio/proto': 1.14.0 + abort-controller: 3.0.0 + long: 5.3.2 + uuid: 11.1.0 + + '@temporalio/common@1.14.0': + dependencies: + '@temporalio/proto': 1.14.0 + long: 5.3.2 + ms: 3.0.0-canary.1 + nexus-rpc: 0.0.1 + proto3-json-serializer: 2.0.2 + + '@temporalio/core-bridge@1.14.0': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@temporalio/common': 1.14.0 + arg: 5.0.2 + cargo-cp-artifact: 0.1.9 + which: 4.0.0 + + '@temporalio/nexus@1.14.0': + dependencies: + '@temporalio/client': 1.14.0 + '@temporalio/common': 1.14.0 + '@temporalio/proto': 1.14.0 + long: 5.3.2 + nexus-rpc: 0.0.1 + + '@temporalio/proto@1.14.0': + dependencies: + long: 5.3.2 + protobufjs: 7.5.4 + + '@temporalio/worker@1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12)': + dependencies: + '@grpc/grpc-js': 1.14.3 + '@swc/core': 1.5.7(@swc/helpers@0.5.13) + '@temporalio/activity': 1.14.0 + '@temporalio/client': 1.14.0 + '@temporalio/common': 1.14.0 + '@temporalio/core-bridge': 1.14.0 + '@temporalio/nexus': 1.14.0 + '@temporalio/proto': 1.14.0 + '@temporalio/workflow': 1.14.0 + abort-controller: 3.0.0 + heap-js: 2.7.1 + memfs: 4.51.1 + nexus-rpc: 0.0.1 + proto3-json-serializer: 2.0.2 + protobufjs: 7.5.4 + rxjs: 7.8.2 + source-map: 0.7.6 + source-map-loader: 4.0.2(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + supports-color: 8.1.1 + swc-loader: 0.2.6(@swc/core@1.5.7(@swc/helpers@0.5.13))(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + unionfs: 4.6.0 + webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + transitivePeerDependencies: + - '@swc/helpers' + - esbuild + - uglify-js + - webpack-cli + + '@temporalio/workflow@1.14.0': + dependencies: + '@temporalio/common': 1.14.0 + '@temporalio/proto': 1.14.0 + nexus-rpc: 0.0.1 + '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.27.1 @@ -27145,6 +27450,10 @@ snapshots: dependencies: acorn: 8.15.0 + acorn-import-phases@1.0.4(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -27905,6 +28214,8 @@ snapshots: tslib: 2.8.1 upper-case-first: 2.0.2 + cargo-cp-artifact@0.1.9: {} + caseless@0.12.0: {} cbor-sync@1.0.4: {} @@ -29028,6 +29339,8 @@ snapshots: es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 @@ -30208,6 +30521,10 @@ snapshots: dependencies: is-glob: 4.0.3 + glob-to-regex.js@1.2.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + glob-to-regexp@0.4.1: {} glob@10.5.0: @@ -30646,6 +30963,8 @@ snapshots: capital-case: 1.0.4 tslib: 2.8.1 + heap-js@2.7.1: {} + help-me@5.0.0: {} hermes-compiler@0.14.0: {} @@ -30803,6 +31122,8 @@ snapshots: dependencies: ms: 2.1.3 + hyperdyperid@1.2.0: {} + i18n-iso-countries@7.14.0: dependencies: diacritics: 1.3.0 @@ -31255,6 +31576,8 @@ snapshots: isexe@2.0.0: {} + isexe@3.1.1: {} + iso-3166-1@2.1.1: {} iso-datestring-validator@2.2.2: {} @@ -32642,6 +32965,15 @@ snapshots: dependencies: fs-monkey: 1.1.0 + memfs@4.51.1: + dependencies: + '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) + '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + memoize-one@5.2.1: {} memoizerific@1.11.3: @@ -33298,6 +33630,8 @@ snapshots: ms@2.1.3: {} + ms@3.0.0-canary.1: {} + msgpackr-extract@3.0.3: dependencies: node-gyp-build-optional-packages: 5.2.2 @@ -33396,6 +33730,18 @@ snapshots: '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@supercharge/request-ip': 1.2.0 + nestjs-temporal-core@3.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@temporalio/client@1.14.0)(@temporalio/common@1.14.0)(@temporalio/worker@1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12))(@temporalio/workflow@1.14.0)(reflect-metadata@0.1.14)(rxjs@7.8.2): + dependencies: + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@temporalio/client': 1.14.0 + '@temporalio/common': 1.14.0 + '@temporalio/worker': 1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12) + '@temporalio/workflow': 1.14.0 + ms: 2.1.3 + reflect-metadata: 0.1.14 + rxjs: 7.8.2 + netmask@2.0.2: {} next-plausible@3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -33432,6 +33778,8 @@ snapshots: - '@babel/core' - babel-plugin-macros + nexus-rpc@0.0.1: {} + nice-grpc-client-middleware-retry@3.1.13: dependencies: abort-controller-x: 0.4.3 @@ -34524,6 +34872,10 @@ snapshots: proto-list@1.2.4: {} + proto3-json-serializer@2.0.2: + dependencies: + protobufjs: 7.5.4 + protobufjs@7.5.4: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -36055,6 +36407,12 @@ snapshots: source-map-js@1.2.1: {} + source-map-loader@4.0.2(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + dependencies: + iconv-lite: 0.6.3 + source-map-js: 1.2.1 + webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 @@ -36374,6 +36732,12 @@ snapshots: swagger-ui-dist@5.17.14: {} + swc-loader@0.2.6(@swc/core@1.5.7(@swc/helpers@0.5.13))(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + dependencies: + '@swc/core': 1.5.7(@swc/helpers@0.5.13) + '@swc/counter': 0.1.3 + webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + sweetalert2@11.4.8: {} swr@2.3.8(react@18.3.1): @@ -36449,6 +36813,18 @@ snapshots: dependencies: memoizerific: 1.11.3 + terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + jest-worker: 27.5.1 + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.1 + webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + optionalDependencies: + '@swc/core': 1.5.7(@swc/helpers@0.5.13) + esbuild: 0.25.12 + terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -36486,6 +36862,10 @@ snapshots: dependencies: any-promise: 1.3.0 + thingies@2.5.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + thread-stream@0.15.2: dependencies: real-require: 0.1.0 @@ -36625,6 +37005,10 @@ snapshots: transitivePeerDependencies: - supports-color + tree-dump@1.1.0(tslib@2.8.1): + dependencies: + tslib: 2.8.1 + tree-kill@1.2.2: {} trim-lines@3.0.1: {} @@ -36963,6 +37347,10 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unionfs@4.6.0: + dependencies: + fs-monkey: 1.1.0 + unist-util-filter@5.0.1: dependencies: '@types/unist': 3.0.3 @@ -37469,6 +37857,38 @@ snapshots: webpack-virtual-modules@0.5.0: {} + webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12): + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.4 + es-module-lexer: 2.0.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.1 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + watchpack: 2.4.4 + webpack-sources: 3.3.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12): dependencies: '@types/eslint-scope': 3.7.7 @@ -37585,6 +38005,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@4.0.0: + dependencies: + isexe: 3.1.1 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 diff --git a/tsconfig.base.json b/tsconfig.base.json index 585db7ce..90122b62 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -33,6 +33,7 @@ "@gitroom/react/*": ["libraries/react-shared-libraries/src/*"], "@gitroom/plugins/*": ["libraries/plugins/src/*"], "@gitroom/workers/*": ["apps/workers/src/*"], + "@gitroom/orchestrator/*": ["apps/orchestrator/src/*"], "@gitroom/extension/*": ["apps/extension/src/*"] } }, From da0045428ac3a9ce08658073d324ecef530631f4 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 15:49:19 +0700 Subject: [PATCH 087/340] feat: temporal - huge refactor --- apps/backend/src/api/api.module.ts | 6 - .../src/api/routes/agencies.controller.ts | 37 - .../src/api/routes/analytics.controller.ts | 47 +- .../src/api/routes/marketplace.controller.ts | 242 ----- .../src/api/routes/messages.controller.ts | 50 - .../src/api/routes/monitor.controller.ts | 26 +- .../src/api/routes/posts.controller.ts | 17 - .../src/api/routes/settings.controller.ts | 87 -- .../src/api/routes/stripe.controller.ts | 37 - apps/backend/src/app.module.ts | 3 - apps/backend/src/main.ts | 8 +- apps/commands/src/command.module.ts | 6 +- apps/commands/src/tasks/check.stars.ts | 52 - apps/cron/.gitignore | 8 - apps/cron/nest-cli.json | 20 - apps/cron/package.json | 14 - apps/cron/src/cron.module.ts | 20 - apps/cron/src/main.ts | 12 - apps/cron/src/tasks/.gitkeep | 0 apps/cron/src/tasks/check.missing.queues.ts | 45 - .../cron/src/tasks/post.now.pending.queues.ts | 43 - apps/cron/tsconfig.build.json | 23 - apps/cron/tsconfig.json | 13 - .../components/layout/settings.component.tsx | 1 - .../components/marketplace/buyer.seller.tsx | 40 - .../src/components/marketplace/buyer.tsx | 568 ----------- .../marketplace/marketplace.provider.tsx | 36 - .../components/marketplace/marketplace.tsx | 3 - .../src/components/marketplace/order.list.tsx | 79 -- .../marketplace/order.top.actions.tsx | 387 -------- .../marketplace/preview.popup.dynamic.tsx | 19 - .../src/components/marketplace/seller.tsx | 241 ----- .../marketplace/special.message.tsx | 430 -------- .../src/components/messages/layout.tsx | 123 --- .../src/components/messages/messages.tsx | 285 ------ .../src/activities/autopost.activity.ts | 27 + .../src/activities/email.activity.ts | 23 + apps/orchestrator/src/app.module.ts | 8 +- apps/orchestrator/src/signals/email.signal.ts | 9 + .../src/workflows/autopost.workflow.ts | 30 + .../src/workflows/digest.email.workflow.ts | 69 ++ apps/orchestrator/src/workflows/index.ts | 2 + .../src/workflows/post.workflow.ts | 38 +- apps/workers/.gitignore | 8 - apps/workers/.swcrc | 38 - apps/workers/nest-cli.json | 20 - apps/workers/package.json | 14 - apps/workers/src/app/app.module.ts | 15 - apps/workers/src/app/posts.controller.ts | 54 -- apps/workers/src/main.ts | 25 - apps/workers/tsconfig.build.json | 23 - apps/workers/tsconfig.json | 11 - .../helpers/src/utils/concurrency.service.ts | 52 - .../bull-mq-transport-new/bull.mq.module.ts | 9 - .../src/bull-mq-transport-new/client.ts | 121 --- .../src/bull-mq-transport-new/strategy.ts | 61 -- .../prisma/autopost/autopost.service.ts | 48 +- .../src/database/prisma/database.module.ts | 12 - .../integrations/integration.service.ts | 11 +- .../marketplace/item.user.repository.ts | 41 - .../prisma/marketplace/item.user.service.ts | 15 - .../prisma/marketplace/messages.repository.ts | 915 ------------------ .../prisma/marketplace/messages.service.ts | 252 ----- .../database/prisma/marketplace/tags.list.ts | 138 --- .../notifications/notification.service.ts | 64 +- .../organizations/organization.repository.ts | 2 + .../database/prisma/posts/posts.service.ts | 168 +--- .../src/database/prisma/schema.prisma | 1 - .../database/prisma/stars/stars.repository.ts | 198 ---- .../database/prisma/stars/stars.service.ts | 485 ---------- .../subscriptions/subscription.service.ts | 33 - .../database/prisma/users/users.repository.ts | 92 -- .../database/prisma/users/users.service.ts | 13 - .../prisma/webhooks/webhooks.service.ts | 78 +- .../dtos/marketplace/add.remove.item.dto.ts | 11 - .../src/dtos/marketplace/audience.dto.ts | 8 - .../src/dtos/marketplace/change.active.dto.ts | 6 - .../src/dtos/marketplace/create.offer.dto.ts | 27 - .../src/dtos/marketplace/items.dto.ts | 12 - .../dtos/marketplace/new.conversation.dto.ts | 10 - .../src/dtos/messages/add.message.ts | 7 - .../src/integrations/integration.manager.ts | 3 +- .../src/integrations/social.abstract.ts | 42 +- .../integrations/social/bluesky.provider.ts | 270 +++--- .../integrations/social/discord.provider.ts | 153 ++- .../integrations/social/facebook.provider.ts | 74 +- .../integrations/social/instagram.provider.ts | 101 +- .../social/instagram.standalone.provider.ts | 19 + .../social/linkedin.page.provider.ts | 19 + .../integrations/social/linkedin.provider.ts | 43 +- .../social/mastodon.custom.provider.ts | 19 + .../integrations/social/mastodon.provider.ts | 134 ++- .../integrations/social/reddit.provider.ts | 93 +- .../integrations/social/threads.provider.ts | 91 +- .../src/integrations/social/x.provider.ts | 193 ++-- .../src/services/email.service.ts | 1 - .../src/services/stripe.service.ts | 144 --- .../src/services/trending.service.ts | 31 - .../nestjs-libraries/src/services/trending.ts | 217 ----- .../src/temporal/temporal.module.ts | 31 +- package.json | 13 +- pnpm-lock.yaml | 19 +- tsconfig.base.json | 2 - 103 files changed, 1108 insertions(+), 6936 deletions(-) delete mode 100644 apps/backend/src/api/routes/agencies.controller.ts delete mode 100644 apps/backend/src/api/routes/marketplace.controller.ts delete mode 100644 apps/backend/src/api/routes/messages.controller.ts delete mode 100644 apps/commands/src/tasks/check.stars.ts delete mode 100644 apps/cron/.gitignore delete mode 100644 apps/cron/nest-cli.json delete mode 100644 apps/cron/package.json delete mode 100644 apps/cron/src/cron.module.ts delete mode 100644 apps/cron/src/main.ts delete mode 100644 apps/cron/src/tasks/.gitkeep delete mode 100644 apps/cron/src/tasks/check.missing.queues.ts delete mode 100644 apps/cron/src/tasks/post.now.pending.queues.ts delete mode 100644 apps/cron/tsconfig.build.json delete mode 100644 apps/cron/tsconfig.json delete mode 100644 apps/frontend/src/components/marketplace/buyer.seller.tsx delete mode 100644 apps/frontend/src/components/marketplace/buyer.tsx delete mode 100644 apps/frontend/src/components/marketplace/marketplace.provider.tsx delete mode 100644 apps/frontend/src/components/marketplace/marketplace.tsx delete mode 100644 apps/frontend/src/components/marketplace/order.list.tsx delete mode 100644 apps/frontend/src/components/marketplace/order.top.actions.tsx delete mode 100644 apps/frontend/src/components/marketplace/preview.popup.dynamic.tsx delete mode 100644 apps/frontend/src/components/marketplace/seller.tsx delete mode 100644 apps/frontend/src/components/marketplace/special.message.tsx delete mode 100644 apps/frontend/src/components/messages/layout.tsx delete mode 100644 apps/frontend/src/components/messages/messages.tsx create mode 100644 apps/orchestrator/src/activities/autopost.activity.ts create mode 100644 apps/orchestrator/src/activities/email.activity.ts create mode 100644 apps/orchestrator/src/signals/email.signal.ts create mode 100644 apps/orchestrator/src/workflows/autopost.workflow.ts create mode 100644 apps/orchestrator/src/workflows/digest.email.workflow.ts delete mode 100644 apps/workers/.gitignore delete mode 100644 apps/workers/.swcrc delete mode 100644 apps/workers/nest-cli.json delete mode 100644 apps/workers/package.json delete mode 100644 apps/workers/src/app/app.module.ts delete mode 100644 apps/workers/src/app/posts.controller.ts delete mode 100644 apps/workers/src/main.ts delete mode 100644 apps/workers/tsconfig.build.json delete mode 100644 apps/workers/tsconfig.json delete mode 100644 libraries/helpers/src/utils/concurrency.service.ts delete mode 100644 libraries/nestjs-libraries/src/bull-mq-transport-new/bull.mq.module.ts delete mode 100644 libraries/nestjs-libraries/src/bull-mq-transport-new/client.ts delete mode 100644 libraries/nestjs-libraries/src/bull-mq-transport-new/strategy.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/marketplace/messages.repository.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/marketplace/messages.service.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts delete mode 100644 libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/marketplace/audience.dto.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/marketplace/create.offer.dto.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/marketplace/new.conversation.dto.ts delete mode 100644 libraries/nestjs-libraries/src/dtos/messages/add.message.ts delete mode 100644 libraries/nestjs-libraries/src/services/trending.service.ts delete mode 100644 libraries/nestjs-libraries/src/services/trending.ts diff --git a/apps/backend/src/api/api.module.ts b/apps/backend/src/api/api.module.ts index 586dcd2f..291479c7 100644 --- a/apps/backend/src/api/api.module.ts +++ b/apps/backend/src/api/api.module.ts @@ -16,13 +16,10 @@ import { MediaController } from '@gitroom/backend/api/routes/media.controller'; import { UploadModule } from '@gitroom/nestjs-libraries/upload/upload.module'; import { BillingController } from '@gitroom/backend/api/routes/billing.controller'; import { NotificationsController } from '@gitroom/backend/api/routes/notifications.controller'; -import { MarketplaceController } from '@gitroom/backend/api/routes/marketplace.controller'; -import { MessagesController } from '@gitroom/backend/api/routes/messages.controller'; import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service'; import { ExtractContentService } from '@gitroom/nestjs-libraries/openai/extract.content.service'; import { CodesService } from '@gitroom/nestjs-libraries/services/codes.service'; import { CopilotController } from '@gitroom/backend/api/routes/copilot.controller'; -import { AgenciesController } from '@gitroom/backend/api/routes/agencies.controller'; import { PublicController } from '@gitroom/backend/api/routes/public.controller'; import { RootController } from '@gitroom/backend/api/routes/root.controller'; import { TrackService } from '@gitroom/nestjs-libraries/track/track.service'; @@ -44,10 +41,7 @@ const authenticatedController = [ MediaController, BillingController, NotificationsController, - MarketplaceController, - MessagesController, CopilotController, - AgenciesController, WebhookController, SignatureController, AutopostController, diff --git a/apps/backend/src/api/routes/agencies.controller.ts b/apps/backend/src/api/routes/agencies.controller.ts deleted file mode 100644 index e2849f96..00000000 --- a/apps/backend/src/api/routes/agencies.controller.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { Body, Controller, Get, Param, Post } from '@nestjs/common'; -import { User } from '@prisma/client'; -import { ApiTags } from '@nestjs/swagger'; -import { AgenciesService } from '@gitroom/nestjs-libraries/database/prisma/agencies/agencies.service'; -import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; -import { CreateAgencyDto } from '@gitroom/nestjs-libraries/dtos/agencies/create.agency.dto'; - -@ApiTags('Agencies') -@Controller('/agencies') -export class AgenciesController { - constructor(private _agenciesService: AgenciesService) {} - @Get('/') - async getAgencyByUser(@GetUserFromRequest() user: User) { - return (await this._agenciesService.getAgencyByUser(user)) || {}; - } - - @Post('/') - async createAgency( - @GetUserFromRequest() user: User, - @Body() body: CreateAgencyDto - ) { - return this._agenciesService.createAgency(user, body); - } - - @Post('/action/:action/:id') - async updateAgency( - @GetUserFromRequest() user: User, - @Param('action') action: string, - @Param('id') id: string - ) { - if (!user.isSuperAdmin) { - return 400; - } - - return this._agenciesService.approveOrDecline(user.email, action, id); - } -} diff --git a/apps/backend/src/api/routes/analytics.controller.ts b/apps/backend/src/api/routes/analytics.controller.ts index 98caae8c..8c3201ef 100644 --- a/apps/backend/src/api/routes/analytics.controller.ts +++ b/apps/backend/src/api/routes/analytics.controller.ts @@ -1,56 +1,13 @@ -import { - Body, - Controller, - Get, - Inject, - Param, - Post, - Query, -} from '@nestjs/common'; +import { Controller, Get, Param, Query } from '@nestjs/common'; import { Organization } from '@prisma/client'; import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; -import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service'; -import dayjs from 'dayjs'; -import { StarsListDto } from '@gitroom/nestjs-libraries/dtos/analytics/stars.list.dto'; import { ApiTags } from '@nestjs/swagger'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; -import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; @ApiTags('Analytics') @Controller('/analytics') export class AnalyticsController { - constructor( - private _starsService: StarsService, - private _integrationService: IntegrationService - ) {} - @Get('/') - async getStars(@GetOrgFromRequest() org: Organization) { - return this._starsService.getStars(org.id); - } - - @Get('/trending') - async getTrending() { - const todayTrending = dayjs(dayjs().format('YYYY-MM-DDT12:00:00')); - const last = todayTrending.isAfter(dayjs()) - ? todayTrending.subtract(1, 'day') - : todayTrending; - const nextTrending = last.add(1, 'day'); - - return { - last: last.format('YYYY-MM-DD HH:mm:ss'), - predictions: nextTrending.format('YYYY-MM-DD HH:mm:ss'), - }; - } - - @Post('/stars') - async getStarsFilter( - @GetOrgFromRequest() org: Organization, - @Body() starsFilter: StarsListDto - ) { - return { - stars: await this._starsService.getStarsFilter(org.id, starsFilter), - }; - } + constructor(private _integrationService: IntegrationService) {} @Get('/:integration') async getIntegration( diff --git a/apps/backend/src/api/routes/marketplace.controller.ts b/apps/backend/src/api/routes/marketplace.controller.ts deleted file mode 100644 index afb961e9..00000000 --- a/apps/backend/src/api/routes/marketplace.controller.ts +++ /dev/null @@ -1,242 +0,0 @@ -import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common'; -import { Organization, User } from '@prisma/client'; -import { ApiTags } from '@nestjs/swagger'; -import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; -import { ItemUserService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.service'; -import { AddRemoveItemDto } from '@gitroom/nestjs-libraries/dtos/marketplace/add.remove.item.dto'; -import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service'; -import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; -import { ChangeActiveDto } from '@gitroom/nestjs-libraries/dtos/marketplace/change.active.dto'; -import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto'; -import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; -import { AudienceDto } from '@gitroom/nestjs-libraries/dtos/marketplace/audience.dto'; -import { NewConversationDto } from '@gitroom/nestjs-libraries/dtos/marketplace/new.conversation.dto'; -import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; -import { CreateOfferDto } from '@gitroom/nestjs-libraries/dtos/marketplace/create.offer.dto'; -import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; - -@ApiTags('Marketplace') -@Controller('/marketplace') -export class MarketplaceController { - constructor( - private _itemUserService: ItemUserService, - private _stripeService: StripeService, - private _userService: UsersService, - private _messagesService: MessagesService, - private _postsService: PostsService - ) {} - - @Post('/') - getInfluencers( - @GetOrgFromRequest() organization: Organization, - @GetUserFromRequest() user: User, - @Body() body: ItemsDto - ) { - return this._userService.getMarketplacePeople( - organization.id, - user.id, - body - ); - } - - @Post('/conversation') - createConversation( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Body() body: NewConversationDto - ) { - return this._messagesService.createConversation( - user.id, - organization.id, - body - ); - } - - @Get('/bank') - connectBankAccount( - @GetUserFromRequest() user: User, - @Query('country') country: string - ) { - return this._stripeService.createAccountProcess( - user.id, - user.email, - country - ); - } - - @Post('/item') - async addItems( - @GetUserFromRequest() user: User, - @Body() body: AddRemoveItemDto - ) { - return this._itemUserService.addOrRemoveItem(body.state, user.id, body.key); - } - - @Post('/active') - async changeActive( - @GetUserFromRequest() user: User, - @Body() body: ChangeActiveDto - ) { - await this._userService.changeMarketplaceActive(user.id, body.active); - } - - @Post('/audience') - async changeAudience( - @GetUserFromRequest() user: User, - @Body() body: AudienceDto - ) { - await this._userService.changeAudienceSize(user.id, body.audience); - } - - @Get('/item') - async getItems(@GetUserFromRequest() user: User) { - return this._itemUserService.getItems(user.id); - } - - @Get('/orders') - async getOrders( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Query('type') type: 'seller' | 'buyer' - ) { - return this._messagesService.getOrders(user.id, organization.id, type); - } - - @Get('/account') - async getAccount(@GetUserFromRequest() user: User) { - const { account, marketplace, connectedAccount, name, picture, audience } = - await this._userService.getUserByEmail(user.email); - return { - account, - marketplace, - connectedAccount, - fullname: name, - audience, - picture, - }; - } - - @Post('/offer') - async createOffer( - @GetUserFromRequest() user: User, - @Body() body: CreateOfferDto - ) { - return this._messagesService.createOffer(user.id, body); - } - - @Get('/posts/:id') - async post( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Param('id') id: string - ) { - const getPost = await this._messagesService.getPost( - user.id, - organization.id, - id - ); - if (!getPost) { - return; - } - - return { - ...(await this._postsService.getPost(getPost.organizationId, id)), - providerId: getPost.integration.providerIdentifier, - }; - } - - @Post('/posts/:id/revision') - async revision( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Param('id') id: string, - @Body('message') message: string - ) { - return this._messagesService.requestRevision( - user.id, - organization.id, - id, - message - ); - } - - @Post('/posts/:id/approve') - async approve( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Param('id') id: string, - @Body('message') message: string - ) { - return this._messagesService.requestApproved( - user.id, - organization.id, - id, - message - ); - } - - @Post('/posts/:id/cancel') - async cancel( - @GetOrgFromRequest() organization: Organization, - @Param('id') id: string - ) { - return this._messagesService.requestCancel(organization.id, id); - } - - @Post('/offer/:id/complete') - async completeOrder( - @GetOrgFromRequest() organization: Organization, - @Param('id') id: string - ) { - const order = await this._messagesService.completeOrderAndPay( - organization.id, - id - ); - - if (!order) { - return; - } - - try { - await this._stripeService.payout( - id, - order.charge, - order.account, - order.price - ); - } catch (e) { - await this._messagesService.payoutProblem( - id, - order.sellerId, - order.price - ); - } - await this._messagesService.completeOrder(id); - } - - @Post('/orders/:id/payment') - async payOrder( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Param('id') id: string - ) { - const orderDetails = await this._messagesService.getOrderDetails( - user.id, - organization.id, - id - ); - const payment = await this._stripeService.payAccountStepOne( - user.id, - organization, - orderDetails.seller, - orderDetails.order.id, - orderDetails.order.ordersItems.map((p) => ({ - quantity: p.quantity, - integrationType: p.integration.providerIdentifier, - price: p.price, - })), - orderDetails.order.messageGroupId - ); - return payment; - } -} diff --git a/apps/backend/src/api/routes/messages.controller.ts b/apps/backend/src/api/routes/messages.controller.ts deleted file mode 100644 index 2af0d9b0..00000000 --- a/apps/backend/src/api/routes/messages.controller.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Body, Controller, Get, Param, Post } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; -import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; -import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; -import { Organization, User } from '@prisma/client'; -import { AddMessageDto } from '@gitroom/nestjs-libraries/dtos/messages/add.message'; -import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; - -@ApiTags('Messages') -@Controller('/messages') -export class MessagesController { - constructor(private _messagesService: MessagesService) {} - - @Get('/') - getMessagesGroup( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization - ) { - return this._messagesService.getMessagesGroup(user.id, organization.id); - } - - @Get('/:groupId/:page') - getMessages( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Param('groupId') groupId: string, - @Param('page') page: string - ) { - return this._messagesService.getMessages( - user.id, - organization.id, - groupId, - +page - ); - } - @Post('/:groupId') - createMessage( - @GetUserFromRequest() user: User, - @GetOrgFromRequest() organization: Organization, - @Param('groupId') groupId: string, - @Body() message: AddMessageDto - ) { - return this._messagesService.createMessage( - user.id, - organization.id, - groupId, - message - ); - } -} diff --git a/apps/backend/src/api/routes/monitor.controller.ts b/apps/backend/src/api/routes/monitor.controller.ts index 6409def7..df4a857c 100644 --- a/apps/backend/src/api/routes/monitor.controller.ts +++ b/apps/backend/src/api/routes/monitor.controller.ts @@ -1,30 +1,14 @@ -import { Controller, Get, HttpException, Param } from '@nestjs/common'; +import { Controller, Get, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; @ApiTags('Monitor') @Controller('/monitor') export class MonitorController { - constructor(private _workerServiceProducer: BullMqClient) {} - @Get('/queue/:name') async getMessagesGroup(@Param('name') name: string) { - const { valid } = - await this._workerServiceProducer.checkForStuckWaitingJobs(name); - - if (valid) { - return { - status: 'success', - message: `Queue ${name} is healthy.`, - }; - } - - throw new HttpException( - { - status: 'error', - message: `Queue ${name} has stuck waiting jobs.`, - }, - 503 - ); + return { + status: 'success', + message: `Queue ${name} is healthy.`, + }; } } diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index 18cd1bfa..c51852ee 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -13,10 +13,8 @@ import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/po import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; import { Organization, User } from '@prisma/client'; import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; -import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service'; import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability'; import { ApiTags } from '@nestjs/swagger'; -import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; import { GeneratorDto } from '@gitroom/nestjs-libraries/dtos/generator/generator.dto'; import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generator/create.generated.posts.dto'; import { AgentGraphService } from '@gitroom/nestjs-libraries/agent/agent.graph.service'; @@ -31,8 +29,6 @@ import { AuthorizationActions, Sections } from '@gitroom/backend/services/auth/p export class PostsController { constructor( private _postsService: PostsService, - private _starsService: StarsService, - private _messagesService: MessagesService, private _agentGraphService: AgentGraphService, private _shortLinkService: ShortLinkService ) {} @@ -50,14 +46,6 @@ export class PostsController { return { ask: this._shortLinkService.askShortLinkedin(body.messages) }; } - @Get('/marketplace/:id') - async getMarketplacePosts( - @GetOrgFromRequest() org: Organization, - @Param('id') id: string - ) { - return this._messagesService.getMarketplaceAvailableOffers(org.id, id); - } - @Post('/:id/comments') async createComment( @GetOrgFromRequest() org: Organization, @@ -115,11 +103,6 @@ export class PostsController { return { date: await this._postsService.findFreeDateTime(org.id, id) }; } - @Get('/predict-trending') - predictTrending() { - return this._starsService.predictTrending(); - } - @Get('/old') oldPosts( @GetOrgFromRequest() org: Organization, diff --git a/apps/backend/src/api/routes/settings.controller.ts b/apps/backend/src/api/routes/settings.controller.ts index bcc5044e..c92537b4 100644 --- a/apps/backend/src/api/routes/settings.controller.ts +++ b/apps/backend/src/api/routes/settings.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Delete, Get, Param, Post } from '@nestjs/common'; import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; import { Organization } from '@prisma/client'; -import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service'; import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; import { AddTeamMemberDto } from '@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto'; @@ -12,95 +11,9 @@ import { AuthorizationActions, Sections } from '@gitroom/backend/services/auth/p @Controller('/settings') export class SettingsController { constructor( - private _starsService: StarsService, private _organizationService: OrganizationService ) {} - @Get('/github') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - async getConnectedGithubAccounts(@GetOrgFromRequest() org: Organization) { - return { - github: ( - await this._starsService.getGitHubRepositoriesByOrgId(org.id) - ).map((repo) => ({ - id: repo.id, - login: repo.login, - })), - }; - } - - @Post('/github') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - async addGitHub( - @GetOrgFromRequest() org: Organization, - @Body('code') code: string - ) { - if (!code) { - throw new Error('No code provided'); - } - await this._starsService.addGitHub(org.id, code); - } - - @Get('/github/url') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - authUrl() { - return { - url: `https://github.com/login/oauth/authorize?client_id=${ - process.env.GITHUB_CLIENT_ID - }&scope=${encodeURIComponent( - 'user:email' - )}&redirect_uri=${encodeURIComponent( - `${process.env.FRONTEND_URL}/settings` - )}`, - }; - } - - @Get('/organizations/:id') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - async getOrganizations( - @GetOrgFromRequest() org: Organization, - @Param('id') id: string - ) { - return { - organizations: await this._starsService.getOrganizations(org.id, id), - }; - } - - @Get('/organizations/:id/:github') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - async getRepositories( - @GetOrgFromRequest() org: Organization, - @Param('id') id: string, - @Param('github') github: string - ) { - return { - repositories: await this._starsService.getRepositoriesOfOrganization( - org.id, - id, - github - ), - }; - } - - @Post('/organizations/:id') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - async updateGitHubLogin( - @GetOrgFromRequest() org: Organization, - @Param('id') id: string, - @Body('login') login: string - ) { - return this._starsService.updateGitHubLogin(org.id, id, login); - } - - @Delete('/repository/:id') - @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) - async deleteRepository( - @GetOrgFromRequest() org: Organization, - @Param('id') id: string - ) { - return this._starsService.deleteRepository(org.id, id); - } - @Get('/team') @CheckPolicies( [AuthorizationActions.Create, Sections.TEAM_MEMBERS], diff --git a/apps/backend/src/api/routes/stripe.controller.ts b/apps/backend/src/api/routes/stripe.controller.ts index 12c6a40a..37aadf24 100644 --- a/apps/backend/src/api/routes/stripe.controller.ts +++ b/apps/backend/src/api/routes/stripe.controller.ts @@ -1,47 +1,19 @@ import { Controller, - Get, - Header, HttpException, - Param, Post, RawBodyRequest, Req, } from '@nestjs/common'; import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service'; import { ApiTags } from '@nestjs/swagger'; -import { CodesService } from '@gitroom/nestjs-libraries/services/codes.service'; @ApiTags('Stripe') @Controller('/stripe') export class StripeController { constructor( private readonly _stripeService: StripeService, - private readonly _codesService: CodesService ) {} - @Post('/connect') - stripeConnect(@Req() req: RawBodyRequest<Request>) { - const event = this._stripeService.validateRequest( - req.rawBody, - // @ts-ignore - req.headers['stripe-signature'], - process.env.STRIPE_SIGNING_KEY_CONNECT - ); - - // Maybe it comes from another stripe webhook - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (event?.data?.object?.metadata?.service !== 'gitroom') { - return { ok: true }; - } - - switch (event.type) { - case 'account.updated': - return this._stripeService.updateAccount(event); - default: - return { ok: true }; - } - } @Post('/') stripe(@Req() req: RawBodyRequest<Request>) { @@ -66,8 +38,6 @@ export class StripeController { switch (event.type) { case 'invoice.payment_succeeded': return this._stripeService.paymentSucceeded(event); - case 'account.updated': - return this._stripeService.updateAccount(event); case 'customer.subscription.created': return this._stripeService.createSubscription(event); case 'customer.subscription.updated': @@ -81,11 +51,4 @@ export class StripeController { throw new HttpException(e, 500); } } - - @Get('/lifetime-deal-codes/:provider') - @Header('Content-disposition', 'attachment; filename=codes.csv') - @Header('Content-type', 'text/csv') - async getStripeCodes(@Param('provider') providerToken: string) { - return this._codesService.generateCodes(providerToken); - } } diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index d4f800d4..1a5f89b0 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -3,7 +3,6 @@ import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/databa import { ApiModule } from '@gitroom/backend/api/api.module'; import { APP_GUARD } from '@nestjs/core'; import { PoliciesGuard } from '@gitroom/backend/services/auth/permissions/permissions.guard'; -import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; import { PublicApiModule } from '@gitroom/backend/public-api/public.api.module'; import { ThrottlerBehindProxyGuard } from '@gitroom/nestjs-libraries/throttler/throttler.provider'; import { ThrottlerModule } from '@nestjs/throttler'; @@ -20,7 +19,6 @@ import { TemporalRegisterMissingSearchAttributesModule } from '@gitroom/nestjs-l @Module({ imports: [ SentryModule.forRoot(), - BullMqModule, DatabaseModule, ApiModule, PublicApiModule, @@ -50,7 +48,6 @@ import { TemporalRegisterMissingSearchAttributesModule } from '@gitroom/nestjs-l }, ], exports: [ - BullMqModule, DatabaseModule, ApiModule, PublicApiModule, diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index d1ee6fb7..3f1c1ae5 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -3,6 +3,8 @@ initializeSentry('backend', true); import { loadSwagger } from '@gitroom/helpers/swagger/load.swagger'; import { json } from 'express'; +import { Runtime } from '@temporalio/worker'; +Runtime.install({ shutdownSignals: [] }); process.env.TZ = 'UTC'; @@ -21,7 +23,11 @@ async function start() { rawBody: true, cors: { ...(!process.env.NOT_SECURED ? { credentials: true } : {}), - allowedHeaders: ['Content-Type', 'Authorization', 'x-copilotkit-runtime-client-gql-version'], + allowedHeaders: [ + 'Content-Type', + 'Authorization', + 'x-copilotkit-runtime-client-gql-version', + ], exposedHeaders: [ 'reload', 'onboarding', diff --git a/apps/commands/src/command.module.ts b/apps/commands/src/command.module.ts index 724182b5..6320bd30 100644 --- a/apps/commands/src/command.module.ts +++ b/apps/commands/src/command.module.ts @@ -1,17 +1,15 @@ import { Module } from '@nestjs/common'; import { CommandModule as ExternalCommandModule } from 'nestjs-command'; -import { CheckStars } from './tasks/check.stars'; import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; import { RefreshTokens } from './tasks/refresh.tokens'; -import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; import { ConfigurationTask } from './tasks/configuration'; import { AgentRun } from './tasks/agent.run'; import { AgentModule } from '@gitroom/nestjs-libraries/agent/agent.module'; @Module({ - imports: [ExternalCommandModule, DatabaseModule, BullMqModule, AgentModule], + imports: [ExternalCommandModule, DatabaseModule, AgentModule], controllers: [], - providers: [CheckStars, RefreshTokens, ConfigurationTask, AgentRun], + providers: [RefreshTokens, ConfigurationTask, AgentRun], get exports() { return [...this.imports, ...this.providers]; }, diff --git a/apps/commands/src/tasks/check.stars.ts b/apps/commands/src/tasks/check.stars.ts deleted file mode 100644 index b80ce434..00000000 --- a/apps/commands/src/tasks/check.stars.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { Command, Positional } from 'nestjs-command'; -import { Injectable } from '@nestjs/common'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; - -@Injectable() -export class CheckStars { - constructor(private _workerServiceProducer: BullMqClient) {} - @Command({ - command: 'sync:stars <login>', - describe: 'Sync stars for a login', - }) - async create( - @Positional({ - name: 'login', - describe: 'login {owner}/{repo}', - type: 'string', - }) - login: string - ) { - this._workerServiceProducer - .emit('check_stars', { payload: { login } }) - .subscribe(); - return true; - } - - @Command({ - command: 'sync:all_stars <login>', - describe: 'Sync all stars for a login', - }) - async syncAllStars( - @Positional({ - name: 'login', - describe: 'login {owner}/{repo}', - type: 'string', - }) - login: string - ) { - this._workerServiceProducer - .emit('sync_all_stars', { payload: { login } }) - .subscribe(); - return true; - } - - @Command({ - command: 'sync:trending', - describe: 'Sync trending', - }) - async syncTrending() { - this._workerServiceProducer.emit('sync_trending', {}).subscribe(); - return true; - } -} diff --git a/apps/cron/.gitignore b/apps/cron/.gitignore deleted file mode 100644 index 0dff6fb6..00000000 --- a/apps/cron/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -dist/ -node_modules/ -[._]*.s[a-v][a-z] -[._]*.sw[a-p] -[._]s[a-rt-v][a-z] -[._]ss[a-gi-z] -[._]sw[a-p] - diff --git a/apps/cron/nest-cli.json b/apps/cron/nest-cli.json deleted file mode 100644 index 1fcd4a33..00000000 --- a/apps/cron/nest-cli.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "monorepo": false, - "sourceRoot": "src", - "entryFile": "../../dist/cron/apps/cron/src/main", - "language": "ts", - "generateOptions": { - "spec": false - }, - "compilerOptions": { - "manualRestart": true, - "tsConfigPath": "./tsconfig.build.json", - "webpack": false, - "deleteOutDir": true, - "assets": [], - "watchAssets": false, - "plugins": [] - } -} diff --git a/apps/cron/package.json b/apps/cron/package.json deleted file mode 100644 index 6276445d..00000000 --- a/apps/cron/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "postiz-cron", - "version": "1.0.0", - "description": "", - "scripts": { - "dev": "dotenv -e ../../.env -- nest start --watch --entryFile=./apps/cron/src/main", - "build": "cross-env NODE_ENV=production nest build", - "start": "dotenv -e ../../.env -- node --experimental-require-module ./dist/apps/cron/src/main.js", - "pm2": "pm2 start pnpm --name cron -- start" - }, - "keywords": [], - "author": "", - "license": "ISC" -} diff --git a/apps/cron/src/cron.module.ts b/apps/cron/src/cron.module.ts deleted file mode 100644 index 01eb6ed7..00000000 --- a/apps/cron/src/cron.module.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ScheduleModule } from '@nestjs/schedule'; -import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; -import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; -import { SentryModule } from '@sentry/nestjs/setup'; -import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception'; -import { CheckMissingQueues } from '@gitroom/cron/tasks/check.missing.queues'; -import { PostNowPendingQueues } from '@gitroom/cron/tasks/post.now.pending.queues'; - -@Module({ - imports: [ - SentryModule.forRoot(), - DatabaseModule, - ScheduleModule.forRoot(), - BullMqModule, - ], - controllers: [], - providers: [FILTER, CheckMissingQueues, PostNowPendingQueues], -}) -export class CronModule {} diff --git a/apps/cron/src/main.ts b/apps/cron/src/main.ts deleted file mode 100644 index cc2684f9..00000000 --- a/apps/cron/src/main.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { initializeSentry } from '@gitroom/nestjs-libraries/sentry/initialize.sentry'; -initializeSentry('cron'); - -import { NestFactory } from '@nestjs/core'; -import { CronModule } from './cron.module'; - -async function bootstrap() { - // some comment again - await NestFactory.createApplicationContext(CronModule); -} - -bootstrap(); diff --git a/apps/cron/src/tasks/.gitkeep b/apps/cron/src/tasks/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/cron/src/tasks/check.missing.queues.ts b/apps/cron/src/tasks/check.missing.queues.ts deleted file mode 100644 index 0664676c..00000000 --- a/apps/cron/src/tasks/check.missing.queues.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; -import dayjs from 'dayjs'; - -@Injectable() -export class CheckMissingQueues { - constructor( - private _postService: PostsService, - private _workerServiceProducer: BullMqClient - ) {} - @Cron('0 * * * *') - async handleCron() { - const list = await this._postService.searchForMissingThreeHoursPosts(); - const notExists = ( - await Promise.all( - list.map(async (p) => ({ - id: p.id, - publishDate: p.publishDate, - isJob: - ['delayed', 'waiting'].indexOf( - await this._workerServiceProducer - .getQueue('post') - .getJobState(p.id) - ) > -1, - })) - ) - ).filter((p) => !p.isJob); - - - for (const job of notExists) { - this._workerServiceProducer.emit('post', { - id: job.id, - options: { - delay: dayjs(job.publishDate).diff(dayjs(), 'millisecond'), - }, - payload: { - id: job.id, - delay: dayjs(job.publishDate).diff(dayjs(), 'millisecond'), - }, - }); - } - } -} diff --git a/apps/cron/src/tasks/post.now.pending.queues.ts b/apps/cron/src/tasks/post.now.pending.queues.ts deleted file mode 100644 index 69105304..00000000 --- a/apps/cron/src/tasks/post.now.pending.queues.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; -import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; - -@Injectable() -export class PostNowPendingQueues { - constructor( - private _postService: PostsService, - private _workerServiceProducer: BullMqClient - ) {} - @Cron('*/16 * * * *') - async handleCron() { - const list = await this._postService.checkPending15minutesBack(); - const notExists = ( - await Promise.all( - list.map(async (p) => ({ - id: p.id, - publishDate: p.publishDate, - isJob: - ['delayed', 'waiting'].indexOf( - await this._workerServiceProducer - .getQueue('post') - .getJobState(p.id) - ) > -1, - })) - ) - ).filter((p) => !p.isJob); - - for (const job of notExists) { - this._workerServiceProducer.emit('post', { - id: job.id, - options: { - delay: 0, - }, - payload: { - id: job.id, - delay: 0, - }, - }); - } - } -} diff --git a/apps/cron/tsconfig.build.json b/apps/cron/tsconfig.build.json deleted file mode 100644 index bf14cec5..00000000 --- a/apps/cron/tsconfig.build.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"], - "compilerOptions": { - "module": "CommonJS", - "resolveJsonModule": true, - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false, - "outDir": "./dist" - } -} diff --git a/apps/cron/tsconfig.json b/apps/cron/tsconfig.json deleted file mode 100644 index 77fa915a..00000000 --- a/apps/cron/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "allowSyntheticDefaultImports": true, - "noLib": false, - "target": "ES2021", - "sourceMap": true, - "esModuleInterop": true, - } -} diff --git a/apps/frontend/src/components/layout/settings.component.tsx b/apps/frontend/src/components/layout/settings.component.tsx index 90c16e4e..fc1f6a28 100644 --- a/apps/frontend/src/components/layout/settings.component.tsx +++ b/apps/frontend/src/components/layout/settings.component.tsx @@ -77,7 +77,6 @@ export const SettingsPopup: FC<{ return; } toast.show(t('profile_updated', 'Profile updated')); - swr.mutate('/marketplace/account'); close(); }, []); diff --git a/apps/frontend/src/components/marketplace/buyer.seller.tsx b/apps/frontend/src/components/marketplace/buyer.seller.tsx deleted file mode 100644 index 1e910ce7..00000000 --- a/apps/frontend/src/components/marketplace/buyer.seller.tsx +++ /dev/null @@ -1,40 +0,0 @@ -'use client'; - -import { FC } from 'react'; -import { usePathname } from 'next/navigation'; -import clsx from 'clsx'; -import Link from 'next/link'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -export const BuyerSeller: FC = () => { - const path = usePathname(); - const t = useT(); - const pathComputed = path === '/marketplace' ? '/marketplace/seller' : path; - return ( - <div className="relative"> - <div className="w-[286px] h-[50px] bg-third p-[9px] flex select-none absolute -translate-y-[63px] end-0"> - <div className="bg-input flex flex-1"> - <Link - href="/marketplace/seller" - className={clsx( - 'flex justify-center items-center flex-1', - pathComputed.indexOf('/marketplace/seller') > -1 && - 'bg-forth text-white' - )} - > - {t('seller', 'Seller')} - </Link> - <Link - href="/marketplace/buyer" - className={clsx( - 'flex justify-center items-center flex-1', - pathComputed.indexOf('/marketplace/buyer') > -1 && - 'bg-forth text-white' - )} - > - {t('buyer', 'Buyer')} - </Link> - </div> - </div> - </div> - ); -}; diff --git a/apps/frontend/src/components/marketplace/buyer.tsx b/apps/frontend/src/components/marketplace/buyer.tsx deleted file mode 100644 index 064c2faf..00000000 --- a/apps/frontend/src/components/marketplace/buyer.tsx +++ /dev/null @@ -1,568 +0,0 @@ -'use client'; - -import React, { - FC, - Fragment, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { Checkbox } from '@gitroom/react/form/checkbox'; -import { useRouter, useSearchParams } from 'next/navigation'; -import clsx from 'clsx'; -import { Button } from '@gitroom/react/form/button'; -import { - allTagsOptions, - tagsList, -} from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list'; -import { capitalize, chunk, fill } from 'lodash'; -import useSWR from 'swr'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; -import { Textarea } from '@gitroom/react/form/textarea'; -import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { classValidatorResolver } from '@hookform/resolvers/class-validator'; -import { NewConversationDto } from '@gitroom/nestjs-libraries/dtos/marketplace/new.conversation.dto'; -import { OrderList } from '@gitroom/frontend/components/marketplace/order.list'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -export interface Root { - list: List[]; - count: number; -} -export interface List { - id: string; - name: any; - bio: string; - audience: number; - picture: { - id: string; - path: string; - }; - organizations: Organization[]; - items: Item[]; -} -export interface Organization { - organization: Organization2; -} -export interface Organization2 { - Integration: Integration[]; -} -export interface Integration { - providerIdentifier: string; -} -export interface Item { - key: string; -} -export const LabelCheckbox: FC<{ - label: string; - name: string; - value: string; - checked: boolean; - onChange: (value: string, status: boolean) => void; -}> = (props) => { - const { label, name, value, checked, onChange } = props; - const ref = useRef<any>(null); - const [innerCheck, setInnerCheck] = useState(checked); - const change = useCallback(() => { - setInnerCheck(!innerCheck); - onChange(value, !innerCheck); - }, [innerCheck]); - return ( - <div className="flex items-center gap-[10px] select-none"> - <Checkbox - ref={ref} - variant="hollow" - name={name} - checked={checked} - onChange={change} - disableForm={true} - /> - <label - onClick={() => ref.current.click()} - className="text-[20px]" - htmlFor={name} - > - {label} - </label> - </div> - ); -}; -const Pagination: FC<{ - results: number; -}> = (props) => { - const { results } = props; - const router = useRouter(); - const search = useSearchParams(); - const page = +(parseInt(search.get('page')!) || 1) - 1; - const t = useT(); - const from = page * 8; - const to = (page + 1) * 8; - const pagesArray = useMemo(() => { - return Array.from( - { - length: Math.ceil(results / 8), - }, - (_, i) => i + 1 - ); - }, [results]); - const changePage = useCallback( - (newPage: number) => () => { - const params = new URLSearchParams(window.location.search); - params.set('page', String(newPage)); - router.replace('?' + params.toString(), { - scroll: true, - }); - }, - [page] - ); - if (results < 8) { - return null; - } - return ( - <div className="flex items-center relative"> - <div className="absolute start-0"> - {t('showing', 'Showing')} - {from + 1} - {t('to', 'to')} - {to > results ? results : to} - {t('from', 'from')} - {results} - {t('results', 'Results')} - </div> - <div className="flex mx-auto"> - {page > 0 && ( - <div> - <svg - width="41" - height="40" - viewBox="0 0 41 40" - fill="none" - xmlns="http://www.w3.org/2000/svg" - onClick={changePage(page)} - > - <g clipPath="url(#clip0_703_22324)"> - <path - d="M22.5 25L17.5 20L22.5 15" - stroke="#64748B" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </g> - <defs> - <clipPath id="clip0_703_22324"> - <rect x="0.5" width="40" height="40" rx="8" fill="white" /> - </clipPath> - </defs> - </svg> - </div> - )} - {pagesArray.map((p) => ( - <div - key={p} - onClick={changePage(p)} - className={clsx( - 'w-[40px] h-[40px] flex justify-center items-center rounded-[8px] cursor-pointer', - p === page + 1 ? 'bg-customColor4' : 'text-inputText' - )} - > - {p} - </div> - ))} - {page + 1 < pagesArray[pagesArray.length - 1] && ( - <svg - onClick={changePage(page + 2)} - width="41" - height="40" - viewBox="0 0 41 40" - fill="none" - xmlns="http://www.w3.org/2000/svg" - > - <path - d="M18.5 15L23.5 20L18.5 25" - stroke="#64748B" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> - )} - </div> - </div> - ); -}; -export const Options: FC<{ - title: string; - options: Array<{ - key: string; - value: string; - }>; - onChange?: (key: string, value: boolean) => void; - preSelected?: string[]; - rows?: number; - search: boolean; -}> = (props) => { - const { title, onChange, search, preSelected } = props; - const query = 'services'; - const [selected, setPreSelected] = useState<string[]>( - preSelected?.slice(0) || [] - ); - const rows = props.rows || 1; - const optionsGroupList = chunk( - props.options, - Math.ceil(props.options.length / rows) - ); - const optionsGroup = - optionsGroupList.length < rows - ? [ - ...optionsGroupList, - ...fill(Array(rows - optionsGroupList.length), []), - ] - : optionsGroupList; - const router = useRouter(); - const searchParams = (useSearchParams().get(query) || '')?.split(',') || []; - const change = (value: string, state: boolean) => { - if (onChange) { - onChange(value, state); - } - if (!search) { - return; - } - const getAll = new URLSearchParams(window.location.search).get(query); - const splitAll = (getAll?.split(',') || []).filter((f) => f); - if (state) { - splitAll?.push(value); - } else { - splitAll?.splice(splitAll.indexOf(value), 1); - } - const params = new URLSearchParams(window.location.search); - if (!splitAll?.length) { - params.delete(query); - } else { - params.set(query, splitAll?.join(',') || ''); - } - router.replace('?' + params.toString()); - return params.toString(); - }; - return ( - <> - <div className="h-[56px] text-[20px] font-[600] flex items-center px-[24px] bg-customColor8"> - {title} - </div> - <div className="bg-customColor3 flex px-[32px] py-[24px]"> - {optionsGroup.map((options, key) => ( - <div - key={`options_` + key} - className="flex gap-[16px] flex-col flex-1 justify-start" - > - {options.map((option) => ( - <div key={option.key} className="flex gap-[10px]"> - <LabelCheckbox - value={option.key} - label={option.value} - checked={ - selected?.indexOf(option.key) > -1 || - searchParams.indexOf(option.key) > -1 - } - name={query} - onChange={change} - /> - </div> - ))} - </div> - ))} - </div> - </> - ); -}; -export const RequestService: FC<{ - toId: string; - name: string; -}> = (props) => { - const { toId, name } = props; - const router = useRouter(); - const fetch = useFetch(); - const modal = useModals(); - const resolver = useMemo(() => { - return classValidatorResolver(NewConversationDto); - }, []); - const form = useForm({ - resolver, - values: { - to: toId, - message: '', - }, - }); - const close = useCallback(() => { - return modal.closeAll(); - }, []); - - const t = useT(); - - const createConversation: SubmitHandler<NewConversationDto> = useCallback( - async (data) => { - const { id } = await ( - await fetch('/marketplace/conversation', { - method: 'POST', - body: JSON.stringify(data), - }) - ).json(); - close(); - router.push(`/messages/${id}`); - }, - [] - ); - return ( - <form onSubmit={form.handleSubmit(createConversation)}> - <FormProvider {...form}> - <div className="w-full max-w-[920px] mx-auto bg-sixth px-[16px] rounded-[4px] border border-customColor6 gap-[24px] flex flex-col relative"> - <button - onClick={close} - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - <div className="text-[18px] font-[500] flex flex-col"> - <TopTitle title={`Send a message to ${name}`} /> - <Textarea - placeholder="Add a message like: I'm intrested in 3 posts for Linkedin... (min 50 chars)" - className="mt-[14px] resize-none h-[400px]" - name="message" - label="" - /> - <div className="flex justify-end"> - <Button - disabled={!form.formState.isValid} - type="submit" - className="w-[144px] mb-[16px] rounded-[4px] text-[14px]" - > - {t('send_message', 'Send Message')} - </Button> - </div> - </div> - </div> - </FormProvider> - </form> - ); -}; -export const Card: FC<{ - data: List; -}> = (props) => { - const { data } = props; - const modal = useModals(); - const tags = useMemo(() => { - return data.items - .filter((f) => !['content-writer', 'influencers'].includes(f.key)) - .map((p) => { - return allTagsOptions?.find((t) => t.key === p.key)?.value; - }); - }, [data]); - const requestService = useCallback(() => { - modal.openModal({ - children: <RequestService toId={data.id} name={data.name || 'Noname'} />, - classNames: { - modal: 'bg-transparent text-textColor', - }, - withCloseButton: false, - size: '100%', - }); - }, []); - const t = useT(); - - const identifier = useMemo(() => { - return [ - ...new Set( - data.organizations.flatMap((p) => - p.organization.Integration.flatMap((d) => d.providerIdentifier) - ) - ), - ]; - }, []); - return ( - <div className="min-h-[155px] bg-sixth p-[24px] flex"> - <div className="flex gap-[16px] flex-1"> - <div> - <div className="h-[103px] w-[103px] bg-red-500/10 rounded-full relative"> - {data?.picture?.path && ( - <img - src={data?.picture?.path} - className="rounded-full w-full h-full" - /> - )} - <div className="w-[80px] h-[28px] bg-customColor4 absolute bottom-0 start-[50%] -translate-x-[50%] rounded-[30px] flex gap-[4px] justify-center items-center"> - <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - viewBox="0 0 16 16" - fill="none" - > - <path - d="M7.82348 9.80876C8.45164 9.28076 8.90221 8.57235 9.11409 7.77958C9.32597 6.98682 9.28891 6.14807 9.00792 5.37709C8.72694 4.60611 8.21563 3.9402 7.54334 3.46967C6.87106 2.99914 6.07032 2.74677 5.24973 2.74677C4.42914 2.74677 3.62841 2.99914 2.95612 3.46967C2.28383 3.9402 1.77253 4.60611 1.49154 5.37709C1.21056 6.14807 1.17349 6.98682 1.38537 7.77958C1.59725 8.57235 2.04782 9.28076 2.67598 9.80876C1.69509 10.2523 0.845054 10.9411 0.207856 11.8088C0.0901665 11.9691 0.0410049 12.1697 0.0711866 12.3663C0.101368 12.5629 0.208421 12.7395 0.368794 12.8572C0.529167 12.9749 0.729724 13.024 0.926344 12.9939C1.12296 12.9637 1.29954 12.8566 1.41723 12.6963C1.85832 12.0938 2.43521 11.6039 3.10109 11.2662C3.76697 10.9284 4.50309 10.7524 5.24973 10.7524C5.99637 10.7524 6.73249 10.9284 7.39837 11.2662C8.06426 11.6039 8.64114 12.0938 9.08223 12.6963C9.19992 12.8567 9.37653 12.9638 9.57321 12.9941C9.76989 13.0243 9.97053 12.9752 10.131 12.8575C10.2914 12.7398 10.3986 12.5632 10.4288 12.3665C10.459 12.1699 10.4099 11.9692 10.2922 11.8088C9.65465 10.9412 8.80445 10.2524 7.82348 9.80876ZM2.74973 6.75001C2.74973 6.25556 2.89635 5.77221 3.17106 5.36108C3.44576 4.94996 3.83621 4.62953 4.29302 4.44031C4.74984 4.25109 5.2525 4.20158 5.73746 4.29805C6.22241 4.39451 6.66787 4.63261 7.0175 4.98224C7.36713 5.33187 7.60523 5.77733 7.70169 6.26228C7.79816 6.74724 7.74865 7.2499 7.55943 7.70672C7.37021 8.16353 7.04978 8.55398 6.63866 8.82868C6.22753 9.10339 5.74418 9.25001 5.24973 9.25001C4.58669 9.25001 3.95081 8.98662 3.48196 8.51778C3.01312 8.04894 2.74973 7.41305 2.74973 6.75001ZM15.631 12.8544C15.5516 12.9127 15.4615 12.9549 15.3658 12.9784C15.2701 13.0019 15.1707 13.0063 15.0733 12.9914C14.9759 12.9765 14.8824 12.9425 14.7982 12.8914C14.7139 12.8404 14.6405 12.7732 14.5822 12.6938C14.1401 12.0925 13.5629 11.6034 12.8973 11.2658C12.2317 10.9282 11.4961 10.7515 10.7497 10.75C10.5508 10.75 10.3601 10.671 10.2194 10.5303C10.0787 10.3897 9.99973 10.1989 9.99973 10C9.99973 9.8011 10.0787 9.61033 10.2194 9.46968C10.3601 9.32903 10.5508 9.25001 10.7497 9.25001C11.1178 9.24958 11.4813 9.16786 11.8142 9.01071C12.147 8.85355 12.4411 8.62482 12.6753 8.34086C12.9096 8.05691 13.0782 7.72473 13.1692 7.36805C13.2602 7.01138 13.2713 6.63901 13.2017 6.27754C13.1322 5.91607 12.9837 5.57443 12.7668 5.277C12.5499 4.97958 12.27 4.73373 11.9471 4.557C11.6242 4.38027 11.2662 4.27702 10.8988 4.25464C10.5314 4.23225 10.1636 4.29128 9.82161 4.42751C9.73 4.46502 9.63188 4.48402 9.5329 4.48343C9.43392 4.48283 9.33603 4.46265 9.24488 4.42404C9.15374 4.38543 9.07114 4.32916 9.00184 4.25848C8.93255 4.18779 8.87793 4.10409 8.84114 4.0122C8.80435 3.9203 8.78612 3.82204 8.78749 3.72306C8.78885 3.62409 8.8098 3.52636 8.84912 3.43552C8.88844 3.34468 8.94535 3.26252 9.01658 3.19378C9.0878 3.12504 9.17193 3.07108 9.26411 3.03501C10.1468 2.6816 11.1265 2.65418 12.0276 2.95767C12.9287 3.26115 13.6922 3.87569 14.1812 4.6911C14.6703 5.50652 14.8528 6.46947 14.6962 7.4073C14.5396 8.34512 14.0541 9.1965 13.3266 9.80876C14.3075 10.2523 15.1575 10.9411 15.7947 11.8088C15.9113 11.9693 15.9594 12.1694 15.9288 12.3654C15.8981 12.5613 15.791 12.7372 15.631 12.8544Z" - fill="white" - /> - </svg> - </div> - <div className="text-[14px]">{data?.audience}</div> - </div> - </div> - </div> - <div className="flex flex-col gap-[8px]"> - <div className="flex gap-[14px] items-center"> - <div className="text-[24px]">{data.name || 'Noname'}</div> - <div className="flex gap-[3px]"> - {data.items.some((i) => i.key === 'content-writer') && ( - <div - className={clsx( - 'bg-customColor6 rounded-[34px] py-[8px] px-[12px] text-[12px]', - )} - > - {t('content_writer', 'Content Writer')} - </div> - )} - {data.items.some((i) => i.key === 'influencers') && ( - <div - className={clsx( - 'bg-customColor6 rounded-[34px] py-[8px] px-[12px] text-[12px]', - )} - > - {t('influencer', 'Influencer')} - </div> - )} - </div> - <div className="flex gap-[10px]"> - {identifier.map((i) => ( - <img - key={i} - src={`/icons/platforms/${i}.png`} - className="w-[24px] h-[24px] rounded-full" - /> - ))} - </div> - </div> - <div className="text-[18px] text-customColor18 font-[400]"> - {data.bio || 'No bio'} - </div> - <div - className={clsx( - 'gap-[8px] flex items-center text-[10px] font-[300] text-customColor41 tracking-[1.2px] uppercase', - )} - > - {tags.map((tag, index) => ( - <Fragment key={tag}> - <div>{tag}</div> - {index !== tags.length - 1 && ( - <div> - <div className="w-[4px] h-[4px] bg-customColor1 rounded-full" /> - </div> - )} - </Fragment> - ))} - </div> - </div> - </div> - <div className="ms-[100px] items-center flex"> - <Button onClick={requestService}> - {t('request_service', 'Request Service')} - </Button> - </div> - </div> - ); -}; -export const Buyer = () => { - const search = useSearchParams(); - const services = search.get('services'); - const page = +(search.get('page') || 1); - const router = useRouter(); - const fetch = useFetch(); - const marketplace = useCallback(async () => { - return await ( - await fetch('/marketplace', { - method: 'POST', - body: JSON.stringify({ - items: services?.split(',').filter((f) => f) || [], - page: page === 0 ? 1 : page, - }), - }) - ).json(); - }, [services, page]); - - const t = useT(); - - useEffect(() => { - if (!services) { - return; - } - const params = new URLSearchParams(window.location.search); - params.set('page', '1'); - router.replace('?' + params.toString()); - }, [services]); - const { data: list } = useSWR<Root>('search' + services + page, marketplace); - return ( - <div className="flex flex-col items-center mt-[100px] gap-[27px] text-center"> - <div> - <img src="/peoplemarketplace.svg" /> - </div> - <div className="text-[48px]"> - {t( - 'the_marketplace_is_not_opened_yet', - 'The marketplace is not opened yet' - )} - <br /> - {t('check_again_soon', 'Check again soon!')} - </div> - </div> - ); - return ( - <> - <div> - <OrderList type="buyer" /> - </div> - <div className="flex mt-[29px] w-full gap-[43px]"> - <div className="w-[330px]"> - <div className="flex flex-col gap-[16px]"> - <h2 className="text-[20px]">{t('filter', 'Filter')}</h2> - <div className="flex flex-col"> - {tagsList.map((tag) => ( - <Options - search={true} - key={tag.key} - options={tag.options} - title={tag.name} - /> - ))} - </div> - </div> - </div> - <div className="flex-1 gap-[16px] flex-col flex"> - <div className="text-[20px] text-right"> - {list?.count || 0} - {t('result', 'Result')} - </div> - {list?.list?.map((item, index) => ( - <Card key={String(index)} data={item} /> - ))} - {/*<Pagination results={list?.count || 0} />*/} - </div> - </div> - </> - ); -}; diff --git a/apps/frontend/src/components/marketplace/marketplace.provider.tsx b/apps/frontend/src/components/marketplace/marketplace.provider.tsx deleted file mode 100644 index ebfdb6b7..00000000 --- a/apps/frontend/src/components/marketplace/marketplace.provider.tsx +++ /dev/null @@ -1,36 +0,0 @@ -'use client'; - -import { createContext } from 'react'; -import { Orders } from '@prisma/client'; -export interface Root2 { - id: string; - buyerId: string; - sellerId: string; - createdAt: string; - updatedAt: string; - buyer: SellerBuyer; - seller: SellerBuyer; - messages: Message[]; - orders: Orders[]; -} -export interface SellerBuyer { - id: string; - name: any; - picture: Picture; -} -export interface Picture { - id: string; - path: string; -} -export interface Message { - id: string; - from: string; - content: string; - groupId: string; - createdAt: string; - updatedAt: string; - deletedAt: any; -} -export const MarketplaceProvider = createContext<{ - message?: Root2; -}>({}); diff --git a/apps/frontend/src/components/marketplace/marketplace.tsx b/apps/frontend/src/components/marketplace/marketplace.tsx deleted file mode 100644 index d2374570..00000000 --- a/apps/frontend/src/components/marketplace/marketplace.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const Marketplace = () => { - return <div />; -}; diff --git a/apps/frontend/src/components/marketplace/order.list.tsx b/apps/frontend/src/components/marketplace/order.list.tsx deleted file mode 100644 index 2c39024e..00000000 --- a/apps/frontend/src/components/marketplace/order.list.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { FC, useCallback, useMemo } from 'react'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import useSWR from 'swr'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -export const OrderList: FC<{ - type: 'seller' | 'buyer'; -}> = (props) => { - const fetch = useFetch(); - const { type } = props; - const getOrderDetails = useCallback(async () => { - return (await fetch(`/marketplace/orders?type=${type}`)).json(); - }, [type]); - const { data, isLoading } = useSWR( - `/marketplace/orders/${type}`, - getOrderDetails - ); - const t = useT(); - - const biggerRow = useMemo(() => { - return data?.orders?.reduce((all: any, current: any) => { - if (current.details.length > all) return current.details.length; - return all; - }, 0); - }, [data]); - if (isLoading || !data?.orders?.length) return <></>; - return ( - <div className="bg-sixth p-[24px] flex flex-col gap-[24px] border border-customColor6 rounded-[4px]"> - <h3 className="text-[24px]">{t('orders', 'Orders')}</h3> - <div className="pt-[20px] px-[24px] border border-customColor6 flex"> - <table className="w-full"> - <tr> - <td colSpan={biggerRow + 1} className="pb-[20px]"> - {type === 'seller' ? 'Buyer' : 'Seller'} - </td> - <td className="pb-[20px]">{t('price', 'Price')}</td> - <td className="pb-[20px]">{t('state', 'State')}</td> - </tr> - {data.orders.map((order: any) => ( - <tr key={order.id}> - <td className="pb-[20px]">{order.name}</td> - {order.details.map((details: any, index: number) => ( - <td - className="pb-[20px]" - key={details.id} - {...(index === order.details.length - 1 - ? { - colSpan: biggerRow - order.details.length + 1, - } - : {})} - > - <div className="flex gap-[20px] items-center"> - <div className="relative"> - <img - src={details.integration.picture} - alt="platform" - className="w-[24px] h-[24px] rounded-full" - /> - <img - className="absolute start-[15px] top-[15px] w-[15px] h-[15px] rounded-full" - src={`/icons/platforms/${details.integration.providerIdentifier}.png`} - alt={details.integration.name} - /> - </div> - <div> - {details.integration.name} ({details.total}/ - {details.submitted}) - </div> - </div> - </td> - ))} - <td className="pb-[20px]">{order.price}</td> - <td className="pb-[20px]">{order.status}</td> - </tr> - ))} - </table> - </div> - </div> - ); -}; diff --git a/apps/frontend/src/components/marketplace/order.top.actions.tsx b/apps/frontend/src/components/marketplace/order.top.actions.tsx deleted file mode 100644 index aaa5f072..00000000 --- a/apps/frontend/src/components/marketplace/order.top.actions.tsx +++ /dev/null @@ -1,387 +0,0 @@ -import React, { FC, useCallback, useContext, useMemo, useState } from 'react'; -import { MarketplaceProvider } from '@gitroom/frontend/components/marketplace/marketplace.provider'; -import { useUser } from '@gitroom/frontend/components/layout/user.context'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; -import { Input } from '@gitroom/react/form/input'; -import { CustomSelect } from '@gitroom/react/form/custom.select'; -import { FormProvider, useFieldArray, useForm } from 'react-hook-form'; -import { Total } from '@gitroom/react/form/total'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import useSWR from 'swr'; -import { Button } from '@gitroom/react/form/button'; -import { array, number, object, string } from 'yup'; -import { yupResolver } from '@hookform/resolvers/yup'; -import { useToaster } from '@gitroom/react/toaster/toaster'; -import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -const schema = object({ - socialMedia: array() - .min(1) - .of( - object({ - total: number().required(), - value: object({ - value: string().required('Platform is required'), - }).required(), - price: string().matches(/^\d+$/, 'Price must be a number').required(), - }) - ) - .required(), -}).required(); -export const NewOrder: FC<{ - group: string; -}> = (props) => { - const { group } = props; - const t = useT(); - const modal = useModals(); - const fetch = useFetch(); - const [update, setUpdate] = useState(0); - const toast = useToaster(); - const loadIntegrations = useCallback(async () => { - return ( - await (await fetch('/integrations/list')).json() - ).integrations.filter((f: any) => !f.disabled); - }, []); - const { data } = useSWR('integrations', loadIntegrations); - const options: Array<{ - label: string; - value: string; - icon: string; - }> = useMemo(() => { - if (!data) { - return []; - } - return data?.map((p: any) => ({ - label: p.name, - value: p.identifier, - id: p.id, - icon: ( - <div className="relative"> - <img - className="w-[20px] h-[20px] rounded-full" - src={p.picture} - alt={p.name} - /> - <img - className="absolute start-[10px] top-[10px] w-[15px] h-[15px] rounded-full" - src={`/icons/platforms/${p.identifier}.png`} - alt={p.name} - /> - </div> - ), - })); - }, [data]); - const change = useCallback(() => { - setUpdate((prev) => prev + 1); - }, [update]); - const form = useForm<{ - price: string; - socialMedia: Array<{ - value?: string; - total: number; - price: any; - }>; - }>({ - values: { - price: '', - socialMedia: [ - { - value: undefined, - total: 1, - price: '', - }, - ], - }, - criteriaMode: 'all', - // @ts-ignore - resolver: yupResolver(schema), - mode: 'onChange', - }); - const { fields, append, remove } = useFieldArray({ - control: form.control, - name: 'socialMedia', - }); - const possibleOptions = useMemo(() => { - return fields.map((z, index) => { - const field = form.getValues(`socialMedia.${index}.value`) as { - value?: { - value?: string; - total?: number; - }; - }; - return options.filter((f) => { - const getAllValues = fields.reduce((all, p, innerIndex) => { - if (index === innerIndex) { - return all; - } - const newField = form.getValues( - `socialMedia.${innerIndex}.value` - ) as { - value?: { - value?: string; - }; - }; - all.push(newField); - return all; - }, [] as any[]); - return ( - field?.value?.value === f.value || - !getAllValues.some((v) => v?.value === f.value) - ); - }); - }); - }, [update, fields, options]); - const canAddMoreOptions = useMemo(() => { - return fields.length < options.length; - }, [update, fields, options]); - const close = useCallback(() => { - return modal.closeAll(); - }, []); - const submit = useCallback(async (data: any) => { - await ( - await fetch('/marketplace/offer', { - method: 'POST', - body: JSON.stringify({ - group, - socialMedia: data.socialMedia.map((z: any) => ({ - total: z.total, - price: +z.price, - value: z.value.id, - })), - }), - }) - ).json(); - toast.show('Offer sent successfully'); - modal.closeAll(); - }, []); - const totalPrice = useMemo(() => { - return fields.reduce((total, field, index) => { - return ( - total + - (+(form.getValues(`socialMedia.${index}.price`) || 0) * - form.getValues(`socialMedia.${index}.total`) || 0) - ); - }, 0); - }, [update, fields, options]); - return ( - <form onSubmit={form.handleSubmit(submit)}> - <FormProvider {...form}> - <div className="w-full max-w-[647px] mx-auto bg-sixth px-[16px] rounded-[4px] border border-customColor6 gap-[24px] flex flex-col relative"> - <button - onClick={close} - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - <div className="text-[18px] font-[500] flex flex-col"> - <TopTitle title={`Send a new offer`} /> - <div className="p-[16px] -mx-[16px]"> - {fields.map((field, index) => ( - <div className="relative flex gap-[10px]" key={field.id}> - {index !== 0 && ( - <div - onClick={() => remove(index)} - className="cursor-pointer top-[3px] z-[99] w-[15px] h-[15px] bg-red-500 rounded-full text-textColor absolute start-[60px] text-[12px] flex justify-center items-center pb-[2px] select-none" - > - x - </div> - )} - <div className="flex-1"> - <CustomSelect - {...form.register(`socialMedia.${index}.value`)} - onChange={change} - options={possibleOptions[index]} - placeholder="Select social media" - label="Platform" - translationKey="label_platform" - disableForm={true} - /> - </div> - <div> - <Total - customOnChange={change} - {...form.register(`socialMedia.${index}.total`)} - /> - </div> - <div> - <Input - icon={<div className="text-[14px]">$</div>} - className="text-[14px]" - label="Price per post" - translationKey="label_price_per_post" - error={ - form.formState.errors?.socialMedia?.[index]?.price - ?.message - } - customUpdate={change} - name={`socialMedia.${index}.price`} - /> - </div> - </div> - ))} - {canAddMoreOptions && ( - <div> - <div - onClick={() => - append({ - value: undefined, - total: 1, - price: '', - }) - } - className="select-none rounded-[4px] border-2 border-customColor21 flex py-[9.5px] px-[24px] items-center gap-[4px] text-[14px] float-left cursor-pointer" - > - <div> - <svg - xmlns="http://www.w3.org/2000/svg" - width="18" - height="18" - viewBox="0 0 18 18" - fill="none" - > - <path - d="M15.75 9C15.75 9.14918 15.6907 9.29226 15.5852 9.39775C15.4798 9.50324 15.3367 9.5625 15.1875 9.5625H9.5625V15.1875C9.5625 15.3367 9.50324 15.4798 9.39775 15.5852C9.29226 15.6907 9.14918 15.75 9 15.75C8.85082 15.75 8.70774 15.6907 8.60225 15.5852C8.49676 15.4798 8.4375 15.3367 8.4375 15.1875V9.5625H2.8125C2.66332 9.5625 2.52024 9.50324 2.41475 9.39775C2.30926 9.29226 2.25 9.14918 2.25 9C2.25 8.85082 2.30926 8.70774 2.41475 8.60225C2.52024 8.49676 2.66332 8.4375 2.8125 8.4375H8.4375V2.8125C8.4375 2.66332 8.49676 2.52024 8.60225 2.41475C8.70774 2.30926 8.85082 2.25 9 2.25C9.14918 2.25 9.29226 2.30926 9.39775 2.41475C9.50324 2.52024 9.5625 2.66332 9.5625 2.8125V8.4375H15.1875C15.3367 8.4375 15.4798 8.49676 15.5852 8.60225C15.6907 8.70774 15.75 8.85082 15.75 9Z" - fill="white" - /> - </svg> - </div> - <div> - {t('add_another_platform', 'Add another platform')} - </div> - </div> - </div> - )} - </div> - <div className="py-[16px] flex justify-end"> - <Button type="submit" className="rounded-[4px]"> - {t('send_an_offer_for', 'Send an offer for $')} - {totalPrice} - </Button> - </div> - </div> - </div> - </FormProvider> - </form> - ); -}; -export const OrderInProgress: FC<{ - group: string; - buyer: boolean; - order: string; -}> = (props) => { - const { group, buyer, order } = props; - const t = useT(); - const fetch = useFetch(); - const completeOrder = useCallback(async () => { - if ( - await deleteDialog( - 'Are you sure you want to pay the seller and end the order? this is irreversible action' - ) - ) { - await ( - await fetch(`/marketplace/offer/${order}/complete`, { - method: 'POST', - }) - ).json(); - } - }, [order]); - return ( - <div className="flex gap-[10px]"> - {buyer && ( - <div - onClick={completeOrder} - className="rounded-[34px] border-[1px] border-customColor21 !bg-sixth h-[28px] justify-center items-center text-[12px] px-[12px] flex font-[600] cursor-pointer" - > - {t('complete_order_and_pay_early', 'Complete order and pay early')} - </div> - )} - <div className="h-[28px] justify-center items-center bg-customColor42 text-[12px] px-[12px] flex rounded-[34px] font-[600]"> - {t('order_in_progress', 'Order in progress')} - </div> - </div> - ); -}; -export const CreateNewOrder: FC<{ - group: string; -}> = (props) => { - const { group } = props; - const modals = useModals(); - const t = useT(); - const createOrder = useCallback(() => { - modals.openModal({ - classNames: { - modal: 'bg-transparent text-textColor', - }, - withCloseButton: false, - size: '100%', - children: <NewOrder group={group} />, - }); - }, [group]); - return ( - <div - className="h-[28px] justify-center items-center bg-customColor42 text-[12px] px-[12px] flex rounded-[34px] font-[600] cursor-pointer" - onClick={createOrder} - > - {t('create_a_new_offer', 'Create a new offer')} - </div> - ); -}; -enum OrderOptions { - CREATE_A_NEW_ORDER = 'CREATE_A_NEW_ORDER', - IN_PROGRESS = 'IN_PROGRESS', - WAITING_PUBLICATION = 'WAITING_PUBLICATION', -} -export const OrderTopActions = () => { - const { message } = useContext(MarketplaceProvider); - const user = useUser(); - const isBuyer = useMemo(() => { - return user?.id === message?.buyerId; - }, [user, message]); - const myOptions: OrderOptions | undefined = useMemo(() => { - if ( - !isBuyer && - (!message?.orders.length || - message.orders[0].status === 'COMPLETED' || - message.orders[0].status === 'CANCELED') - ) { - return OrderOptions.CREATE_A_NEW_ORDER; - } - if (message?.orders?.[0]?.status === 'PENDING') { - return OrderOptions.IN_PROGRESS; - } - if (message?.orders?.[0]?.status === 'ACCEPTED') { - return OrderOptions.WAITING_PUBLICATION; - } - }, [isBuyer, user, message]); - if (!myOptions) { - return null; - } - switch (myOptions) { - case OrderOptions.CREATE_A_NEW_ORDER: - return <CreateNewOrder group={message?.id!} />; - case OrderOptions.WAITING_PUBLICATION: - return ( - <OrderInProgress - group={message?.id!} - buyer={isBuyer} - order={message?.orders[0]?.id!} - /> - ); - } - return <div />; -}; diff --git a/apps/frontend/src/components/marketplace/preview.popup.dynamic.tsx b/apps/frontend/src/components/marketplace/preview.popup.dynamic.tsx deleted file mode 100644 index 4b445458..00000000 --- a/apps/frontend/src/components/marketplace/preview.popup.dynamic.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import 'reflect-metadata'; -import { FC } from 'react'; -import { Post as PrismaPost } from '.prisma/client'; -import { Providers } from '@gitroom/frontend/components/new-launch/providers/show.all.providers'; -export const PreviewPopupDynamic: FC<{ - postId: string; - providerId: string; - post: { - integration: string; - group: string; - posts: PrismaPost[]; - settings: any; - }; -}> = (props) => { - const { component: ProviderComponent } = Providers.find( - (p) => p.identifier === props.providerId - )!; - return null; -}; diff --git a/apps/frontend/src/components/marketplace/seller.tsx b/apps/frontend/src/components/marketplace/seller.tsx deleted file mode 100644 index 7370c257..00000000 --- a/apps/frontend/src/components/marketplace/seller.tsx +++ /dev/null @@ -1,241 +0,0 @@ -'use client'; - -import { Slider } from '@gitroom/react/form/slider'; -import { Button } from '@gitroom/react/form/button'; -import { tagsList } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list'; -import { Options } from '@gitroom/frontend/components/marketplace/buyer'; -import { ChangeEvent, FC, useCallback, useEffect, useState } from 'react'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import useSWR from 'swr'; -import { Input } from '@gitroom/react/form/input'; -import { useDebouncedCallback } from 'use-debounce'; -import { OrderList } from '@gitroom/frontend/components/marketplace/order.list'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { Select } from '@gitroom/react/form/select'; -import { countries } from '@gitroom/nestjs-libraries/services/stripe.country.list'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -export const AddAccount: FC<{ - openBankAccount: (country: string) => void; -}> = (props) => { - const { openBankAccount } = props; - const t = useT(); - - const [country, setCountry] = useState(''); - const [loading, setLoading] = useState(false); - return ( - <div className="bg-sixth p-[32px] text-[20px] w-full max-w-[600px] mx-auto flex flex-col gap-[24px] rounded-[4px] border border-customColor6 relative"> - {t( - 'please_select_your_country_where_your_business_is', - 'Please select your country where your business is.' - )} - <br /> - <Select - label="Country" - name="country" - disableForm={true} - value={country} - onChange={(e) => setCountry(e.target.value)} - > - <option value="">{t('select_country', '--SELECT COUNTRY--')}</option> - {countries.map((country) => ( - <option key={country.value} value={country.value}> - {country.label} - </option> - ))} - </Select> - <Button - className="w-full" - disabled={!country} - loading={loading} - type="button" - onClick={() => { - openBankAccount(country); - setLoading(true); - }} - > - {t('connect_bank_account', 'Connect Bank Account')} - </Button> - </div> - ); -}; -export const Seller = () => { - const fetch = useFetch(); - const [loading, setLoading] = useState<boolean>(true); - const [keys, setKeys] = useState< - Array<{ - key: string; - id: string; - user: string; - }> - >([]); - const [connectedLoading, setConnectedLoading] = useState(false); - const [state, setState] = useState(true); - const [audience, setAudience] = useState<number>(0); - const modals = useModals(); - const accountInformation = useCallback(async () => { - const account = await ( - await fetch('/marketplace/account', { - method: 'GET', - }) - ).json(); - setState(account.marketplace); - setAudience(account.audience); - return account; - }, []); - const onChange = useCallback((key: string, state: boolean) => { - fetch('/marketplace/item', { - method: 'POST', - body: JSON.stringify({ - key, - state, - }), - }); - }, []); - const connectBankAccountLink = useCallback(async (country: string) => { - setConnectedLoading(true); - const { url } = await ( - await fetch(`/marketplace/bank?country=${country}`, { - method: 'GET', - }) - ).json(); - window.location.href = url; - }, []); - const loadItems = useCallback(async () => { - const data = await ( - await fetch('/marketplace/item', { - method: 'GET', - }) - ).json(); - setKeys(data); - setLoading(false); - }, []); - const changeAudienceBackend = useDebouncedCallback( - useCallback(async (aud: number) => { - fetch('/marketplace/audience', { - method: 'POST', - body: JSON.stringify({ - audience: aud, - }), - }); - }, []), - 500 - ); - const changeAudience = useCallback((e: ChangeEvent<HTMLInputElement>) => { - const num = String(+e.target.value.replace(/\D/g, '') || 0).slice(0, 8); - setAudience(+num); - changeAudienceBackend(+num); - }, []); - const changeMarketplace = useCallback( - async (value: string) => { - await fetch('/marketplace/active', { - method: 'POST', - body: JSON.stringify({ - active: value === 'on', - }), - }); - setState(!state); - }, - [state] - ); - - const t = useT(); - - const { data } = useSWR('/marketplace/account', accountInformation); - const connectBankAccount = useCallback(async () => { - if (!data?.connectedAccount) { - modals.openModal({ - size: '100%', - classNames: { - modal: 'bg-transparent text-textColor', - }, - withCloseButton: false, - children: <AddAccount openBankAccount={connectBankAccountLink} />, - }); - return; - } - connectBankAccountLink(''); - }, [data, connectBankAccountLink]); - useEffect(() => { - loadItems(); - }, []); - if (loading) { - return <></>; - } - return ( - <> - <OrderList type="seller" /> - <div className="flex mt-[29px] w-full gap-[26px]"> - <div className="w-[328px] flex flex-col gap-[16px]"> - <h2 className="text-[20px]">{t('seller_mode', 'Seller Mode')}</h2> - <div className="flex p-[24px] bg-sixth rounded-[4px] border border-customColor6 flex-col items-center gap-[16px]"> - <div className="w-[64px] h-[64px] bg-customColor38 rounded-full"> - {!!data?.picture?.path && ( - <img - className="w-full h-full rounded-full" - src={data?.picture?.path || ''} - alt="avatar" - /> - )} - </div> - <div className="text-[24px]">{data?.fullname || ''}</div> - {data?.connectedAccount && ( - <div className="flex gap-[16px] items-center pb-[8px]"> - <Slider - fill={true} - value={state ? 'on' : 'off'} - onChange={changeMarketplace} - /> - <div className="text-[18px]">{t('active', 'Active')}</div> - </div> - )} - <div className="border-t border-t-customColor43 w-full" /> - <div className="w-full"> - <Button - className="w-full" - onClick={connectBankAccount} - loading={connectedLoading} - > - {!data?.connectedAccount - ? 'Connect Bank Account' - : 'Update Bank Account'} - </Button> - </div> - </div> - </div> - <div className="flex-1 flex gap-[16px] flex-col"> - <h2 className="text-[20px]">{t('details', 'Details')}</h2> - <div className="bg-sixth rounded-[4px] border border-customColor6"> - {tagsList.map((tag) => ( - <Options - rows={3} - key={tag.key} - onChange={onChange} - preSelected={keys.map((key) => key.key)} - search={false} - options={tag.options} - title={tag.name} - /> - ))} - <div className="h-[56px] text-[20px] font-[600] flex items-center px-[24px] bg-customColor8"> - {t('audience_size', 'Audience Size')} - </div> - <div className="bg-customColor3 flex px-[32px] py-[24px]"> - <div className="flex-1"> - <Input - label="Audience size on all platforms" - name="audience" - type="text" - pattern="\d*" - max={8} - disableForm={true} - value={audience} - onChange={changeAudience} - /> - </div> - </div> - </div> - </div> - </div> - </> - ); -}; diff --git a/apps/frontend/src/components/marketplace/special.message.tsx b/apps/frontend/src/components/marketplace/special.message.tsx deleted file mode 100644 index f48d8d62..00000000 --- a/apps/frontend/src/components/marketplace/special.message.tsx +++ /dev/null @@ -1,430 +0,0 @@ -'use client'; - -import React, { FC, useCallback, useContext, useMemo } from 'react'; -import { MarketplaceProvider } from '@gitroom/frontend/components/marketplace/marketplace.provider'; -import { useUser } from '@gitroom/frontend/components/layout/user.context'; -import { Button } from '@gitroom/react/form/button'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import useSWR from 'swr'; -import { capitalize } from 'lodash'; -import removeMd from 'remove-markdown'; -import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { Post as PrismaPost } from '@prisma/client'; -import dynamic from 'next/dynamic'; -import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration'; -import dayjs from 'dayjs'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; -const PreviewPopupDynamic = dynamic(() => - import('@gitroom/frontend/components/marketplace/preview.popup.dynamic').then( - (mod) => mod.PreviewPopupDynamic - ) -); -interface SpecialMessageInterface { - type: string; - data: { - id: string; - [key: string]: any; - }; -} -export const OrderCompleted: FC = () => { - const t = useT(); - - return ( - <div className="border border-customColor44 flex flex-col rounded-[6px] overflow-hidden"> - <div className="flex items-center bg-customColor8 px-[24px] py-[16px] text-[20px]"> - <div className="flex-1">{t('order_completed', 'Order completed')}</div> - </div> - <div className="py-[16px] px-[24px] flex flex-col gap-[20px] text-[18px]"> - {t('the_order_has_been_completed', 'The order has been completed')} - </div> - </div> - ); -}; -export const Published: FC<{ - isCurrentOrder: boolean; - isSellerOrBuyer: 'BUYER' | 'SELLER'; - orderStatus: string; - data: SpecialMessageInterface; -}> = (props) => { - const t = useT(); - const { data, isSellerOrBuyer } = props; - return ( - <div className="border border-customColor44 flex flex-col rounded-[6px] overflow-hidden"> - <div className="flex items-center bg-customColor8 px-[24px] py-[16px] text-[20px]"> - <div className="flex-1"> - {isSellerOrBuyer === 'BUYER' ? 'Your' : 'The'} - {t('post_has_been_published', 'post has been published')} - </div> - </div> - - <div className="py-[16px] px-[24px] flex flex-col gap-[20px]"> - <div className="flex gap-[20px]"> - <div className="relative"> - <img - src={data.data.picture} - alt="platform" - className="w-[24px] h-[24px] rounded-full" - /> - <img - className="absolute start-[15px] top-[15px] w-[15px] h-[15px] rounded-full" - src={`/icons/platforms/${data.data.integration}.png`} - alt={data.data.name} - /> - </div> - - <div className="flex-1 text-[18px]">{data.data.name}</div> - </div> - <div className="text-[14px]"> - {t('url_1', 'URL:')} - <a className="underline hover:font-bold" href={data.data.url}> - {data.data.url} - </a> - </div> - </div> - </div> - ); -}; -export const PreviewPopup: FC<{ - postId: string; - providerId: string; - post: { - integration: string; - group: string; - posts: PrismaPost[]; - settings: any; - }; -}> = (props) => { - const modal = useModals(); - const close = useCallback(() => { - return modal.closeAll(); - }, []); - return ( - <div className="bg-primary p-[20px] w-full relative"> - <button - onClick={close} - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - <PreviewPopupDynamic {...props} /> - </div> - ); -}; -export const Offer: FC<{ - isCurrentOrder: boolean; - isSellerOrBuyer: 'BUYER' | 'SELLER'; - orderStatus: string; - data: SpecialMessageInterface; -}> = (props) => { - const { data, isSellerOrBuyer, isCurrentOrder, orderStatus } = props; - const fetch = useFetch(); - const acceptOrder = useCallback(async () => { - const { url } = await ( - await fetch(`/marketplace/orders/${data.data.id}/payment`, { - method: 'POST', - }) - ).json(); - window.location.href = url; - }, [data.data.id]); - const totalPrice = useMemo(() => { - return data?.data?.ordersItems?.reduce((all: any, current: any) => { - return all + current.price * current.quantity; - }, 0); - }, [data?.data?.ordersItems]); - - const t = useT(); - - return ( - <div className="border border-customColor44 flex flex-col rounded-[6px] overflow-hidden"> - <div className="flex items-center bg-customColor8 px-[24px] py-[16px] text-[20px]"> - <div className="flex-1">{t('new_offer', 'New Offer')}</div> - <div className="text-customColor42">${totalPrice}</div> - </div> - <div className="py-[16px] px-[24px] flex flex-col gap-[20px]"> - <div className="text-inputText text-[12px]"> - {t('platform', 'Platform')} - </div> - {data.data.ordersItems.map((item: any) => ( - <div - key={item.integration.id} - className="flex gap-[10px] items-center" - > - <div className="relative"> - <img - src={item.integration.picture} - alt="platform" - className="w-[24px] h-[24px] rounded-full" - /> - <img - className="absolute start-[15px] top-[15px] w-[15px] h-[15px] rounded-full" - src={`/icons/platforms/${item.integration.providerIdentifier}.png`} - alt={item.integration.name} - /> - </div> - <div className="flex-1 text-[18px]">{item.integration.name}</div> - <div className="text-[18px]"> - {item.quantity} - {t('posts', 'Posts')} - </div> - </div> - ))} - {orderStatus === 'PENDING' && - isCurrentOrder && - isSellerOrBuyer === 'BUYER' && ( - <div className="flex justify-end"> - <Button - className="rounded-[4px] text-[14px]" - onClick={acceptOrder} - > - {t('pay_accept_offer', 'Pay & Accept Offer')} - </Button> - </div> - )} - {orderStatus === 'ACCEPTED' && ( - <div className="flex justify-end"> - <Button className="rounded-[4px] text-[14px] border border-tableBorder !bg-sixth text-tableBorder"> - {t('accepted', 'Accepted')} - </Button> - </div> - )} - </div> - </div> - ); -}; -export const Post: FC<{ - isCurrentOrder: boolean; - isSellerOrBuyer: 'BUYER' | 'SELLER'; - orderStatus: string; - message: string; - data: SpecialMessageInterface; -}> = (props) => { - const { data, isSellerOrBuyer, message, isCurrentOrder, orderStatus } = props; - const fetch = useFetch(); - const modal = useModals(); - const getIntegration = useCallback(async () => { - return ( - await fetch( - `/integrations/${data.data.integration}?order=${data.data.id}`, - { - method: 'GET', - } - ) - ).json(); - }, []); - const requestRevision = useCallback(async () => { - if ( - !(await deleteDialog( - 'Are you sure you want to request a revision?', - 'Yes' - )) - ) { - return; - } - await fetch(`/marketplace/posts/${data.data.postId}/revision`, { - method: 'POST', - body: JSON.stringify({ - message, - }), - headers: { - 'Content-Type': 'application/json', - }, - }); - }, [data]); - const requestApproved = useCallback(async () => { - if ( - !(await deleteDialog( - 'Are you sure you want to approve this post?', - 'Yes' - )) - ) { - return; - } - await fetch(`/marketplace/posts/${data.data.postId}/approve`, { - method: 'POST', - body: JSON.stringify({ - message, - }), - headers: { - 'Content-Type': 'application/json', - }, - }); - }, [data]); - const preview = useCallback(async () => { - const post = await ( - await fetch(`/marketplace/posts/${data.data.postId}`) - ).json(); - const integration = await getIntegration(); - modal.openModal({ - classNames: { - modal: 'bg-transparent text-textColor', - }, - size: 'auto', - withCloseButton: false, - children: ( - <IntegrationContext.Provider - value={{ - allIntegrations: [], - date: newDayjs(), - integration, - value: [], - }} - > - <PreviewPopup - providerId={post?.providerId!} - post={post} - postId={data?.data?.postId!} - /> - </IntegrationContext.Provider> - ), - }); - }, [data?.data]); - const { data: integrationData } = useSWR<{ - id: string; - name: string; - picture: string; - providerIdentifier: string; - }>(`/integrations/${data.data.integration}`, getIntegration); - - const t = useT(); - - return ( - <div className="border border-customColor44 flex flex-col rounded-[6px] overflow-hidden"> - <div className="flex items-center bg-customColor8 px-[24px] py-[16px] text-[20px]"> - <div className="flex-1"> - {t('post_draft', 'Post Draft')} - {capitalize(integrationData?.providerIdentifier || '')} - </div> - </div> - <div className="py-[16px] px-[24px] flex gap-[20px]"> - <div> - <div className="relative"> - <img - src={integrationData?.picture} - alt="platform" - className="w-[24px] h-[24px] rounded-full" - /> - <img - className="absolute start-[15px] top-[15px] w-[15px] h-[15px] rounded-full" - src={`/icons/platforms/${integrationData?.providerIdentifier}.png`} - alt={integrationData?.name} - /> - </div> - </div> - <div className="flex flex-1 flex-col text-[16px] gap-[2px]"> - <div className="text-[18px]">{integrationData?.name}</div> - <div>{removeMd(data.data.description)}</div> - {isSellerOrBuyer === 'BUYER' && - isCurrentOrder && - data.data.status === 'PENDING' && - orderStatus === 'ACCEPTED' && ( - <div className="mt-[18px] flex gap-[10px] justify-end"> - <Button - onClick={requestRevision} - className="rounded-[4px] text-[14px] border-[2px] border-customColor21 !bg-sixth" - > - {t('revision_needed', 'Revision Needed')} - </Button> - <Button - onClick={requestApproved} - className="rounded-[4px] text-[14px] border-[2px] border-customColor21 !bg-sixth" - > - {t('approve', 'Approve')} - </Button> - <Button className="rounded-[4px]" onClick={preview}> - {t('preview', 'Preview')} - </Button> - </div> - )} - - {data.data.status === 'REVISION' && ( - <div className="flex justify-end"> - <Button className="rounded-[4px] text-[14px] border border-tableBorder !bg-sixth text-tableBorder"> - {t('revision_requested', 'Revision Requested')} - </Button> - </div> - )} - {data.data.status === 'APPROVED' && ( - <div className="flex justify-end gap-[10px]"> - <Button className="rounded-[4px] text-[14px] border border-tableBorder !bg-sixth text-tableBorder"> - {t('accepted_1', 'ACCEPTED')} - </Button> - </div> - )} - - {data.data.status === 'CANCELED' && ( - <div className="flex justify-end gap-[10px]"> - <Button className="rounded-[4px] text-[14px] border border-tableBorder !bg-sixth text-tableBorder"> - {t('cancelled_by_the_seller', 'Cancelled by the seller')} - </Button> - </div> - )} - </div> - </div> - </div> - ); -}; -export const SpecialMessage: FC<{ - data: SpecialMessageInterface; - id: string; -}> = (props) => { - const { data, id } = props; - const { message } = useContext(MarketplaceProvider); - const user = useUser(); - const isCurrentOrder = useMemo(() => { - return message?.orders?.[0]?.id === data?.data?.id; - }, [message, data]); - const isSellerOrBuyer = useMemo(() => { - return user?.id === message?.buyerId ? 'BUYER' : 'SELLER'; - }, [user, message]); - if (data.type === 'offer') { - return ( - <Offer - data={data} - orderStatus={message?.orders?.[0]?.status!} - isCurrentOrder={isCurrentOrder} - isSellerOrBuyer={isSellerOrBuyer} - /> - ); - } - if (data.type === 'post') { - return ( - <Post - data={data} - orderStatus={message?.orders?.[0]?.status!} - isCurrentOrder={isCurrentOrder} - isSellerOrBuyer={isSellerOrBuyer} - message={id} - /> - ); - } - if (data.type === 'published') { - return ( - <Published - data={data} - orderStatus={message?.orders?.[0]?.status!} - isCurrentOrder={isCurrentOrder} - isSellerOrBuyer={isSellerOrBuyer} - /> - ); - } - if (data.type === 'order-completed') { - return <OrderCompleted />; - } - return null; -}; diff --git a/apps/frontend/src/components/messages/layout.tsx b/apps/frontend/src/components/messages/layout.tsx deleted file mode 100644 index 01c20da0..00000000 --- a/apps/frontend/src/components/messages/layout.tsx +++ /dev/null @@ -1,123 +0,0 @@ -'use client'; - -import { FC, ReactNode, useCallback, useMemo } from 'react'; -import clsx from 'clsx'; -import useSWR from 'swr'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import { useParams, useRouter } from 'next/navigation'; -import { - MarketplaceProvider, - Root2, -} from '@gitroom/frontend/components/marketplace/marketplace.provider'; -import { useUser } from '@gitroom/frontend/components/layout/user.context'; -import { Button } from '@gitroom/react/form/button'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -const Card: FC<{ - message: Root2; -}> = (props) => { - const { message } = props; - const path = useParams(); - const router = useRouter(); - const user = useUser(); - const changeConversation = useCallback(() => { - router.push(`/messages/${message.id}`); - }, []); - const t = useT(); - - const showFrom = useMemo(() => { - return user?.id === message?.buyerId ? message?.seller : message?.buyer; - }, [message, user]); - return ( - <div - onClick={changeConversation} - className={clsx( - 'h-[89px] p-[24px] flex gap-[16px] rounded-[4px] cursor-pointer', - path?.id === message.id && 'bg-sixth border border-customColor6' - )} - > - <div className="w-[40px] h-[40px] rounded-full bg-amber-200"> - {showFrom?.picture?.path && ( - <img - src={showFrom.picture.path} - alt={showFrom.name || 'Noname'} - className="w-full h-full rounded-full" - /> - )} - </div> - <div className="flex-1 relative"> - <div className="absolute start-0 top-0 w-full h-full flex flex-col whitespace-nowrap"> - <div>{showFrom?.name || 'Noname'}</div> - <div className="text-[12px] w-full overflow-ellipsis overflow-hidden"> - {message.messages[0]?.content} - </div> - </div> - </div> - <div className="text-[12px]">{t('mar_28', 'Mar 28')}</div> - </div> - ); -}; -export const Layout: FC<{ - renderChildren: ReactNode; -}> = (props) => { - const { renderChildren } = props; - const fetch = useFetch(); - const params = useParams(); - const router = useRouter(); - const loadMessagesGroup = useCallback(async () => { - return await (await fetch('/messages')).json(); - }, []); - const messagesGroup = useSWR<Root2[]>('messagesGroup', loadMessagesGroup, { - refreshInterval: 5000, - }); - const t = useT(); - - const marketplace = useCallback(() => { - router.push('/marketplace'); - }, [router]); - const currentMessage = useMemo(() => { - return messagesGroup?.data?.find((message) => message.id === params.id); - }, [params.id, messagesGroup.data]); - if (messagesGroup.isLoading) { - return null; - } - if (!messagesGroup.isLoading && !messagesGroup?.data?.length) { - return ( - <div className="flex flex-col justify-center items-center mt-[100px] gap-[27px] text-center"> - <div> - <img src="/peoplemarketplace.svg" /> - </div> - <div className="text-[48px]"> - {t('there_are_no_messages_yet', 'There are no messages yet.')} - <br /> - {t('checkout_the_marketplace', 'Checkout the Marketplace')} - </div> - <div> - <Button onClick={marketplace}> - {t('go_to_marketplace', 'Go to marketplace')} - </Button> - </div> - </div> - ); - } - return ( - <div className="flex gap-[20px]"> - <div className="pt-[7px] w-[330px] flex flex-col"> - <div className="text-[20px] mb-[18px]"> - {t('all_messages', 'All Messages')} - </div> - <div className="flex flex-col"> - {messagesGroup.data?.map((message) => ( - <Card key={message.id} message={message} /> - ))} - </div> - </div> - <MarketplaceProvider.Provider - value={{ - message: currentMessage, - }} - > - <div className="flex-1 flex flex-col">{renderChildren}</div> - </MarketplaceProvider.Provider> - </div> - ); -}; diff --git a/apps/frontend/src/components/messages/messages.tsx b/apps/frontend/src/components/messages/messages.tsx deleted file mode 100644 index 3416c8ff..00000000 --- a/apps/frontend/src/components/messages/messages.tsx +++ /dev/null @@ -1,285 +0,0 @@ -'use client'; - -export interface Root { - id: string; - buyerId: string; - sellerId: string; - createdAt: string; - updatedAt: string; - messages: Message[]; -} -export interface SellerBuyer { - id: string; - name?: string; - picture: Picture; -} -export interface Picture { - id: string; - path: string; -} -export interface Message { - id: string; - from: string; - content: string; - special?: string; - groupId: string; - createdAt: string; - updatedAt: string; - deletedAt: any; -} -import { Textarea } from '@gitroom/react/form/textarea'; -import clsx from 'clsx'; -import useSWR from 'swr'; -import { - FC, - UIEventHandler, - useCallback, - useContext, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { useParams } from 'next/navigation'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import { reverse } from 'lodash'; -import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; -import { classValidatorResolver } from '@hookform/resolvers/class-validator'; -import { AddMessageDto } from '@gitroom/nestjs-libraries/dtos/messages/add.message'; -import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; -import { useUser } from '@gitroom/frontend/components/layout/user.context'; -import { OrderTopActions } from '@gitroom/frontend/components/marketplace/order.top.actions'; -import { MarketplaceProvider } from '@gitroom/frontend/components/marketplace/marketplace.provider'; -import { SpecialMessage } from '@gitroom/frontend/components/marketplace/special.message'; -import { usePageVisibility } from '@gitroom/react/helpers/use.is.visible'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; -export const Message: FC<{ - message: Message; - seller: SellerBuyer; - buyer: SellerBuyer; - scrollDown: () => void; -}> = (props) => { - const { message, seller, buyer, scrollDown } = props; - const user = useUser(); - const t = useT(); - const amITheBuyerOrSeller = useMemo(() => { - return user?.id === buyer?.id ? 'BUYER' : 'SELLER'; - }, [buyer, user]); - useEffect(() => { - scrollDown(); - }, []); - const person = useMemo(() => { - if (message.from === 'BUYER') { - return buyer; - } - if (message.from === 'SELLER') { - return seller; - } - }, [amITheBuyerOrSeller, buyer, seller, message]); - const data = useMemo(() => { - if (!message.special) { - return false; - } - return JSON.parse(message.special); - }, [message]); - const isMe = useMemo(() => { - return ( - (amITheBuyerOrSeller === 'BUYER' && message.from === 'BUYER') || - (amITheBuyerOrSeller === 'SELLER' && message.from === 'SELLER') - ); - }, [amITheBuyerOrSeller, message]); - const time = useMemo(() => { - return newDayjs(message.createdAt).format('h:mm A'); - }, [message]); - return ( - <div className="flex gap-[10px]"> - <div> - <div className="w-[24px] h-[24px] rounded-full bg-amber-200"> - {!!person?.picture?.path && ( - <img - src={person.picture.path} - alt="person" - className="w-[24px] h-[24px] rounded-full" - /> - )} - </div> - </div> - <div className="flex-1 flex flex-col max-w-[534px] gap-[10px]"> - <div className="flex gap-[10px] items-center"> - <div>{isMe ? t('me', 'Me') : person?.name}</div> - <div className="w-[6px] h-[6px] bg-customColor34 rounded-full" /> - <div className="text-[14px] text-inputText">{time}</div> - </div> - <pre - className={clsx( - 'whitespace-pre-line font-[400] text-[12px]', - )} - > - {message.content} - {data && <SpecialMessage data={data} id={message.id} />} - </pre> - </div> - </div> - ); -}; -const Page: FC<{ - page: number; - group: string; - refChange: any; -}> = (props) => { - const { page, group, refChange } = props; - const fetch = useFetch(); - const { message } = useContext(MarketplaceProvider); - const visible = usePageVisibility(page); - const loadMessages = useCallback(async () => { - return await (await fetch(`/messages/${group}/${page}`)).json(); - }, []); - const { data, mutate } = useSWR<Root>(`load-${page}-${group}`, loadMessages, { - ...(page === 1 - ? { - refreshInterval: visible ? 5000 : 0, - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, - revalidateIfStale: false, - } - : {}), - }); - const scrollDown = useCallback(() => { - if (page > 1) { - return; - } - // @ts-ignore - refChange.current?.scrollTo(0, refChange.current.scrollHeight); - }, [refChange]); - const messages = useMemo(() => { - return reverse([...(data?.messages || [])]); - }, [data]); - return ( - <> - {messages.map((m) => ( - <Message - key={m.id} - message={m} - seller={message?.seller!} - buyer={message?.buyer!} - scrollDown={scrollDown} - /> - ))} - </> - ); -}; -export const Messages = () => { - const [pages, setPages] = useState([makeId(3)]); - const user = useUser(); - const params = useParams(); - const fetch = useFetch(); - const t = useT(); - - const ref = useRef(null); - const { message } = useContext(MarketplaceProvider); - const showFrom = useMemo(() => { - return user?.id === message?.buyerId ? message?.seller : message?.buyer; - }, [message, user]); - const resolver = useMemo(() => { - return classValidatorResolver(AddMessageDto); - }, []); - const form = useForm({ - resolver, - values: { - message: '', - }, - }); - useEffect(() => { - setPages([makeId(3)]); - }, [params.id]); - const loadMessages = useCallback(async () => { - return await (await fetch(`/messages/${params.id}/1`)).json(); - }, []); - const { data, mutate, isLoading } = useSWR<Root>( - `load-1-${params.id}`, - loadMessages - ); - const submit: SubmitHandler<AddMessageDto> = useCallback(async (values) => { - await fetch(`/messages/${params.id}`, { - method: 'POST', - body: JSON.stringify(values), - }); - mutate(); - form.reset(); - }, []); - const changeScroll: UIEventHandler<HTMLDivElement> = useCallback( - (e) => { - // @ts-ignore - if (e.target.scrollTop === 0) { - // @ts-ignore - e.target.scrollTop = 1; - setPages((prev) => [...prev, makeId(3)]); - } - }, - [pages, setPages] - ); - return ( - <form onSubmit={form.handleSubmit(submit)}> - <FormProvider {...form}> - <div className="flex-1 flex flex-col rounded-[4px] border border-customColor6 bg-customColor3 pb-[16px]"> - <div className="bg-customColor8 h-[64px] px-[24px] py-[16px] flex gap-[10px] items-center"> - <div className="w-[32px] h-[32px] rounded-full bg-amber-200"> - {!!showFrom?.picture?.path && ( - <img - src={showFrom?.picture?.path} - alt="seller" - className="w-[32px] h-[32px] rounded-full" - /> - )} - </div> - <div className="text-[20px] flex-1"> - {showFrom?.name || t('noname', 'Noname')} - </div> - <div> - <OrderTopActions /> - </div> - </div> - <div className="flex-1 min-h-[658px] max-h-[658px] relative"> - <div - className="pt-[18px] pb-[18px] absolute top-0 start-0 w-full h-full px-[24px] flex flex-col gap-[24px] overflow-x-hidden overflow-y-auto" - onScroll={changeScroll} - ref={ref} - > - {pages.map((p, index) => ( - <Page - key={'page_' + (pages.length - index)} - refChange={ref} - page={pages.length - index} - group={params.id as string} - /> - ))} - </div> - </div> - - <div className="border-t border-t-customColor46 p-[16px] flex flex-col"> - <div> - <Textarea - className="!min-h-[100px] resize-none" - label="" - name="message" - /> - </div> - <div className="flex justify-end"> - <button - className={clsx( - 'rounded-[4px] border border-customColor21 h-[48px] px-[24px]', - !form.formState.isValid && 'opacity-40' - )} - disabled={!form.formState.isValid} - > - {t('send_message', 'Send Message')} - </button> - </div> - </div> - </div> - </FormProvider> - </form> - ); -}; diff --git a/apps/orchestrator/src/activities/autopost.activity.ts b/apps/orchestrator/src/activities/autopost.activity.ts new file mode 100644 index 00000000..83926938 --- /dev/null +++ b/apps/orchestrator/src/activities/autopost.activity.ts @@ -0,0 +1,27 @@ +import { Injectable } from '@nestjs/common'; +import { Activity, ActivityMethod } from 'nestjs-temporal-core'; +import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; +import { + NotificationService, + NotificationType, +} from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; +import { Integration, Post, State } from '@prisma/client'; +import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; +import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; +import { timer } from '@gitroom/helpers/utils/timer'; +import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; +import { WebhooksService } from '@gitroom/nestjs-libraries/database/prisma/webhooks/webhooks.service'; +import { AutopostService } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.service'; + +@Injectable() +@Activity() +export class AutopostActivity { + constructor(private _autoPostService: AutopostService) {} + + @ActivityMethod() + async autoPost(id: string) { + return this._autoPostService.startAutopost(id) + } +} diff --git a/apps/orchestrator/src/activities/email.activity.ts b/apps/orchestrator/src/activities/email.activity.ts new file mode 100644 index 00000000..a48cbc0b --- /dev/null +++ b/apps/orchestrator/src/activities/email.activity.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { Activity, ActivityMethod } from 'nestjs-temporal-core'; +import { EmailService } from '@gitroom/nestjs-libraries/services/email.service'; +import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; + +@Injectable() +@Activity() +export class EmailActivity { + constructor( + private _emailService: EmailService, + private _organizationService: OrganizationService + ) {} + + @ActivityMethod() + async sendEmail(to: string, subject: string, html: string, replyTo?: string) { + return this._emailService.sendEmail(to, subject, html, replyTo); + } + + @ActivityMethod() + async getUserOrgs(id: string) { + return this._organizationService.getTeam(id); + } +} diff --git a/apps/orchestrator/src/app.module.ts b/apps/orchestrator/src/app.module.ts index cf5115cb..1d2acf0b 100644 --- a/apps/orchestrator/src/app.module.ts +++ b/apps/orchestrator/src/app.module.ts @@ -2,14 +2,12 @@ import { Module } from '@nestjs/common'; import { PostActivity } from '@gitroom/orchestrator/activities/post.activity'; import { getTemporalModule } from '@gitroom/nestjs-libraries/temporal/temporal.module'; import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; -import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; +import { AutopostService } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.service'; +import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; -const activities = [ - PostActivity, -]; +const activities = [PostActivity, AutopostService, EmailActivity]; @Module({ imports: [ - BullMqModule, DatabaseModule, getTemporalModule(true, require.resolve('./workflows'), activities), ], diff --git a/apps/orchestrator/src/signals/email.signal.ts b/apps/orchestrator/src/signals/email.signal.ts new file mode 100644 index 00000000..339e8218 --- /dev/null +++ b/apps/orchestrator/src/signals/email.signal.ts @@ -0,0 +1,9 @@ +import { defineSignal } from '@temporalio/workflow'; + +export type Email = { + message: string; + title?: string; + type: 'success' | 'fail' | 'info'; +}; + +export const emailSignal = defineSignal<[Email[]]>('email'); diff --git a/apps/orchestrator/src/workflows/autopost.workflow.ts b/apps/orchestrator/src/workflows/autopost.workflow.ts new file mode 100644 index 00000000..62afe982 --- /dev/null +++ b/apps/orchestrator/src/workflows/autopost.workflow.ts @@ -0,0 +1,30 @@ +import { proxyActivities, sleep } from '@temporalio/workflow'; +import { AutopostActivity } from '@gitroom/orchestrator/activities/autopost.activity'; + +const { autoPost } = proxyActivities<AutopostActivity>({ + startToCloseTimeout: '10 minute', + taskQueue: 'main', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, +}); + +export async function autoPostWorkflow({ + id, + immediately, +}: { + id: string; + immediately: boolean; +}) { + while (true) { + try { + if (immediately) { + await autoPost(id); + } + } catch (err) {} + immediately = true; + await sleep(3600000); + } +} diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts new file mode 100644 index 00000000..92802ec2 --- /dev/null +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -0,0 +1,69 @@ +import { + condition, + continueAsNew, + proxyActivities, + setHandler, + sleep, +} from '@temporalio/workflow'; +import { Email, emailSignal } from '@gitroom/orchestrator/signals/email.signal'; +import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; + +const { sendEmail, getUserOrgs } = proxyActivities<EmailActivity>({ + startToCloseTimeout: '10 minute', + taskQueue: 'main', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, +}); + +export async function digestEmailWorkflow({ + organizationId, + queue = [], +}: { + organizationId: string; + queue?: Email[]; +}) { + setHandler(emailSignal, (data) => { + queue.push(...data); + }); + + while (true) { + await condition(() => queue.length > 0); + await sleep(60000); + + // Take a snapshot batch and immediately clear queue. + const batch = queue.splice(0, queue.length); + queue = []; + + const org = await getUserOrgs(organizationId); + + for (const user of org.users) { + const allowFailure = user.user.sendFailureEmails ? 'fail' : null; + const allowSuccess = user.user.sendSuccessEmails ? 'success' : null; + + const toSend = batch.filter( + (email) => + email.type === allowFailure || + email.type === allowSuccess || + email.type === 'info' + ); + + if (toSend.length === 0) continue; + + await sendEmail( + user.user.email, + toSend.length === 1 + ? toSend[0].title + : `[Postiz] Your latest notifications`, + toSend.map((p) => p.message).join('<br/>') + ); + } + + return continueAsNew({ + organizationId, + queue, + }); + } +} diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index a64cfc6e..f6c0dabc 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -1 +1,3 @@ export * from './post.workflow'; +export * from './autopost.workflow'; +export * from './digest.email.workflow'; diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index aef3e4a1..8705519f 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -14,18 +14,23 @@ import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { TypedSearchAttributes } from '@temporalio/common'; import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; +const proxyTaskQueue = (taskQueue: string) => { + return proxyActivities<PostActivity>({ + startToCloseTimeout: '10 minute', + taskQueue, + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, + }); +}; + const { getPostsList, inAppNotification, - postSocial, - postComment, - refreshToken, - internalPlugs, changeState, - globalPlugs, updatePost, - processInternalPlug, - processPlug, sendWebhooks, isCommentable, } = proxyActivities<PostActivity>({ @@ -38,14 +43,28 @@ const { }); export async function postWorkflow({ + taskQueue, postId, organizationId, postNow = false, }: { + taskQueue: string; postId: string; organizationId: string; postNow?: boolean; }) { + + // Dynamic task queue, for concurrency + const { + postSocial, + postComment, + refreshToken, + internalPlugs, + globalPlugs, + processInternalPlug, + processPlug, + } = proxyTaskQueue(taskQueue); + const startTime = new Date(); // get all the posts and comments to post const postsList = await getPostsList(organizationId, postId); @@ -115,7 +134,9 @@ export async function postWorkflow({ postsResults.push( ...(await postComment( postsResults[0].postId, - postsResults.length === 1 ? undefined : postsResults[i - 1].id, + postsResults.length === 1 + ? undefined + : postsResults[i - 1].postId, post.integration, [postsList[i]] )) @@ -313,6 +334,7 @@ export async function postWorkflow({ parentClosePolicy: 'ABANDON', args: [ { + taskQueue, postId, organizationId, postNow: true, diff --git a/apps/workers/.gitignore b/apps/workers/.gitignore deleted file mode 100644 index 0dff6fb6..00000000 --- a/apps/workers/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -dist/ -node_modules/ -[._]*.s[a-v][a-z] -[._]*.sw[a-p] -[._]s[a-rt-v][a-z] -[._]ss[a-gi-z] -[._]sw[a-p] - diff --git a/apps/workers/.swcrc b/apps/workers/.swcrc deleted file mode 100644 index 7d41ef14..00000000 --- a/apps/workers/.swcrc +++ /dev/null @@ -1,38 +0,0 @@ -{ - "jsc": { - "parser": { - "syntax": "typescript", - "tsx": false, - "decorators": true, - "dynamicImport": true - }, - "target": "es2020", - "baseUrl": "/Users/nevodavid/Projects/gitroom", - "paths": { - "@gitroom/backend/*": ["apps/backend/src/*"], - "@gitroom/cron/*": ["apps/cron/src/*"], - "@gitroom/frontend/*": ["apps/frontend/src/*"], - "@gitroom/helpers/*": ["libraries/helpers/src/*"], - "@gitroom/nestjs-libraries/*": ["libraries/nestjs-libraries/src/*"], - "@gitroom/react/*": ["libraries/react-shared-libraries/src/*"], - "@gitroom/plugins/*": ["libraries/plugins/src/*"], - "@gitroom/workers/*": ["apps/workers/src/*"], - "@gitroom/extension/*": ["apps/extension/src/*"] - }, - "keepClassNames": true, - "transform": { - "legacyDecorator": true, - "decoratorMetadata": true - }, - "loose": true - }, - "module": { - "type": "commonjs", - "strict": false, - "strictMode": true, - "lazy": false, - "noInterop": false - }, - "sourceMaps": true, - "minify": false -} \ No newline at end of file diff --git a/apps/workers/nest-cli.json b/apps/workers/nest-cli.json deleted file mode 100644 index 874969aa..00000000 --- a/apps/workers/nest-cli.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "monorepo": false, - "sourceRoot": "src", - "entryFile": "../../dist/workers/apps/workers/src/main", - "language": "ts", - "generateOptions": { - "spec": false - }, - "compilerOptions": { - "manualRestart": true, - "tsConfigPath": "./tsconfig.build.json", - "webpack": false, - "deleteOutDir": true, - "assets": [], - "watchAssets": false, - "plugins": [] - } -} diff --git a/apps/workers/package.json b/apps/workers/package.json deleted file mode 100644 index a4f17b0d..00000000 --- a/apps/workers/package.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "postiz-workers", - "version": "1.0.0", - "description": "", - "scripts": { - "dev": "dotenv -e ../../.env -- nest start --watch --entryFile=./apps/workers/src/main", - "build": "cross-env NODE_ENV=production nest build", - "start": "dotenv -e ../../.env -- node --experimental-require-module ./dist/apps/workers/src/main.js", - "pm2": "pm2 start pnpm --name workers -- start" - }, - "keywords": [], - "author": "", - "license": "ISC" -} diff --git a/apps/workers/src/app/app.module.ts b/apps/workers/src/app/app.module.ts deleted file mode 100644 index dfd45413..00000000 --- a/apps/workers/src/app/app.module.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Module } from '@nestjs/common'; - -import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; -import { PostsController } from '@gitroom/workers/app/posts.controller'; -import { BullMqModule } from '@gitroom/nestjs-libraries/bull-mq-transport-new/bull.mq.module'; -import { PlugsController } from '@gitroom/workers/app/plugs.controller'; -import { SentryModule } from '@sentry/nestjs/setup'; -import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception'; - -@Module({ - imports: [SentryModule.forRoot(), DatabaseModule, BullMqModule], - controllers: [PostsController, PlugsController], - providers: [FILTER], -}) -export class AppModule {} diff --git a/apps/workers/src/app/posts.controller.ts b/apps/workers/src/app/posts.controller.ts deleted file mode 100644 index 2bfbaeb8..00000000 --- a/apps/workers/src/app/posts.controller.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { EventPattern, Transport } from '@nestjs/microservices'; -import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; -import { WebhooksService } from '@gitroom/nestjs-libraries/database/prisma/webhooks/webhooks.service'; -import { AutopostService } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.service'; - -@Controller() -export class PostsController { - constructor( - private _postsService: PostsService, - private _webhooksService: WebhooksService, - private _autopostsService: AutopostService - ) {} - - @EventPattern('sendDigestEmail', Transport.REDIS) - async sendDigestEmail(data: { subject: string; org: string; since: string }) { - try { - return await this._postsService.sendDigestEmail( - data.subject, - data.org, - data.since - ); - } catch (err) { - console.log( - "Unhandled error, let's avoid crashing the digest worker", - err - ); - } - } - - @EventPattern('webhooks', Transport.REDIS) - async webhooks(data: { org: string; since: string }) { - try { - return await this._webhooksService.fireWebhooks(data.org, data.since); - } catch (err) { - console.log( - "Unhandled error, let's avoid crashing the webhooks worker", - err - ); - } - } - - @EventPattern('cron', Transport.REDIS) - async cron(data: { id: string }) { - try { - return await this._autopostsService.startAutopost(data.id); - } catch (err) { - console.log( - "Unhandled error, let's avoid crashing the autopost worker", - err - ); - } - } -} diff --git a/apps/workers/src/main.ts b/apps/workers/src/main.ts deleted file mode 100644 index ade2eff0..00000000 --- a/apps/workers/src/main.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { initializeSentry } from '@gitroom/nestjs-libraries/sentry/initialize.sentry'; -initializeSentry('workers'); - -import { NestFactory } from '@nestjs/core'; - -import { MicroserviceOptions } from '@nestjs/microservices'; -import { BullMqServer } from '@gitroom/nestjs-libraries/bull-mq-transport-new/strategy'; - -import { AppModule } from './app/app.module'; - -async function bootstrap() { - process.env.IS_WORKER = 'true'; - - // some comment again - const app = await NestFactory.createMicroservice<MicroserviceOptions>( - AppModule, - { - strategy: new BullMqServer(), - } - ); - - await app.listen(); -} - -bootstrap(); diff --git a/apps/workers/tsconfig.build.json b/apps/workers/tsconfig.build.json deleted file mode 100644 index bf14cec5..00000000 --- a/apps/workers/tsconfig.build.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"], - "compilerOptions": { - "module": "CommonJS", - "resolveJsonModule": true, - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": false, - "noImplicitAny": false, - "strictBindCallApply": false, - "forceConsistentCasingInFileNames": false, - "noFallthroughCasesInSwitch": false, - "outDir": "./dist" - } -} diff --git a/apps/workers/tsconfig.json b/apps/workers/tsconfig.json deleted file mode 100644 index d2cc80fd..00000000 --- a/apps/workers/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../../tsconfig.base.json", - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "allowSyntheticDefaultImports": true, - "target": "es2017", - "sourceMap": true - } -} diff --git a/libraries/helpers/src/utils/concurrency.service.ts b/libraries/helpers/src/utils/concurrency.service.ts deleted file mode 100644 index bd2c02c1..00000000 --- a/libraries/helpers/src/utils/concurrency.service.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; -import Bottleneck from 'bottleneck'; -import { BadBody } from '@gitroom/nestjs-libraries/integrations/social.abstract'; - -const connection = new Bottleneck.IORedisConnection({ - client: ioRedis, -}); - -const mapper = {} as Record<string, Bottleneck>; - -export const concurrency = async <T>( - identifier: string, - maxConcurrent = 1, - func: (...args: any[]) => Promise<T>, - ignoreConcurrency = false -) => { - const strippedIdentifier = identifier.toLowerCase().split('-')[0]; - mapper[strippedIdentifier] ??= new Bottleneck({ - id: strippedIdentifier + '-concurrency-new', - maxConcurrent, - datastore: 'ioredis', - connection, - minTime: 1000, - }); - let load: T; - - if (ignoreConcurrency) { - return await func(); - } - - try { - load = await mapper[strippedIdentifier].schedule<T>( - { expiration: 60000 }, - async () => { - try { - return await func(); - } catch (err) { - console.log(err); - } - } - ); - } catch (err) { - throw new BadBody( - identifier, - JSON.stringify({}), - {} as any, - `Something is wrong with ${identifier}` - ); - } - - return load; -}; diff --git a/libraries/nestjs-libraries/src/bull-mq-transport-new/bull.mq.module.ts b/libraries/nestjs-libraries/src/bull-mq-transport-new/bull.mq.module.ts deleted file mode 100644 index 30431ec6..00000000 --- a/libraries/nestjs-libraries/src/bull-mq-transport-new/bull.mq.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; - -@Global() -@Module({ - providers: [BullMqClient], - exports: [BullMqClient], -}) -export class BullMqModule {} diff --git a/libraries/nestjs-libraries/src/bull-mq-transport-new/client.ts b/libraries/nestjs-libraries/src/bull-mq-transport-new/client.ts deleted file mode 100644 index b7581521..00000000 --- a/libraries/nestjs-libraries/src/bull-mq-transport-new/client.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { ClientProxy, ReadPacket, WritePacket } from '@nestjs/microservices'; -import { Queue, QueueEvents } from 'bullmq'; -import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; -import { v4 } from 'uuid'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class BullMqClient extends ClientProxy { - queues = new Map<string, Queue>(); - queueEvents = new Map<string, QueueEvents>(); - - async connect(): Promise<any> { - return; - } - - async close() { - return; - } - - publish( - packet: ReadPacket<any>, - callback: (packet: WritePacket<any>) => void - ) { - // console.log('hello'); - // this.publishAsync(packet, callback); - return () => console.log('sent'); - } - - delete(pattern: string, jobId: string) { - const queue = this.getQueue(pattern); - return queue.remove(jobId); - } - - deleteScheduler(pattern: string, jobId: string) { - const queue = this.getQueue(pattern); - return queue.removeJobScheduler(jobId); - } - - async publishAsync( - packet: ReadPacket<any>, - callback: (packet: WritePacket<any>) => void - ) { - const queue = this.getQueue(packet.pattern); - const queueEvents = this.getQueueEvents(packet.pattern); - const job = await queue.add(packet.pattern, packet.data, { - jobId: packet.data.id ?? v4(), - ...packet.data.options, - removeOnComplete: !packet.data.options.attempts, - removeOnFail: !packet.data.options.attempts, - }); - - try { - await job.waitUntilFinished(queueEvents); - console.log('success'); - callback({ response: job.returnvalue, isDisposed: true }); - } catch (err) { - console.log('err'); - callback({ err, isDisposed: true }); - } - } - - getQueueEvents(pattern: string) { - return ( - this.queueEvents.get(pattern) || - new QueueEvents(pattern, { - connection: ioRedis, - }) - ); - } - - getQueue(pattern: string) { - return ( - this.queues.get(pattern) || - new Queue(pattern, { - connection: ioRedis, - }) - ); - } - - async checkForStuckWaitingJobs(queueName: string) { - const queue = this.getQueue(queueName); - const getJobs = await queue.getJobs('waiting' as const); - const now = Date.now(); - const thresholdMs = 60 * 60 * 1000; - return { - valid: !getJobs.some((job) => { - const age = now - job.timestamp; - return age > thresholdMs; - }), - }; - } - - async dispatchEvent(packet: ReadPacket<any>): Promise<any> { - console.log('event to dispatch: ', packet); - const queue = this.getQueue(packet.pattern); - if (packet?.data?.options?.every) { - const { every, immediately } = packet.data.options; - const id = packet.data.id ?? v4(); - await queue.upsertJobScheduler( - id, - { every, ...(immediately ? { immediately } : {}) }, - { - name: id, - data: packet.data, - opts: { - removeOnComplete: true, - removeOnFail: true, - }, - } - ); - return; - } - - await queue.add(packet.pattern, packet.data, { - jobId: packet.data.id ?? v4(), - ...packet.data.options, - removeOnComplete: true, - removeOnFail: true, - }); - } -} diff --git a/libraries/nestjs-libraries/src/bull-mq-transport-new/strategy.ts b/libraries/nestjs-libraries/src/bull-mq-transport-new/strategy.ts deleted file mode 100644 index d6d7efa7..00000000 --- a/libraries/nestjs-libraries/src/bull-mq-transport-new/strategy.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { CustomTransportStrategy, Server } from '@nestjs/microservices'; -import { Queue, Worker } from 'bullmq'; -import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; - -export class BullMqServer extends Server implements CustomTransportStrategy { - queues: Map<string, Queue>; - workers: Worker[] = []; - - /** - * This method is triggered when you run "app.listen()". - */ - listen(callback: () => void) { - this.queues = [...this.messageHandlers.keys()].reduce((all, pattern) => { - all.set(pattern, new Queue(pattern, { connection: ioRedis })); - return all; - }, new Map()); - - this.workers = Array.from(this.messageHandlers).map( - ([pattern, handler]) => { - return new Worker( - pattern, - async (job) => { - const stream$ = this.transformToObservable( - await handler(job.data.payload, job) - ); - - this.send(stream$, (packet) => { - if (packet.err) { - return job.discard(); - } - - return true; - }); - }, - { - maxStalledCount: 10, - concurrency: 300, - connection: ioRedis, - removeOnComplete: { - count: 0, - }, - removeOnFail: { - count: 0, - }, - } - ); - } - ); - - callback(); - } - - /** - * This method is triggered on application shutdown. - */ - close() { - this.workers.map((worker) => worker.close()); - this.queues.forEach((queue) => queue.close()); - return true; - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts b/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts index 0b3c66d2..68560944 100644 --- a/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { AutopostRepository } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.repository'; import { AutopostDto } from '@gitroom/nestjs-libraries/dtos/autopost/autopost.dto'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; import dayjs from 'dayjs'; import { END, START, StateGraph } from '@langchain/langgraph'; import { AutoPost, Integration } from '@prisma/client'; @@ -15,6 +14,12 @@ import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/po import Parser from 'rss-parser'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { TemporalService } from 'nestjs-temporal-core'; +import { TypedSearchAttributes } from '@temporalio/common'; +import { + organizationId, + postId as postIdSearchParam, +} from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; const parser = new Parser(); interface WorkflowChannelsState { @@ -58,7 +63,7 @@ const dallePrompt = z.object({ export class AutopostService { constructor( private _autopostsRepository: AutopostRepository, - private _workerServiceProducer: BullMqClient, + private _temporalService: TemporalService, private _integrationService: IntegrationService, private _postsService: PostsService ) {} @@ -81,7 +86,7 @@ export class AutopostService { id ); - await this.processCron(body.active, data.id); + await this.processCron(body.active, orgId, data.id); return data; } @@ -92,30 +97,39 @@ export class AutopostService { id, active ); - await this.processCron(active, id); + await this.processCron(active, orgId, id); return data; } - async processCron(active: boolean, id: string) { + async processCron(active: boolean, orgId: string, id: string) { if (active) { - return this._workerServiceProducer.emit('cron', { - id, - options: { - every: 3600000, - immediately: true, - }, - payload: { - id, - }, - }); + try { + return this._temporalService.client + .getRawClient() + ?.workflow.start('postWorkflow', { + workflowId: `autopost-${id}`, + taskQueue: 'main', + args: [{ id, immediately: true }], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: organizationId, + value: orgId, + }, + ]), + }); + } catch (err) {} } - return this._workerServiceProducer.deleteScheduler('cron', id); + try { + return await this._temporalService.terminateWorkflow(`autopost-${id}`); + } catch (err) { + return false; + } } async deleteAutopost(orgId: string, id: string) { const data = await this._autopostsRepository.deleteAutopost(orgId, id); - await this.processCron(false, id); + await this.processCron(false, orgId, id); return data; } diff --git a/libraries/nestjs-libraries/src/database/prisma/database.module.ts b/libraries/nestjs-libraries/src/database/prisma/database.module.ts index 73763d0e..e546ede5 100644 --- a/libraries/nestjs-libraries/src/database/prisma/database.module.ts +++ b/libraries/nestjs-libraries/src/database/prisma/database.module.ts @@ -4,8 +4,6 @@ import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prism import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; import { UsersRepository } from '@gitroom/nestjs-libraries/database/prisma/users/users.repository'; -import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service'; -import { StarsRepository } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.repository'; import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service'; import { SubscriptionRepository } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.repository'; import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; @@ -18,10 +16,6 @@ import { MediaService } from '@gitroom/nestjs-libraries/database/prisma/media/me import { MediaRepository } from '@gitroom/nestjs-libraries/database/prisma/media/media.repository'; import { NotificationsRepository } from '@gitroom/nestjs-libraries/database/prisma/notifications/notifications.repository'; import { EmailService } from '@gitroom/nestjs-libraries/services/email.service'; -import { ItemUserRepository } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.repository'; -import { ItemUserService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.service'; -import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; -import { MessagesRepository } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.repository'; import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service'; import { ExtractContentService } from '@gitroom/nestjs-libraries/openai/extract.content.service'; import { OpenaiService } from '@gitroom/nestjs-libraries/openai/openai.service'; @@ -55,8 +49,6 @@ import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integration UsersRepository, OrganizationService, OrganizationRepository, - StarsService, - StarsRepository, SubscriptionService, SubscriptionRepository, NotificationService, @@ -68,18 +60,14 @@ import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integration PostsService, PostsRepository, StripeService, - MessagesRepository, SignatureRepository, AutopostRepository, AutopostService, SignatureService, MediaService, MediaRepository, - ItemUserRepository, AgenciesService, AgenciesRepository, - ItemUserService, - MessagesService, IntegrationManager, RefreshIntegrationService, ExtractContentService, diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 1ad9c831..102f44ca 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -9,7 +9,6 @@ import { IntegrationRepository } from '@gitroom/nestjs-libraries/database/prisma import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { AnalyticsData, - AuthTokenDetails, SocialProvider, } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; import { Integration, Organization } from '@prisma/client'; @@ -21,11 +20,11 @@ import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abst import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto'; import { UploadFactory } from '@gitroom/nestjs-libraries/upload/upload.factory'; import { PlugDto } from '@gitroom/nestjs-libraries/dtos/plugs/plug.dto'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; import { difference, uniq } from 'lodash'; import utc from 'dayjs/plugin/utc'; import { AutopostRepository } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.repository'; import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; +import { TemporalService } from 'nestjs-temporal-core'; dayjs.extend(utc); @@ -37,16 +36,18 @@ export class IntegrationService { private _autopostsRepository: AutopostRepository, private _integrationManager: IntegrationManager, private _notificationService: NotificationService, - private _workerServiceProducer: BullMqClient, @Inject(forwardRef(() => RefreshIntegrationService)) - private _refreshIntegrationService: RefreshIntegrationService + private _refreshIntegrationService: RefreshIntegrationService, + private _temporalService: TemporalService ) {} async changeActiveCron(orgId: string) { const data = await this._autopostsRepository.getAutoposts(orgId); for (const item of data.filter((f) => f.active)) { - await this._workerServiceProducer.deleteScheduler('cron', item.id); + try { + await this._temporalService.terminateWorkflow(`autopost-${item.id}`); + } catch (err) {} } return true; diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts deleted file mode 100644 index 7381d7c8..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.repository.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class ItemUserRepository { - constructor(private _itemUser: PrismaRepository<'itemUser'>) {} - - addOrRemoveItem(add: boolean, userId: string, item: string) { - if (!add) { - return this._itemUser.model.itemUser.deleteMany({ - where: { - user: { - id: userId, - }, - key: item, - }, - }); - } - - return this._itemUser.model.itemUser.create({ - data: { - key: item, - user: { - connect: { - id: userId, - }, - }, - }, - }); - } - - getItems(userId: string) { - return this._itemUser.model.itemUser.findMany({ - where: { - user: { - id: userId, - }, - }, - }); - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts deleted file mode 100644 index cba764cd..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/marketplace/item.user.service.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { ItemUserRepository } from '@gitroom/nestjs-libraries/database/prisma/marketplace/item.user.repository'; - -@Injectable() -export class ItemUserService { - constructor(private _itemUserRepository: ItemUserRepository) {} - - addOrRemoveItem(add: boolean, userId: string, item: string) { - return this._itemUserRepository.addOrRemoveItem(add, userId, item); - } - - getItems(userId: string) { - return this._itemUserRepository.getItems(userId); - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/messages.repository.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/messages.repository.ts deleted file mode 100644 index 17873550..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/marketplace/messages.repository.ts +++ /dev/null @@ -1,915 +0,0 @@ -import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; -import { Injectable } from '@nestjs/common'; -import { NewConversationDto } from '@gitroom/nestjs-libraries/dtos/marketplace/new.conversation.dto'; -import { From, OrderStatus } from '@prisma/client'; -import { AddMessageDto } from '@gitroom/nestjs-libraries/dtos/messages/add.message'; -import { CreateOfferDto } from '@gitroom/nestjs-libraries/dtos/marketplace/create.offer.dto'; - -@Injectable() -export class MessagesRepository { - constructor( - private _messagesGroup: PrismaRepository<'messagesGroup'>, - private _messages: PrismaRepository<'messages'>, - private _orders: PrismaRepository<'orders'>, - private _organizations: PrismaRepository<'organization'>, - private _post: PrismaRepository<'post'>, - private _payoutProblems: PrismaRepository<'payoutProblems'>, - private _users: PrismaRepository<'user'> - ) {} - - async createConversation( - userId: string, - organizationId: string, - body: NewConversationDto - ) { - const { id } = - (await this._messagesGroup.model.messagesGroup.findFirst({ - where: { - buyerOrganizationId: organizationId, - buyerId: userId, - sellerId: body.to, - }, - })) || - (await this._messagesGroup.model.messagesGroup.create({ - data: { - buyerOrganizationId: organizationId, - buyerId: userId, - sellerId: body.to, - }, - })); - - await this._messagesGroup.model.messagesGroup.update({ - where: { - id, - }, - data: { - updatedAt: new Date(), - }, - }); - - await this._messages.model.messages.create({ - data: { - groupId: id, - from: From.BUYER, - content: body.message, - }, - }); - - return { id }; - } - - getOrgByOrder(orderId: string) { - return this._orders.model.orders.findFirst({ - where: { - id: orderId, - }, - select: { - messageGroup: { - select: { - buyerOrganizationId: true, - }, - }, - }, - }); - } - - async getMessagesGroup(userId: string, organizationId: string) { - return this._messagesGroup.model.messagesGroup.findMany({ - where: { - OR: [ - { - buyerOrganizationId: organizationId, - buyerId: userId, - }, - { - sellerId: userId, - }, - ], - }, - orderBy: { - updatedAt: 'desc', - }, - include: { - seller: { - select: { - id: true, - name: true, - picture: { - select: { - id: true, - path: true, - }, - }, - }, - }, - buyer: { - select: { - id: true, - name: true, - picture: { - select: { - id: true, - path: true, - }, - }, - }, - }, - orders: { - orderBy: { - createdAt: 'desc', - }, - take: 1, - }, - messages: { - orderBy: { - createdAt: 'desc', - }, - take: 1, - }, - }, - }); - } - - async createMessage( - userId: string, - orgId: string, - groupId: string, - body: AddMessageDto - ) { - const group = await this._messagesGroup.model.messagesGroup.findFirst({ - where: { - id: groupId, - OR: [ - { - buyerOrganizationId: orgId, - buyerId: userId, - }, - { - sellerId: userId, - }, - ], - }, - }); - - if (!group) { - throw new Error('Group not found'); - } - - const create = await this.createNewMessage( - groupId, - group.buyerId === userId ? From.BUYER : From.SELLER, - body.message - ); - - await this._messagesGroup.model.messagesGroup.update({ - where: { - id: groupId, - }, - data: { - updatedAt: new Date(), - }, - }); - - if (userId === group.buyerId) { - return create.group.seller; - } - - return create.group.buyer; - } - - async updateOrderOnline(userId: string) { - await this._users.model.user.update({ - where: { - id: userId, - }, - data: { - lastOnline: new Date(), - }, - }); - } - - async getMessages( - userId: string, - organizationId: string, - groupId: string, - page: number - ) { - return this._messagesGroup.model.messagesGroup.findFirst({ - where: { - id: groupId, - OR: [ - { - buyerOrganizationId: organizationId, - buyerId: userId, - }, - { - sellerId: userId, - }, - ], - }, - include: { - messages: { - orderBy: { - createdAt: 'desc', - }, - take: 10, - skip: (page - 1) * 10, - }, - }, - }); - } - - async createOffer(userId: string, body: CreateOfferDto) { - const messageGroup = - await this._messagesGroup.model.messagesGroup.findFirst({ - where: { - id: body.group, - sellerId: userId, - }, - select: { - id: true, - buyer: { - select: { - id: true, - }, - }, - orders: { - orderBy: { - createdAt: 'desc', - }, - take: 1, - }, - }, - }); - - if (!messageGroup?.id) { - throw new Error('Group not found'); - } - - if ( - messageGroup.orders.length && - messageGroup.orders[0].status !== 'COMPLETED' && - messageGroup.orders[0].status !== 'CANCELED' - ) { - throw new Error('Order already exists'); - } - - const data = await this._orders.model.orders.create({ - data: { - sellerId: userId, - buyerId: messageGroup.buyer.id, - messageGroupId: messageGroup.id, - ordersItems: { - createMany: { - data: body.socialMedia.map((item) => ({ - quantity: item.total, - integrationId: item.value, - price: item.price, - })), - }, - }, - status: 'PENDING', - }, - select: { - id: true, - ordersItems: { - select: { - quantity: true, - price: true, - integration: { - select: { - name: true, - providerIdentifier: true, - picture: true, - id: true, - }, - }, - }, - }, - }, - }); - - await this._messages.model.messages.create({ - data: { - groupId: body.group, - from: From.SELLER, - content: '', - special: JSON.stringify({ type: 'offer', data: data }), - }, - }); - - return { success: true }; - } - - async createNewMessage( - group: string, - from: From, - content: string, - special?: object - ) { - return this._messages.model.messages.create({ - data: { - groupId: group, - from, - content, - special: JSON.stringify(special), - }, - select: { - id: true, - group: { - select: { - buyer: { - select: { - lastOnline: true, - id: true, - organizations: true, - }, - }, - seller: { - select: { - lastOnline: true, - id: true, - organizations: true, - }, - }, - }, - }, - }, - }); - } - - async getOrderDetails( - userId: string, - organizationId: string, - orderId: string - ) { - const order = await this._messagesGroup.model.messagesGroup.findFirst({ - where: { - buyerId: userId, - buyerOrganizationId: organizationId, - }, - select: { - buyer: true, - seller: true, - orders: { - include: { - ordersItems: { - select: { - quantity: true, - integration: true, - price: true, - }, - }, - }, - where: { - id: orderId, - status: 'PENDING', - }, - }, - }, - }); - - if (!order?.orders[0]?.id) { - throw new Error('Order not found'); - } - - return { - buyer: order.buyer, - seller: order.seller, - order: order.orders[0]!, - }; - } - - async canAddPost(id: string, order: string, integrationId: string) { - const findOrder = await this._orders.model.orders.findFirst({ - where: { - id: order, - status: 'ACCEPTED', - }, - select: { - posts: true, - ordersItems: true, - }, - }); - - if (!findOrder) { - return false; - } - - if ( - findOrder.posts.find( - (p) => p.id === id && p.approvedSubmitForOrder === 'YES' - ) - ) { - return false; - } - - if ( - findOrder.posts.find( - (p) => - p.id === id && p.approvedSubmitForOrder === 'WAITING_CONFIRMATION' - ) - ) { - return true; - } - - const postsForIntegration = findOrder.ordersItems.filter( - (p) => p.integrationId === integrationId - ); - const totalPostsRequired = postsForIntegration.reduce( - (acc, item) => acc + item.quantity, - 0 - ); - const usedPosts = findOrder.posts.filter( - (p) => - p.integrationId === integrationId && - ['WAITING_CONFIRMATION', 'YES'].indexOf(p.approvedSubmitForOrder) > -1 - ).length; - - return totalPostsRequired > usedPosts; - } - - changeOrderStatus( - orderId: string, - status: OrderStatus, - paymentIntent?: string - ) { - return this._orders.model.orders.update({ - where: { - id: orderId, - }, - data: { - status, - captureId: paymentIntent, - }, - }); - } - - async getMarketplaceAvailableOffers(orgId: string, id: string) { - const offers = await this._organizations.model.organization.findFirst({ - where: { - id: orgId, - }, - select: { - users: { - select: { - user: { - select: { - orderSeller: { - where: { - status: 'ACCEPTED', - }, - select: { - id: true, - posts: { - where: { - deletedAt: null, - }, - select: { - id: true, - integrationId: true, - approvedSubmitForOrder: true, - }, - }, - messageGroup: { - select: { - buyerOrganizationId: true, - }, - }, - buyer: { - select: { - id: true, - name: true, - picture: { - select: { - id: true, - path: true, - }, - }, - }, - }, - ordersItems: { - select: { - quantity: true, - integration: { - select: { - id: true, - name: true, - providerIdentifier: true, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }); - - const allOrders = - offers?.users.flatMap((user) => user.user.orderSeller) || []; - - const onlyValidItems = allOrders.filter( - (order) => - (order.posts.find((p) => p.id === id) - ? 0 - : order.posts.filter((f) => f.approvedSubmitForOrder !== 'NO') - .length) < - order.ordersItems.reduce((acc, item) => acc + item.quantity, 0) - ); - - return onlyValidItems - .map((order) => { - const postsNumbers = order.posts - .filter( - (p) => - ['WAITING_CONFIRMATION', 'YES'].indexOf( - p.approvedSubmitForOrder - ) > -1 - ) - .reduce((acc, post) => { - acc[post.integrationId] = acc[post.integrationId] + 1 || 1; - return acc; - }, {} as { [key: string]: number }); - - const missing = order.ordersItems.map((item) => { - return { - integration: item, - missing: item.quantity - (postsNumbers[item.integration.id] || 0), - }; - }); - - return { - id: order.id, - usedIds: order.posts.map((p) => ({ - id: p.id, - status: p.approvedSubmitForOrder, - })), - buyer: order.buyer, - missing, - }; - }) - .filter((f) => f.missing.length); - } - - async requestRevision( - userId: string, - orgId: string, - postId: string, - message: string - ) { - const loadMessage = await this._messages.model.messages.findFirst({ - where: { - id: message, - group: { - buyerOrganizationId: orgId, - }, - }, - select: { - id: true, - special: true, - }, - }); - - const post = await this._post.model.post.findFirst({ - where: { - id: postId, - approvedSubmitForOrder: 'WAITING_CONFIRMATION', - deletedAt: null, - }, - }); - - if (post && loadMessage) { - const special = JSON.parse(loadMessage.special!); - special.data.status = 'REVISION'; - await this._messages.model.messages.update({ - where: { - id: message, - }, - data: { - special: JSON.stringify(special), - }, - }); - - await this._post.model.post.update({ - where: { - id: postId, - deletedAt: null, - }, - data: { - approvedSubmitForOrder: 'NO', - }, - }); - } - } - - async requestCancel(orgId: string, postId: string) { - const getPost = await this._post.model.post.findFirst({ - where: { - id: postId, - organizationId: orgId, - approvedSubmitForOrder: { - in: ['WAITING_CONFIRMATION', 'YES'], - }, - }, - select: { - lastMessage: true, - }, - }); - - if (!getPost) { - throw new Error('Post not found'); - } - - await this._post.model.post.update({ - where: { - id: postId, - }, - data: { - approvedSubmitForOrder: 'NO', - submittedForOrganizationId: null, - }, - }); - - const special = JSON.parse(getPost.lastMessage!.special!); - special.data.status = 'CANCELED'; - await this._messages.model.messages.update({ - where: { - id: getPost.lastMessage!.id, - }, - data: { - special: JSON.stringify(special), - }, - }); - } - - async requestApproved( - userId: string, - orgId: string, - postId: string, - message: string - ) { - const loadMessage = await this._messages.model.messages.findFirst({ - where: { - id: message, - group: { - buyerOrganizationId: orgId, - }, - }, - select: { - id: true, - special: true, - }, - }); - - const post = await this._post.model.post.findFirst({ - where: { - id: postId, - approvedSubmitForOrder: 'WAITING_CONFIRMATION', - deletedAt: null, - }, - }); - - if (post && loadMessage) { - const special = JSON.parse(loadMessage.special!); - special.data.status = 'APPROVED'; - await this._messages.model.messages.update({ - where: { - id: message, - }, - data: { - special: JSON.stringify(special), - }, - }); - - await this._post.model.post.update({ - where: { - id: postId, - deletedAt: null, - }, - data: { - approvedSubmitForOrder: 'YES', - }, - }); - - return post; - } - - return false; - } - - completeOrder(orderId: string) { - return this._orders.model.orders.update({ - where: { - id: orderId, - }, - data: { - status: 'COMPLETED', - }, - }); - } - - async completeOrderAndPay(orgId: string, order: string) { - const findOrder = await this._orders.model.orders.findFirst({ - where: { - id: order, - messageGroup: { - buyerOrganizationId: orgId, - }, - }, - select: { - captureId: true, - seller: { - select: { - account: true, - id: true, - }, - }, - ordersItems: true, - posts: true, - }, - }); - - if (!findOrder) { - return false; - } - - const releasedPosts = findOrder.posts.filter((p) => p.releaseURL); - const nonReleasedPosts = findOrder.posts.filter((p) => !p.releaseURL); - - const totalPosts = releasedPosts.reduce((acc, item) => { - acc[item.integrationId] = (acc[item.integrationId] || 0) + 1; - return acc; - }, {} as { [key: string]: number }); - - const totalOrderItems = findOrder.ordersItems.reduce((acc, item) => { - acc[item.integrationId] = (acc[item.integrationId] || 0) + item.quantity; - return acc; - }, {} as { [key: string]: number }); - - const calculate = Object.keys(totalOrderItems).reduce((acc, key) => { - acc.push({ - price: findOrder.ordersItems.find((p) => p.integrationId === key)! - .price, - quantity: totalOrderItems[key] - (totalPosts[key] || 0), - }); - return acc; - }, [] as { price: number; quantity: number }[]); - - const price = calculate.reduce((acc, item) => { - acc += item.price * item.quantity; - return acc; - }, 0); - - return { - price, - account: findOrder.seller.account, - charge: findOrder.captureId, - posts: nonReleasedPosts, - sellerId: findOrder.seller.id, - }; - } - - payoutProblem( - orderId: string, - sellerId: string, - amount: number, - postId?: string - ) { - return this._payoutProblems.model.payoutProblems.create({ - data: { - amount, - orderId, - ...(postId ? { postId } : {}), - userId: sellerId, - status: 'PAYMENT_ERROR', - }, - }); - } - - async getOrders(userId: string, orgId: string, type: 'seller' | 'buyer') { - const orders = await this._orders.model.orders.findMany({ - where: { - status: { - in: ['ACCEPTED', 'PENDING', 'COMPLETED'], - }, - ...(type === 'seller' - ? { - sellerId: userId, - } - : { - messageGroup: { - buyerOrganizationId: orgId, - }, - }), - }, - orderBy: { - updatedAt: 'desc', - }, - select: { - id: true, - status: true, - ...(type === 'seller' - ? { - buyer: { - select: { - name: true, - }, - }, - } - : { - seller: { - select: { - name: true, - }, - }, - }), - ordersItems: { - select: { - id: true, - quantity: true, - price: true, - integration: { - select: { - id: true, - picture: true, - name: true, - providerIdentifier: true, - }, - }, - }, - }, - posts: { - select: { - id: true, - integrationId: true, - releaseURL: true, - approvedSubmitForOrder: true, - state: true, - }, - }, - }, - }); - - return { - orders: await Promise.all( - orders.map(async (order) => { - return { - id: order.id, - status: order.status, - // @ts-ignore - name: type === 'seller' ? order?.buyer?.name : order?.seller?.name, - price: order.ordersItems.reduce( - (acc, item) => acc + item.price * item.quantity, - 0 - ), - details: await Promise.all( - order.ordersItems.map((item) => { - return { - posted: order.posts.filter( - (p) => - p.releaseURL && p.integrationId === item.integration.id - ).length, - submitted: order.posts.filter( - (p) => - !p.releaseURL && - (p.approvedSubmitForOrder === 'WAITING_CONFIRMATION' || - p.approvedSubmitForOrder === 'YES') && - p.integrationId === item.integration.id - ).length, - integration: item.integration, - total: item.quantity, - price: item.price, - }; - }) - ), - }; - }) - ), - }; - } - - getPost(userId: string, orgId: string, postId: string) { - return this._post.model.post.findFirst({ - where: { - id: postId, - submittedForOrder: { - messageGroup: { - OR: [{ sellerId: userId }, { buyerOrganizationId: orgId }], - }, - }, - }, - select: { - organizationId: true, - integration: { - select: { - providerIdentifier: true, - }, - }, - }, - }); - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/messages.service.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/messages.service.ts deleted file mode 100644 index 6e0e87a1..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/marketplace/messages.service.ts +++ /dev/null @@ -1,252 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { MessagesRepository } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.repository'; -import { NewConversationDto } from '@gitroom/nestjs-libraries/dtos/marketplace/new.conversation.dto'; -import { AddMessageDto } from '@gitroom/nestjs-libraries/dtos/messages/add.message'; -import { CreateOfferDto } from '@gitroom/nestjs-libraries/dtos/marketplace/create.offer.dto'; -import { From, OrderStatus, User } from '@prisma/client'; -import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository'; -import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; -import dayjs from 'dayjs'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; - -@Injectable() -export class MessagesService { - constructor( - private _workerServiceProducer: BullMqClient, - private _messagesRepository: MessagesRepository, - private _organizationRepository: OrganizationRepository, - private _inAppNotificationService: NotificationService - ) {} - - async createConversation( - userId: string, - organizationId: string, - body: NewConversationDto - ) { - const conversation = await this._messagesRepository.createConversation( - userId, - organizationId, - body - ); - - const orgs = await this._organizationRepository.getOrgsByUserId(body.to); - await Promise.all( - orgs.map(async (org) => { - return this._inAppNotificationService.inAppNotification( - org.id, - 'Request for service', - 'A user has requested a service from you', - true - ); - }) - ); - - return conversation; - } - - getMessagesGroup(userId: string, organizationId: string) { - return this._messagesRepository.getMessagesGroup(userId, organizationId); - } - - async getMessages( - userId: string, - organizationId: string, - groupId: string, - page: number - ) { - if (page === 1) { - this._messagesRepository.updateOrderOnline(userId); - } - - return this._messagesRepository.getMessages( - userId, - organizationId, - groupId, - page - ); - } - - async createNewMessage( - group: string, - from: From, - content: string, - special?: object - ) { - const message = await this._messagesRepository.createNewMessage( - group, - from, - content, - special - ); - - const user = from === 'BUYER' ? message.group.seller : message.group.buyer; - - await Promise.all( - user.organizations.map((p) => { - return this.sendMessageNotification({ - id: p.organizationId, - lastOnline: user.lastOnline, - }); - }) - ); - - return message; - } - - async sendMessageNotification(user: { id: string; lastOnline: Date }) { - if (dayjs(user.lastOnline).add(5, 'minute').isBefore(dayjs())) { - await this._inAppNotificationService.inAppNotification( - user.id, - 'New message', - 'You have a new message', - true - ); - } - } - - async createMessage( - userId: string, - orgId: string, - groupId: string, - body: AddMessageDto - ) { - const message = await this._messagesRepository.createMessage( - userId, - orgId, - groupId, - body - ); - - await Promise.all( - message.organizations.map((p) => { - return this.sendMessageNotification({ - id: p.organizationId, - lastOnline: message.lastOnline, - }); - }) - ); - - return message; - } - - createOffer(userId: string, body: CreateOfferDto) { - return this._messagesRepository.createOffer(userId, body); - } - - getOrderDetails(userId: string, organizationId: string, orderId: string) { - return this._messagesRepository.getOrderDetails( - userId, - organizationId, - orderId - ); - } - - canAddPost(id: string, order: string, integrationId: string) { - return this._messagesRepository.canAddPost(id, order, integrationId); - } - - changeOrderStatus( - orderId: string, - status: OrderStatus, - paymentIntent?: string - ) { - return this._messagesRepository.changeOrderStatus( - orderId, - status, - paymentIntent - ); - } - - getOrgByOrder(orderId: string) { - return this._messagesRepository.getOrgByOrder(orderId); - } - - getMarketplaceAvailableOffers(orgId: string, id: string) { - return this._messagesRepository.getMarketplaceAvailableOffers(orgId, id); - } - - getPost(userId: string, orgId: string, postId: string) { - return this._messagesRepository.getPost(userId, orgId, postId); - } - - requestRevision( - userId: string, - orgId: string, - postId: string, - message: string - ) { - return this._messagesRepository.requestRevision( - userId, - orgId, - postId, - message - ); - } - - async requestApproved( - userId: string, - orgId: string, - postId: string, - message: string - ) { - const post = await this._messagesRepository.requestApproved( - userId, - orgId, - postId, - message - ); - if (post) { - this._workerServiceProducer.emit('post', { - id: post.id, - options: { - delay: 0, //dayjs(post.publishDate).diff(dayjs(), 'millisecond'), - }, - payload: { - id: post.id, - }, - }); - } - } - - async requestCancel(orgId: string, postId: string) { - const cancel = await this._messagesRepository.requestCancel(orgId, postId); - await this._workerServiceProducer.delete('post', postId); - return cancel; - } - - async completeOrderAndPay(orgId: string, order: string) { - const orderList = await this._messagesRepository.completeOrderAndPay( - orgId, - order - ); - if (!orderList) { - return false; - } - orderList.posts.forEach((post) => { - this._workerServiceProducer.delete('post', post.id); - }); - return orderList; - } - - completeOrder(orderId: string) { - return this._messagesRepository.completeOrder(orderId); - } - - payoutProblem( - orderId: string, - sellerId: string, - amount: number, - postId?: string - ) { - return this._messagesRepository.payoutProblem( - orderId, - sellerId, - amount, - postId - ); - } - - getOrders(userId: string, orgId: string, type: 'seller' | 'buyer') { - return this._messagesRepository.getOrders(userId, orgId, type); - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts b/libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts deleted file mode 100644 index 0d4faded..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/marketplace/tags.list.ts +++ /dev/null @@ -1,138 +0,0 @@ -export const tagsList = - process.env.isGeneral === 'true' - ? [ - { - key: 'services', - name: 'Niches', - options: [ - { key: 'real-estate', value: 'Real Estate' }, - { key: 'fashion', value: 'Fashion' }, - { key: 'health-and-fitness', value: 'Health and Fitness' }, - { key: 'beauty', value: 'Beauty' }, - { key: 'travel', value: 'Travel' }, - { key: 'food', value: 'Food' }, - { key: 'tech', value: 'Tech' }, - { key: 'gaming', value: 'Gaming' }, - { key: 'parenting', value: 'Parenting' }, - { key: 'education', value: 'Education' }, - { key: 'business', value: 'Business' }, - { key: 'finance', value: 'Finance' }, - { key: 'diy', value: 'DIY' }, - { key: 'pets', value: 'Pets' }, - { key: 'lifestyle', value: 'Lifestyle' }, - { key: 'sports', value: 'Sports' }, - { key: 'entertainment', value: 'Entertainment' }, - { key: 'art', value: 'Art' }, - { key: 'photography', value: 'Photography' }, - { key: 'sustainability', value: 'Sustainability' }, - ], - }, - ] - : [ - { - key: 'services', - name: 'Services', - options: [ - { - key: 'content-writer', - value: 'Content Writer', - }, - { - key: 'influencers', - value: 'Influencers', - }, - ], - }, - { - key: 'niches', - name: 'Niches', - options: [ - { - key: 'kubernetes', - value: 'Kubernetes', - }, - { - key: 'fullstack', - value: 'Fullstack', - }, - { - key: 'security', - value: 'Security', - }, - { - key: 'infrastructure', - value: 'Infrastructure', - }, - { - key: 'productivity', - value: 'Productivity', - }, - { - key: 'web3', - value: 'Web3', - }, - { - key: 'cloud-native', - value: 'Cloud Native', - }, - { - key: 'ml', - value: 'ML', - }, - ], - }, - { - key: 'technologies', - name: 'Technologies', - options: [ - { - key: 'html', - value: 'HTML', - }, - { - key: 'css', - value: 'CSS', - }, - { - key: 'javascript', - value: 'JavaScript', - }, - { - key: 'typescript', - value: 'TypeScript', - }, - { - key: 'rust', - value: 'Rust', - }, - { - key: 'go', - value: 'Go', - }, - { - key: 'python', - value: 'Python', - }, - { - key: 'java', - value: 'Java', - }, - { - key: 'php', - value: 'PHP', - }, - { - key: 'ruby', - value: 'Ruby', - }, - { - key: 'c', - value: 'C/C++', - }, - ], - }, - ]; - -export const allTagsOptions = tagsList.reduce((acc, tag) => { - return [...acc, ...tag.options]; -}, [] as Array<{ key: string; value: string }>); diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index 70dce81b..0a926318 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -2,9 +2,10 @@ import { Injectable } from '@nestjs/common'; import { NotificationsRepository } from '@gitroom/nestjs-libraries/database/prisma/notifications/notifications.repository'; import { EmailService } from '@gitroom/nestjs-libraries/services/email.service'; import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; -import dayjs from 'dayjs'; +import { TemporalService } from 'nestjs-temporal-core'; +import { TypedSearchAttributes } from '@temporalio/common'; +import { organizationId } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; export type NotificationType = 'success' | 'fail' | 'info'; @@ -14,7 +15,7 @@ export class NotificationService { private _notificationRepository: NotificationsRepository, private _emailService: EmailService, private _organizationRepository: OrganizationRepository, - private _workerServiceProducer: BullMqClient + private _temporalService: TemporalService ) {} getMainPageCount(organizationId: string, userId: string) { @@ -46,42 +47,41 @@ export class NotificationService { digest = false, type: NotificationType = 'success' ) { - const date = new Date().toISOString(); await this._notificationRepository.createNotification(orgId, message); if (!sendEmail) { return; } if (digest) { - await ioRedis.watch('digest_' + orgId); - const value = await ioRedis.get('digest_' + orgId); + try { + await this._temporalService.client + .getRawClient() + ?.workflow.start('digestEmailWorkflow', { + workflowId: 'digest_email_workflow_' + orgId, + taskQueue: 'main', + args: [{ organizationId: orgId }], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: organizationId, + value: orgId, + }, + ]), + }); + } catch (err) {} - // Track notification types in the digest - const typesKey = 'digest_types_' + orgId; - await ioRedis.sadd(typesKey, type); - await ioRedis.expire(typesKey, 120); // Slightly longer than digest window - - if (value) { - return; - } - - await ioRedis - .multi() - .set('digest_' + orgId, date) - .expire('digest_' + orgId, 60) - .exec(); - - this._workerServiceProducer.emit('sendDigestEmail', { - id: 'digest_' + orgId, - options: { - delay: 60000, - }, - payload: { - subject, - org: orgId, - since: date, - }, - }); + await this._temporalService.signalWorkflow( + 'digest_email_workflow_' + orgId, + 'email', + [ + [ + { + title: subject, + message, + type, + }, + ], + ] + ); return; } diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index 6a574c78..6da496ca 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -273,6 +273,8 @@ export class OrganizationRepository { select: { email: true, id: true, + sendSuccessEmails: true, + sendFailureEmails: true, }, }, }, diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index d20c7085..8196345d 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -1,6 +1,5 @@ import { BadRequestException, - ForbiddenException, Injectable, ValidationPipe, } from '@nestjs/common'; @@ -10,10 +9,7 @@ import dayjs from 'dayjs'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { Integration, Post, Media, From, State } from '@prisma/client'; import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; -import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; import { shuffle } from 'lodash'; -import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; -import { StripeService } from '@gitroom/nestjs-libraries/services/stripe.service'; import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generator/create.generated.posts.dto'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; @@ -30,7 +26,10 @@ dayjs.extend(utc); import * as Sentry from '@sentry/nestjs'; import { TemporalService } from 'nestjs-temporal-core'; import { TypedSearchAttributes } from '@temporalio/common'; -import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; +import { + organizationId, + postId as postIdSearchParam, +} from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; type PostWithConditionals = Post & { integration?: Integration; @@ -43,23 +42,13 @@ export class PostsService { constructor( private _postRepository: PostsRepository, private _integrationManager: IntegrationManager, - private _notificationService: NotificationService, - private _messagesService: MessagesService, - private _stripeService: StripeService, private _integrationService: IntegrationService, private _mediaService: MediaService, private _shortLinkService: ShortLinkService, - private openaiService: OpenaiService, + private _openaiService: OpenaiService, private _temporalService: TemporalService ) {} - checkPending15minutesBack() { - return this._postRepository.checkPending15minutesBack(); - } - searchForMissingThreeHoursPosts() { - return this._postRepository.searchForMissingThreeHoursPosts(); - } - updatePost(id: string, postId: string, releaseURL: string) { return this._postRepository.updatePost(id, postId, releaseURL); } @@ -434,17 +423,14 @@ export class PostsService { content: updateContent[i], })); - const { previousPost, posts } = - await this._postRepository.createOrUpdatePost( - body.type, - orgId, - body.type === 'now' - ? dayjs().format('YYYY-MM-DDTHH:mm:00') - : body.date, - post, - body.tags, - body.inter - ); + const { posts } = await this._postRepository.createOrUpdatePost( + body.type, + orgId, + body.type === 'now' ? dayjs().format('YYYY-MM-DDTHH:mm:00') : body.date, + post, + body.tags, + body.inter + ); if (!posts?.length) { return [] as any[]; @@ -478,12 +464,22 @@ export class PostsService { ?.workflow.start('postWorkflow', { workflowId: `post_${posts[0].id}`, taskQueue: 'main', - args: [{ postId: posts[0].id, organizationId: orgId }], + args: [ + { + taskQueue: post.settings.__type.split('-')[0].toLowerCase(), + postId: posts[0].id, + organizationId: orgId, + }, + ], typedSearchAttributes: new TypedSearchAttributes([ { key: postIdSearchParam, value: posts[0].id, }, + { + key: organizationId, + value: orgId, + }, ]), }); @@ -498,7 +494,7 @@ export class PostsService { } async separatePosts(content: string, len: number) { - return this.openaiService.separatePosts(content, len); + return this._openaiService.separatePosts(content, len); } async changeState(id: string, state: State, err?: any, body?: any) { @@ -536,94 +532,30 @@ export class PostsService { ?.workflow.start('postWorkflow', { workflowId: `post_${getPostById.id}`, taskQueue: 'main', - args: [{ postId: getPostById.id, organizationId: orgId }], + args: [ + { + taskQueue: getPostById.integration.providerIdentifier + .split('-')[0] + .toLowerCase(), + postId: getPostById.id, + organizationId: orgId, + }, + ], typedSearchAttributes: new TypedSearchAttributes([ { key: postIdSearchParam, value: getPostById.id, }, + { + key: organizationId, + value: orgId, + }, ]), }); return newDate; } - async payout(id: string, url: string) { - const getPost = await this._postRepository.getPostById(id); - if (!getPost || !getPost.submittedForOrder) { - return; - } - - const findPrice = getPost.submittedForOrder.ordersItems.find( - (orderItem) => orderItem.integrationId === getPost.integrationId - )!; - - await this._messagesService.createNewMessage( - getPost.submittedForOrder.messageGroupId, - From.SELLER, - '', - { - type: 'published', - data: { - id: getPost.submittedForOrder.id, - postId: id, - status: 'PUBLISHED', - integrationId: getPost.integrationId, - integration: getPost.integration.providerIdentifier, - picture: getPost.integration.picture, - name: getPost.integration.name, - url, - }, - } - ); - - const totalItems = getPost.submittedForOrder.ordersItems.reduce( - (all, p) => all + p.quantity, - 0 - ); - const totalPosts = getPost.submittedForOrder.posts.length; - - if (totalItems === totalPosts) { - await this._messagesService.completeOrder(getPost.submittedForOrder.id); - await this._messagesService.createNewMessage( - getPost.submittedForOrder.messageGroupId, - From.SELLER, - '', - { - type: 'order-completed', - data: { - id: getPost.submittedForOrder.id, - postId: id, - status: 'PUBLISHED', - }, - } - ); - } - - try { - await this._stripeService.payout( - getPost.submittedForOrder.id, - getPost.submittedForOrder.captureId!, - getPost.submittedForOrder.seller.account!, - findPrice.price - ); - - return this._notificationService.inAppNotification( - getPost.integration.organizationId, - 'Payout completed', - `You have received a payout of $${findPrice.price}`, - true - ); - } catch (err) { - await this._messagesService.payoutProblem( - getPost.submittedForOrder.id, - getPost.submittedForOrder.seller.id, - findPrice.price, - id - ); - } - } - async generatePostsDraft(orgId: string, body: CreateGeneratedPostsDto) { const getAllIntegrations = ( await this._integrationService.getIntegrationsList(orgId) @@ -785,28 +717,4 @@ export class PostsService { ) { return this._postRepository.createComment(orgId, userId, postId, comment); } - - async sendDigestEmail(subject: string, orgId: string, since: string) { - const getNotificationsForOrgSince = - await this._notificationService.getNotificationsSince(orgId, since); - if (getNotificationsForOrgSince.length === 0) { - return; - } - - // Get the types of notifications in this digest - const types = await this._notificationService.getDigestTypes(orgId); - - const message = getNotificationsForOrgSince - .map((p) => p.content) - .join('<br />'); - - await this._notificationService.sendDigestEmailsToOrg( - orgId, - getNotificationsForOrgSince.length === 1 - ? subject - : '[Postiz] Your latest notifications', - message, - types.length > 0 ? types : ['success'] // Default to success if no types tracked - ); - } } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index e0a2c836..56be7279 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -86,7 +86,6 @@ model User { lastReadNotifications DateTime @default(now()) inviteId String? activated Boolean @default(true) - marketplace Boolean @default(true) account String? connectedAccount Boolean @default(false) lastOnline DateTime @default(now()) diff --git a/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts b/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts deleted file mode 100644 index 28b7f186..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/stars/stars.repository.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; -import { Injectable } from '@nestjs/common'; -import { StarsListDto } from '@gitroom/nestjs-libraries/dtos/analytics/stars.list.dto'; - -@Injectable() -export class StarsRepository { - constructor( - private _github: PrismaRepository<'gitHub'>, - private _stars: PrismaRepository<'star'>, - private _trending: PrismaRepository<'trending'> - ) {} - getGitHubRepositoriesByOrgId(org: string) { - return this._github.model.gitHub.findMany({ - where: { - organizationId: org, - }, - }); - } - replaceOrAddTrending( - language: string, - hashedNames: string, - arr: { name: string; position: number }[] - ) { - return this._trending.model.trending.upsert({ - create: { - language, - hash: hashedNames, - trendingList: JSON.stringify(arr), - date: new Date(), - }, - update: { - language, - hash: hashedNames, - trendingList: JSON.stringify(arr), - date: new Date(), - }, - where: { - language, - }, - }); - } - - getAllGitHubRepositories() { - return this._github.model.gitHub.findMany({ - distinct: ['login'], - }); - } - - async getLastStarsByLogin(login: string) { - return ( - await this._stars.model.star.findMany({ - where: { - login, - }, - orderBy: { - date: 'desc', - }, - take: 1, - }) - )?.[0]; - } - - async getStarsByLogin(login: string) { - return this._stars.model.star.findMany({ - where: { - login, - }, - orderBy: { - date: 'asc', - }, - }); - } - - async getGitHubsByNames(names: string[]) { - return this._github.model.gitHub.findMany({ - where: { - login: { - in: names, - }, - }, - }); - } - - findValidToken(login: string) { - return this._github.model.gitHub.findFirst({ - where: { - login, - }, - }); - } - - createStars( - login: string, - totalNewsStars: number, - totalStars: number, - totalNewForks: number, - totalForks: number, - date: Date - ) { - return this._stars.model.star.upsert({ - create: { - login, - stars: totalNewsStars, - forks: totalNewForks, - totalForks, - totalStars, - date, - }, - update: { - stars: totalNewsStars, - totalStars, - forks: totalNewForks, - totalForks, - }, - where: { - login_date: { - date, - login, - }, - }, - }); - } - - getTrendingByLanguage(language: string) { - return this._trending.model.trending.findUnique({ - where: { - language, - }, - }); - } - - getStarsFilter(githubs: string[], starsFilter: StarsListDto) { - return this._stars.model.star.findMany({ - orderBy: { - [starsFilter.key || 'date']: starsFilter.state || 'desc', - }, - where: { - login: { - in: githubs.filter((f) => f), - }, - }, - take: 20, - skip: (starsFilter.page - 1) * 10, - }); - } - - addGitHub(orgId: string, accessToken: string) { - return this._github.model.gitHub.create({ - data: { - token: accessToken, - organizationId: orgId, - jobId: '', - }, - }); - } - - getGitHubById(orgId: string, id: string) { - return this._github.model.gitHub.findUnique({ - where: { - organizationId: orgId, - id, - }, - }); - } - - updateGitHubLogin(orgId: string, id: string, login: string) { - return this._github.model.gitHub.update({ - where: { - organizationId: orgId, - id, - }, - data: { - login, - }, - }); - } - - deleteRepository(orgId: string, id: string) { - return this._github.model.gitHub.delete({ - where: { - organizationId: orgId, - id, - }, - }); - } - - getOrganizationsByGitHubLogin(login: string) { - return this._github.model.gitHub.findMany({ - select: { - organizationId: true, - }, - where: { - login, - }, - distinct: ['organizationId'], - }); - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts b/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts deleted file mode 100644 index 06f28ad2..00000000 --- a/libraries/nestjs-libraries/src/database/prisma/stars/stars.service.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { HttpException, Injectable } from '@nestjs/common'; -import { StarsRepository } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.repository'; -import { chunk, groupBy } from 'lodash'; -import dayjs from 'dayjs'; -import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; -import { StarsListDto } from '@gitroom/nestjs-libraries/dtos/analytics/stars.list.dto'; -import { mean } from 'simple-statistics'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; -enum Inform { - Removed, - New, - Changed, -} -@Injectable() -export class StarsService { - constructor( - private _starsRepository: StarsRepository, - private _notificationsService: NotificationService, - private _workerServiceProducer: BullMqClient - ) {} - - getGitHubRepositoriesByOrgId(org: string) { - return this._starsRepository.getGitHubRepositoriesByOrgId(org); - } - - getAllGitHubRepositories() { - return this._starsRepository.getAllGitHubRepositories(); - } - - getStarsByLogin(login: string) { - return this._starsRepository.getStarsByLogin(login); - } - - getLastStarsByLogin(login: string) { - return this._starsRepository.getLastStarsByLogin(login); - } - - createStars( - login: string, - totalNewsStars: number, - totalStars: number, - totalNewForks: number, - totalForks: number, - date: Date - ) { - return this._starsRepository.createStars( - login, - totalNewsStars, - totalStars, - totalNewForks, - totalForks, - date - ); - } - - async sync(login: string, token?: string) { - const loadAllStars = await this.syncProcess(login, token); - const loadAllForks = await this.syncForksProcess(login, token); - - const allDates = [ - ...new Set([...Object.keys(loadAllStars), ...Object.keys(loadAllForks)]), - ]; - - const sortedArray = allDates.sort( - (a, b) => dayjs(a).unix() - dayjs(b).unix() - ); - - let addPreviousStars = 0; - let addPreviousForks = 0; - for (const date of sortedArray) { - const dateObject = dayjs(date).toDate(); - addPreviousStars += loadAllStars[date] || 0; - addPreviousForks += loadAllForks[date] || 0; - - await this._starsRepository.createStars( - login, - loadAllStars[date] || 0, - addPreviousStars, - loadAllForks[date] || 0, - addPreviousForks, - dateObject - ); - } - } - - async findValidToken(login: string) { - return this._starsRepository.findValidToken(login); - } - - async fetchWillFallback(url: string, userToken?: string): Promise<Response> { - if (userToken) { - const response = await fetch(url, { - headers: { - Accept: 'application/vnd.github.v3.star+json', - Authorization: `Bearer ${userToken}`, - }, - }); - - if (response.status === 200) { - return response; - } - } - - const response2 = await fetch(url, { - headers: { - Accept: 'application/vnd.github.v3.star+json', - ...(process.env.GITHUB_AUTH - ? { Authorization: `token ${process.env.GITHUB_AUTH}` } - : {}), - }, - }); - - const totalRemaining = +( - response2.headers.get('x-ratelimit-remaining') || - response2.headers.get('X-RateLimit-Remaining') || - 0 - ); - const resetTime = +( - response2.headers.get('x-ratelimit-reset') || - response2.headers.get('X-RateLimit-Reset') || - 0 - ); - - if (totalRemaining < 10) { - console.log('waiting for the rate limit'); - const delay = resetTime * 1000 - Date.now() + 1000; - await new Promise((resolve) => setTimeout(resolve, delay)); - - return this.fetchWillFallback(url, userToken); - } - - return response2; - } - - async syncForksProcess(login: string, userToken?: string, page = 1) { - console.log('processing forks'); - const starsRequest = await this.fetchWillFallback( - `https://api.github.com/repos/${login}/forks?page=${page}&per_page=100`, - userToken - ); - - const data: Array<{ created_at: string }> = await starsRequest.json(); - const mapDataToDate = groupBy(data, (p) => - dayjs(p.created_at).format('YYYY-MM-DD') - ); - - // take all the forks from the page - const aggForks: { [key: string]: number } = Object.values( - mapDataToDate - ).reduce( - (acc, value) => ({ - ...acc, - [dayjs(value[0].created_at).format('YYYY-MM-DD')]: value.length, - }), - {} - ); - - // if we have 100 stars, we need to fetch the next page and merge the results (recursively) - const nextOne: { [key: string]: number } = - data.length === 100 - ? await this.syncForksProcess(login, userToken, page + 1) - : {}; - - // merge the results - const allKeys = [ - ...new Set([...Object.keys(aggForks), ...Object.keys(nextOne)]), - ]; - - return { - ...allKeys.reduce( - (acc, key) => ({ - ...acc, - [key]: (aggForks[key] || 0) + (nextOne[key] || 0), - }), - {} as { [key: string]: number } - ), - }; - } - - async syncProcess(login: string, userToken?: string, page = 1) { - console.log('processing stars'); - const starsRequest = await this.fetchWillFallback( - `https://api.github.com/repos/${login}/stargazers?page=${page}&per_page=100`, - userToken - ); - - const data: Array<{ starred_at: string }> = await starsRequest.json(); - const mapDataToDate = groupBy(data, (p) => - dayjs(p.starred_at).format('YYYY-MM-DD') - ); - - // take all the stars from the page - const aggStars: { [key: string]: number } = Object.values( - mapDataToDate - ).reduce( - (acc, value) => ({ - ...acc, - [dayjs(value[0].starred_at).format('YYYY-MM-DD')]: value.length, - }), - {} - ); - - // if we have 100 stars, we need to fetch the next page and merge the results (recursively) - const nextOne: { [key: string]: number } = - data.length === 100 - ? await this.syncProcess(login, userToken, page + 1) - : {}; - - // merge the results - const allKeys = [ - ...new Set([...Object.keys(aggStars), ...Object.keys(nextOne)]), - ]; - - return { - ...allKeys.reduce( - (acc, key) => ({ - ...acc, - [key]: (aggStars[key] || 0) + (nextOne[key] || 0), - }), - {} as { [key: string]: number } - ), - }; - } - - async updateTrending( - language: string, - hash: string, - arr: Array<{ name: string; position: number }> - ) { - const currentTrending = await this._starsRepository.getTrendingByLanguage( - language - ); - - if (currentTrending?.hash === hash) { - return; - } - - if (currentTrending) { - const list: Array<{ name: string; position: number }> = JSON.parse( - currentTrending.trendingList - ); - const removedFromTrending = list.filter( - (p) => !arr.find((a) => a.name === p.name) - ); - const changedPosition = arr.filter((p) => { - const current = list.find((a) => a.name === p.name); - return current && current.position !== p.position; - }); - if (removedFromTrending.length) { - // let people know they are not trending anymore - await this.inform(Inform.Removed, removedFromTrending, language); - } - if (changedPosition.length) { - // let people know they changed position - await this.inform(Inform.Changed, changedPosition, language); - } - } - - const informNewPeople = arr.filter( - (p) => - !currentTrending?.trendingList || - currentTrending?.trendingList?.indexOf(p.name) === -1 - ); - - // let people know they are trending - await this.inform(Inform.New, informNewPeople, language); - await this.replaceOrAddTrending(language, hash, arr); - } - - async inform( - type: Inform, - removedFromTrending: Array<{ name: string; position: number }>, - language: string - ) { - const names = await this._starsRepository.getGitHubsByNames( - removedFromTrending.map((p) => p.name) - ); - const mapDbNamesToList = names.map( - (n) => removedFromTrending.find((p) => p.name === n.login)! - ); - for (const person of mapDbNamesToList) { - const getOrganizationsByGitHubLogin = - await this._starsRepository.getOrganizationsByGitHubLogin(person.name); - for (const org of getOrganizationsByGitHubLogin) { - switch (type) { - case Inform.Removed: - return this._notificationsService.inAppNotification( - org.organizationId, - `${person.name} is not trending on GitHub anymore`, - `${person.name} is not trending anymore in ${language}`, - true - ); - case Inform.New: - return this._notificationsService.inAppNotification( - org.organizationId, - `${person.name} is trending on GitHub`, - `${person.name} is trending in ${ - language || 'On the main feed' - } position #${person.position}`, - true - ); - case Inform.Changed: - return this._notificationsService.inAppNotification( - org.organizationId, - `${person.name} changed trending position on GitHub`, - `${person.name} changed position in ${ - language || 'on the main feed to position' - } position #${person.position}`, - true - ); - } - } - } - } - - async replaceOrAddTrending( - language: string, - hash: string, - arr: Array<{ name: string; position: number }> - ) { - return this._starsRepository.replaceOrAddTrending(language, hash, arr); - } - - async getStars(org: string) { - const getGitHubs = await this.getGitHubRepositoriesByOrgId(org); - const list = []; - for (const gitHub of getGitHubs) { - if (!gitHub.login) { - continue; - } - const getAllByLogin = await this.getStarsByLogin(gitHub.login!); - - const stars = getAllByLogin.filter((f) => f.stars); - const graphSize = stars.length < 10 ? stars.length : stars.length / 10; - - const forks = getAllByLogin.filter((f) => f.forks); - const graphForkSize = - forks.length < 10 ? forks.length : forks.length / 10; - - list.push({ - login: gitHub.login, - stars: chunk(stars, graphSize).reduce((acc, chunkedStars) => { - return [ - ...acc, - { - totalStars: chunkedStars[chunkedStars.length - 1].totalStars, - date: chunkedStars[chunkedStars.length - 1].date, - }, - ]; - }, [] as Array<{ totalStars: number; date: Date }>), - forks: chunk(forks, graphForkSize).reduce((acc, chunkedForks) => { - return [ - ...acc, - { - totalForks: chunkedForks[chunkedForks.length - 1].totalForks, - date: chunkedForks[chunkedForks.length - 1].date, - }, - ]; - }, [] as Array<{ totalForks: number; date: Date }>), - }); - } - - return list; - } - - async getStarsFilter(orgId: string, starsFilter: StarsListDto) { - const getGitHubs = await this.getGitHubRepositoriesByOrgId(orgId); - if (getGitHubs.filter((f) => f.login).length === 0) { - return []; - } - return this._starsRepository.getStarsFilter( - getGitHubs.map((p) => p.login) as string[], - starsFilter - ); - } - - async addGitHub(orgId: string, code: string) { - const { access_token } = await ( - await fetch('https://github.com/login/oauth/access_token', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - body: JSON.stringify({ - client_id: process.env.GITHUB_CLIENT_ID, - client_secret: process.env.GITHUB_CLIENT_SECRET, - code, - redirect_uri: `${process.env.FRONTEND_URL}/settings`, - }), - }) - ).json(); - - return this._starsRepository.addGitHub(orgId, access_token); - } - - async getOrganizations(orgId: string, id: string) { - const getGitHub = await this._starsRepository.getGitHubById(orgId, id); - return ( - await fetch(`https://api.github.com/user/orgs`, { - headers: { - Authorization: `token ${getGitHub?.token!}`, - }, - }) - ).json(); - } - - async getRepositoriesOfOrganization( - orgId: string, - id: string, - github: string - ) { - const getGitHub = await this._starsRepository.getGitHubById(orgId, id); - return ( - await fetch(`https://api.github.com/orgs/${github}/repos`, { - headers: { - Authorization: `token ${getGitHub?.token!}`, - }, - }) - ).json(); - } - - async updateGitHubLogin(orgId: string, id: string, login: string) { - const check = await fetch(`https://github.com/${login}`); - if (check.status === 404) { - throw new HttpException('GitHub repository not found!', 404); - } - - this._workerServiceProducer - .emit('sync_all_stars', { payload: { login } }) - .subscribe(); - return this._starsRepository.updateGitHubLogin(orgId, id, login); - } - - async deleteRepository(orgId: string, id: string) { - return this._starsRepository.deleteRepository(orgId, id); - } - - async predictTrending(max = 500) { - const firstDate = dayjs().subtract(1, 'day'); - return [ - firstDate.format('YYYY-MM-DDT12:00:00'), - ...[...new Array(max)].map((p, index) => { - return firstDate.add(index, 'day').format('YYYY-MM-DDT12:00:00'); - }), - ]; - } - - async predictTrendingLoop( - trendings: Array<{ date: Date }>, - current = 0, - max = 500 - ): Promise<Date[]> { - const dates = trendings.map((result) => dayjs(result.date).toDate()); - const intervals = dates - .slice(1) - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - .map((date, i) => (date - dates[i]) / (1000 * 60 * 60 * 24)); - const nextInterval = intervals.length === 0 ? null : mean(intervals); - const lastTrendingDate = dates[dates.length - 1]; - const nextTrendingDate = !nextInterval - ? false - : dayjs( - new Date( - lastTrendingDate.getTime() + nextInterval * 24 * 60 * 60 * 1000 - ) - ).toDate(); - - if (!nextTrendingDate) { - return []; - } - - return [ - nextTrendingDate, - ...(current < max - ? await this.predictTrendingLoop( - [...trendings, { date: nextTrendingDate }], - current + 1, - max - ) - : []), - ]; - } -} diff --git a/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts b/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts index 9d57b522..ca202428 100644 --- a/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/subscriptions/subscription.service.ts @@ -29,14 +29,6 @@ export class SubscriptionService { return this._subscriptionRepository.getCode(code); } - updateAccount(userId: string, account: string) { - return this._subscriptionRepository.updateAccount(userId, account); - } - - getUserAccount(userId: string) { - return this._subscriptionRepository.getUserAccount(userId); - } - async deleteSubscription(customerId: string) { await this.modifySubscription( customerId, @@ -61,14 +53,6 @@ export class SubscriptionService { subscriptionId ); } - - updateConnectedStatus(account: string, accountCharges: boolean) { - return this._subscriptionRepository.updateConnectedStatus( - account, - accountCharges - ); - } - async modifySubscription( customerId: string, totalChannels: number, @@ -130,23 +114,6 @@ export class SubscriptionService { } return true; - - // if (to.faq < from.faq) { - // await this._faqRepository.deleteFAQs(getCurrentSubscription?.organizationId, from.faq - to.faq); - // } - // if (to.categories < from.categories) { - // await this._categoriesRepository.deleteCategories(getCurrentSubscription?.organizationId, from.categories - to.categories); - // } - // if (to.integrations < from.integrations) { - // await this._integrationsRepository.deleteIntegrations(getCurrentSubscription?.organizationId, from.integrations - to.integrations); - // } - // if (to.user < from.user) { - // await this._integrationsRepository.deleteUsers(getCurrentSubscription?.organizationId, from.user - to.user); - // } - // if (to.domains < from.domains) { - // await this._settingsService.deleteDomainByOrg(getCurrentSubscription?.organizationId); - // await this._organizationRepository.changePowered(getCurrentSubscription?.organizationId); - // } } async createOrUpdateSubscription( diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts index 1c4e6c50..96b35c78 100644 --- a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts @@ -2,8 +2,6 @@ import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/pris import { Injectable } from '@nestjs/common'; import { Provider } from '@prisma/client'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; -import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto'; -import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list'; import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto'; import { EmailNotificationsDto } from '@gitroom/nestjs-libraries/dtos/users/email-notifications.dto'; @@ -109,17 +107,6 @@ export class UsersRepository { }); } - changeMarketplaceActive(userId: string, active: boolean) { - return this._user.model.user.update({ - where: { - id: userId, - }, - data: { - marketplace: active, - }, - }); - } - async getPersonal(userId: string) { const user = await this._user.model.user.findUnique({ where: { @@ -185,83 +172,4 @@ export class UsersRepository { }, }); } - - async getMarketplacePeople(orgId: string, userId: string, items: ItemsDto) { - const info = { - id: { - not: userId, - }, - account: { - not: null, - }, - connectedAccount: true, - marketplace: true, - items: { - ...(items.items.length - ? { - some: { - OR: items.items.map((key) => ({ key })), - }, - } - : { - some: { - OR: allTagsOptions.map((p) => ({ key: p.key })), - }, - }), - }, - }; - - const list = await this._user.model.user.findMany({ - where: { - ...info, - }, - select: { - id: true, - name: true, - bio: true, - audience: true, - picture: { - select: { - id: true, - path: true, - }, - }, - organizations: { - select: { - organization: { - select: { - Integration: { - where: { - disabled: false, - deletedAt: null, - }, - select: { - providerIdentifier: true, - }, - }, - }, - }, - }, - }, - items: { - select: { - key: true, - }, - }, - }, - skip: (items.page - 1) * 8, - take: 8, - }); - - const count = await this._user.model.user.count({ - where: { - ...info, - }, - }); - - return { - list, - count, - }; - } } diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts index 9bc6d9b1..863949cb 100644 --- a/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/users/users.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@nestjs/common'; import { UsersRepository } from '@gitroom/nestjs-libraries/database/prisma/users/users.repository'; import { Provider } from '@prisma/client'; -import { ItemsDto } from '@gitroom/nestjs-libraries/dtos/marketplace/items.dto'; import { UserDetailDto } from '@gitroom/nestjs-libraries/dtos/users/user.details.dto'; import { EmailNotificationsDto } from '@gitroom/nestjs-libraries/dtos/users/email-notifications.dto'; import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository'; @@ -37,18 +36,6 @@ export class UsersService { return this._usersRepository.updatePassword(id, password); } - changeAudienceSize(userId: string, audience: number) { - return this._usersRepository.changeAudienceSize(userId, audience); - } - - changeMarketplaceActive(userId: string, active: boolean) { - return this._usersRepository.changeMarketplaceActive(userId, active); - } - - getMarketplacePeople(orgId: string, userId: string, body: ItemsDto) { - return this._usersRepository.getMarketplacePeople(orgId, userId, body); - } - getPersonal(userId: string) { return this._usersRepository.getPersonal(userId); } diff --git a/libraries/nestjs-libraries/src/database/prisma/webhooks/webhooks.service.ts b/libraries/nestjs-libraries/src/database/prisma/webhooks/webhooks.service.ts index 54672b5e..fa279e0a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/webhooks/webhooks.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/webhooks/webhooks.service.ts @@ -1,17 +1,10 @@ import { Injectable } from '@nestjs/common'; import { WebhooksRepository } from '@gitroom/nestjs-libraries/database/prisma/webhooks/webhooks.repository'; import { WebhooksDto } from '@gitroom/nestjs-libraries/dtos/webhooks/webhooks.dto'; -import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; -import { BullMqClient } from '@gitroom/nestjs-libraries/bull-mq-transport-new/client'; -import { PostsRepository } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.repository'; @Injectable() export class WebhooksService { - constructor( - private _webhooksRepository: WebhooksRepository, - private _postsRepository: PostsRepository, - private _workerServiceProducer: BullMqClient - ) {} + constructor(private _webhooksRepository: WebhooksRepository) {} getTotal(orgId: string) { return this._webhooksRepository.getTotal(orgId); @@ -28,73 +21,4 @@ export class WebhooksService { deleteWebhook(orgId: string, id: string) { return this._webhooksRepository.deleteWebhook(orgId, id); } - - async digestWebhooks(orgId: string, since: string) { - const date = new Date().toISOString(); - await ioRedis.watch('webhook_' + orgId); - const value = await ioRedis.get('webhook_' + orgId); - if (value) { - return; - } - - await ioRedis - .multi() - .set('webhook_' + orgId, date) - .expire('webhook_' + orgId, 60) - .exec(); - - this._workerServiceProducer.emit('webhooks', { - id: 'digest_' + orgId, - options: { - delay: 60000, - }, - payload: { - org: orgId, - since, - }, - }); - } - - async fireWebhooks(orgId: string, since: string) { - const list = await this._postsRepository.getPostsSince(orgId, since); - const webhooks = await this._webhooksRepository.getWebhooks(orgId); - const sendList = []; - for (const webhook of webhooks) { - const toSend = []; - if (webhook.integrations.length === 0) { - toSend.push(...list); - } else { - toSend.push( - ...list.filter((post) => - webhook.integrations.some( - (i) => i.integration.id === post.integration.id - ) - ) - ); - } - - if (toSend.length) { - sendList.push({ - url: webhook.url, - data: toSend, - }); - } - } - - return Promise.all( - sendList.map(async (s) => { - try { - await fetch(s.url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(s.data), - }); - } catch (e) { - /**empty**/ - } - }) - ); - } } diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts deleted file mode 100644 index c8f8bca1..00000000 --- a/libraries/nestjs-libraries/src/dtos/marketplace/add.remove.item.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsBoolean, IsIn, IsString } from 'class-validator'; -import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list'; - -export class AddRemoveItemDto { - @IsString() - @IsIn(allTagsOptions.map((p) => p.key)) - key: string; - - @IsBoolean() - state: boolean; -} diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/audience.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/audience.dto.ts deleted file mode 100644 index 5047b7a9..00000000 --- a/libraries/nestjs-libraries/src/dtos/marketplace/audience.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { IsNumber, Max, Min } from 'class-validator'; - -export class AudienceDto { - @IsNumber() - @Max(99999999) - @Min(1) - audience: number; -} diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts deleted file mode 100644 index fd1f6a0b..00000000 --- a/libraries/nestjs-libraries/src/dtos/marketplace/change.active.dto.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { IsBoolean } from 'class-validator'; - -export class ChangeActiveDto { - @IsBoolean() - active: boolean; -} diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/create.offer.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/create.offer.dto.ts deleted file mode 100644 index 6151b6e7..00000000 --- a/libraries/nestjs-libraries/src/dtos/marketplace/create.offer.dto.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - ArrayMinSize, - IsNumber, - IsString, - ValidateNested, -} from 'class-validator'; -import { Type } from 'class-transformer'; - -export class SocialMedia { - @IsNumber() - total: number; - - @IsString() - value: string; - - @IsNumber() - price: number; -} -export class CreateOfferDto { - @IsString() - group: string; - - @ArrayMinSize(1) - @ValidateNested({ each: true }) - @Type(() => SocialMedia) - socialMedia: SocialMedia[]; -} diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts deleted file mode 100644 index 672251ad..00000000 --- a/libraries/nestjs-libraries/src/dtos/marketplace/items.dto.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { IsArray, IsIn, IsNumber, Min } from 'class-validator'; -import { allTagsOptions } from '@gitroom/nestjs-libraries/database/prisma/marketplace/tags.list'; - -export class ItemsDto { - @IsArray() - @IsIn(allTagsOptions.map((p) => p.key), { each: true }) - items: string[]; - - @IsNumber() - @Min(1) - page: number; -} diff --git a/libraries/nestjs-libraries/src/dtos/marketplace/new.conversation.dto.ts b/libraries/nestjs-libraries/src/dtos/marketplace/new.conversation.dto.ts deleted file mode 100644 index 125c2c83..00000000 --- a/libraries/nestjs-libraries/src/dtos/marketplace/new.conversation.dto.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { IsString, MinLength } from 'class-validator'; - -export class NewConversationDto { - @IsString() - to: string; - - @IsString() - @MinLength(50) - message: string; -} diff --git a/libraries/nestjs-libraries/src/dtos/messages/add.message.ts b/libraries/nestjs-libraries/src/dtos/messages/add.message.ts deleted file mode 100644 index 53c37173..00000000 --- a/libraries/nestjs-libraries/src/dtos/messages/add.message.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { IsString, MinLength } from 'class-validator'; - -export class AddMessageDto { - @IsString() - @MinLength(3) - message: string; -} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index a8ef22a4..3ef95146 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -29,8 +29,9 @@ import { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.pro import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social/wordpress.provider'; import { ListmonkProvider } from '@gitroom/nestjs-libraries/integrations/social/listmonk.provider'; import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; -export const socialIntegrationList: SocialProvider[] = [ +export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ new XProvider(), new LinkedinProvider(), new LinkedinPageProvider(), diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 22fe3a18..f225dae3 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -1,5 +1,4 @@ import { timer } from '@gitroom/helpers/utils/timer'; -import { concurrency } from '@gitroom/helpers/utils/concurrency.service'; import { Integration } from '@prisma/client'; import { ApplicationFailure } from '@temporalio/activity'; @@ -59,19 +58,13 @@ export abstract class SocialAbstract { func: (...args: any[]) => Promise<T>, ignoreConcurrency?: boolean ) { - const value = await concurrency<any>( - this.identifier, - this.maxConcurrentJob, - async () => { - try { - return await func(); - } catch (err) { - const handle = this.handleErrors(JSON.stringify(err)); - return { err: true, ...(handle || {}) }; - } - }, - ignoreConcurrency - ); + let value: any; + try { + value = await func(); + } catch (err) { + const handle = this.handleErrors(JSON.stringify(err)); + value = { err: true, ...(handle || {}) }; + } if (value && value?.err && value?.value) { throw new BadBody('', JSON.stringify({}), {} as any, value.value || ''); @@ -87,12 +80,7 @@ export abstract class SocialAbstract { totalRetries = 0, ignoreConcurrency = false ): Promise<Response> { - const request = await concurrency( - this.identifier, - this.maxConcurrentJob, - () => fetch(url, options), - ignoreConcurrency - ); + const request = await fetch(url, options); if (request.status === 200 || request.status === 201) { return request; @@ -142,10 +130,20 @@ export abstract class SocialAbstract { request.status === 401 && (handleError?.type === 'refresh-token' || !handleError) ) { - throw new RefreshToken(identifier, json, options.body!, handleError?.value); + throw new RefreshToken( + identifier, + json, + options.body!, + handleError?.value + ); } - throw new BadBody(identifier, json, options.body!, handleError?.value || ''); + throw new BadBody( + identifier, + json, + options.body!, + handleError?.value || '' + ); } checkScopes(required: string[], got: string | string[]) { diff --git a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts index 3e1aa59b..bebd448a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts @@ -239,12 +239,7 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { } } - async post( - id: string, - accessToken: string, - postDetails: PostDetails[], - integration: Integration - ): Promise<PostResponse[]> { + private async getAgent(integration: Integration) { const body = JSON.parse( AuthService.fixedDecryption(integration.customInstanceDetails!) ); @@ -261,139 +256,154 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { throw new RefreshToken('bluesky', JSON.stringify(err), {} as BodyInit); } - let loadCid = ''; - let loadUri = ''; - let replyCid = ''; - let replyUri = ''; - const cidUrl = [] as { cid: string; url: string; rev: string }[]; - for (const post of postDetails) { - // Separate images and videos - const imageMedia = - post.media?.filter((p) => p.path.indexOf('mp4') === -1) || []; - const videoMedia = - post.media?.filter((p) => p.path.indexOf('mp4') !== -1) || []; + return agent; + } - // Upload images - const images = await Promise.all( - imageMedia.map(async (p) => { - const { buffer, width, height } = await reduceImageBySize(p.path); - return { - width, - height, - buffer: await agent.uploadBlob(new Blob([buffer])), - }; - }) - ); + private async uploadMediaForPost( + agent: BskyAgent, + post: PostDetails + ): Promise<{ embed: any; images: any[] }> { + // Separate images and videos + const imageMedia = + post.media?.filter((p) => p.path.indexOf('mp4') === -1) || []; + const videoMedia = + post.media?.filter((p) => p.path.indexOf('mp4') !== -1) || []; - // Upload videos (only one video per post is supported by Bluesky) - let videoEmbed: AppBskyEmbedVideo.Main | null = null; - if (videoMedia.length > 0) { - videoEmbed = await uploadVideo(agent, videoMedia[0].path); - } - - const rt = new RichText({ - text: post.message, - }); - - await rt.detectFacets(agent); - - // Determine embed based on media types - let embed: any = {}; - if (videoEmbed) { - // If there's a video, use video embed (Bluesky supports only one video per post) - embed = videoEmbed; - } else if (images.length > 0) { - // If there are images but no video, use image embed - embed = { - $type: 'app.bsky.embed.images', - images: images.map((p, index) => ({ - alt: imageMedia?.[index]?.alt || '', - image: p.buffer.data.blob, - aspectRatio: { - width: p.width, - height: p.height, - }, - })), + // Upload images + const images = await Promise.all( + imageMedia.map(async (p) => { + const { buffer, width, height } = await reduceImageBySize(p.path); + return { + width, + height, + buffer: await agent.uploadBlob(new Blob([buffer])), }; - } + }) + ); - // @ts-ignore - const { cid, uri, commit } = await agent.post({ - text: rt.text, - facets: rt.facets, - createdAt: new Date().toISOString(), - ...(Object.keys(embed).length > 0 ? { embed } : {}), - ...(loadCid - ? { - reply: { - root: { - uri: replyUri, - cid: replyCid, - }, - parent: { - uri: loadUri, - cid: loadCid, - }, - }, - } - : {}), - }); - - loadCid = loadCid || cid; - loadUri = loadUri || uri; - replyCid = cid; - replyUri = uri; - - cidUrl.push({ cid, url: uri, rev: commit.rev }); + // Upload videos (only one video per post is supported by Bluesky) + let videoEmbed: AppBskyEmbedVideo.Main | null = null; + if (videoMedia.length > 0) { + videoEmbed = await uploadVideo(agent, videoMedia[0].path); } - if (postDetails?.[0]?.settings?.active_thread_finisher) { - const rt = new RichText({ - text: stripHtmlValidation( - 'normal', - postDetails?.[0]?.settings?.thread_finisher, - true - ), - }); - - await rt.detectFacets(agent); - - await agent.post({ - text: stripHtmlValidation('normal', rt.text, true), - facets: rt.facets, - createdAt: new Date().toISOString(), - embed: { - $type: 'app.bsky.embed.record', - record: { - uri: cidUrl[0].url, - cid: cidUrl[0].cid, + // Determine embed based on media types + let embed: any = {}; + if (videoEmbed) { + embed = videoEmbed; + } else if (images.length > 0) { + embed = { + $type: 'app.bsky.embed.images', + images: images.map((p, index) => ({ + alt: imageMedia?.[index]?.alt || '', + image: p.buffer.data.blob, + aspectRatio: { + width: p.width, + height: p.height, }, - }, - ...(loadCid - ? { - reply: { - root: { - uri: loadUri, - cid: loadCid, - }, - parent: { - uri: loadUri, - cid: loadCid, - }, - }, - } - : {}), - }); + })), + }; } - return postDetails.map((p, index) => ({ - id: p.id, - postId: cidUrl[index].url, - status: 'completed', - releaseURL: `https://bsky.app/profile/${id}/post/${cidUrl[index].url - .split('/') - .pop()}`, - })); + return { embed, images }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const agent = await this.getAgent(integration); + const [firstPost] = postDetails; + + const { embed } = await this.uploadMediaForPost(agent, firstPost); + + const rt = new RichText({ + text: firstPost.message, + }); + + await rt.detectFacets(agent); + + // @ts-ignore + const { cid, uri, commit } = await agent.post({ + text: rt.text, + facets: rt.facets, + createdAt: new Date().toISOString(), + ...(Object.keys(embed).length > 0 ? { embed } : {}), + }); + + return [ + { + id: firstPost.id, + postId: uri, + status: 'completed', + releaseURL: `https://bsky.app/profile/${id}/post/${uri.split('/').pop()}`, + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const agent = await this.getAgent(integration); + const [commentPost] = postDetails; + + const { embed } = await this.uploadMediaForPost(agent, commentPost); + + const rt = new RichText({ + text: commentPost.message, + }); + + await rt.detectFacets(agent); + + // Get the parent post info to get its CID + const parentUri = lastCommentId || postId; + + // Fetch the parent post to get its CID + const parentThread = await agent.getPostThread({ + uri: parentUri, + depth: 0, + }); + + // @ts-ignore + const parentCid = parentThread.data.thread.post?.cid; + // @ts-ignore + const rootUri = parentThread.data.thread.post?.record?.reply?.root?.uri || postId; + // @ts-ignore + const rootCid = parentThread.data.thread.post?.record?.reply?.root?.cid || parentCid; + + // @ts-ignore + const { cid, uri, commit } = await agent.post({ + text: rt.text, + facets: rt.facets, + createdAt: new Date().toISOString(), + ...(Object.keys(embed).length > 0 ? { embed } : {}), + reply: { + root: { + uri: rootUri, + cid: rootCid, + }, + parent: { + uri: parentUri, + cid: parentCid, + }, + }, + }); + + return [ + { + id: commentPost.id, + postId: uri, + status: 'completed', + releaseURL: `https://bsky.app/profile/${id}/post/${uri.split('/').pop()}`, + }, + ]; } @Plug({ diff --git a/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts b/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts index e1692347..6402920a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/discord.provider.ts @@ -140,11 +140,76 @@ export class DiscordProvider extends SocialAbstract implements SocialProvider { accessToken: string, postDetails: PostDetails[] ): Promise<PostResponse[]> { - let channel = postDetails[0].settings.channel; - if (postDetails.length > 1) { + const [firstPost] = postDetails; + const channel = firstPost.settings.channel; + + const form = new FormData(); + form.append( + 'payload_json', + JSON.stringify({ + content: firstPost.message.replace(/\[\[\[(@.*?)]]]/g, (match, p1) => { + return `<${p1}>`; + }), + attachments: firstPost.media?.map((p, index) => ({ + id: index, + description: `Picture ${index}`, + filename: p.path.split('/').pop(), + })), + }) + ); + + let index = 0; + for (const media of firstPost.media || []) { + const loadMedia = await fetch(media.path); + + form.append( + `files[${index}]`, + await loadMedia.blob(), + media.path.split('/').pop() + ); + index++; + } + + const data = await ( + await fetch(`https://discord.com/api/channels/${channel}/messages`, { + method: 'POST', + headers: { + Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`, + }, + body: form, + }) + ).json(); + + return [ + { + id: firstPost.id, + releaseURL: `https://discord.com/channels/${id}/${channel}/${data.id}`, + postId: data.id, + status: 'success', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const channel = commentPost.settings.channel; + + // For Discord, we create a thread from the original message for comments + // If we don't have a thread yet, create one + let threadChannel = channel; + + // Create thread if this is the first comment + if (!lastCommentId) { const { id: threadId } = await ( await fetch( - `https://discord.com/api/channels/${postDetails[0].settings.channel}/threads`, + `https://discord.com/api/channels/${channel}/messages/${postId}/threads`, { method: 'POST', headers: { @@ -152,64 +217,66 @@ export class DiscordProvider extends SocialAbstract implements SocialProvider { 'Content-Type': 'application/json', }, body: JSON.stringify({ - name: postDetails[0].message, + name: 'Thread', auto_archive_duration: 1440, - type: 11, // Public thread type }), } ) ).json(); - channel = threadId; + threadChannel = threadId; + } else { + // Extract thread channel from the last comment's URL or use channel directly + threadChannel = channel; } - const finalData = []; - for (const post of postDetails) { - const form = new FormData(); + const form = new FormData(); + form.append( + 'payload_json', + JSON.stringify({ + content: commentPost.message.replace(/\[\[\[(@.*?)]]]/g, (match, p1) => { + return `<${p1}>`; + }), + attachments: commentPost.media?.map((p, index) => ({ + id: index, + description: `Picture ${index}`, + filename: p.path.split('/').pop(), + })), + }) + ); + + let index = 0; + for (const media of commentPost.media || []) { + const loadMedia = await fetch(media.path); + form.append( - 'payload_json', - JSON.stringify({ - content: post.message.replace(/\[\[\[(@.*?)]]]/g, (match, p1) => { - return `<${p1}>`; - }), - attachments: post.media?.map((p, index) => ({ - id: index, - description: `Picture ${index}`, - filename: p.path.split('/').pop(), - })), - }) + `files[${index}]`, + await loadMedia.blob(), + media.path.split('/').pop() ); + index++; + } - let index = 0; - for (const media of post.media || []) { - const loadMedia = await fetch(media.path); - - form.append( - `files[${index}]`, - await loadMedia.blob(), - media.path.split('/').pop() - ); - index++; - } - - const data = await ( - await fetch(`https://discord.com/api/channels/${channel}/messages`, { + const data = await ( + await fetch( + `https://discord.com/api/channels/${threadChannel}/messages`, + { method: 'POST', headers: { Authorization: `Bot ${process.env.DISCORD_BOT_TOKEN_ID}`, }, body: form, - }) - ).json(); + } + ) + ).json(); - finalData.push({ - id: post.id, - releaseURL: `https://discord.com/channels/${id}/${channel}/${data.id}`, + return [ + { + id: commentPost.id, + releaseURL: `https://discord.com/channels/${id}/${threadChannel}/${data.id}`, postId: data.id, status: 'success', - }); - } - - return finalData; + }, + ]; } async changeNickname(id: string, accessToken: string, name: string) { diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index c9d02841..ffd89c2e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -10,6 +10,7 @@ import dayjs from 'dayjs'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/facebook.dto'; import { DribbbleDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dribbble.dto'; +import { Integration } from '@prisma/client'; export class FacebookProvider extends SocialAbstract implements SocialProvider { identifier = 'facebook'; @@ -293,7 +294,7 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { accessToken: string, postDetails: PostDetails<FacebookDto>[] ): Promise<PostResponse[]> { - const [firstPost, ...comments] = postDetails; + const [firstPost] = postDetails; let finalId = ''; let finalUrl = ''; @@ -377,36 +378,6 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { finalId = postId; } - const postsArray = []; - let commentId = finalId; - for (const comment of comments) { - const data = await ( - await this.fetch( - `https://graph.facebook.com/v20.0/${commentId}/comments?access_token=${accessToken}&fields=id,permalink_url`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - ...(comment.media?.length - ? { attachment_url: comment.media[0].path } - : {}), - message: comment.message, - }), - }, - 'add comment' - ) - ).json(); - - commentId = data.id; - postsArray.push({ - id: comment.id, - postId: data.id, - releaseURL: data.permalink_url, - status: 'success', - }); - } return [ { id: firstPost.id, @@ -414,7 +385,46 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { releaseURL: finalUrl, status: 'success', }, - ...postsArray, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<FacebookDto>[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const replyToId = lastCommentId || postId; + + const data = await ( + await this.fetch( + `https://graph.facebook.com/v20.0/${replyToId}/comments?access_token=${accessToken}&fields=id,permalink_url`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + ...(commentPost.media?.length + ? { attachment_url: commentPost.media[0].path } + : {}), + message: commentPost.message, + }), + }, + 'add comment' + ) + ).json(); + + return [ + { + id: commentPost.id, + postId: data.id, + releaseURL: data.permalink_url, + status: 'success', + }, ]; } diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 13e3701e..a167a612 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -454,7 +454,7 @@ export class InstagramProvider integration: Integration, type = 'graph.facebook.com' ): Promise<PostResponse[]> { - const [firstPost, ...theRest] = postDetails; + const [firstPost] = postDetails; console.log('in progress', id); const isStory = firstPost.settings.post_type === 'story'; const medias = await Promise.all( @@ -521,10 +521,6 @@ export class InstagramProvider }) || [] ); - const arr = []; - - let containerIdGlobal = ''; - let linkGlobal = ''; if (medias.length === 1) { const { id: mediaId } = await ( await this.fetch( @@ -535,22 +531,20 @@ export class InstagramProvider ) ).json(); - containerIdGlobal = mediaId; - const { permalink } = await ( await this.fetch( `https://${type}/v20.0/${mediaId}?fields=permalink&access_token=${accessToken}` ) ).json(); - arr.push({ - id: firstPost.id, - postId: mediaId, - releaseURL: permalink, - status: 'success', - }); - - linkGlobal = permalink; + return [ + { + id: firstPost.id, + postId: mediaId, + releaseURL: permalink, + status: 'success', + }, + ]; } else { const { id: containerId, ...all3 } = await ( await this.fetch( @@ -589,45 +583,60 @@ export class InstagramProvider ) ).json(); - containerIdGlobal = mediaId; - const { permalink } = await ( await this.fetch( `https://${type}/v20.0/${mediaId}?fields=permalink&access_token=${accessToken}` ) ).json(); - arr.push({ - id: firstPost.id, - postId: mediaId, + return [ + { + id: firstPost.id, + postId: mediaId, + releaseURL: permalink, + status: 'success', + }, + ]; + } + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<InstagramDto>[], + integration: Integration, + type = 'graph.facebook.com' + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + + const { id: commentId } = await ( + await this.fetch( + `https://${type}/v20.0/${postId}/comments?message=${encodeURIComponent( + commentPost.message + )}&access_token=${accessToken}`, + { + method: 'POST', + } + ) + ).json(); + + // Get the permalink from the parent post + const { permalink } = await ( + await this.fetch( + `https://${type}/v20.0/${postId}?fields=permalink&access_token=${accessToken}` + ) + ).json(); + + return [ + { + id: commentPost.id, + postId: commentId, releaseURL: permalink, status: 'success', - }); - - linkGlobal = permalink; - } - - for (const post of theRest) { - const { id: commentId } = await ( - await this.fetch( - `https://${type}/v20.0/${containerIdGlobal}/comments?message=${encodeURIComponent( - post.message - )}&access_token=${accessToken}`, - { - method: 'POST', - } - ) - ).json(); - - arr.push({ - id: post.id, - postId: commentId, - releaseURL: linkGlobal, - status: 'success', - }); - } - - return arr; + }, + ]; } private setTitle(name: string) { diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts index bc058502..0b853b86 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts @@ -165,6 +165,25 @@ export class InstagramStandaloneProvider ); } + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<InstagramDto>[], + integration: Integration + ): Promise<PostResponse[]> { + return instagramProvider.comment( + id, + postId, + lastCommentId, + accessToken, + postDetails, + integration, + 'graph.instagram.com' + ); + } + async analytics(id: string, accessToken: string, date: number) { return instagramProvider.analytics( id, diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index b30ae6f5..8aebb98c 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -258,6 +258,25 @@ export class LinkedinPageProvider return super.post(id, accessToken, postDetails, integration, 'company'); } + override async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + return super.comment( + id, + postId, + lastCommentId, + accessToken, + postDetails, + integration, + 'company' + ); + } + async analytics( id: string, accessToken: string, diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 0e6f9872..d0415bfc 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -652,11 +652,11 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { ); } - const [processedFirstPost, ...restPosts] = processedPostDetails; + const [processedFirstPost] = processedPostDetails; - // Process and upload media for all posts + // Process and upload media for the first post only const uploadedMedia = await this.processMediaForPosts( - processedPostDetails, + [processedFirstPost], accessToken, id, type @@ -677,25 +677,30 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { !!firstPost.settings?.post_as_images_carousel ); - // Build response array starting with main post - const responses: PostResponse[] = [ - this.createPostResponse(mainPostId, processedFirstPost.id, true), - ]; + // Return response for main post only + return [this.createPostResponse(mainPostId, processedFirstPost.id, true)]; + } - // Create comment posts for remaining posts - for (const post of restPosts) { - const commentPostId = await this.createCommentPost( - id, - accessToken, - post, - mainPostId, - type - ); + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<LinkedinDto>[], + integration: Integration, + type = 'personal' as 'company' | 'personal' + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; - responses.push(this.createPostResponse(commentPostId, post.id, false)); - } + const commentPostId = await this.createCommentPost( + id, + accessToken, + commentPost, + postId, + type + ); - return responses; + return [this.createPostResponse(commentPostId, commentPost.id, false)]; } @PostPlug({ diff --git a/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts index f1c078a6..540ff433 100644 --- a/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/mastodon.custom.provider.ts @@ -5,6 +5,7 @@ import { } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; import { MastodonProvider } from '@gitroom/nestjs-libraries/integrations/social/mastodon.provider'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { Integration } from '@prisma/client'; export class MastodonCustomProvider extends MastodonProvider { override identifier = 'mastodon-custom'; @@ -81,4 +82,22 @@ export class MastodonCustomProvider extends MastodonProvider { postDetails ); } + + override async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + return this.dynamicComment( + id, + postId, + lastCommentId, + accessToken, + process.env.MASTODON_URL || 'https://mastodon.social', + postDetails + ); + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts index 15f1b934..43e9f984 100644 --- a/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/mastodon.provider.ts @@ -7,6 +7,7 @@ import { import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; export class MastodonProvider extends SocialAbstract implements SocialProvider { override maxConcurrentJob = 5; // Mastodon instances typically have generous limits @@ -133,47 +134,88 @@ export class MastodonProvider extends SocialAbstract implements SocialProvider { url: string, postDetails: PostDetails[] ): Promise<PostResponse[]> { - let loadId = ''; - const ids = [] as string[]; - for (const getPost of postDetails) { - const uploadFiles = await Promise.all( - getPost?.media?.map((media) => - this.uploadFile(url, media.path, accessToken) - ) || [] - ); + const [firstPost] = postDetails; - const form = new FormData(); - form.append('status', getPost.message); - form.append('visibility', 'public'); - if (loadId) { - form.append('in_reply_to_id', loadId); + const uploadFiles = await Promise.all( + firstPost?.media?.map((media) => + this.uploadFile(url, media.path, accessToken) + ) || [] + ); + + const form = new FormData(); + form.append('status', firstPost.message); + form.append('visibility', 'public'); + if (uploadFiles.length) { + for (const file of uploadFiles) { + form.append('media_ids[]', file); } - if (uploadFiles.length) { - for (const file of uploadFiles) { - form.append('media_ids[]', file); - } - } - - const post = await ( - await this.fetch(`${url}/api/v1/statuses`, { - method: 'POST', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - body: form, - }) - ).json(); - - ids.push(post.id); - loadId = loadId || post.id; } - return postDetails.map((p, i) => ({ - id: p.id, - postId: ids[i], - releaseURL: `${url}/statuses/${ids[i]}`, - status: 'completed', - })); + const post = await ( + await this.fetch(`${url}/api/v1/statuses`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + body: form, + }) + ).json(); + + return [ + { + id: firstPost.id, + postId: post.id, + releaseURL: `${url}/statuses/${post.id}`, + status: 'completed', + }, + ]; + } + + async dynamicComment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + url: string, + postDetails: PostDetails[] + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const replyToId = lastCommentId || postId; + + const uploadFiles = await Promise.all( + commentPost?.media?.map((media) => + this.uploadFile(url, media.path, accessToken) + ) || [] + ); + + const form = new FormData(); + form.append('status', commentPost.message); + form.append('visibility', 'public'); + form.append('in_reply_to_id', replyToId); + if (uploadFiles.length) { + for (const file of uploadFiles) { + form.append('media_ids[]', file); + } + } + + const post = await ( + await this.fetch(`${url}/api/v1/statuses`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + body: form, + }) + ).json(); + + return [ + { + id: commentPost.id, + postId: post.id, + releaseURL: `${url}/statuses/${post.id}`, + status: 'completed', + }, + ]; } async post( @@ -188,4 +230,22 @@ export class MastodonProvider extends SocialAbstract implements SocialProvider { postDetails ); } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + return this.dynamicComment( + id, + postId, + lastCommentId, + accessToken, + process.env.MASTODON_URL || 'https://mastodon.social', + postDetails + ); + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts index 8a3eb0e0..d58911a7 100644 --- a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts @@ -13,6 +13,7 @@ import { lookup } from 'mime-types'; import axios from 'axios'; import WebSocket from 'ws'; import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; +import { Integration } from '@prisma/client'; // @ts-ignore global.WebSocket = WebSocket; @@ -183,7 +184,7 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { accessToken: string, postDetails: PostDetails<RedditSettingsDto>[] ): Promise<PostResponse[]> { - const [post, ...rest] = postDetails; + const [post] = postDetails; const valueArray: PostResponse[] = []; for (const firstPostSettings of post.settings.subreddit) { @@ -235,7 +236,7 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { }) ).json(); - const { id, name, url } = await new Promise<{ + const { id: redditId, name, url } = await new Promise<{ id: string; name: string; url: string; @@ -268,50 +269,12 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { }); valueArray.push({ - postId: id, + postId: redditId, releaseURL: url, id: post.id, status: 'published', }); - for (const comment of rest) { - const { - json: { - data: { - things: [ - { - data: { id: commentId, permalink }, - }, - ], - }, - }, - } = await ( - await this.fetch('https://oauth.reddit.com/api/comment', { - method: 'POST', - headers: { - Authorization: `Bearer ${accessToken}`, - 'Content-Type': 'application/x-www-form-urlencoded', - }, - body: new URLSearchParams({ - text: comment.message, - thing_id: name, - api_type: 'json', - }), - }) - ).json(); - - valueArray.push({ - postId: commentId, - releaseURL: 'https://www.reddit.com' + permalink, - id: comment.id, - status: 'published', - }); - - if (rest.length > 1) { - await timer(5000); - } - } - if (post.settings.subreddit.length > 1) { await timer(5000); } @@ -325,6 +288,54 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { })); } + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<RedditSettingsDto>[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + + // Reddit uses thing_id format like t3_xxx for posts + const thingId = postId.startsWith('t3_') ? postId : `t3_${postId}`; + + const { + json: { + data: { + things: [ + { + data: { id: commentId, permalink }, + }, + ], + }, + }, + } = await ( + await this.fetch('https://oauth.reddit.com/api/comment', { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + text: commentPost.message, + thing_id: thingId, + api_type: 'json', + }), + }) + ).json(); + + return [ + { + postId: commentId, + releaseURL: 'https://www.reddit.com' + permalink, + id: commentPost.id, + status: 'published', + }, + ]; + } + @Tool({ description: 'Get list of subreddits with information', dataSchema: [ diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index bac4c775..5c852c4b 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -342,7 +342,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { return []; } - const [firstPost, ...replies] = postDetails; + const [firstPost] = postDetails; // Create the initial thread const initialContentId = await this.createThreadContent( @@ -358,8 +358,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { initialContentId ); - // Track the responses - const responses: PostResponse[] = [ + // Return the main post response + return [ { id: firstPost.id, postId: threadId, @@ -367,60 +367,49 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { releaseURL: permalink, }, ]; + } - // Handle replies if any - let lastReplyId = threadId; + async comment( + userId: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<{ + active_thread_finisher: boolean; + thread_finisher: string; + }>[], + integration: Integration + ): Promise<PostResponse[]> { + if (!postDetails.length) { + return []; + } - for (const reply of replies) { - // Create reply content - const replyContentId = await this.createThreadContent( - userId, - accessToken, - reply, - lastReplyId - ); + const [commentPost] = postDetails; + const replyToId = lastCommentId || postId; - // Publish the reply - const { threadId: replyThreadId } = await this.publishThread( - userId, - accessToken, - replyContentId - ); + // Create reply content + const replyContentId = await this.createThreadContent( + userId, + accessToken, + commentPost, + replyToId + ); - // Update the last reply ID for chaining - lastReplyId = replyThreadId; + // Publish the reply + const { threadId: replyThreadId, permalink } = await this.publishThread( + userId, + accessToken, + replyContentId + ); - // Add to responses - responses.push({ - id: reply.id, - postId: threadId, // Main thread ID + return [ + { + id: commentPost.id, + postId: replyThreadId, status: 'success', - releaseURL: permalink, // Main thread URL - }); - } - - if (postDetails?.[0]?.settings?.active_thread_finisher) { - try { - const replyContentId = await this.createThreadContent( - userId, - accessToken, - { - id: makeId(10), - media: [], - message: postDetails?.[0]?.settings?.thread_finisher, - settings: {}, - }, - lastReplyId, - threadId - ); - - await this.publishThread(userId, accessToken, replyContentId); - } catch (err) { - console.log(err); - } - } - - return responses; + releaseURL: permalink, + }, + ]; } async analytics( diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index fe2c215a..5dccf2dc 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -291,38 +291,21 @@ export class XProvider extends SocialAbstract implements SocialProvider { }; } - async post( - id: string, - accessToken: string, - postDetails: PostDetails<{ - active_thread_finisher: boolean; - thread_finisher: string; - community?: string; - who_can_reply_post: - | 'everyone' - | 'following' - | 'mentionedUsers' - | 'subscribers' - | 'verified'; - }>[] - ): Promise<PostResponse[]> { + private async getClient(accessToken: string) { const [accessTokenSplit, accessSecretSplit] = accessToken.split(':'); - const client = new TwitterApi({ + return new TwitterApi({ appKey: process.env.X_API_KEY!, appSecret: process.env.X_API_SECRET!, accessToken: accessTokenSplit, accessSecret: accessSecretSplit, }); - const { - data: { username }, - } = await this.runInConcurrent(async () => - client.v2.me({ - 'user.fields': 'username', - }) - ); + } - // upload everything before, you don't want it to fail between the posts - const uploadAll = ( + private async uploadMedia( + client: TwitterApi, + postDetails: PostDetails<any>[] + ) { + return ( await Promise.all( postDetails.flatMap((p) => p?.media?.flatMap(async (m) => { @@ -361,67 +344,119 @@ export class XProvider extends SocialAbstract implements SocialProvider { return acc; }, {} as Record<string, string[]>); + } - const ids: Array<{ postId: string; id: string; releaseURL: string }> = []; - for (const post of postDetails) { - const media_ids = (uploadAll[post.id] || []).filter((f) => f); + async post( + id: string, + accessToken: string, + postDetails: PostDetails<{ + active_thread_finisher: boolean; + thread_finisher: string; + community?: string; + who_can_reply_post: + | 'everyone' + | 'following' + | 'mentionedUsers' + | 'subscribers' + | 'verified'; + }>[] + ): Promise<PostResponse[]> { + const client = await this.getClient(accessToken); + const { + data: { username }, + } = await this.runInConcurrent(async () => + client.v2.me({ + 'user.fields': 'username', + }) + ); - // @ts-ignore - const { data }: { data: { id: string } } = await this.runInConcurrent( - async () => - // @ts-ignore - client.v2.tweet({ - ...(!postDetails?.[0]?.settings?.who_can_reply_post || - postDetails?.[0]?.settings?.who_can_reply_post === 'everyone' - ? {} - : { - reply_settings: - postDetails?.[0]?.settings?.who_can_reply_post, - }), - ...(postDetails?.[0]?.settings?.community - ? { - community_id: - postDetails?.[0]?.settings?.community?.split('/').pop() || - '', - } - : {}), - text: post.message, - ...(media_ids.length ? { media: { media_ids } } : {}), - ...(ids.length - ? { reply: { in_reply_to_tweet_id: ids[ids.length - 1].postId } } - : {}), - }) - ); + const [firstPost] = postDetails; - ids.push({ + // upload media for the first post + const uploadAll = await this.uploadMedia(client, [firstPost]); + + const media_ids = (uploadAll[firstPost.id] || []).filter((f) => f); + + // @ts-ignore + const { data }: { data: { id: string } } = await this.runInConcurrent( + async () => + // @ts-ignore + client.v2.tweet({ + ...(!firstPost?.settings?.who_can_reply_post || + firstPost?.settings?.who_can_reply_post === 'everyone' + ? {} + : { + reply_settings: firstPost?.settings?.who_can_reply_post, + }), + ...(firstPost?.settings?.community + ? { + community_id: + firstPost?.settings?.community?.split('/').pop() || '', + } + : {}), + text: firstPost.message, + ...(media_ids.length ? { media: { media_ids } } : {}), + }) + ); + + return [ + { postId: data.id, - id: post.id, + id: firstPost.id, releaseURL: `https://twitter.com/${username}/status/${data.id}`, - }); - } + status: 'posted', + }, + ]; + } - if (postDetails?.[0]?.settings?.active_thread_finisher) { - try { - await this.runInConcurrent(async () => - client.v2.tweet({ - text: - stripHtmlValidation( - 'normal', - postDetails?.[0]?.settings?.thread_finisher!, - true - ) + - '\n' + - ids[0].releaseURL, - reply: { in_reply_to_tweet_id: ids[ids.length - 1].postId }, - }) - ); - } catch (err) {} - } + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<{ + active_thread_finisher: boolean; + thread_finisher: string; + }>[], + integration: Integration + ): Promise<PostResponse[]> { + const client = await this.getClient(accessToken); + const { + data: { username }, + } = await this.runInConcurrent(async () => + client.v2.me({ + 'user.fields': 'username', + }) + ); - return ids.map((p) => ({ - ...p, - status: 'posted', - })); + const [commentPost] = postDetails; + + // upload media for the comment + const uploadAll = await this.uploadMedia(client, [commentPost]); + + const media_ids = (uploadAll[commentPost.id] || []).filter((f) => f); + + const replyToId = lastCommentId || postId; + + // @ts-ignore + const { data }: { data: { id: string } } = await this.runInConcurrent( + async () => + // @ts-ignore + client.v2.tweet({ + text: commentPost.message, + ...(media_ids.length ? { media: { media_ids } } : {}), + reply: { in_reply_to_tweet_id: replyToId }, + }) + ); + + return [ + { + postId: data.id, + id: commentPost.id, + releaseURL: `https://twitter.com/${username}/status/${data.id}`, + status: 'posted', + }, + ]; } private loadAllTweets = async ( diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index 8c4fc989..574167d6 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -3,7 +3,6 @@ import { EmailInterface } from '@gitroom/nestjs-libraries/emails/email.interface import { ResendProvider } from '@gitroom/nestjs-libraries/emails/resend.provider'; import { EmptyProvider } from '@gitroom/nestjs-libraries/emails/empty.provider'; import { NodeMailerProvider } from '@gitroom/nestjs-libraries/emails/node.mailer.provider'; -import { concurrency } from '@gitroom/helpers/utils/concurrency.service'; @Injectable() export class EmailService { diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index c7832e2d..6462b317 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -6,7 +6,6 @@ import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/o import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { BillingSubscribeDto } from '@gitroom/nestjs-libraries/dtos/billing/billing.subscribe.dto'; import { capitalize, groupBy } from 'lodash'; -import { MessagesService } from '@gitroom/nestjs-libraries/database/prisma/marketplace/messages.service'; import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; import { TrackService } from '@gitroom/nestjs-libraries/track/track.service'; @@ -29,21 +28,6 @@ export class StripeService { return stripe.webhooks.constructEvent(rawBody, signature, endpointSecret); } - async updateAccount(event: Stripe.AccountUpdatedEvent) { - if (!event.account) { - return; - } - - const accountCharges = - event.data.object.payouts_enabled && - event.data.object.charges_enabled && - !event?.data?.object?.requirements?.disabled_reason; - await this._subscriptionService.updateConnectedStatus( - event.account!, - accountCharges - ); - } - async checkValidCard( event: | Stripe.CustomerSubscriptionCreatedEvent @@ -473,62 +457,6 @@ export class StripeService { return { url }; } - async createAccountProcess(userId: string, email: string, country: string) { - const account = await this._subscriptionService.getUserAccount(userId); - - if (account?.account && account?.connectedAccount) { - return { url: await this.addBankAccount(account.account) }; - } - - if (account?.account && !account?.connectedAccount) { - await stripe.accounts.del(account.account); - } - - const createAccount = await this.createAccount(userId, email, country); - - return { url: await this.addBankAccount(createAccount) }; - } - - async createAccount(userId: string, email: string, country: string) { - const account = await stripe.accounts.create({ - type: 'custom', - capabilities: { - transfers: { - requested: true, - }, - card_payments: { - requested: true, - }, - }, - tos_acceptance: { - service_agreement: 'full', - }, - metadata: { - service: 'gitroom', - }, - country, - email, - }); - - await this._subscriptionService.updateAccount(userId, account.id); - - return account.id; - } - - async addBankAccount(userId: string) { - const accountLink = await stripe.accountLinks.create({ - account: userId, - refresh_url: process.env['FRONTEND_URL'] + '/marketplace/seller', - return_url: process.env['FRONTEND_URL'] + '/marketplace/seller', - type: 'account_onboarding', - collection_options: { - fields: 'eventually_due', - }, - }); - - return accountLink.url; - } - async finishTrial(paymentId: string) { const list = ( await stripe.subscriptions.list({ @@ -635,63 +563,6 @@ export class StripeService { return 0; } - async payAccountStepOne( - userId: string, - organization: Organization, - seller: User, - orderId: string, - ordersItems: Array<{ - integrationType: string; - quantity: number; - price: number; - }>, - groupId: string - ) { - const customer = (await this.createOrGetCustomer(organization))!; - - const price = ordersItems.reduce((all, current) => { - return all + current.price * current.quantity; - }, 0); - - const { url } = await stripe.checkout.sessions.create({ - customer, - mode: 'payment', - currency: 'usd', - success_url: process.env['FRONTEND_URL'] + `/messages/${groupId}`, - metadata: { - orderId, - service: 'gitroom', - type: 'marketplace', - }, - line_items: [ - ...ordersItems, - { - integrationType: `Gitroom Fee (${+process.env.FEE_AMOUNT! * 100}%)`, - quantity: 1, - price: price * +process.env.FEE_AMOUNT!, - }, - ].map((item) => ({ - price_data: { - currency: 'usd', - product_data: { - // @ts-ignore - name: - (!item.price ? 'Platform: ' : '') + - capitalize(item.integrationType), - }, - // @ts-ignore - unit_amount: item.price * 100, - }, - quantity: item.quantity, - })), - payment_intent_data: { - transfer_group: orderId, - }, - }); - - return { url }; - } - async embedded( uniqueId: string, organizationId: string, @@ -883,21 +754,6 @@ export class StripeService { return { ok: true }; } - async payout( - orderId: string, - charge: string, - account: string, - price: number - ) { - return stripe.transfers.create({ - amount: price * 100, - currency: 'usd', - destination: account, - source_transaction: charge, - transfer_group: orderId, - }); - } - async lifetimeDeal(organizationId: string, code: string) { const getCurrentSubscription = await this._subscriptionService.getSubscriptionByOrganizationId( diff --git a/libraries/nestjs-libraries/src/services/trending.service.ts b/libraries/nestjs-libraries/src/services/trending.service.ts deleted file mode 100644 index 104ae136..00000000 --- a/libraries/nestjs-libraries/src/services/trending.service.ts +++ /dev/null @@ -1,31 +0,0 @@ -import json from './trending'; -import { Injectable } from '@nestjs/common'; -import { JSDOM } from 'jsdom'; -import { StarsService } from '@gitroom/nestjs-libraries/database/prisma/stars/stars.service'; -import md5 from 'md5'; - -@Injectable() -export class TrendingService { - constructor(private _starsService: StarsService) {} - async syncTrending() { - for (const language of json) { - const data = await ( - await fetch(`https://github.com/trending/${language.link}`) - ).text(); - const dom = new JSDOM(data); - const trending = Array.from( - dom.window.document.querySelectorAll('[class="Link"]') - ); - const arr = trending.map((el, index) => { - return { - name: el?.textContent?.trim().replace(/\s/g, '') || '', - position: index + 1, - }; - }); - - const hashedNames = md5(arr.map((p) => p.name).join('')); - console.log('Updating GitHub trending topic', language, hashedNames); - await this._starsService.updateTrending(language.name, hashedNames, arr); - } - } -} diff --git a/libraries/nestjs-libraries/src/services/trending.ts b/libraries/nestjs-libraries/src/services/trending.ts deleted file mode 100644 index d1831b1f..00000000 --- a/libraries/nestjs-libraries/src/services/trending.ts +++ /dev/null @@ -1,217 +0,0 @@ -export default [ - { link: '', name: '' }, - { link: '1c-enterprise', name: '1C Enterprise' }, - { link: 'abap', name: 'ABAP' }, - { link: 'actionscript', name: 'ActionScript' }, - { link: 'adblock-filter-list', name: 'Adblock Filter List' }, - { link: 'al', name: 'AL' }, - { link: 'angelscript', name: 'AngelScript' }, - { link: 'apacheconf', name: 'ApacheConf' }, - { link: 'apex', name: 'Apex' }, - { link: 'apl', name: 'APL' }, - { link: 'applescript', name: 'AppleScript' }, - { link: 'arc', name: 'Arc' }, - { link: 'asl', name: 'ASL' }, - { link: 'classic-asp', name: 'Classic ASP' }, - { link: 'assembly', name: 'Assembly' }, - { link: 'astro', name: 'Astro' }, - { link: 'autohotkey', name: 'AutoHotkey' }, - { link: 'autoit', name: 'AutoIt' }, - { link: 'awk', name: 'Awk' }, - { link: 'batchfile', name: 'Batchfile' }, - { link: 'bicep', name: 'Bicep' }, - { link: 'bikeshed', name: 'Bikeshed' }, - { link: 'bitbake', name: 'BitBake' }, - { link: 'blade', name: 'Blade' }, - { link: 'boo', name: 'Boo' }, - { link: 'brainfuck', name: 'Brainfuck' }, - { link: 'brighterscript', name: 'BrighterScript' }, - { link: 'c', name: 'C' }, - { link: 'c%23', name: 'C#' }, - { link: 'c++', name: 'C++' }, - { link: 'cairo', name: 'Cairo' }, - { link: "cap'n-proto", name: "Cap'n Proto" }, - { link: 'cartocss', name: 'CartoCSS' }, - { link: 'chapel', name: 'Chapel' }, - { link: 'circom', name: 'Circom' }, - { link: 'classic-asp', name: 'Classic ASP' }, - { link: 'clojure', name: 'Clojure' }, - { link: 'cmake', name: 'CMake' }, - { link: 'codeql', name: 'CodeQL' }, - { link: 'coffeescript', name: 'CoffeeScript' }, - { link: 'common-lisp', name: 'Common Lisp' }, - { link: 'component-pascal', name: 'Component Pascal' }, - { link: 'crystal', name: 'Crystal' }, - { link: 'css', name: 'CSS' }, - { link: 'cuda', name: 'Cuda' }, - { link: 'cue', name: 'CUE' }, - { link: 'cython', name: 'Cython' }, - { link: 'd', name: 'D' }, - { link: 'dart', name: 'Dart' }, - { link: 'denizenscript', name: 'DenizenScript' }, - { link: 'digital-command-language', name: 'DIGITAL Command Language' }, - { link: 'dm', name: 'DM' }, - { link: 'dockerfile', name: 'Dockerfile' }, - { link: 'earthly', name: 'Earthly' }, - { link: 'ejs', name: 'EJS' }, - { link: 'elixir', name: 'Elixir' }, - { link: 'elm', name: 'Elm' }, - { link: 'emacs-lisp', name: 'Emacs Lisp' }, - { link: 'emberscript', name: 'EmberScript' }, - { link: 'erlang', name: 'Erlang' }, - { link: 'f%23', name: 'F#' }, - { link: 'f*', name: 'F*' }, - { link: 'fennel', name: 'Fennel' }, - { link: 'fluent', name: 'Fluent' }, - { link: 'forth', name: 'Forth' }, - { link: 'fortran', name: 'Fortran' }, - { link: 'freemarker', name: 'FreeMarker' }, - { link: 'g-code', name: 'G-code' }, - { link: 'gdscript', name: 'GDScript' }, - { link: 'gherkin', name: 'Gherkin' }, - { link: 'gleam', name: 'Gleam' }, - { link: 'glsl', name: 'GLSL' }, - { link: 'go', name: 'Go' }, - { link: 'groovy', name: 'Groovy' }, - { link: 'hack', name: 'Hack' }, - { link: 'handlebars', name: 'Handlebars' }, - { link: 'haskell', name: 'Haskell' }, - { link: 'haxe', name: 'Haxe' }, - { link: 'hcl', name: 'HCL' }, - { link: 'hlsl', name: 'HLSL' }, - { link: 'holyc', name: 'HolyC' }, - { link: 'hoon', name: 'hoon' }, - { link: 'hosts-file', name: 'Hosts File' }, - { link: 'html', name: 'HTML' }, - { link: 'idris', name: 'Idris' }, - { link: 'inform-7', name: 'Inform 7' }, - { link: 'inno-setup', name: 'Inno Setup' }, - { link: 'io', name: 'Io' }, - { link: 'java', name: 'Java' }, - { link: 'javascript', name: 'JavaScript' }, - { link: 'json', name: 'JSON' }, - { link: 'jsonnet', name: 'Jsonnet' }, - { link: 'julia', name: 'Julia' }, - { link: 'jupyter-notebook', name: 'Jupyter Notebook' }, - { link: 'just', name: 'Just' }, - { link: 'kicad-layout', name: 'KiCad Layout' }, - { link: 'kotlin', name: 'Kotlin' }, - { link: 'labview', name: 'LabVIEW' }, - { link: 'lean', name: 'Lean' }, - { link: 'less', name: 'Less' }, - { link: 'lfe', name: 'LFE' }, - { link: 'liquid', name: 'Liquid' }, - { link: 'llvm', name: 'LLVM' }, - { link: 'logos', name: 'Logos' }, - { link: 'lookml', name: 'LookML' }, - { link: 'lua', name: 'Lua' }, - { link: 'm4', name: 'M4' }, - { link: 'makefile', name: 'Makefile' }, - { link: 'markdown', name: 'Markdown' }, - { link: 'mathematica', name: 'Mathematica' }, - { link: 'matlab', name: 'MATLAB' }, - { link: 'mcfunction', name: 'mcfunction' }, - { link: 'mdx', name: 'MDX' }, - { link: 'mermaid', name: 'Mermaid' }, - { link: 'meson', name: 'Meson' }, - { link: 'metal', name: 'Metal' }, - { link: 'mlir', name: 'MLIR' }, - { link: 'move', name: 'Move' }, - { link: 'mustache', name: 'Mustache' }, - { link: 'nasl', name: 'NASL' }, - { link: 'nesc', name: 'nesC' }, - { link: 'nextflow', name: 'Nextflow' }, - { link: 'nim', name: 'Nim' }, - { link: 'nix', name: 'Nix' }, - { link: 'nsis', name: 'NSIS' }, - { link: 'nunjucks', name: 'Nunjucks' }, - { link: 'objective-c', name: 'Objective-C' }, - { link: 'objective-c++', name: 'Objective-C++' }, - { link: 'ocaml', name: 'OCaml' }, - { link: 'odin', name: 'Odin' }, - { link: 'open-policy-agent', name: 'Open Policy Agent' }, - { link: 'openscad', name: 'OpenSCAD' }, - { link: 'papyrus', name: 'Papyrus' }, - { link: 'pascal', name: 'Pascal' }, - { link: 'perl', name: 'Perl' }, - { link: 'php', name: 'PHP' }, - { link: 'plpgsql', name: 'PLpgSQL' }, - { link: 'plsql', name: 'PLSQL' }, - { link: 'pony', name: 'Pony' }, - { link: 'postscript', name: 'PostScript' }, - { link: 'powershell', name: 'PowerShell' }, - { link: 'processing', name: 'Processing' }, - { link: 'prolog', name: 'Prolog' }, - { link: 'pug', name: 'Pug' }, - { link: 'puppet', name: 'Puppet' }, - { link: 'purebasic', name: 'PureBasic' }, - { link: 'purescript', name: 'PureScript' }, - { link: 'python', name: 'Python' }, - { link: 'qml', name: 'QML' }, - { link: 'r', name: 'R' }, - { link: 'racket', name: 'Racket' }, - { link: 'raku', name: 'Raku' }, - { link: 'raml', name: 'RAML' }, - { link: "ren'py", name: "Ren'Py" }, - { link: 'rescript', name: 'ReScript' }, - { link: 'restructuredtext', name: 'reStructuredText' }, - { link: 'rich-text-format', name: 'Rich Text Format' }, - { link: 'robotframework', name: 'RobotFramework' }, - { link: 'roff', name: 'Roff' }, - { link: 'routeros-script', name: 'RouterOS Script' }, - { link: 'rpm-spec', name: 'RPM Spec' }, - { link: 'ruby', name: 'Ruby' }, - { link: 'rust', name: 'Rust' }, - { link: 'sass', name: 'Sass' }, - { link: 'scala', name: 'Scala' }, - { link: 'scheme', name: 'Scheme' }, - { link: 'scss', name: 'SCSS' }, - { link: 'shaderlab', name: 'ShaderLab' }, - { link: 'shell', name: 'Shell' }, - { link: 'smali', name: 'Smali' }, - { link: 'smalltalk', name: 'Smalltalk' }, - { link: 'smarty', name: 'Smarty' }, - { link: 'solidity', name: 'Solidity' }, - { link: 'sqf', name: 'SQF' }, - { link: 'sql', name: 'SQL' }, - { link: 'squirrel', name: 'Squirrel' }, - { link: 'standard-ml', name: 'Standard ML' }, - { link: 'starlark', name: 'Starlark' }, - { link: 'stylus', name: 'Stylus' }, - { link: 'supercollider', name: 'SuperCollider' }, - { link: 'svelte', name: 'Svelte' }, - { link: 'svg', name: 'SVG' }, - { link: 'swift', name: 'Swift' }, - { link: 'swig', name: 'SWIG' }, - { link: 'systemverilog', name: 'SystemVerilog' }, - { link: 'tcl', name: 'Tcl' }, - { link: 'tex', name: 'TeX' }, - { link: 'text', name: 'Text' }, - { link: 'thrift', name: 'Thrift' }, - { link: 'tsql', name: 'TSQL' }, - { link: 'twig', name: 'Twig' }, - { link: 'typescript', name: 'TypeScript' }, - { link: 'typst', name: 'Typst' }, - { link: 'unrealscript', name: 'UnrealScript' }, - { link: 'v', name: 'V' }, - { link: 'vala', name: 'Vala' }, - { link: 'vbscript', name: 'VBScript' }, - { link: 'verilog', name: 'Verilog' }, - { link: 'vhdl', name: 'VHDL' }, - { link: 'vim-script', name: 'Vim Script' }, - { link: 'vim-snippet', name: 'Vim Snippet' }, - { link: 'visual-basic-.net', name: 'Visual Basic .NET' }, - { link: 'visual-basic-.net', name: 'Visual Basic .NET' }, - { link: 'vue', name: 'Vue' }, - { link: 'webassembly', name: 'WebAssembly' }, - { link: 'wgsl', name: 'WGSL' }, - { link: 'witcher-script', name: 'Witcher Script' }, - { link: 'xc', name: 'XC' }, - { link: 'xslt', name: 'XSLT' }, - { link: 'yacc', name: 'Yacc' }, - { link: 'yaml', name: 'YAML' }, - { link: 'yara', name: 'YARA' }, - { link: 'zap', name: 'ZAP' }, - { link: 'zenscript', name: 'ZenScript' }, - { link: 'zig', name: 'Zig' }, -]; diff --git a/libraries/nestjs-libraries/src/temporal/temporal.module.ts b/libraries/nestjs-libraries/src/temporal/temporal.module.ts index 0e0225fb..a69a8ce0 100644 --- a/libraries/nestjs-libraries/src/temporal/temporal.module.ts +++ b/libraries/nestjs-libraries/src/temporal/temporal.module.ts @@ -1,9 +1,10 @@ import { TemporalModule } from 'nestjs-temporal-core'; +import { socialIntegrationList } from '@gitroom/nestjs-libraries/integrations/integration.manager'; export const getTemporalModule = ( isWorkers: boolean, path?: string, - activityClasses?: any[], + activityClasses?: any[] ) => { return TemporalModule.register({ isGlobal: true, @@ -12,16 +13,28 @@ export const getTemporalModule = ( namespace: process.env.TEMPORAL_NAMESPACE || 'default', }, taskQueue: 'main', + logLevel: 'error', ...(isWorkers ? { - worker: { - workflowsPath: path!, - activityClasses: activityClasses!, - autoStart: true, - workerOptions: { - maxConcurrentActivityTaskExecutions: 24, - }, - }, + workers: [ + { identifier: 'main', maxConcurrentJob: undefined }, + ...socialIntegrationList, + ] + .filter((f) => f.identifier.indexOf('-') === -1) + .map((integration) => ({ + taskQueue: integration.identifier.split('-')[0], + workflowsPath: path!, + activityClasses: activityClasses!, + autoStart: true, + ...(integration.maxConcurrentJob + ? { + workerOptions: { + maxConcurrentActivityTaskExecutions: + integration.maxConcurrentJob, + }, + } + : {}), + })), } : {}), }); diff --git a/package.json b/package.json index f7881301..546d680f 100644 --- a/package.json +++ b/package.json @@ -11,31 +11,24 @@ }, "packageManager": "pnpm@10.6.1", "scripts": { - "dev": "pnpm run --filter ./apps/extension --filter ./apps/cron --filter ./apps/orchestrator --filter ./apps/backend --filter ./apps/frontend --parallel dev", + "dev": "pnpm run --filter ./apps/extension --filter ./apps/orchestrator --filter ./apps/backend --filter ./apps/frontend --parallel dev", "pm2": "pnpm run pm2-run", "publish-sdk": "pnpm run --filter ./apps/sdk publish", "pm2-run": "pm2 delete all || true && pnpm run prisma-db-push && pnpm run --parallel pm2 && pm2 logs", "dev:stripe": "pnpm dlx concurrently \"stripe listen --forward-to localhost:3000/stripe\" \"pnpm run dev\"", - "build": "pnpm -r --workspace-concurrency=1 --filter ./apps/frontend --filter ./apps/backend --filter ./apps/workers --filter ./apps/cron run build", + "build": "pnpm -r --workspace-concurrency=1 --filter ./apps/frontend --filter ./apps/backend --filter ./apps/cron run build", "build:backend": "rm -rf apps/backend/dist && pnpm --filter ./apps/backend run build", "build:frontend": "rm -rf apps/frontend/dist && pnpm --filter ./apps/frontend run build", - "build:workers": "rm -rf apps/workers/dist && pnpm --filter ./apps/workers run build", "build:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run build", - "build:cron": "rm -rf apps/cron/dist && pnpm --filter ./apps/cron run build", "build:extension": "rm -rf apps/extension/dist && pnpm --filter ./apps/extension run build", "dev:backend": "rm -rf apps/backend/dist && pnpm --filter ./apps/backend run dev", "dev:frontend": "rm -rf apps/frontend/dist && pnpm --filter ./apps/frontend run dev", - "dev:workers": "rm -rf apps/workers/dist && pnpm --filter ./apps/workers run dev", "dev:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run dev", - "dev:cron": "rm -rf apps/cron/dist && pnpm --filter ./apps/cron run dev", "start:prod:backend": "pnpm --filter ./apps/backend run start", "start:prod:frontend": "pnpm --filter ./apps/frontend run start", - "start:prod:workers": "pnpm --filter ./apps/workers run start", "start:prod:cron": "pnpm --filter ./apps/cron run start", "dev:docker": "docker compose -f ./docker-compose.dev.yaml up -d", "commands:build:development": "pnpm --filter ./apps/commands run build", - "workers": "rm -rf dist/workers && pnpm --filter ./apps/workers run dev", - "cron": "rm -rf dist/cron && pnpm --filter ./apps/cron run dev", "prisma-generate": "pnpm dlx prisma@6.5.0 generate --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-db-push": "pnpm dlx prisma@6.5.0 db push --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-db-pull": "pnpm dlx prisma@6.5.0 db pull --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", @@ -145,12 +138,12 @@ "@uppy/xhr-upload": "^4.3.3", "accept-language": "^3.0.20", "array-move": "^4.0.0", + "async-mutex": "^0.5.0", "axios": "^1.7.7", "bcrypt": "^5.1.1", "bottleneck": "^2.19.5", "bs58": "^6.0.0", "bufferutil": "^4.0.8", - "bullmq": "^5.12.12", "canvas": "^2.11.2", "chart.js": "^4.4.1", "class-transformer": "^0.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 07c334b9..e346ca56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,7 +128,7 @@ importers: version: 10.32.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) '@sentry/nextjs': specifier: ^10.26.0 - version: 10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + version: 10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) '@sentry/profiling-node': specifier: ^10.25.0 version: 10.32.1 @@ -309,6 +309,9 @@ importers: array-move: specifier: ^4.0.0 version: 4.0.0 + async-mutex: + specifier: ^0.5.0 + version: 0.5.0 axios: specifier: ^1.7.7 version: 1.13.2(debug@4.4.3) @@ -645,7 +648,7 @@ importers: version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20) '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.7 - version: 0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + version: 0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) '@svgr/webpack': specifier: ^8.0.1 version: 8.1.0(typescript@5.5.4) @@ -22508,7 +22511,7 @@ snapshots: dependencies: playwright: 1.57.0 - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.47.0 @@ -22518,7 +22521,7 @@ snapshots: react-refresh: 0.10.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) optionalDependencies: type-fest: 4.41.0 @@ -23813,7 +23816,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@sentry/nextjs@10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/nextjs@10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.38.0 @@ -23825,7 +23828,7 @@ snapshots: '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) '@sentry/react': 10.32.1(react@18.3.1) '@sentry/vercel-edge': 10.32.1 - '@sentry/webpack-plugin': 4.6.1(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + '@sentry/webpack-plugin': 4.6.1(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) resolve: 1.22.8 rollup: 4.54.0 @@ -23925,12 +23928,12 @@ snapshots: '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) '@sentry/core': 10.32.1 - '@sentry/webpack-plugin@4.6.1(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/webpack-plugin@4.6.1(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: '@sentry/bundler-plugin-core': 4.6.1 unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) transitivePeerDependencies: - encoding - supports-color diff --git a/tsconfig.base.json b/tsconfig.base.json index 90122b62..5d95366a 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -26,13 +26,11 @@ "strict": true, "paths": { "@gitroom/backend/*": ["apps/backend/src/*"], - "@gitroom/cron/*": ["apps/cron/src/*"], "@gitroom/frontend/*": ["apps/frontend/src/*"], "@gitroom/helpers/*": ["libraries/helpers/src/*"], "@gitroom/nestjs-libraries/*": ["libraries/nestjs-libraries/src/*"], "@gitroom/react/*": ["libraries/react-shared-libraries/src/*"], "@gitroom/plugins/*": ["libraries/plugins/src/*"], - "@gitroom/workers/*": ["apps/workers/src/*"], "@gitroom/orchestrator/*": ["apps/orchestrator/src/*"], "@gitroom/extension/*": ["apps/extension/src/*"] } From 6633fab9249d6a38911a74e37482e932b234e8d6 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 17:28:53 +0700 Subject: [PATCH 088/340] feat: final temporal touches --- apps/backend/src/app.module.ts | 2 + .../src/activities/post.activity.ts | 49 ++++++++++- apps/orchestrator/src/workflows/index.ts | 1 + .../src/workflows/missing.post.workflow.ts | 19 +++++ .../src/workflows/post.workflow.ts | 13 ++- docker-compose.dev.yaml | 84 +++++++++++++++++++ dynamicconfig/development-cass.yaml | 3 + dynamicconfig/development-sql.yaml | 6 ++ .../notifications/notification.service.ts | 73 +++------------- .../database/prisma/posts/posts.repository.ts | 28 ++----- .../database/prisma/posts/posts.service.ts | 4 + .../temporal/infinite.workflow.register.ts | 31 +++++++ package.json | 3 +- var/docker/create-namespace-default.sh | 7 ++ 14 files changed, 236 insertions(+), 87 deletions(-) create mode 100644 apps/orchestrator/src/workflows/missing.post.workflow.ts create mode 100644 dynamicconfig/development-cass.yaml create mode 100644 dynamicconfig/development-sql.yaml create mode 100644 libraries/nestjs-libraries/src/temporal/infinite.workflow.register.ts create mode 100755 var/docker/create-namespace-default.sh diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 1a5f89b0..64135061 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -14,6 +14,7 @@ import { FILTER } from '@gitroom/nestjs-libraries/sentry/sentry.exception'; import { ChatModule } from '@gitroom/nestjs-libraries/chat/chat.module'; import { getTemporalModule } from '@gitroom/nestjs-libraries/temporal/temporal.module'; import { TemporalRegisterMissingSearchAttributesModule } from '@gitroom/nestjs-libraries/temporal/temporal.register'; +import { InfiniteWorkflowRegisterModule } from '@gitroom/nestjs-libraries/temporal/infinite.workflow.register'; @Global() @Module({ @@ -28,6 +29,7 @@ import { TemporalRegisterMissingSearchAttributesModule } from '@gitroom/nestjs-l ChatModule, getTemporalModule(false), TemporalRegisterMissingSearchAttributesModule, + InfiniteWorkflowRegisterModule, ThrottlerModule.forRoot([ { ttl: 3600000, diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index 88fdcef0..b7e3e027 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -1,5 +1,9 @@ import { Injectable } from '@nestjs/common'; -import { Activity, ActivityMethod } from 'nestjs-temporal-core'; +import { + Activity, + ActivityMethod, + TemporalService, +} from 'nestjs-temporal-core'; import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; import { NotificationService, @@ -13,6 +17,12 @@ import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integration import { timer } from '@gitroom/helpers/utils/timer'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { WebhooksService } from '@gitroom/nestjs-libraries/database/prisma/webhooks/webhooks.service'; +import { TypedSearchAttributes } from '@temporalio/common'; +import { + organizationId, + postId as postIdSearchParam, +} from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; +import { postWorkflow } from '@gitroom/orchestrator/workflows'; @Injectable() @Activity() @@ -23,9 +33,44 @@ export class PostActivity { private _integrationManager: IntegrationManager, private _integrationService: IntegrationService, private _refreshIntegrationService: RefreshIntegrationService, - private _webhookService: WebhooksService + private _webhookService: WebhooksService, + private _temporalService: TemporalService ) {} + @ActivityMethod() + async searchForMissingThreeHoursPosts() { + const list = await this._postService.searchForMissingThreeHoursPosts(); + for (const post of list) { + await this._temporalService.client + .getRawClient() + .workflow.signalWithStart('postWorkflow', { + workflowId: `post_${post.id}`, + taskQueue: 'main', + signal: 'poke', + signalArgs: [], + args: [ + { + taskQueue: post.integration.providerIdentifier + .split('-')[0] + .toLowerCase(), + postId: post.id, + organizationId: post.organizationId, + }, + ], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: post.id, + }, + { + key: organizationId, + value: post.organizationId, + }, + ]), + }); + } + } + @ActivityMethod() async updatePost(id: string, postId: string, releaseURL: string) { return this._postService.updatePost(id, postId, releaseURL); diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index f6c0dabc..1781c2ea 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -1,3 +1,4 @@ export * from './post.workflow'; export * from './autopost.workflow'; export * from './digest.email.workflow'; +export * from './missing.post.workflow'; diff --git a/apps/orchestrator/src/workflows/missing.post.workflow.ts b/apps/orchestrator/src/workflows/missing.post.workflow.ts new file mode 100644 index 00000000..17babfd2 --- /dev/null +++ b/apps/orchestrator/src/workflows/missing.post.workflow.ts @@ -0,0 +1,19 @@ +import { proxyActivities, sleep } from '@temporalio/workflow'; +import { PostActivity } from '@gitroom/orchestrator/activities/post.activity'; + +const { searchForMissingThreeHoursPosts } = proxyActivities<PostActivity>({ + startToCloseTimeout: '10 minute', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, +}); + +export async function missingPostWorkflow() { + await searchForMissingThreeHoursPosts(); + while (true) { + await sleep('1 hour'); + await searchForMissingThreeHoursPosts(); + } +} diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index 8705519f..8fe685b2 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -5,6 +5,8 @@ import { startChild, proxyActivities, sleep, + defineSignal, + setHandler, } from '@temporalio/workflow'; import dayjs from 'dayjs'; import { Integration } from '@prisma/client'; @@ -42,6 +44,8 @@ const { }, }); +const poke = defineSignal('poke'); + export async function postWorkflow({ taskQueue, postId, @@ -53,7 +57,6 @@ export async function postWorkflow({ organizationId: string; postNow?: boolean; }) { - // Dynamic task queue, for concurrency const { postSocial, @@ -65,6 +68,11 @@ export async function postWorkflow({ processPlug, } = proxyTaskQueue(taskQueue); + let poked = false; + setHandler(poke, () => { + poked = true; + }); + const startTime = new Date(); // get all the posts and comments to post const postsList = await getPostsList(organizationId, postId); @@ -77,6 +85,9 @@ export async function postWorkflow({ // if it's a repeatable post, we should ignore this if (!postNow) { + if (dayjs(post.publishDate).isBefore(dayjs())) { + return; + } await sleep(dayjs(post.publishDate).diff(dayjs(), 'millisecond')); } diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index e0f06084..38ee985b 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -53,6 +53,87 @@ services: - postiz-network restart: always + temporal-elasticsearch: + container_name: temporal-elasticsearch + image: elasticsearch:7.17.27 + environment: + - cluster.routing.allocation.disk.threshold_enabled=true + - cluster.routing.allocation.disk.watermark.low=512mb + - cluster.routing.allocation.disk.watermark.high=256mb + - cluster.routing.allocation.disk.watermark.flood_stage=128mb + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms256m -Xmx256m + - xpack.security.enabled=false + networks: + - temporal-network + expose: + - 9200 + volumes: + - /var/lib/elasticsearch/data + + temporal-postgresql: + container_name: temporal-postgresql + image: postgres:16 + environment: + POSTGRES_PASSWORD: temporal + POSTGRES_USER: temporal + networks: + - temporal-network + expose: + - 5432 + volumes: + - /var/lib/postgresql/data + + temporal: + container_name: temporal + ports: + - "7233:7233" + image: temporalio/auto-setup:1.28.1 + depends_on: + - temporal-postgresql + - temporal-elasticsearch + environment: + - DB=postgres12 + - DB_PORT=5432 + - POSTGRES_USER=temporal + - POSTGRES_PWD=temporal + - POSTGRES_SEEDS=temporal-postgresql + - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml + - ENABLE_ES=true + - ES_SEEDS=temporal-elasticsearch + - ES_VERSION=v7 + - TEMPORAL_NAMESPACE=default + networks: + - temporal-network + volumes: + - ./dynamicconfig:/etc/temporal/config/dynamicconfig + labels: + kompose.volume.type: configMap + + temporal-admin-tools: + container_name: temporal-admin-tools + image: temporalio/admin-tools:1.28.1-tctl-1.18.4-cli-1.4.1 + environment: + - TEMPORAL_ADDRESS=temporal:7233 + - TEMPORAL_CLI_ADDRESS=temporal:7233 + networks: + - temporal-network + stdin_open: true + depends_on: + - temporal + tty: true + + temporal-ui: + container_name: temporal-ui + image: temporalio/ui:2.34.0 + environment: + - TEMPORAL_ADDRESS=temporal:7233 + - TEMPORAL_CORS_ORIGINS=http://127.0.0.1:3000 + networks: + - temporal-network + ports: + - "8080:8080" + volumes: redisinsight: postgres-volume: @@ -61,3 +142,6 @@ volumes: networks: postiz-network: external: false + temporal-network: + driver: bridge + name: temporal-network diff --git a/dynamicconfig/development-cass.yaml b/dynamicconfig/development-cass.yaml new file mode 100644 index 00000000..4b916163 --- /dev/null +++ b/dynamicconfig/development-cass.yaml @@ -0,0 +1,3 @@ +system.forceSearchAttributesCacheRefreshOnRead: + - value: true # Dev setup only. Please don't turn this on in production. + constraints: {} diff --git a/dynamicconfig/development-sql.yaml b/dynamicconfig/development-sql.yaml new file mode 100644 index 00000000..228c752f --- /dev/null +++ b/dynamicconfig/development-sql.yaml @@ -0,0 +1,6 @@ +limit.maxIDLength: + - value: 255 + constraints: {} +system.forceSearchAttributesCacheRefreshOnRead: + - value: true # Dev setup only. Please don't turn this on in production. + constraints: {} \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index 0a926318..a8ba9e12 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common'; import { NotificationsRepository } from '@gitroom/nestjs-libraries/database/prisma/notifications/notifications.repository'; import { EmailService } from '@gitroom/nestjs-libraries/services/email.service'; import { OrganizationRepository } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.repository'; -import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; import { TemporalService } from 'nestjs-temporal-core'; import { TypedSearchAttributes } from '@temporalio/common'; import { organizationId } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; @@ -32,13 +31,6 @@ export class NotificationService { ); } - getNotificationsSince(organizationId: string, since: string) { - return this._notificationRepository.getNotificationsSince( - organizationId, - since - ); - } - async inAppNotification( orgId: string, subject: string, @@ -56,8 +48,18 @@ export class NotificationService { try { await this._temporalService.client .getRawClient() - ?.workflow.start('digestEmailWorkflow', { + ?.workflow.signalWithStart('digestEmailWorkflow', { workflowId: 'digest_email_workflow_' + orgId, + signal: 'email', + signalArgs: [ + [ + { + title: subject, + message, + type, + }, + ], + ], taskQueue: 'main', args: [{ organizationId: orgId }], typedSearchAttributes: new TypedSearchAttributes([ @@ -69,20 +71,6 @@ export class NotificationService { }); } catch (err) {} - await this._temporalService.signalWorkflow( - 'digest_email_workflow_' + orgId, - 'email', - [ - [ - { - title: subject, - message, - type, - }, - ], - ] - ); - return; } @@ -111,45 +99,6 @@ export class NotificationService { } } - async getDigestTypes(orgId: string): Promise<NotificationType[]> { - const typesKey = 'digest_types_' + orgId; - const types = await ioRedis.smembers(typesKey); - // Clean up the types key after reading - await ioRedis.del(typesKey); - return types as NotificationType[]; - } - - async sendDigestEmailsToOrg( - orgId: string, - subject: string, - message: string, - types: NotificationType[] - ) { - const userOrg = await this._organizationRepository.getAllUsersOrgs(orgId); - const hasInfo = types.includes('info'); - const hasSuccess = types.includes('success'); - const hasFail = types.includes('fail'); - - for (const user of userOrg?.users || []) { - // 'info' type is always sent regardless of preferences - if (hasInfo) { - await this.sendEmail(user.user.email, subject, message); - continue; - } - - // For digest, check if user wants any of the notification types in the digest - const wantsSuccess = hasSuccess && user.user.sendSuccessEmails; - const wantsFail = hasFail && user.user.sendFailureEmails; - - // Only send if user wants at least one type of notification in the digest - if (!wantsSuccess && !wantsFail) { - continue; - } - - await this.sendEmail(user.user.email, subject, message); - } - } - async sendEmail(to: string, subject: string, html: string, replyTo?: string) { await this._emailService.sendEmail(to, subject, html, replyTo); } diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 5dba914f..937a9d87 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -27,24 +27,6 @@ export class PostsRepository { private _errors: PrismaRepository<'errors'> ) {} - checkPending15minutesBack() { - return this._post.model.post.findMany({ - where: { - publishDate: { - lte: dayjs.utc().subtract(15, 'minute').toDate(), - gte: dayjs.utc().subtract(30, 'minute').toDate(), - }, - state: 'QUEUE', - deletedAt: null, - parentPostId: null, - }, - select: { - id: true, - publishDate: true, - }, - }); - } - searchForMissingThreeHoursPosts() { return this._post.model.post.findMany({ where: { @@ -54,8 +36,8 @@ export class PostsRepository { disabled: false, }, publishDate: { - gte: dayjs.utc().toDate(), - lt: dayjs.utc().add(3, 'hour').toDate(), + gte: dayjs.utc().subtract(2, 'hour').toDate(), + lt: dayjs.utc().add(2, 'hour').toDate(), }, state: 'QUEUE', deletedAt: null, @@ -63,6 +45,12 @@ export class PostsRepository { }, select: { id: true, + organizationId: true, + integration: { + select: { + providerIdentifier: true, + } + }, publishDate: true, }, }); diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 8196345d..36f56392 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -49,6 +49,10 @@ export class PostsService { private _temporalService: TemporalService ) {} + searchForMissingThreeHoursPosts() { + return this._postRepository.searchForMissingThreeHoursPosts(); + } + updatePost(id: string, postId: string, releaseURL: string) { return this._postRepository.updatePost(id, postId, releaseURL); } diff --git a/libraries/nestjs-libraries/src/temporal/infinite.workflow.register.ts b/libraries/nestjs-libraries/src/temporal/infinite.workflow.register.ts new file mode 100644 index 00000000..405bac5e --- /dev/null +++ b/libraries/nestjs-libraries/src/temporal/infinite.workflow.register.ts @@ -0,0 +1,31 @@ +import { Global, Injectable, Module, OnModuleInit } from '@nestjs/common'; +import { TemporalService } from 'nestjs-temporal-core'; + +@Injectable() +export class InfiniteWorkflowRegister implements OnModuleInit { + constructor(private _temporalService: TemporalService) {} + + async onModuleInit(): Promise<void> { + if (!!process.env.RUN_CRON) { + try { + await this._temporalService.client + ?.getRawClient() + ?.workflow?.start('missingPostWorkflow', { + workflowId: 'missing-post-workflow', + taskQueue: 'main', + }); + } catch (err) {} + } + } +} + +@Global() +@Module({ + imports: [], + controllers: [], + providers: [InfiniteWorkflowRegister], + get exports() { + return this.providers; + }, +}) +export class InfiniteWorkflowRegisterModule {} diff --git a/package.json b/package.json index 546d680f..01dcdf6b 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "publish-sdk": "pnpm run --filter ./apps/sdk publish", "pm2-run": "pm2 delete all || true && pnpm run prisma-db-push && pnpm run --parallel pm2 && pm2 logs", "dev:stripe": "pnpm dlx concurrently \"stripe listen --forward-to localhost:3000/stripe\" \"pnpm run dev\"", - "build": "pnpm -r --workspace-concurrency=1 --filter ./apps/frontend --filter ./apps/backend --filter ./apps/cron run build", + "build": "pnpm -r --workspace-concurrency=1 --filter ./apps/frontend --filter ./apps/backend --filter ./apps/orchestrator run build", "build:backend": "rm -rf apps/backend/dist && pnpm --filter ./apps/backend run build", "build:frontend": "rm -rf apps/frontend/dist && pnpm --filter ./apps/frontend run build", "build:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run build", @@ -26,7 +26,6 @@ "dev:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run dev", "start:prod:backend": "pnpm --filter ./apps/backend run start", "start:prod:frontend": "pnpm --filter ./apps/frontend run start", - "start:prod:cron": "pnpm --filter ./apps/cron run start", "dev:docker": "docker compose -f ./docker-compose.dev.yaml up -d", "commands:build:development": "pnpm --filter ./apps/commands run build", "prisma-generate": "pnpm dlx prisma@6.5.0 generate --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", diff --git a/var/docker/create-namespace-default.sh b/var/docker/create-namespace-default.sh new file mode 100755 index 00000000..79eea34f --- /dev/null +++ b/var/docker/create-namespace-default.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +sleep 5 + +tctl namespace create --namespace "default" --description 'Default namespace' --rd 1 + +tini -s -- sleep infinity \ No newline at end of file From c740da360bda140633b51c781d34de780bb0be84 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 17:38:46 +0700 Subject: [PATCH 089/340] feat: fix lockfile --- pnpm-lock.yaml | 116 ------------------------------------------------- 1 file changed, 116 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e346ca56..7046d4cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -327,9 +327,6 @@ importers: bufferutil: specifier: ^4.0.8 version: 4.1.0 - bullmq: - specifier: ^5.12.12 - version: 5.66.2 canvas: specifier: ^2.11.2 version: 2.11.2 @@ -807,8 +804,6 @@ importers: apps/commands: {} - apps/cron: {} - apps/extension: {} apps/frontend: {} @@ -821,8 +816,6 @@ importers: specifier: ^3.3.2 version: 3.3.2 - apps/workers: {} - packages: '@0no-co/graphql.web@1.2.0': @@ -3509,36 +3502,6 @@ packages: resolution: {integrity: sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} - cpu: [arm64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} - cpu: [x64] - os: [darwin] - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} - cpu: [arm64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} - cpu: [arm] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} - cpu: [x64] - os: [linux] - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} - cpu: [x64] - os: [win32] - '@mui/core-downloads-tracker@5.18.0': resolution: {integrity: sha512-jbhwoQ1AY200PSSOrNXmrFCaSDSJWP7qk6urkTmIirvRXDROkqe+QwcLlUiw/PrREwsIF/vm3/dAXvjlMHF0RA==} @@ -8952,9 +8915,6 @@ packages: builtins@5.1.0: resolution: {integrity: sha512-SW9lzGTLvWTP1AY8xeAMZimqDrIaSdLQUcVr9DMef51niJ022Ri87SwRRKYm4A6iHfkPaiVUu/Duw2Wc4J7kKg==} - bullmq@5.66.2: - resolution: {integrity: sha512-0PrkpIakIntkBcPLltPIRWdLC1FTLUa/VhJkmEfobb5YUQjoUwJdmmf7HX+o/vMonS5048JpP+abf9lVRUFEjA==} - bundle-name@4.1.0: resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} engines: {node: '>=18'} @@ -9488,10 +9448,6 @@ packages: crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} - cron-parser@4.9.0: - resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} - engines: {node: '>=12.0.0'} - cron@3.2.1: resolution: {integrity: sha512-w2n5l49GMmmkBFEsH9FIDhjZ1n1QgTMOCMGuQtOXs5veNiosZmso6bQGuqOJSYAXXrG84WQFVneNk+Yt0Ua9iw==} @@ -12573,10 +12529,6 @@ packages: resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==} engines: {node: '>=12'} - luxon@3.7.2: - resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} - engines: {node: '>=12'} - lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -13136,13 +13088,6 @@ packages: resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==} engines: {node: '>=12.13'} - msgpackr-extract@3.0.3: - resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} - hasBin: true - - msgpackr@1.11.5: - resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==} - multer@1.4.5-lts.2: resolution: {integrity: sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==} engines: {node: '>= 6.0.0'} @@ -13323,10 +13268,6 @@ packages: resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} engines: {node: '>= 6.13.0'} - node-gyp-build-optional-packages@5.2.2: - resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} - hasBin: true - node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true @@ -20860,24 +20801,6 @@ snapshots: got: 11.8.6 os-filter-obj: 2.0.0 - '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': - optional: true - - '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': - optional: true - '@mui/core-downloads-tracker@5.18.0': {} '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -28124,18 +28047,6 @@ snapshots: dependencies: semver: 7.7.3 - bullmq@5.66.2: - dependencies: - cron-parser: 4.9.0 - ioredis: 5.8.2 - msgpackr: 1.11.5 - node-abort-controller: 3.1.1 - semver: 7.7.3 - tslib: 2.8.1 - uuid: 11.1.0 - transitivePeerDependencies: - - supports-color - bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 @@ -28707,10 +28618,6 @@ snapshots: crelt@1.0.6: {} - cron-parser@4.9.0: - dependencies: - luxon: 3.7.2 - cron@3.2.1: dependencies: '@types/luxon': 3.4.2 @@ -32635,8 +32542,6 @@ snapshots: luxon@3.5.0: {} - luxon@3.7.2: {} - lz-string@1.5.0: {} macos-release@2.5.1: {} @@ -33635,22 +33540,6 @@ snapshots: ms@3.0.0-canary.1: {} - msgpackr-extract@3.0.3: - dependencies: - node-gyp-build-optional-packages: 5.2.2 - optionalDependencies: - '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 - '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 - optional: true - - msgpackr@1.11.5: - optionalDependencies: - msgpackr-extract: 3.0.3 - multer@1.4.5-lts.2: dependencies: append-field: 1.0.0 @@ -33834,11 +33723,6 @@ snapshots: node-forge@1.3.3: {} - node-gyp-build-optional-packages@5.2.2: - dependencies: - detect-libc: 2.1.2 - optional: true - node-gyp-build@4.8.4: {} node-int64@0.4.0: {} From 78e1a14388ee7ec89ba6f68088acb487ee8d43f8 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 17:41:04 +0700 Subject: [PATCH 090/340] feat: redeploy --- apps/backend/src/api/routes/users.controller.ts | 2 +- apps/orchestrator/src/workflows/post.workflow.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/api/routes/users.controller.ts b/apps/backend/src/api/routes/users.controller.ts index 88e80263..bdbcb46c 100644 --- a/apps/backend/src/api/routes/users.controller.ts +++ b/apps/backend/src/api/routes/users.controller.ts @@ -75,7 +75,7 @@ export class UsersController { } @Get('/personal') - async getPersonal(@GetUserFromRequest() user: User) { + async getPersonalInformation(@GetUserFromRequest() user: User) { return this._userService.getPersonal(user.id); } diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index 8fe685b2..75ef3c64 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -83,7 +83,7 @@ export async function postWorkflow({ return; } - // if it's a repeatable post, we should ignore this + // if it's a repeatable post, we should ignore this. if (!postNow) { if (dayjs(post.publishDate).isBefore(dayjs())) { return; From 6eb55e128f29e83157fd353f1eb95cd8f058c42b Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 17:49:11 +0700 Subject: [PATCH 091/340] fix: run prod --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 01dcdf6b..fad2eab8 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "dev:backend": "rm -rf apps/backend/dist && pnpm --filter ./apps/backend run dev", "dev:frontend": "rm -rf apps/frontend/dist && pnpm --filter ./apps/frontend run dev", "dev:orchestrator": "rm -rf apps/orchestrator/dist && pnpm --filter ./apps/orchestrator run dev", + "start:prod:orchestrator": "pnpm --filter ./apps/orchestrator run start", "start:prod:backend": "pnpm --filter ./apps/backend run start", "start:prod:frontend": "pnpm --filter ./apps/frontend run start", "dev:docker": "docker compose -f ./docker-compose.dev.yaml up -d", From b5979b290d7fdf3f3410da06fa575f14dbdc195f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 17:55:02 +0700 Subject: [PATCH 092/340] feat: run old also --- apps/orchestrator/src/workflows/post.workflow.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index 75ef3c64..d31ad082 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -85,9 +85,6 @@ export async function postWorkflow({ // if it's a repeatable post, we should ignore this. if (!postNow) { - if (dayjs(post.publishDate).isBefore(dayjs())) { - return; - } await sleep(dayjs(post.publishDate).diff(dayjs(), 'millisecond')); } From 1291937cb2b26535a33f245780ca72e4d0fe458c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 17:58:21 +0700 Subject: [PATCH 093/340] fix: only queue state --- apps/orchestrator/src/workflows/post.workflow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index d31ad082..0901a5e8 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -79,7 +79,7 @@ export async function postWorkflow({ const [post] = postsList; // in case doesn't exists for some reason, fail it - if (!post) { + if (!post || (!postNow && post.state !== 'QUEUE')) { return; } From 0144e3984167da885f7bf177eee5baaadc64e7b8 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 18:00:49 +0700 Subject: [PATCH 094/340] feat: no sleep option --- apps/orchestrator/src/workflows/post.workflow.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index 0901a5e8..37ca1864 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -85,7 +85,11 @@ export async function postWorkflow({ // if it's a repeatable post, we should ignore this. if (!postNow) { - await sleep(dayjs(post.publishDate).diff(dayjs(), 'millisecond')); + await sleep( + dayjs(post.publishDate).isBefore(dayjs()) + ? 0 + : dayjs(post.publishDate).diff(dayjs(), 'millisecond') + ); } // if refresh is needed from last time, let's inform the user From 204739f0496fa20ae465b3e4e9cd016edd0a4220 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 18:38:17 +0700 Subject: [PATCH 095/340] feat: fix refresh token --- apps/orchestrator/src/workflows/post.workflow.ts | 2 +- .../src/integrations/social.abstract.ts | 10 +++++++++- .../src/integrations/social/youtube.provider.ts | 8 ++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index 37ca1864..135cda74 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -194,7 +194,7 @@ export async function postWorkflow({ `Error posting on ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, `An error occurred while posting on ${ post.integration?.providerIdentifier - }${err?.message ? `: ${err?.message}` : ``}`, + }${err?.cause?.message ? `: ${err?.cause?.message}` : ``}`, true, false, 'fail' diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index f225dae3..a3dd40e7 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -63,10 +63,18 @@ export abstract class SocialAbstract { value = await func(); } catch (err) { const handle = this.handleErrors(JSON.stringify(err)); - value = { err: true, ...(handle || {}) }; + value = { err: true, value: 'Unknown Error', ...(handle || {}) }; } if (value && value?.err && value?.value) { + if (value.type === 'refresh-token') { + throw new RefreshToken( + '', + JSON.stringify({}), + {} as any, + value.value || '' + ); + } throw new BadBody('', JSON.stringify({}), {} as any, value.value || ''); } diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts index d35762f5..96b8858e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts @@ -117,6 +117,14 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { }; } + if (body.includes('Unauthorized')) { + return { + type: 'refresh-token', + value: + 'Token expired or invalid, please reconnect your YouTube account.', + }; + } + return undefined; } From dac6e8e1898602478585234690e36e35cf443b03 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 18:50:25 +0700 Subject: [PATCH 096/340] fix: refresh token --- apps/orchestrator/src/workflows/post.workflow.ts | 6 +++--- .../src/integrations/refresh.integration.service.ts | 2 +- .../src/integrations/social/x.provider.ts | 11 +++++++++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index 135cda74..cf9c711f 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -172,7 +172,7 @@ export async function postWorkflow({ err.cause.type === 'refresh_token' ) { const refresh = await refreshToken(post.integration); - if (!refresh) { + if (!refresh || !refresh.accessToken) { return false; } @@ -286,7 +286,7 @@ export async function postWorkflow({ err.cause.type === 'refresh_token' ) { const refresh = await refreshToken(post.integration); - if (!refresh) { + if (!refresh || !refresh.accessToken) { return false; } @@ -328,7 +328,7 @@ export async function postWorkflow({ err.cause.type === 'refresh_token' ) { const refresh = await refreshToken(post.integration); - if (!refresh) { + if (!refresh || !refresh.accessToken) { return false; } diff --git a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts index 80f7e3d7..f40e5281 100644 --- a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts +++ b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts @@ -50,7 +50,7 @@ export class RefreshIntegrationService { .refreshToken(integration.refreshToken) .catch((err) => false); - if (!refresh) { + if (!refresh || !refresh.accessToken) { await this._integrationService.refreshNeeded( integration.organizationId, integration.id diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index 5dccf2dc..95e7559c 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -45,15 +45,22 @@ export class XProvider extends SocialAbstract implements SocialProvider { value: string; } | undefined { - if (body.includes('usage-capped')) { + if (body.includes('Unsupported Authentication')) { return { type: 'refresh-token', + value: 'X authentication has expired, please reconnect your account', + }; + } + + if (body.includes('usage-capped')) { + return { + type: 'bad-body', value: 'Posting failed - capped reached. Please try again later', }; } if (body.includes('duplicate-rules')) { return { - type: 'refresh-token', + type: 'bad-body', value: 'You have already posted this post, please wait before posting again', }; From 691d15db03318ea9abab8d29a4c43e5367e41084 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 18:54:11 +0700 Subject: [PATCH 097/340] fix: youtube and gmb --- .../src/integrations/social/gmb.provider.ts | 8 ++++++++ .../src/integrations/social/youtube.provider.ts | 12 ++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts index c3e15314..3fa7de95 100644 --- a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -64,6 +64,14 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { }; } + if (body.includes('Unauthorized')) { + return { + type: 'refresh-token', + value: + 'Token expired or invalid, please reconnect your YouTube account.', + }; + } + if (body.includes('PERMISSION_DENIED')) { return { type: 'refresh-token', diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts index 96b8858e..3b17e8e6 100644 --- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts @@ -125,6 +125,13 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { }; } + if (body.includes('UNAUTHENTICATED') || body.includes('invalid_grant')) { + return { + type: 'refresh-token', + value: 'Please re-authenticate your YouTube account', + }; + } + return undefined; } @@ -228,10 +235,7 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { } } - async fetchPageInformation( - accessToken: string, - data: { id: string } - ) { + async fetchPageInformation(accessToken: string, data: { id: string }) { const { client, youtube } = clientAndYoutube(); client.setCredentials({ access_token: accessToken }); const youtubeClient = youtube(client); From de74191bab765410b106e237e667892d9365763d Mon Sep 17 00:00:00 2001 From: Enno Gelhaus <egelhaus@ennogelhaus.de> Date: Mon, 5 Jan 2026 14:29:16 +0100 Subject: [PATCH 098/340] feat: add spotlight integration to Sentry initialization --- libraries/nestjs-libraries/src/sentry/initialize.sentry.ts | 1 + .../src/sentry/initialize.sentry.next.basic.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/libraries/nestjs-libraries/src/sentry/initialize.sentry.ts b/libraries/nestjs-libraries/src/sentry/initialize.sentry.ts index 6ace751c..b92c6627 100644 --- a/libraries/nestjs-libraries/src/sentry/initialize.sentry.ts +++ b/libraries/nestjs-libraries/src/sentry/initialize.sentry.ts @@ -22,6 +22,7 @@ export const initializeSentry = (appName: string, allowLogs = false) => { }, environment: process.env.NODE_ENV || 'development', dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + spotlight: process.env.SENTRY_SPOTLIGHT === '1', integrations: [ // Add our Profiling integration nodeProfilingIntegration(), diff --git a/libraries/react-shared-libraries/src/sentry/initialize.sentry.next.basic.ts b/libraries/react-shared-libraries/src/sentry/initialize.sentry.next.basic.ts index 0d8a64f3..ef267949 100644 --- a/libraries/react-shared-libraries/src/sentry/initialize.sentry.next.basic.ts +++ b/libraries/react-shared-libraries/src/sentry/initialize.sentry.next.basic.ts @@ -33,6 +33,7 @@ export const initializeSentryBasic = (environment: string, dsn: string, extensio Sentry.consoleLoggingIntegration({ levels: ['log', 'info', 'warn', 'error', 'debug', 'assert', 'trace'] }), ], environment: environment || 'development', + spotlight: process.env.SENTRY_SPOTLIGHT === '1', dsn, sendDefaultPii: true, ...extension, From 3f674de4016aee2b506a410858ab4bc4272a1d54 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 21:10:12 +0700 Subject: [PATCH 099/340] fix: old providers without selection --- .../src/activities/post.activity.ts | 1 + .../integrations/integration.repository.ts | 10 ++++++++++ .../prisma/integrations/integration.service.ts | 4 ++++ .../refresh.integration.service.ts | 18 ++++++++++++++++-- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index b7e3e027..1db29ca7 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -288,6 +288,7 @@ export class PostActivity { return refresh; } catch (err) { + await this._refreshIntegrationService.setBetweenSteps(integration); return false; } } diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts index 6024b921..bb81ae8c 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -309,6 +309,16 @@ export class IntegrationRepository { }); } + async setBetweenRefreshSteps(id: string) { + return this._integration.model.integration.update({ + where: { + id, + }, + data: { + inBetweenSteps: true, + }, + }); + } refreshNeeded(org: string, id: string) { return this._integration.model.integration.update({ where: { diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 102f44ca..0a145eb0 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -204,6 +204,10 @@ export class IntegrationService { return this._integrationRepository.refreshNeeded(org, id); } + async setBetweenRefreshSteps(id: string) { + return this._integrationRepository.setBetweenRefreshSteps(id); + } + async refreshTokens() { const integrations = await this._integrationRepository.needsToBeRefreshed(); for (const integration of integrations) { diff --git a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts index f40e5281..1966aed8 100644 --- a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts +++ b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts @@ -42,6 +42,14 @@ export class RefreshIntegrationService { return refresh; } + public async setBetweenSteps(integration: Integration) { + await this._integrationService.setBetweenRefreshSteps(integration.id); + await this._integrationService.informAboutRefreshError( + integration.organizationId, + integration + ); + } + private async refreshProcess( integration: Integration, socialProvider: SocialProvider @@ -61,12 +69,18 @@ export class RefreshIntegrationService { integration ); - await this._integrationService.disconnectChannel(integration.organizationId, integration); + await this._integrationService.disconnectChannel( + integration.organizationId, + integration + ); return false; } - if (!socialProvider.reConnect) { + if ( + !socialProvider.reConnect || + integration.rootInternalId === integration.internalId + ) { return refresh; } From fc349942b50d03c839098ddd7d037cf2482b0885 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 21:30:04 +0700 Subject: [PATCH 100/340] feat: fix refresh token --- .../src/integrations/social.abstract.ts | 5 +++-- .../src/integrations/social/threads.provider.ts | 13 +++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index a3dd40e7..150d642a 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -135,8 +135,9 @@ export abstract class SocialAbstract { } if ( - request.status === 401 && - (handleError?.type === 'refresh-token' || !handleError) + (request.status === 401 && + (handleError?.type === 'refresh-token' || !handleError)) || + handleError?.type === 'refresh-token' ) { throw new RefreshToken( identifier, diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 5c852c4b..fa8e0dc2 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -32,6 +32,19 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { return 500; } + override handleErrors(body: string): + | { + type: 'refresh-token' | 'bad-body'; + value: string; + } + | undefined { + if (body.includes('Error validating access token')) { + return { type: 'refresh-token', value: 'Threads access token expired' }; + } + + return undefined; + } + async refreshToken(refresh_token: string): Promise<AuthTokenDetails> { const { access_token } = await ( await this.fetch( From ead98cb41f475d769c8bcf77f55a7c79968733ba Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 22:17:51 +0700 Subject: [PATCH 101/340] fix: no refresh - set error --- apps/orchestrator/src/workflows/post.workflow.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post.workflow.ts index cf9c711f..96e3b06d 100644 --- a/apps/orchestrator/src/workflows/post.workflow.ts +++ b/apps/orchestrator/src/workflows/post.workflow.ts @@ -173,6 +173,7 @@ export async function postWorkflow({ ) { const refresh = await refreshToken(post.integration); if (!refresh || !refresh.accessToken) { + await changeState(postsList[0].id, 'ERROR', err, postsList); return false; } @@ -287,6 +288,7 @@ export async function postWorkflow({ ) { const refresh = await refreshToken(post.integration); if (!refresh || !refresh.accessToken) { + await changeState(postsList[0].id, 'ERROR', err, postsList); return false; } @@ -329,6 +331,7 @@ export async function postWorkflow({ ) { const refresh = await refreshToken(post.integration); if (!refresh || !refresh.accessToken) { + await changeState(postsList[0].id, 'ERROR', err, postsList); return false; } From bb9aa1aee6b6676a448dbfebbfb771d86cd56979 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 5 Jan 2026 22:34:36 +0700 Subject: [PATCH 102/340] feat: fix post edit --- .../components/new-launch/manage.modal.tsx | 100 +++++++++++++----- .../components/new-launch/select.current.tsx | 27 ++--- 2 files changed, 85 insertions(+), 42 deletions(-) diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index b118ab73..8f59d674 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -4,6 +4,7 @@ import React, { FC, ReactNode, useCallback, + useEffect, useMemo, useRef, useState, @@ -74,9 +75,11 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { locked, current, activateExitButton, + setHide, } = useLaunchStore( useShallow((state) => ({ hide: state.hide, + setHide: state.setHide, date: state.date, setDate: state.setDate, current: state.current, @@ -92,6 +95,12 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { })) ); + useEffect(() => { + if (hide) { + setHide(false); + } + }, [hide]); + const currentIntegrationText = useMemo(() => { if (current === 'global') { return ''; @@ -100,20 +109,22 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const currentIntegration = integrations.find((p) => p.id === current)!; return ( - <div className="flex items-center gap-[10px]"> - <div className="relative"> - <img - src={`/icons/platforms/${currentIntegration.identifier}.png`} - className="w-[20px] h-[20px] rounded-[4px]" - alt={currentIntegration.identifier} - /> - <SettingsIcon - size={15} - className="text-white absolute -end-[5px] -bottom-[5px]" - /> - </div> - <div>{currentIntegration.name} {t('channel_settings', 'Settings')}</div> + <div className="flex items-center gap-[10px]"> + <div className="relative"> + <img + src={`/icons/platforms/${currentIntegration.identifier}.png`} + className="w-[20px] h-[20px] rounded-[4px]" + alt={currentIntegration.identifier} + /> + <SettingsIcon + size={15} + className="text-white absolute -end-[5px] -bottom-[5px]" + /> </div> + <div> + {currentIntegration.name} {t('channel_settings', 'Settings')} + </div> + </div> ); }, [current]); @@ -158,7 +169,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { setLoading(true); if ( !(await deleteDialog( - t('are_you_sure_you_want_to_delete_post', 'Are you sure you want to delete this post?'), + t( + 'are_you_sure_you_want_to_delete_post', + 'Are you sure you want to delete this post?' + ), t('yes_delete_it', 'Yes, delete it!') )) ) { @@ -191,9 +205,14 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { for (const item of notEnoughChars) { toaster.show( - '' + - item.integration.name + - ' ' + t('post_needs_content_or_image', 'Your post should have at least one character or one image.'), + `${capitalize(item.integration.identifier.split('-')[0])} (${ + item.integration.name + }):` + + ' ' + + t( + 'post_needs_content_or_image', + 'Your post should have at least one character or one image.' + ), 'warning' ); setLoading(false); @@ -203,7 +222,12 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { for (const item of checkAllValid) { if (item.valid === false) { - toaster.show(t('please_fix_your_settings', 'Please fix your settings'), 'warning'); + toaster.show( + `${capitalize(item.integration.identifier.split('-')[0])} (${ + item.integration.name + }): ${t('please_fix_your_settings', 'Please fix your settings')}`, + 'warning' + ); item.fix(); setLoading(false); setShowSettings(true); @@ -240,7 +264,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { for (const item of sliceNeeded) { toaster.show( - `${item?.integration?.name} (${item?.integration?.identifier}) ${t('post_is_too_long', 'post is too long, please fix it')}`, + `${item?.integration?.name} (${item?.integration?.identifier}) ${t( + 'post_is_too_long', + 'post is too long, please fix it' + )}`, 'warning' ); item.preview(); @@ -265,7 +292,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const shortLink = !shortLinkUrl.ask ? false : await deleteDialog( - t('shortlink_urls_question', 'Do you want to shortlink the URLs? it will let you get statistics over clicks'), + t( + 'shortlink_urls_question', + 'Do you want to shortlink the URLs? it will let you get statistics over clicks' + ), t('yes_shortlink_it', 'Yes, shortlink it!') ); @@ -486,9 +516,16 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { selectedIntegrations.length === 0 || loading || locked } onClick={schedule('draft')} - className="cursor-pointer disabled:cursor-not-allowed px-[20px] h-[44px] bg-btnSimple justify-center items-center flex rounded-[8px] text-[15px] font-[600]" + className="relative cursor-pointer disabled:cursor-not-allowed px-[20px] h-[44px] bg-btnSimple justify-center items-center flex rounded-[8px] text-[15px] font-[600]" > - {t('save_as_draft', 'Save as Draft')} + {loading && ( + <div className="absolute left-[50%] top-[50%] -translate-y-[50%] -translate-x-[50%]"> + <div className="animate-spin h-[20px] w-[20px] border-4 border-textColor border-t-transparent rounded-full" /> + </div> + )} + <div className={clsx(loading && 'invisible')}> + {t('save_as_draft', 'Save as Draft')} + </div> </button> )} {addEditSets && ( @@ -509,9 +546,19 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { selectedIntegrations.length === 0 || loading || locked } onClick={schedule('schedule')} - className="text-white min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] ps-[20px] pe-[16px]" + className="text-white relative min-w-[180px] btnSub disabled:cursor-not-allowed disabled:opacity-80 outline-none gap-[8px] flex justify-center items-center h-[44px] rounded-[8px] bg-[#612BD3] ps-[20px] pe-[16px]" > - <div className="text-[15px] font-[600]"> + {loading && ( + <div className="absolute left-[50%] top-[50%] -translate-y-[50%] -translate-x-[50%]"> + <div className="animate-spin h-[20px] w-[20px] border-4 border-white border-t-transparent rounded-full" /> + </div> + )} + <div + className={clsx( + 'text-[15px] font-[600]', + loading && 'invisible' + )} + > {selectedIntegrations.length === 0 ? t('check_circles_above', 'Check the circles above') : dummy @@ -563,7 +610,10 @@ After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} f `} labels={{ title: t('your_assistant', 'Your Assistant'), - initial: t('assistant_initial_message', 'Hi! I can help you to refine your social media posts.'), + initial: t( + 'assistant_initial_message', + 'Hi! I can help you to refine your social media posts.' + ), }} /> </div> diff --git a/apps/frontend/src/components/new-launch/select.current.tsx b/apps/frontend/src/components/new-launch/select.current.tsx index f687643c..07fda877 100644 --- a/apps/frontend/src/components/new-launch/select.current.tsx +++ b/apps/frontend/src/components/new-launch/select.current.tsx @@ -43,14 +43,13 @@ export function useHasScroll(ref: RefObject<HTMLElement>): boolean { } export const SelectCurrent: FC = () => { - const { selectedIntegrations, current, setCurrent, locked, setHide, hide } = + const { selectedIntegrations, current, setCurrent, locked, setHide } = useLaunchStore( useShallow((state) => ({ selectedIntegrations: state.selectedIntegrations, current: state.current, setCurrent: state.setCurrent, locked: state.locked, - hide: state.hide, setHide: state.setHide, })) ); @@ -58,14 +57,6 @@ export const SelectCurrent: FC = () => { const contentRef = useRef<HTMLDivElement>(null); const hasScroll = useHasScroll(contentRef); - useEffect(() => { - if (!hide) { - return; - } - - setHide(false); - }, [hide]); - return ( <> <div className="select-none left-0 absolute w-full z-[100] px-[20px]"> @@ -146,12 +137,11 @@ export const SelectCurrent: FC = () => { export const IsGlobal: FC<{ id: string }> = ({ id }) => { const t = useT(); - const { isInternal } = - useLaunchStore( - useShallow((state) => ({ - isInternal: !!state.internal.find(p => p.integration.id === id), - })) - ); + const { isInternal } = useLaunchStore( + useShallow((state) => ({ + isInternal: !!state.internal.find((p) => p.integration.id === id), + })) + ); if (!isInternal) { return null; @@ -160,7 +150,10 @@ export const IsGlobal: FC<{ id: string }> = ({ id }) => { return ( <div data-tooltip-id="tooltip" - data-tooltip-content={t('no_longer_global_mode', 'No longer in global mode')} + data-tooltip-content={t( + 'no_longer_global_mode', + 'No longer in global mode' + )} className="w-[8px] h-[8px] bg-[#FC69FF] -top-[1px] -end-[3px] absolute rounded-full" /> ); From 5d76475801cd0c4dfa5a867b73ed62609ab03abd Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 12:34:54 +0700 Subject: [PATCH 103/340] fix: 50ms between emails, prevent resend from crashing --- .../src/activities/email.activity.ts | 2 +- .../src/signals/send.email.signal.ts | 9 +++ .../src/workflows/digest.email.workflow.ts | 2 +- apps/orchestrator/src/workflows/index.ts | 1 + .../src/workflows/send.email.workflow.ts | 59 +++++++++++++++++++ .../src/services/email.service.ts | 20 ++++++- 6 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 apps/orchestrator/src/signals/send.email.signal.ts create mode 100644 apps/orchestrator/src/workflows/send.email.workflow.ts diff --git a/apps/orchestrator/src/activities/email.activity.ts b/apps/orchestrator/src/activities/email.activity.ts index a48cbc0b..3430a871 100644 --- a/apps/orchestrator/src/activities/email.activity.ts +++ b/apps/orchestrator/src/activities/email.activity.ts @@ -13,7 +13,7 @@ export class EmailActivity { @ActivityMethod() async sendEmail(to: string, subject: string, html: string, replyTo?: string) { - return this._emailService.sendEmail(to, subject, html, replyTo); + return this._emailService.sendEmailSync(to, subject, html, replyTo); } @ActivityMethod() diff --git a/apps/orchestrator/src/signals/send.email.signal.ts b/apps/orchestrator/src/signals/send.email.signal.ts new file mode 100644 index 00000000..f1a99bbd --- /dev/null +++ b/apps/orchestrator/src/signals/send.email.signal.ts @@ -0,0 +1,9 @@ +import { defineSignal } from '@temporalio/workflow'; + +export type SendEmail = { + to: string; + subject: string; + html: string; + replyTo?: string; +}; +export const sendEmailSignal = defineSignal<[SendEmail]>('sendEmail'); diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index 92802ec2..acfe9eb6 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -61,7 +61,7 @@ export async function digestEmailWorkflow({ ); } - return continueAsNew({ + return await continueAsNew({ organizationId, queue, }); diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index 1781c2ea..3a865864 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -2,3 +2,4 @@ export * from './post.workflow'; export * from './autopost.workflow'; export * from './digest.email.workflow'; export * from './missing.post.workflow'; +export * from './send.email.workflow'; diff --git a/apps/orchestrator/src/workflows/send.email.workflow.ts b/apps/orchestrator/src/workflows/send.email.workflow.ts new file mode 100644 index 00000000..71e552eb --- /dev/null +++ b/apps/orchestrator/src/workflows/send.email.workflow.ts @@ -0,0 +1,59 @@ +import { + proxyActivities, + setHandler, + condition, + sleep, + continueAsNew, +} from '@temporalio/workflow'; +import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; +import { + SendEmail, + sendEmailSignal, +} from '@gitroom/orchestrator/signals/send.email.signal'; + +const { sendEmail } = proxyActivities<EmailActivity>({ + startToCloseTimeout: '10 minute', + taskQueue: 'main', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, +}); + +// Rate limit: 2 requests per second = 500ms between requests +const RATE_LIMIT_MS = 500; + +export async function sendEmailWorkflow({ + queue = [], +}: { + queue: SendEmail[]; +}) { + let processedThisRun = 0; + // Handle incoming email signals + setHandler(sendEmailSignal, (email: SendEmail) => { + queue.push(email); + }); + + // Process emails with rate limiting + while (true) { + // Wait until there's an email in the queue or timeout after 1 hour of inactivity + const waitForQueue = await condition(() => queue.length > 0, '1 hour'); + if (!waitForQueue) { + break; + } + + try { + const email = queue.shift()!; + + await sendEmail(email.to, email.subject, email.html, email.replyTo); + processedThisRun++; + } catch (err) {} + + await sleep(RATE_LIMIT_MS); + + if (processedThisRun >= 100) { + return await continueAsNew({ queue }); + } + } +} diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index 574167d6..529228e1 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -3,11 +3,12 @@ import { EmailInterface } from '@gitroom/nestjs-libraries/emails/email.interface import { ResendProvider } from '@gitroom/nestjs-libraries/emails/resend.provider'; import { EmptyProvider } from '@gitroom/nestjs-libraries/emails/empty.provider'; import { NodeMailerProvider } from '@gitroom/nestjs-libraries/emails/node.mailer.provider'; +import { TemporalService } from 'nestjs-temporal-core'; @Injectable() export class EmailService { emailService: EmailInterface; - constructor() { + constructor(private _temporalService: TemporalService) { this.emailService = this.selectProvider(process.env.EMAIL_PROVIDER!); console.log('Email service provider:', this.emailService.name); for (const key of this.emailService.validateEnvKeys) { @@ -33,6 +34,23 @@ export class EmailService { } async sendEmail(to: string, subject: string, html: string, replyTo?: string) { + return this._temporalService.client + .getRawClient() + ?.workflow.signalWithStart('sendEmailWorkflow', { + taskQueue: 'main', + workflowId: 'send_email', + signal: 'sendEmail', + args: [{ queue: [] }], + signalArgs: [{ to, subject, html, replyTo }], + }); + } + + async sendEmailSync( + to: string, + subject: string, + html: string, + replyTo?: string + ) { if (to.indexOf('@') === -1) { return; } From 7ac99e8259347d3a71f98eb87963e4fc1fa5addf Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 12:51:06 +0700 Subject: [PATCH 104/340] fix: digest send async --- apps/orchestrator/src/activities/email.activity.ts | 5 +++++ apps/orchestrator/src/workflows/digest.email.workflow.ts | 4 ++-- apps/orchestrator/src/workflows/send.email.workflow.ts | 3 +-- .../src/integrations/social/tiktok.provider.ts | 1 - 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/orchestrator/src/activities/email.activity.ts b/apps/orchestrator/src/activities/email.activity.ts index 3430a871..edaff56c 100644 --- a/apps/orchestrator/src/activities/email.activity.ts +++ b/apps/orchestrator/src/activities/email.activity.ts @@ -16,6 +16,11 @@ export class EmailActivity { return this._emailService.sendEmailSync(to, subject, html, replyTo); } + @ActivityMethod() + async sendEmailAsync(to: string, subject: string, html: string, replyTo?: string) { + return this._emailService.sendEmail(to, subject, html, replyTo); + } + @ActivityMethod() async getUserOrgs(id: string) { return this._organizationService.getTeam(id); diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index acfe9eb6..bc5bf4cb 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -8,7 +8,7 @@ import { import { Email, emailSignal } from '@gitroom/orchestrator/signals/email.signal'; import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; -const { sendEmail, getUserOrgs } = proxyActivities<EmailActivity>({ +const { getUserOrgs, sendEmailAsync } = proxyActivities<EmailActivity>({ startToCloseTimeout: '10 minute', taskQueue: 'main', retry: { @@ -52,7 +52,7 @@ export async function digestEmailWorkflow({ if (toSend.length === 0) continue; - await sendEmail( + await sendEmailAsync( user.user.email, toSend.length === 1 ? toSend[0].title diff --git a/apps/orchestrator/src/workflows/send.email.workflow.ts b/apps/orchestrator/src/workflows/send.email.workflow.ts index 71e552eb..932e5092 100644 --- a/apps/orchestrator/src/workflows/send.email.workflow.ts +++ b/apps/orchestrator/src/workflows/send.email.workflow.ts @@ -21,8 +21,7 @@ const { sendEmail } = proxyActivities<EmailActivity>({ }, }); -// Rate limit: 2 requests per second = 500ms between requests -const RATE_LIMIT_MS = 500; +const RATE_LIMIT_MS = 700; export async function sendEmailWorkflow({ queue = [], diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index bf4f7264..d6b52960 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -444,7 +444,6 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { integration: Integration ): Promise<PostResponse[]> { const [firstPost] = postDetails; - console.log('hello'); const isPhoto = (firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) === -1; const { data: { publish_id }, From 5f1a77a3b0b1ef9f279c2eae15ba1a0b343b6f9f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 16:35:42 +0700 Subject: [PATCH 105/340] fix: use existing workflow --- apps/orchestrator/src/activities/post.activity.ts | 1 + .../src/database/prisma/notifications/notification.service.ts | 1 + libraries/nestjs-libraries/src/services/email.service.ts | 1 + 3 files changed, 3 insertions(+) diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index 1db29ca7..e6dc8fd7 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -47,6 +47,7 @@ export class PostActivity { workflowId: `post_${post.id}`, taskQueue: 'main', signal: 'poke', + workflowIdConflictPolicy: 'USE_EXISTING', signalArgs: [], args: [ { diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index a8ba9e12..1deb7dae 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -61,6 +61,7 @@ export class NotificationService { ], ], taskQueue: 'main', + workflowIdConflictPolicy: 'USE_EXISTING', args: [{ organizationId: orgId }], typedSearchAttributes: new TypedSearchAttributes([ { diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index 529228e1..474520b2 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -42,6 +42,7 @@ export class EmailService { signal: 'sendEmail', args: [{ queue: [] }], signalArgs: [{ to, subject, html, replyTo }], + workflowIdConflictPolicy: 'USE_EXISTING', }); } From 95eace849b72ac27bb8de38a0d9d84524a6d0c4c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 17:02:50 +0700 Subject: [PATCH 106/340] fix: send email --- .../src/workflows/send.email.workflow.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/orchestrator/src/workflows/send.email.workflow.ts b/apps/orchestrator/src/workflows/send.email.workflow.ts index 932e5092..7b8255e1 100644 --- a/apps/orchestrator/src/workflows/send.email.workflow.ts +++ b/apps/orchestrator/src/workflows/send.email.workflow.ts @@ -14,11 +14,7 @@ import { const { sendEmail } = proxyActivities<EmailActivity>({ startToCloseTimeout: '10 minute', taskQueue: 'main', - retry: { - maximumAttempts: 3, - backoffCoefficient: 1, - initialInterval: '2 minutes', - }, + cancellationType: 'ABANDON', }); const RATE_LIMIT_MS = 700; @@ -30,8 +26,10 @@ export async function sendEmailWorkflow({ }) { let processedThisRun = 0; // Handle incoming email signals - setHandler(sendEmailSignal, (email: SendEmail) => { - queue.push(email); + setHandler(sendEmailSignal, (addEmail: SendEmail) => { + if (addEmail.to && addEmail.subject) { + queue.push(addEmail); + } }); // Process emails with rate limiting @@ -39,12 +37,14 @@ export async function sendEmailWorkflow({ // Wait until there's an email in the queue or timeout after 1 hour of inactivity const waitForQueue = await condition(() => queue.length > 0, '1 hour'); if (!waitForQueue) { - break; + return; } try { const email = queue.shift()!; - + if (!email) { + continue; + } await sendEmail(email.to, email.subject, email.html, email.replyTo); processedThisRun++; } catch (err) {} From 8b0fe19b417dcee48cfbb03920195c03af7d6d2a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 20:04:56 +0700 Subject: [PATCH 107/340] feat: change cancellation type --- apps/orchestrator/src/workflows/digest.email.workflow.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index bc5bf4cb..f54acbc5 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -11,6 +11,7 @@ import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; const { getUserOrgs, sendEmailAsync } = proxyActivities<EmailActivity>({ startToCloseTimeout: '10 minute', taskQueue: 'main', + cancellationType: 'ABANDON', retry: { maximumAttempts: 3, backoffCoefficient: 1, From 633c08fde78f7f61f79db8a1f21f47e7f5770de9 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 20:37:28 +0700 Subject: [PATCH 108/340] feat: try await in send email --- apps/orchestrator/src/activities/email.activity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/orchestrator/src/activities/email.activity.ts b/apps/orchestrator/src/activities/email.activity.ts index edaff56c..60ee03c5 100644 --- a/apps/orchestrator/src/activities/email.activity.ts +++ b/apps/orchestrator/src/activities/email.activity.ts @@ -18,7 +18,7 @@ export class EmailActivity { @ActivityMethod() async sendEmailAsync(to: string, subject: string, html: string, replyTo?: string) { - return this._emailService.sendEmail(to, subject, html, replyTo); + return await this._emailService.sendEmail(to, subject, html, replyTo); } @ActivityMethod() From 012a347ed677cb44c98562ec56a71e71f651d84a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 20:54:55 +0700 Subject: [PATCH 109/340] feat: logging error --- apps/orchestrator/src/workflows/send.email.workflow.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/send.email.workflow.ts b/apps/orchestrator/src/workflows/send.email.workflow.ts index 7b8255e1..7b9f3d43 100644 --- a/apps/orchestrator/src/workflows/send.email.workflow.ts +++ b/apps/orchestrator/src/workflows/send.email.workflow.ts @@ -47,7 +47,9 @@ export async function sendEmailWorkflow({ } await sendEmail(email.to, email.subject, email.html, email.replyTo); processedThisRun++; - } catch (err) {} + } catch (err) { + console.log(err); + } await sleep(RATE_LIMIT_MS); From 0f4c39ede0e12ad2415a035c65adca3ea116c8e3 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 6 Jan 2026 21:13:51 +0700 Subject: [PATCH 110/340] feat: never exit workflow --- apps/orchestrator/src/workflows/send.email.workflow.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/orchestrator/src/workflows/send.email.workflow.ts b/apps/orchestrator/src/workflows/send.email.workflow.ts index 7b9f3d43..ac4d9342 100644 --- a/apps/orchestrator/src/workflows/send.email.workflow.ts +++ b/apps/orchestrator/src/workflows/send.email.workflow.ts @@ -35,10 +35,7 @@ export async function sendEmailWorkflow({ // Process emails with rate limiting while (true) { // Wait until there's an email in the queue or timeout after 1 hour of inactivity - const waitForQueue = await condition(() => queue.length > 0, '1 hour'); - if (!waitForQueue) { - return; - } + await condition(() => queue.length > 0); try { const email = queue.shift()!; @@ -53,7 +50,7 @@ export async function sendEmailWorkflow({ await sleep(RATE_LIMIT_MS); - if (processedThisRun >= 100) { + if (processedThisRun >= 30) { return await continueAsNew({ queue }); } } From 475cf8e0fbdd73c958ab680d0ac7be8d35470542 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 01:21:11 +0700 Subject: [PATCH 111/340] feat: longer digest --- apps/orchestrator/src/workflows/digest.email.workflow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index f54acbc5..67bc6221 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -32,7 +32,7 @@ export async function digestEmailWorkflow({ while (true) { await condition(() => queue.length > 0); - await sleep(60000); + await sleep(3600000); // Take a snapshot batch and immediately clear queue. const batch = queue.splice(0, queue.length); From 7001295fa58119175788b6fb916a347729d0c133 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 01:22:02 +0700 Subject: [PATCH 112/340] feat: longer digest --- apps/orchestrator/src/workflows/digest.email.workflow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index 67bc6221..f54acbc5 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -32,7 +32,7 @@ export async function digestEmailWorkflow({ while (true) { await condition(() => queue.length > 0); - await sleep(3600000); + await sleep(60000); // Take a snapshot batch and immediately clear queue. const batch = queue.splice(0, queue.length); From bddb9372587d6f2196131720806606a8ad6bd68e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 01:25:42 +0700 Subject: [PATCH 113/340] feat: longer digest --- apps/orchestrator/src/workflows/digest.email.workflow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index f54acbc5..67bc6221 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -32,7 +32,7 @@ export async function digestEmailWorkflow({ while (true) { await condition(() => queue.length > 0); - await sleep(60000); + await sleep(3600000); // Take a snapshot batch and immediately clear queue. const batch = queue.splice(0, queue.length); From 7e73017d3ff29122113b5873a1e827746eb3ed0f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 09:54:45 +0700 Subject: [PATCH 114/340] fix: priority fix for send email --- apps/backend/src/services/auth/auth.service.ts | 8 ++++++-- apps/orchestrator/src/activities/email.activity.ts | 4 ++-- apps/orchestrator/src/signals/send.email.signal.ts | 1 + apps/orchestrator/src/workflows/send.email.workflow.ts | 6 +++++- .../database/prisma/notifications/notification.service.ts | 2 +- libraries/nestjs-libraries/src/services/email.service.ts | 4 ++-- 6 files changed, 17 insertions(+), 8 deletions(-) diff --git a/apps/backend/src/services/auth/auth.service.ts b/apps/backend/src/services/auth/auth.service.ts index 59c4d11a..d2468d0d 100644 --- a/apps/backend/src/services/auth/auth.service.ts +++ b/apps/backend/src/services/auth/auth.service.ts @@ -21,7 +21,10 @@ export class AuthService { private _emailService: EmailService ) {} async canRegister(provider: string) { - if (process.env.DISABLE_REGISTRATION !== 'true' || provider === Provider.GENERIC) { + if ( + process.env.DISABLE_REGISTRATION !== 'true' || + provider === Provider.GENERIC + ) { return true; } @@ -69,7 +72,8 @@ export class AuthService { await this._emailService.sendEmail( body.email, 'Activate your account', - `Click <a href="${process.env.FRONTEND_URL}/auth/activate/${obj.jwt}">here</a> to activate your account` + `Click <a href="${process.env.FRONTEND_URL}/auth/activate/${obj.jwt}">here</a> to activate your account`, + 'top' ); return obj; } diff --git a/apps/orchestrator/src/activities/email.activity.ts b/apps/orchestrator/src/activities/email.activity.ts index 60ee03c5..c591c8d8 100644 --- a/apps/orchestrator/src/activities/email.activity.ts +++ b/apps/orchestrator/src/activities/email.activity.ts @@ -17,8 +17,8 @@ export class EmailActivity { } @ActivityMethod() - async sendEmailAsync(to: string, subject: string, html: string, replyTo?: string) { - return await this._emailService.sendEmail(to, subject, html, replyTo); + async sendEmailAsync(to: string, subject: string, html: string, sendTo: 'top' | 'bottom', replyTo?: string) { + return await this._emailService.sendEmail(to, subject, html, sendTo, replyTo); } @ActivityMethod() diff --git a/apps/orchestrator/src/signals/send.email.signal.ts b/apps/orchestrator/src/signals/send.email.signal.ts index f1a99bbd..5158b386 100644 --- a/apps/orchestrator/src/signals/send.email.signal.ts +++ b/apps/orchestrator/src/signals/send.email.signal.ts @@ -5,5 +5,6 @@ export type SendEmail = { subject: string; html: string; replyTo?: string; + addTo: 'top' | 'bottom'; }; export const sendEmailSignal = defineSignal<[SendEmail]>('sendEmail'); diff --git a/apps/orchestrator/src/workflows/send.email.workflow.ts b/apps/orchestrator/src/workflows/send.email.workflow.ts index ac4d9342..fc103888 100644 --- a/apps/orchestrator/src/workflows/send.email.workflow.ts +++ b/apps/orchestrator/src/workflows/send.email.workflow.ts @@ -28,7 +28,11 @@ export async function sendEmailWorkflow({ // Handle incoming email signals setHandler(sendEmailSignal, (addEmail: SendEmail) => { if (addEmail.to && addEmail.subject) { - queue.push(addEmail); + if (addEmail.addTo === 'top') { + queue.unshift(addEmail); + } else { + queue.push(addEmail); + } } }); diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index 1deb7dae..fa5f036d 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -101,7 +101,7 @@ export class NotificationService { } async sendEmail(to: string, subject: string, html: string, replyTo?: string) { - await this._emailService.sendEmail(to, subject, html, replyTo); + await this._emailService.sendEmail(to, subject, html, 'bottom', replyTo); } hasEmailProvider() { diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index 474520b2..a6063135 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -33,7 +33,7 @@ export class EmailService { } } - async sendEmail(to: string, subject: string, html: string, replyTo?: string) { + async sendEmail(to: string, subject: string, html: string, sendTo: 'top' | 'bottom', replyTo?: string) { return this._temporalService.client .getRawClient() ?.workflow.signalWithStart('sendEmailWorkflow', { @@ -41,7 +41,7 @@ export class EmailService { workflowId: 'send_email', signal: 'sendEmail', args: [{ queue: [] }], - signalArgs: [{ to, subject, html, replyTo }], + signalArgs: [{ to, subject, html, replyTo, sendTo }], workflowIdConflictPolicy: 'USE_EXISTING', }); } From 407cfa67ef38b4655c3c6ace9bdbdfdc5bf7e811 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 09:59:33 +0700 Subject: [PATCH 115/340] fix: priority fix for send email --- apps/orchestrator/src/workflows/digest.email.workflow.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/orchestrator/src/workflows/digest.email.workflow.ts b/apps/orchestrator/src/workflows/digest.email.workflow.ts index 67bc6221..db746036 100644 --- a/apps/orchestrator/src/workflows/digest.email.workflow.ts +++ b/apps/orchestrator/src/workflows/digest.email.workflow.ts @@ -58,7 +58,8 @@ export async function digestEmailWorkflow({ toSend.length === 1 ? toSend[0].title : `[Postiz] Your latest notifications`, - toSend.map((p) => p.message).join('<br/>') + toSend.map((p) => p.message).join('<br/>'), + 'bottom' ); } From 4836b2e7c1c7377c95c65593dc7d7bb34e190392 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 11:35:48 +0700 Subject: [PATCH 116/340] feat: delay --- .../components/new-launch/add.edit.modal.tsx | 1 + .../components/new-launch/delay.component.tsx | 50 + .../src/components/new-launch/editor.tsx | 60 +- .../src/components/new-launch/store.ts | 28 +- .../src/components/ui/icons/index.tsx | 21 + .../translation/locales/en/translation.json | 1329 +++++++++-------- 6 files changed, 809 insertions(+), 680 deletions(-) create mode 100644 apps/frontend/src/components/new-launch/delay.component.tsx diff --git a/apps/frontend/src/components/new-launch/add.edit.modal.tsx b/apps/frontend/src/components/new-launch/add.edit.modal.tsx index 961d5c43..42eef82c 100644 --- a/apps/frontend/src/components/new-launch/add.edit.modal.tsx +++ b/apps/frontend/src/components/new-launch/add.edit.modal.tsx @@ -148,6 +148,7 @@ export const AddEditModalInnerInner: FC<AddEditModalProps> = (props) => { 0, existingData.integration, existingData.posts.map((post) => ({ + delay: 0, content: post.content.indexOf('<p>') > -1 ? post.content diff --git a/apps/frontend/src/components/new-launch/delay.component.tsx b/apps/frontend/src/components/new-launch/delay.component.tsx new file mode 100644 index 00000000..e8607e1d --- /dev/null +++ b/apps/frontend/src/components/new-launch/delay.component.tsx @@ -0,0 +1,50 @@ +'use client'; + +import React, { FC, useCallback } from 'react'; +import { DelayIcon } from '@gitroom/frontend/components/ui/icons'; +import clsx from 'clsx'; +import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { useShallow } from 'zustand/react/shallow'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +export const DelayComponent: FC<{ + currentIndex: number; + currentDelay: number; +}> = ({ currentIndex, currentDelay }) => { + const t = useT(); + const { current, setInternalDelay, setGlobalDelay } = useLaunchStore( + useShallow((state) => ({ + current: state.current, + setGlobalDelay: state.setGlobalDelay, + setInternalDelay: state.setInternalDelay, + })) + ); + + const setDelay = useCallback( + (index: number) => (minutes: number) => { + if (current !== 'global') { + return setInternalDelay(current, index, minutes); + } + + return setGlobalDelay(index, minutes); + }, + [currentIndex, current] + ); + + return ( + <DelayIcon + // move it into the modal + onClick={() => setDelay(currentIndex)(100)} + data-tooltip-id="tooltip" + data-tooltip-content={ + !currentDelay + ? t('delay_comment', 'Delay comment') + : `Comment delayed by ${currentDelay} minutes` + } + className={clsx( + 'cursor-pointer', + currentDelay > 0 && 'bg-[#D82D7E] text-white rounded-full' + )} + /> + ); +}; diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 2842b3d0..0c56ed0e 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -63,7 +63,9 @@ import { ResetIcon, TrashIcon, EmojiIcon, + DelayIcon, } from '@gitroom/frontend/components/ui/icons'; +import { DelayComponent } from '@gitroom/frontend/components/new-launch/delay.component'; const InterceptBoldShortcut = Extension.create({ name: 'preventBoldWithUnderline', @@ -118,6 +120,8 @@ export const EditorWrapper: FC<{ deleteInternalValue, setGlobalValue, setInternalValue, + setInternalDelay, + setGlobalDelay, internalFromAll, totalChars, postComment, @@ -150,6 +154,8 @@ export const EditorWrapper: FC<{ deleteInternalValue: state.deleteInternalValue, setGlobalValue: state.setGlobalValue, setInternalValue: state.setInternalValue, + setGlobalDelay: state.setGlobalDelay, + setInternalDelay: state.setInternalDelay, totalChars: state.totalChars, appendInternalValueMedia: state.appendInternalValueMedia, appendGlobalValueMedia: state.appendGlobalValueMedia, @@ -191,6 +197,7 @@ export const EditorWrapper: FC<{ const newValue = value.map((p, index) => { return { id: makeId(10), + delay: 0, ...(items?.[index]?.media ? { media: items[index].media } : { media: [] }), @@ -275,7 +282,10 @@ export const EditorWrapper: FC<{ const goBackToGlobal = useCallback(async () => { if ( await deleteDialog( - t('are_you_sure_go_back_to_global_mode', 'This action is irreversible. Are you sure you want to go back to global mode?'), + t( + 'are_you_sure_go_back_to_global_mode', + 'This action is irreversible. Are you sure you want to go back to global mode?' + ), t('yes_go_back_to_global_mode', 'Yes, go back to global mode') ) ) { @@ -295,6 +305,7 @@ export const EditorWrapper: FC<{ if (internal) { return addInternalValue(index, current, [ { + delay: 0, content: '', id: makeId(10), media: [], @@ -304,6 +315,7 @@ export const EditorWrapper: FC<{ return addGlobalValue(index, [ { + delay: 0, content: '', id: makeId(10), media: [], @@ -317,7 +329,10 @@ export const EditorWrapper: FC<{ (index: number) => async () => { if ( !(await deleteDialog( - t('are_you_sure_delete_this_post', 'Are you sure you want to delete this post?'), + t( + 'are_you_sure_delete_this_post', + 'Are you sure you want to delete this post?' + ), t('yes_delete_it', 'Yes, delete it!') )) ) { @@ -358,7 +373,10 @@ export const EditorWrapper: FC<{ <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> </div> <div className="text-[14px] font-[600] text-white"> - {t('cant_edit_networks_when_creating_set', "You can't edit networks when creating a set")} + {t( + 'cant_edit_networks_when_creating_set', + "You can't edit networks when creating a set" + )} </div> </div> <div className="absolute w-full h-full left-0 top-0 bg-newBackdrop opacity-60 z-[100] rounded-[12px]" /> @@ -380,7 +398,10 @@ export const EditorWrapper: FC<{ <div className="w-[54px] h-[54px] rounded-full bg-newSettings opacity-80" /> </div> <div className="text-[14px] font-[600] text-white"> - {t('click_to_exit_global_editing', 'Click this button to exit global editing and customize the post for this channel')} + {t( + 'click_to_exit_global_editing', + 'Click this button to exit global editing and customize the post for this channel' + )} </div> <div> <div className="text-white rounded-[8px] h-[44px] px-[20px] bg-[#D82D7E] cursor-pointer flex justify-center items-center"> @@ -430,7 +451,7 @@ export const EditorWrapper: FC<{ chars={chars} childButton={ <> - {((canEdit && items.length - 1 === index) || !comments) ? ( + {(canEdit && items.length - 1 === index) || !comments ? ( <div className="flex items-center"> <div className="flex-1"> {comments && ( @@ -449,7 +470,10 @@ export const EditorWrapper: FC<{ <div className="flex gap-[6px] items-center"> <div className="w-[8px] h-[8px] rounded-full bg-[#FC69FF]" /> <div className="text-[14px] font-[600]"> - {t('editing_a_specific_network', 'Editing a Specific Network')} + {t( + 'editing_a_specific_network', + 'Editing a Specific Network' + )} </div> </div> <div className="flex gap-[6px] items-center"> @@ -479,10 +503,16 @@ export const EditorWrapper: FC<{ <TrashIcon onClick={deletePost(index)} data-tooltip-id="tooltip" - data-tooltip-content={t('delete_post_tooltip', 'Delete Post')} + data-tooltip-content={t( + 'delete_post_tooltip', + 'Delete Post' + )} className="cursor-pointer text-[#FF3F3F]" /> )} + {index > 0 && ( + <DelayComponent currentIndex={index} currentDelay={g.delay} /> + )} </div> )} </div> @@ -622,14 +652,14 @@ export const Editor: FC<{ > <div className="relative cursor-text flex flex-1 flex-col"> <div {...getRootProps()} className="flex flex-1 flex-col"> -<div - className={clsx( - 'absolute left-0 top-0 w-full h-full bg-black/70 z-[300] transition-all items-center justify-center flex text-white text-sm', - !isDragActive ? 'pointer-events-none opacity-0' : 'opacity-100' - )} - > - {t('drop_files_here_to_upload', 'Drop your files here to upload')} - </div> + <div + className={clsx( + 'absolute left-0 top-0 w-full h-full bg-black/70 z-[300] transition-all items-center justify-center flex text-white text-sm', + !isDragActive ? 'pointer-events-none opacity-0' : 'opacity-100' + )} + > + {t('drop_files_here_to_upload', 'Drop your files here to upload')} + </div> <div className="px-[10px] pt-[10px] bg-newBgColorInner rounded-t-[6px] relative z-[99]"> <OnlyEditor value={props.value} diff --git a/apps/frontend/src/components/new-launch/store.ts b/apps/frontend/src/components/new-launch/store.ts index 143a590c..e54585c2 100644 --- a/apps/frontend/src/components/new-launch/store.ts +++ b/apps/frontend/src/components/new-launch/store.ts @@ -11,10 +11,11 @@ import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; interface Values { id: string; content: string; + delay: number; media: { id: string; path: string; thumbnail?: string }[]; } -interface Internal { +export interface Internal { integration: Integrations; integrationValue: Values[]; } @@ -47,6 +48,12 @@ interface StoreState { global: Values[]; internal: Internal[]; addGlobalValue: (index: number, value: Values[]) => void; + setGlobalDelay: (index: number, minutes: number) => void; + setInternalDelay: ( + integrationId: string, + index: number, + minutes: number + ) => void; addInternalValue: ( index: number, integrationId: string, @@ -557,4 +564,23 @@ export const useLaunchStore = create<StoreState>()((set) => ({ set((state) => ({ comments, })), + setGlobalDelay: (index: number, minutes: number) => + set((state) => ({ + global: state.global.map((item, i) => + i === index ? { ...item, delay: minutes } : item + ), + })), + setInternalDelay: (integrationId: string, index: number, minutes: number) => + set((state) => ({ + internal: state.internal.map((item) => + item.integration.id === integrationId + ? { + ...item, + integrationValue: item.integrationValue.map((v, i) => + i === index ? { ...v, delay: minutes } : v + ), + } + : item + ), + })), })); diff --git a/apps/frontend/src/components/ui/icons/index.tsx b/apps/frontend/src/components/ui/icons/index.tsx index b43f7cd8..1e785024 100644 --- a/apps/frontend/src/components/ui/icons/index.tsx +++ b/apps/frontend/src/components/ui/icons/index.tsx @@ -164,6 +164,27 @@ export const TrashIcon: FC<IconProps> = ({ </svg> ); +export const DelayIcon: FC<IconProps> = ({ + size = 20, + className, + ...props +}) => ( + <svg + width={size} + height={size} + viewBox="0 0 32 32" + fill="red" + xmlns="http://www.w3.org/2000/svg" + className={className} + {...props} + > + <path + d="M16 3C13.4288 3 10.9154 3.76244 8.77759 5.1909C6.63975 6.61935 4.97351 8.64968 3.98957 11.0251C3.00563 13.4006 2.74819 16.0144 3.2498 18.5362C3.75141 21.0579 4.98953 23.3743 6.80762 25.1924C8.6257 27.0105 10.9421 28.2486 13.4638 28.7502C15.9856 29.2518 18.5995 28.9944 20.9749 28.0104C23.3503 27.0265 25.3807 25.3603 26.8091 23.2224C28.2376 21.0846 29 18.5712 29 16C28.9964 12.5533 27.6256 9.24882 25.1884 6.81163C22.7512 4.37445 19.4467 3.00364 16 3ZM16 27C13.8244 27 11.6977 26.3549 9.88873 25.1462C8.07979 23.9375 6.66989 22.2195 5.83733 20.2095C5.00477 18.1995 4.78693 15.9878 5.21137 13.854C5.63581 11.7202 6.68345 9.7602 8.22183 8.22183C9.76021 6.68345 11.7202 5.6358 13.854 5.21136C15.9878 4.78692 18.1995 5.00476 20.2095 5.83733C22.2195 6.66989 23.9375 8.07979 25.1462 9.88873C26.3549 11.6977 27 13.8244 27 16C26.9967 18.9164 25.8367 21.7123 23.7745 23.7745C21.7123 25.8367 18.9164 26.9967 16 27ZM24 16C24 16.2652 23.8946 16.5196 23.7071 16.7071C23.5196 16.8946 23.2652 17 23 17H16C15.7348 17 15.4804 16.8946 15.2929 16.7071C15.1054 16.5196 15 16.2652 15 16V9C15 8.73478 15.1054 8.48043 15.2929 8.29289C15.4804 8.10536 15.7348 8 16 8C16.2652 8 16.5196 8.10536 16.7071 8.29289C16.8946 8.48043 17 8.73478 17 9V15H23C23.2652 15 23.5196 15.1054 23.7071 15.2929C23.8946 15.4804 24 15.7348 24 16Z" + fill="currentColor" + /> + </svg> +); + // Dropdown Arrow (filled triangle) export const DropdownArrowIcon: FC<IconProps & { rotated?: boolean }> = ({ size = 20, diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index 4e86bbd7..0cad5c0d 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -1,666 +1,667 @@ { - "calendar": "Calendar", - "webhooks": "Webhooks", - "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks are a way to get notified when something happens in Postiz via\n an HTTP request.", - "name": "Name", - "url": "URL", - "edit": "Edit", - "delete": "Delete", - "add_a_webhook": "Add a webhook", - "save": "Save", - "send_test": "Send Test", - "select_role": "Select Role", - "video_made_with_ai": "Video made with AI", - "please_add_at_least": "Please add at least 20 characters", - "send_invitation_via_email": "Send invitation via email?", - "global_settings": "Global Settings", - "copy_id": "Copy Channel ID", - "team_members": "Team Members", - "invite_your_assistant_or_team_member_to_manage_your_account": "Invite your assistant or team member to manage your account", - "remove": "Remove", - "add_another_member": "Add another member", - "signatures": "Signatures", - "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "You can add signatures to your account to be used in your posts.", - "content": "Content", - "auto_add": "Auto Add?", - "actions": "Actions", - "use_signature": "Use Signature", - "add_a_signature": "Add a signature", - "no": "No", - "yes": "Yes", - "your_git_repository": "Your Git Repository", - "connect_your_github_repository_to_receive_updates_and_analytics": "Connect your GitHub repository to receive updates and analytics", - "connected": "Connected:", - "disconnect": "Disconnect", - "connect_your_repository": "Connect your repository", - "cancel": "Cancel", - "connect": "Connect", - "public_api": "Public API", - "check_n8n": "Check out our N8N custom node for Postiz.", - "use_postiz_api_to_integrate_with_your_tools": "Use Postiz API to integrate with your tools.", - "read_how_to_use_it_over_the_documentation": "Read how to use it over the documentation.", - "reveal": "Reveal", - "copy_key": "Copy Key", - "mcp": "MCP", - "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Connect Postiz MCP server to your client (Http streaming) to schedule your posts faster!", - "share_with_a_client": "Share with a client", - "post": "Post", - "comments": "Comments", - "user": "User", - "login_register_to_add_comments": "Login / Register to add comments", - "status": "Status:", - "there_are_not_plugs_matching_your_channels": "There are not plugs matching your channels", - "you_have_to_add_x_or_linkedin_or_threads": "You have to add: X or LinkedIn or Threads", - "go_to_the_calendar_to_add_channels": "Go to the calendar to add channels", - "channels": "Channels", - "activate": "Activate", - "this_channel_needs_to_be_refreshed": "This channel needs to be refreshed,", - "click_here_to_refresh": "click here to refresh", - "can_t_show_analytics_yet": "Can't show analytics yet", - "you_have_to_add_social_media_channels": "You have to add Social Media channels", - "supported": "Supported:", - "step": "STEP", - "skip_onboarding": "Skip onboarding", - "onboarding": "Onboarding", - "next": "Next", - "you_are_done_from_here_you_can": "You are done, from here you can:", - "view_analytics": "View Analytics", - "schedule_a_new_post": "Schedule a new post", - "to_sell_posts_you_would_have_to": "To sell posts you would have to:", - "1_connect_at_least_one_channel": "1. Connect at least one channel", - "2_connect_you_bank_account": "2. Connect you bank account", - "go_back_to_connect_channels": "Go back to connect channels", - "move_to_the_seller_page_to_connect_you_bank": "Move to the seller page to connect you bank", - "connect_channels": "Connect Channels", - "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Connect your social media and publishing websites channels to\n schedule posts later", - "social": "Social", - "publishing_platforms": "Publishing Platforms", - "no_channels": "No channels yet", - "connect_your_accounts": "Connect your social accounts to start scheduling, publishing, and analyzing — all in one place.", - "notifications": "Notifications", - "no_notifications": "No notifications", - "send_message": "Send Message", - "mar_28": "Mar 28", - "there_are_no_messages_yet": "There are no messages yet.", - "checkout_the_marketplace": "Checkout the Marketplace", - "go_to_marketplace": "Go to marketplace", - "all_messages": "All Messages", - "previous": "Previous", - "select_or_upload_pictures_maximum_5_at_a_time": "Select or upload pictures (maximum 5 at a time)", - "you_can_also_drag_drop_pictures": "You can also drag & drop pictures", - "you_don_t_have_any_assets_yet": "You don't have any assets yet.", - "click_the_button_below_to_upload_one": "Click the button below to upload one", - "click_the_button_below_to_upload_other": "Click the button below to upload multiple", - "add_selected_media": "Add selected media", - "insert_media": "Insert Media", - "design_media": "Design Media", - "select": "Select", - "editor": "Editor", - "clear": "Clear", - "order_completed": "Order completed", - "the_order_has_been_completed": "The order has been completed", - "post_has_been_published": "post has been published", - "url_1": "URL:", - "new_offer": "New Offer", - "platform": "Platform", - "posts": "Posts", - "pay_accept_offer": "Pay & Accept Offer", - "accepted": "Accepted", - "post_draft": "Post Draft", - "revision_needed": "Revision Needed", - "approve": "Approve", - "preview": "Preview", - "revision_requested": "Revision Requested", - "accepted_1": "ACCEPTED", - "cancelled_by_the_seller": "Cancelled by the seller", - "please_select_your_country_where_your_business_is": "Please select your country where your business is.", - "select_country": "--SELECT COUNTRY--", - "connect_bank_account": "Connect Bank Account", - "seller_mode": "Seller Mode", - "active": "Active", - "details": "Details", - "audience_size": "Audience Size", - "add_another_platform": "Add another platform", - "send_an_offer_for": "Send an offer for $", - "complete_order_and_pay_early": "Complete order and pay early", - "order_in_progress": "Order in progress", - "create_a_new_offer": "Create a new offer", - "orders": "Orders", - "price": "Price", - "state": "State", - "showing": "Showing", - "to": "to", - "from": "from", - "results": "Results", - "content_writer": "Content Writer", - "influencer": "Influencer", - "request_service": "Request Service", - "the_marketplace_is_not_opened_yet": "The marketplace is not opened yet", - "check_again_soon": "Check again soon!", - "filter": "Filter", - "result": "Result", - "seller": "Seller", - "buyer": "Buyer", - "discord_support": "Discord Support", - "teams": "Teams", - "webhooks_1": "Webhooks", - "auto_post": "Auto Post", - "logout_from": "Logout from", - "join_10000_entrepreneurs_who_use_postiz": "Join 10,000+ Entrepreneurs Who Use Postiz", - "to_manage_all_your_social_media_channels": "To Manage All Your Social Media Channels", - "100_no_risk_trial": "100% no-risk trial", - "pay_nothing_for_the_first_7_days": "Pay nothing for the first 7 days", - "cancel_anytime_hassle_free": "Cancel anytime, hassle-free", - "add_free_subscription": "-- ADD FREE SUBSCRIPTION --", - "currently_impersonating": "Currently Impersonating", - "user_1": "user:", - "drag_n_drop_some_files_here": "Drag n drop some files here", - "add_time_slot": "Add Time Slot", - "add_slot": "Add Slot", - "cancel_publication": "Cancel publication", - "statistics": "Statistics", - "loading": "Loading", - "short_link": "Short Link", - "original_link": "Original Link", - "clicks": "Clicks", - "selected_customer": "Selected Customer", - "customer": "Customer:", - "repeat_post_every": "Repeat Post Every...", - "use_this_media": "Use this media", - "create_new_post": "Create Post", - "update_post": "Update Existing Post", - "merge_comments_into_one_post": "Merge comments into one post", - "accounts_that_will_engage": "Accounts that will engage:", - "day": "Day", - "week": "Week", - "month": "Month", - "remove_from_customer": "Remove from customer", - "show_more": "+ Show more", - "show_less": "- Show less", - "upload": "Upload", - "ai": "AI", - "add_channel": "Add Channel", - "add_platform": "Add platform", - "articles": "Articles", - "add_comment": "Add comment", - "add_post": "Add post in a thread", - "add_comment_or_post": "Add comment / post", - "you_are_in_global_editing_mode": "You are in global editing mode", - "the_post_should_be_at_least_6_characters_long": "The post should be at least 6 characters long", - "are_you_sure_you_want_to_delete_post": "Are you sure you want to delete this post?", - "post_deleted_successfully": "Post deleted successfully", - "delete_post": "Delete Post", - "save_as_draft": "Save as draft", - "post_now": "Post now", - "please_add": "Please add", - "to_your_telegram_group_channel_and_click_here": "to your\n telegram group / channel and click here:", - "connect_telegram": "Connect Telegram", - "please_add_the_following_command_in_your_chat": "Please add the following command in your chat:", - "copy": "Copy", - "settings": "Settings", - "integrations": "Integrations", - "add_integration": "Add Integration", - "you_are_now_editing_only": "You are now editing only", - "tag_a_company": "Tag a company", - "video_length_is_invalid_must_be_up_to": "Video length is invalid, must be up to", - "seconds": "seconds", - "this_feature_available_only_for_photos": "This feature available only for photos, it will add a default music that you can change later.", - "allow_user_to": "Allow User To:", - "your_video_will_be_labeled_promotional": "Your video will be labeled \"Promotional Content\".", - "this_cannot_be_changed_once_posted": "This cannot be changed once your video is posted.", - "turn_on_to_disclose_video_promotes": "Turn on to disclose that this video promotes goods or services in exchange for something of value. You video could promote yourself, a third party, or both.", - "you_are_promoting_yourself": "You are promoting yourself or your own brand.", - "this_video_will_be_classified_brand_organic": "This video will be classified as Brand Organic.", - "you_are_promoting_another_brand": "You are promoting another brand or a third party.", - "this_video_will_be_classified_branded_content": "This video will be classified as Branded Content.", - "by_posting_you_agree_to_tiktoks": "By posting, you agree to TikTok's", - "music_usage_confirmation": "Music Usage Confirmation", - "branded_content_policy": "Branded Content Policy", - "select_1": "--Select--", - "select_flair": "--Select Flair--", - "link": "Link", - "add_subreddit": "Add Subreddit", - "please_add_at_least_one_subreddit": "Please add at least one Subreddit", - "add_community": "Add Community", - "select_post_type": "Select Post Type...", - "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "We couldn't find any business connected to your LinkedIn Page.", - "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Please close this dialog, create a new page, and add a new channel again.", - "select_linkedin_page": "Select Linkedin Page:", - "we_couldn_t_find_any_business_connected_to_the_selected_pages": "We couldn't find any business connected to the selected pages.", - "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "We recommend you to connect all the pages and all the businesses.", - "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Please close this dialog, delete your integration and add a new channel\n again.", - "select_instagram_account": "Select Instagram Account:", - "select_page": "Select Page:", - "generate_image_with_ai": "Generate image with AI", - "reconnect_channel": "Reconnect channel", - "update_credentials": "Update Credentials", - "additional_settings": "Additional Settings", - "change_bot": "Change Bot", - "move_add_to_customer": "Move / add to customer", - "edit_time_slots": "Edit Time Slots", - "enable_channel": "Enable Channel", - "disable_channel": "Disable Channel", - "add": "Add", - "short_post": "Short post", - "long_post": "Long post", - "a_thread_with_short_posts": "A thread with short posts", - "a_thread_with_long_posts": "A thread with long posts", - "personal_voice_i_am_happy_to_announce": "Personal voice (\"I am happy to announce\")", - "company_voice_we_are_happy_to_announce": "Company voice (\"We are happy to announce\")", - "generate": "Generate", - "generate_posts": "Generate Posts", - "purchase_a_life_time_pro_account_with_sol_199": "Purchase a Life-time PRO account with SOL ($199), Please be advised that there is no refund for this purchase.", - "purchase_now": "Purchase now", - "pay_today": "Pay Today", - "we_are_sorry_to_see_you_go": "We are sorry to see you go :(", - "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Would you mind shortly tell us what we could have done better?", - "cancel_subscription": "Cancel Subscription", - "plans": "Plans", - "monthly": "MONTHLY", - "yearly": "YEARLY", - "reactivate_subscription": "Reactivate subscription", - "update_payment_method_invoices_history": "Update Payment Method / Invoices History", - "cancel_subscription_1": "Cancel subscription", - "your_subscription_will_be_canceled_at": "Your subscription will be canceled at", - "you_will_never_be_charged_again": "You will never be charged again", - "current_package": "Current Package:", - "next_package": "Next Package:", - "claim": "Claim", - "frequently_asked_questions": "Frequently Asked Questions", - "autopost": "Autopost", - "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Autopost can automatically posts your RSS new items to social media", - "title": "Title", - "add_an_autopost": "Add an autopost", - "post_content": "Post content", - "sign_up": "Sign Up", - "or": "OR", - "by_registering_you_agree_to_our": "By registering you agree to our", - "and": "and", - "terms_of_service": "Terms of Service", - "privacy_policy": "Privacy Policy", - "create_account": "Create Account", - "already_have_an_account": "Already Have An Account?", - "sign_in": "Sign In", - "sign_in_1": "Sign in", - "don_t_have_an_account": "Don't Have An Account?", - "forgot_password": "Forgot password", - "forgot_password_1": "Forgot Password", - "send_password_reset_email": "Send Password Reset Email", - "go_back_to_login": "Go back to login", - "we_have_send_you_an_email_with_a_link_to_reset_your_password": "We have send you an email with a link to reset your password.", - "change_password": "Change Password", - "we_successfully_reset_your_password_you_can_now_login_with_your": "We successfully reset your password. You can now login with your", - "click_here_to_go_back_to_login": "Click here to go back to login", - "activate_your_account": "Activate your account", - "thank_you_for_registering": "Thank you for registering!", - "please_check_your_email_to_activate_your_account": "Please check your email to activate your account.", - "sign_in_with": "Sign in with", - "continue_with_google": "Continue with Google", - "sign_in_with_github": "Sign in with GitHub", - "continue_with_farcaster": "Continue with Farcaster", - "continue_with_your_wallet": "Continue with your Wallet", - "stars_per_day": "Stars per day", - "media": "Media", - "check_launch": "Check Launch", - "load_your_github_repository_from_settings_to_see_analytics": "Load your GitHub repository from settings to see analytics", - "stars": "Stars", - "processing_stars": "Processing stars...", - "forks": "Forks", - "registration_is_disabled": "Registration is disabled", - "login_instead": "Login instead", - "gitroom": "Gitroom", - "select_a_conversation_and_chat_away": "Select a conversation and chat away.", - "adding_channel_redirecting_you": "Adding channel, Redirecting You", - "could_not_add_provider": "Could not add provider.", - "you_are_being_redirected_back": "You are being redirected back", - "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "We are experiencing some difficulty, try to refresh the page", - "post_not_found": "Post not found", - "publication_date": "Publication Date:", - "analytics": "Analytics", - "launches": "Launches", - "plugs": "Plugs", - "billing": "Billing", - "affiliate": "Affiliate", - "monday": "Monday", - "tuesday": "Tuesday", - "wednesday": "Wednesday", - "thursday": "Thursday", - "friday": "Friday", - "saturday": "Saturday", - "sunday": "Sunday", - "can_t_change_date_remove_post_from_publication": "Can't change date, remove post from publication", - "predicted_github_trending_change": "Predicted GitHub Trending Change", - "duplicate_post": "Duplicate Post", - "preview_post": "Preview Post", - "post_statistics": "Post Statistics", - "draft": "Draft", - "week_number": "Week {{number}}", - "top_title_edit_webhook": "Edit webhook", - "top_title_add_webhook": "Add webhook", - "top_title_oh_no": "Oh no", - "top_title_auto_plug": "Auto Plug: {{title}}", - "top_title_edit_autopost": "Edit autopost", - "top_title_add_autopost": "Add autopost", - "top_title_send_a_new_offer": "Send a new offer", - "top_title_media_library": "Media Library", - "top_title_add_signature": "Add signature", - "top_title_send_a_message_to": "Send a message to {{name}}", - "top_title_configure_provider": "Configure Provider", - "top_title_add_member": "Add Member", - "top_title_change_bot_picture": "Change Bot Picture", - "top_title_create_a_new_tag": "Create a new tag", - "top_title_select_company": "Select Company", - "top_title_additional_settings": "Additional Settings", - "top_title_time_table_slots": "Time Table Slots", - "top_title_design_media": "Design Media", - "top_title_edit_post": "Edit Post", - "top_title_create_post": "Create Post", - "top_title_move__add_to_customer": "Move / Add to customer", - "top_title_add_api_key_for": "Add API key for {{name}}", - "top_title_instance_url": "Instance URL", - "top_title_custom_url": "Custom URL", - "top_title_add_channel": "Add Channel", - "top_title_add_telegram": "Add Telegram", - "top_title_add_wrapcast": "Add Wrapcast", - "top_title_comments_for": "Comments for {{date}}", - "top_title_edit_signature": "Edit Signature", - "label_name": "Name", - "label_url": "URL", - "label_title": "Title", - "label_subtitle": "Subtitle", - "label_email": "Email", - "label_full_name": "Full Name", - "label_password": "Password", - "label_confirm_password": "Confirm Password", - "label_api_key": "API Key", - "label_instance_url": "Instance URL", - "label_custom_url": "Custom URL", - "label_feedback": "Feedback", - "label_bio": "Bio", - "label_role": "Role", - "label_country": "Country", - "label_audience_size": "Audience size on all platforms", - "label_pick_time": "Pick time", - "label_nickname": "Nickname", - "label_write_anything": "Write anything", - "label_output_format": "Output format", - "label_add_pictures": "Add pictures?", - "label_hour": "Hour", - "label_minutes": "Minutes", - "label_select_publication": "Select publication", - "label_canonical_link": "Canonical Link", - "label_cover_picture": "Cover picture", - "label_tags": "Tags", - "label_topics": "Topics", - "label_tags_maximum_4": "Tags (Maximum 4)", - "label_attachments": "Attachments", - "label_type": "Type", - "label_thumbnail": "Thumbnail", - "label_who_can_see_this_video": "Who can see this video?", - "label_content_posting_method": "Content posting method", - "label_auto_add_music": "Auto add music", - "label_duet": "Duet", - "label_stitch": "Stitch", - "label_comments": "Comments", - "label_disclose_video_content": "Disclose Video Content", - "label_your_brand": "Your brand", - "label_branded_content": "Branded content", - "label_subreddit": "Subreddit", - "label_flair": "Flair", - "label_media": "Media", - "label_search_subreddit": "Search Subreddit", - "label_delay": "Delay", - "label_post_type": "Post Type", - "label_collaborators": "Collaborators (max 3) - accounts can't be private", - "label_community": "Community", - "label_search_community": "Search Community", - "label_channel": "Channel", - "label_search_channel": "Search Channel", - "label_select_channel": "Select Channel", - "label_new_password": "New Password", - "label_repeat_password": "Repeat Password", - "label_platform": "Platform", - "label_price_per_post": "Price per post", - "label_integrations": "Integrations", - "label_code": "Code", - "label_should_sync_last_post": "Should we sync the current last post?", - "label_when_post": "When should we post it?", - "label_autogenerate_content": "Autogenerate content", - "label_generate_picture": "Generate Picture?", - "label_company": "Company", - "label_tag_color": "Tag Color", - "label_select_board": "Select board", - "label_select_organization": "Select organization", - "label_auto_add_signature": "Auto add signature?", - "enable_color_picker": "Enable color picker", - "cancel_the_color_picker": "Cancel the color picker", - "no_content_yet": "No Content Yet", - "write_your_reply": "Write your post...", - "add_a_tag": "Add a tag", - "add_to_calendar": "Add to Calendar", - "select_channels_from_circles": "Select channels from the circles above", - "not_matching_order": "Not matching order", - "submit_for_order": "Submit for order", - "schedule": "Schedule", - "update": "Update", - "attachments": "Attachments", - "tags": "Tags", - "public_to_everyone": "Public to everyone", - "mutual_follow_friends": "Mutual follow friends", - "follower_of_creator": "Follower of creator", - "self_only": "Self only", - "post_content_directly_to_tiktok": "Post content directly to TikTok", - "upload_content_to_tiktok_without_posting": "Upload content to TikTok without posting it", - "choose_upload_without_posting_description": "Choose upload without posting if you want to review and edit your content within TikTok's app before publishing. This gives you access to TikTok's built-in editing tools and lets you make final adjustments before posting.", - "faq_am_i_going_to_be_charged_by_postiz": "Am I going to be charged by Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "To confirm credit card information Postiz will hold $2 and release it immediately", - "faq_can_i_trust_postiz_gitroom": "Can I trust Postiz?", - "faq_postiz_gitroom_is_proudly_open_source": "Postiz is proudly open-source! We believe in an ethical and transparent culture, meaning that Postiz will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">click here</a>.", - "faq_what_are_channels": "What are channels?", - "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz allows you to schedule your posts between different channels.\nA channel is a publishing platform where you can schedule your posts.\nFor example, you can schedule your posts on X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads and Pinterest.", - "faq_what_are_team_members": "What are team members?", - "faq_if_you_have_a_team_with_multiple_members": "If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels", - "faq_what_is_ai_auto_complete": "What is AI auto-complete?", - "faq_we_automate_chatgpt_to_help_you_write": "We automate ChatGPT to help you write social posts and articles.", - "enter_email": "Enter email", - "are_you_sure": "Are you sure?", - "yes_delete_it": "Yes, delete it!", - "no_cancel": "No, cancel!", - "are_you_sure_you_want_to_delete": "Are you sure you want to delete {{name}}?", - "are_you_sure_you_want_to_delete_the_image": "Are you sure you want to delete the image?", - "are_you_sure_you_want_to_logout": "Are you sure you want to logout?", - "yes_logout": "Yes logout", - "are_you_sure_you_want_to_delete_this_slot": "Are you sure you want to delete this slot?", - "are_you_sure_you_want_to_delete_this_subreddit": "Are you sure you want to delete this Subreddit?", - "are_you_sure_you_want_to_close_the_window": "Are you sure you want to close the window?", - "yes_close": "Yes, close", - "link_copied_to_clipboard": "Link copied to clipboard", - "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Are you sure you want to close this modal? (all data will be lost)", - "yes_close_it": "Yes, close it!", - "uploading_pictures": "Uploading pictures...", - "agent_starting": "Agent starting", - "researching_your_content": "Researching your content...", - "understanding_the_category": "Understanding the category...", - "finding_the_topic": "Finding the topic...", - "finding_popular_posts_to_match_with": "Finding popular posts to match with...", - "generating_hook": "Generating hook...", - "generating_content": "Generating content...", - "generating_pictures": "Generating pictures...", - "finding_time_to_post": "Finding time to post...", - "write_anything": "Write anything", - "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "You can write anything you want, and also add links, we will do the research for you...", - "output_format": "Output format", - "add_pictures": "Add pictures?", - "7_days": "7 Days", - "30_days": "30 Days", - "90_days": "90 Days", - "start_7_days_free_trial": "Start 7 days free trial", - "change_language": "Change Language", - "that_a_wrap": "That's a wrap!\n\nIf you enjoyed this thread:\n\n1. Follow me @{{username}} for more of these\n2. RT the tweet below to share this thread with your audience\n", - "post_as_images_carousel": "Post as images carousel", - "save_set": "Save Set", - "separate_post": "Separate post to multiple posts", - "label_who_can_reply_to_this_post": "Who can reply to this post?", - "delete_integration": "Delete Integration", - "start_writing_your_post": "Start writing your post for a preview", - "billing_join_over": "Join Over", - "billing_entrepreneurs_count": "18,000+ Entrepreneurs", - "billing_who_use": "who use", - "billing_postiz_grow_social": "Postiz To Grow Their Social Presence", - "billing_no_risk_trial": "100% No-Risk Free Trial", - "billing_pay_nothing_7_days": "Pay NOTHING for the first 7-days", - "billing_cancel_anytime": "Cancel anytime, hassle-free", - "billing_choose_plan": "Choose a Plan", - "billing_monthly": "Monthly", - "billing_yearly": "Yearly", - "billing_20_percent_off": "20% Off", - "billing_features": "Features", - "billing_channel": "channel", - "billing_channels": "channels", - "billing_unlimited": "Unlimited", - "billing_posts_per_month": "posts per month", - "billing_unlimited_team_members": "Unlimited team members", - "billing_ai_auto_complete": "AI auto-complete", - "billing_ai_copilots": "AI copilots", - "billing_ai_autocomplete": "AI Autocomplete", - "billing_advanced_picture_editor": "Advanced Picture Editor", - "billing_ai_images_per_month": "AI Images per month", - "billing_ai_videos_per_month": "AI Videos per month", - "billing_billing_address": "Billing Address", - "billing_payment": "Payment", - "billing_powered_by_stripe": "Secure payments processed by", - "billing_your_7_day_trial_is": "Your 7-day trial is", - "billing_100_percent_free": "100% free", - "billing_ending": "ending", - "billing_cancel_anytime_short": "Cancel anytime.", - "billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!", - "billing_pay_now": "Pay Now", - "billing_per_month": "/ month", - "select_channels": "Select Channels", - "start_a_new_chat": "Start a new chat", - "your_assistant": "Your Assistant", - "agent_welcome_message": "Hello, I am your Postiz agent 🙌🏻.\n\nI can schedule a post or multiple posts to multiple channels and generate pictures and videos.\n\nYou can select the channels you want to use from the left menu.\n\nYou can see your previous conversations from the right menu.\n\nYou can also use me as an MCP Server, check Settings >> Public API", - "last_github_trending": "Last Github Trending", - "next_predicted_github_trending": "Next Predicted GitHub Trending", - "repository": "Repository", - "date": "Date", - "total_stars": "Total Stars", - "total_forks": "Total Forks", - "continue_with": "Continue With", - "email_address": "Email Address", - "label_company": "Company", - "email_already_exists": "Email already exists", - "google": "Google", - "farcaster": "Farcaster", - "edit_autopost": "Edit Autopost", - "add_autopost_title": "Add Autopost", - "webhook_deleted_successfully": "Webhook deleted successfully", - "all_integrations": "All integrations", - "specific_integrations": "Specific integrations", - "post_on_next_available_slot": "Post on the next available slot", - "post_immediately": "Post Immediately", - "could_not_use_rss_feed": "Could not use this RSS feed", - "rss_valid": "RSS valid!", - "autopost_updated_successfully": "Autopost updated successfully", - "autopost_added_successfully": "Autopost added successfully", - "write_your_post_placeholder": "Write your post...", - "select_or_upload_pictures_max_5": "Select or upload pictures (maximum 5 at a time).", - "you_can_drag_drop_pictures": "You can also drag & drop pictures.", - "you_dont_have_any_media_yet": "You don't have any media yet", - "media_library": "Media Library", - "media_settings": "Media Settings", - "media_editor": "Media Editor", - "close": "Close", - "me": "Me", - "noname": "Noname", - "password_reset_link_expired": "Your password reset link has expired. Please try again.", - "invalid_api_key": "Invalid API key", - "could_not_connect_to_platform": "Could not connect to the platform", - "web3_provider": "Web3 provider", - "add_provider_title": "Add Provider", - "profile_updated": "Profile updated", - "sets": "Sets", - "email_address": "Email Address", - "invitation_link_sent": "Invitation link sent", - "send_invitation_link": "Send Invitation Link", - "copy_link": "Copy Link", - "are_you_sure_remove_team_member": "Are you sure you want to remove this team member?", - "admin": "Admin", - "super_admin": "Super Admin", - "update_webhook": "Update webhook", - "add_webhook": "Add webhook", - "webhook_updated_successfully": "Webhook updated successfully", - "webhook_added_successfully": "Webhook added successfully", - "webhook_sent": "Webhook send", - "today": "Today", - "channel_disconnected_click_to_reconnect": "Channel disconnected, click to reconnect.", - "channel_disabled_upgrade_plan": "This channel is disabled, please upgrade your plan to enable it.", - "channel_added": "Channel added", - "are_you_sure_disable_channel": "Are you sure you want to disable this channel?", - "disable_channel_title": "Disable Channel", - "channel_disabled": "Channel Disabled", - "are_you_sure_delete_channel": "Are you sure you want to delete this channel?", - "delete_channel_title": "Delete Channel", - "delete_posts_before_channel": "You have to delete all the posts associated with this channel before deleting it", - "channel_deleted": "Channel Deleted", - "channel_enabled": "Channel Enabled", - "time_table_slots": "Time Table Slots", - "channel_id_copied": "Channel ID copied to clipboard", - "settings_updated": "Settings Updated", - "customer_updated": "Customer Updated", - "custom_url": "Custom URL", - "picture": "Picture", - "upgrade_required": "You need to upgrade to use this feature", - "move_to_billing": "Move to billing", - "payment_required": "Payment Required", - "no_content": "no content", - "select_customer_tooltip": "Select Customer", - "customers": "Customers", - "hour": "Hour", - "minutes": "Minutes", - "updated": "Updated", - "change_bot_picture_title": "Change Bot Picture", - "select_customer_label": "Select Customer", - "start_typing": "Start typing...", - "choose_set_or_continue": "Choose a set or continue without one", - "continue_without_set": "Continue without set", - "select_set": "Select a Set", - "channel_settings": "Settings", - "post_needs_content_or_image": "Your post should have at least one character or one image.", - "please_fix_your_settings": "Please fix your settings", - "shortlink_urls_question": "Do you want to shortlink the URLs? it will let you get statistics over clicks", - "yes_shortlink_it": "Yes, shortlink it!", - "added_successfully": "Added successfully", - "updated_successfully": "Updated successfully", - "create_post_title": "Create Post", - "post_preview": "Post Preview", - "check_circles_above": "Check the circles above", - "create_output": "Create output", - "assistant_initial_message": "Hi! I can help you to refine your social media posts.", - "no_longer_global_mode": "No longer in global mode", - "two_days": "Two Days", - "three_days": "Three Days", - "four_days": "Four Days", - "five_days": "Five Days", - "six_days": "Six Days", - "two_weeks": "Two Weeks", - "repeat_post_every_label": "Repeat Post Every", - "add_new_tag": "Add New Tag", - "tag_name": "Name", - "post_is_too_long": "post is too long, please fix it", - "your_post_should_have_at_least_one_character_or_one_image": "Your post should have at least one character or one image.", - "internal_edit": "Internal Edit", - "are_you_sure_go_back_to_global_mode": "This action is irreversible. Are you sure you want to go back to global mode?", - "yes_go_back_to_global_mode": "Yes, go back to global mode", - "are_you_sure_delete_this_post": "Are you sure you want to delete this post?", - "yes_delete_it": "Yes, delete it!", - "cant_edit_networks_when_creating_set": "You can't edit networks when creating a set", - "click_to_exit_global_editing": "Click this button to exit global editing and customize the post for this channel", - "edit_content": "Edit content", - "editing_a_specific_network": "Editing a Specific Network", - "back_to_global": "Back to global", - "delete_post_tooltip": "Delete Post", - "drop_files_here_to_upload": "Drop your files here to upload", - "insert_emoji": "Insert Emoji", - "write_something": "Write something …" + "calendar": "Calendar", + "webhooks": "Webhooks", + "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks are a way to get notified when something happens in Postiz via\n an HTTP request.", + "name": "Name", + "url": "URL", + "edit": "Edit", + "delete": "Delete", + "add_a_webhook": "Add a webhook", + "save": "Save", + "send_test": "Send Test", + "select_role": "Select Role", + "video_made_with_ai": "Video made with AI", + "please_add_at_least": "Please add at least 20 characters", + "send_invitation_via_email": "Send invitation via email?", + "global_settings": "Global Settings", + "copy_id": "Copy Channel ID", + "team_members": "Team Members", + "invite_your_assistant_or_team_member_to_manage_your_account": "Invite your assistant or team member to manage your account", + "remove": "Remove", + "add_another_member": "Add another member", + "signatures": "Signatures", + "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "You can add signatures to your account to be used in your posts.", + "content": "Content", + "auto_add": "Auto Add?", + "delay_comment": "Delay comment", + "actions": "Actions", + "use_signature": "Use Signature", + "add_a_signature": "Add a signature", + "no": "No", + "yes": "Yes", + "your_git_repository": "Your Git Repository", + "connect_your_github_repository_to_receive_updates_and_analytics": "Connect your GitHub repository to receive updates and analytics", + "connected": "Connected:", + "disconnect": "Disconnect", + "connect_your_repository": "Connect your repository", + "cancel": "Cancel", + "connect": "Connect", + "public_api": "Public API", + "check_n8n": "Check out our N8N custom node for Postiz.", + "use_postiz_api_to_integrate_with_your_tools": "Use Postiz API to integrate with your tools.", + "read_how_to_use_it_over_the_documentation": "Read how to use it over the documentation.", + "reveal": "Reveal", + "copy_key": "Copy Key", + "mcp": "MCP", + "connect_your_mcp_client_to_postiz_to_schedule_your_posts_faster": "Connect Postiz MCP server to your client (Http streaming) to schedule your posts faster!", + "share_with_a_client": "Share with a client", + "post": "Post", + "comments": "Comments", + "user": "User", + "login_register_to_add_comments": "Login / Register to add comments", + "status": "Status:", + "there_are_not_plugs_matching_your_channels": "There are not plugs matching your channels", + "you_have_to_add_x_or_linkedin_or_threads": "You have to add: X or LinkedIn or Threads", + "go_to_the_calendar_to_add_channels": "Go to the calendar to add channels", + "channels": "Channels", + "activate": "Activate", + "this_channel_needs_to_be_refreshed": "This channel needs to be refreshed,", + "click_here_to_refresh": "click here to refresh", + "can_t_show_analytics_yet": "Can't show analytics yet", + "you_have_to_add_social_media_channels": "You have to add Social Media channels", + "supported": "Supported:", + "step": "STEP", + "skip_onboarding": "Skip onboarding", + "onboarding": "Onboarding", + "next": "Next", + "you_are_done_from_here_you_can": "You are done, from here you can:", + "view_analytics": "View Analytics", + "schedule_a_new_post": "Schedule a new post", + "to_sell_posts_you_would_have_to": "To sell posts you would have to:", + "1_connect_at_least_one_channel": "1. Connect at least one channel", + "2_connect_you_bank_account": "2. Connect you bank account", + "go_back_to_connect_channels": "Go back to connect channels", + "move_to_the_seller_page_to_connect_you_bank": "Move to the seller page to connect you bank", + "connect_channels": "Connect Channels", + "connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later": "Connect your social media and publishing websites channels to\n schedule posts later", + "social": "Social", + "publishing_platforms": "Publishing Platforms", + "no_channels": "No channels yet", + "connect_your_accounts": "Connect your social accounts to start scheduling, publishing, and analyzing — all in one place.", + "notifications": "Notifications", + "no_notifications": "No notifications", + "send_message": "Send Message", + "mar_28": "Mar 28", + "there_are_no_messages_yet": "There are no messages yet.", + "checkout_the_marketplace": "Checkout the Marketplace", + "go_to_marketplace": "Go to marketplace", + "all_messages": "All Messages", + "previous": "Previous", + "select_or_upload_pictures_maximum_5_at_a_time": "Select or upload pictures (maximum 5 at a time)", + "you_can_also_drag_drop_pictures": "You can also drag & drop pictures", + "you_don_t_have_any_assets_yet": "You don't have any assets yet.", + "click_the_button_below_to_upload_one": "Click the button below to upload one", + "click_the_button_below_to_upload_other": "Click the button below to upload multiple", + "add_selected_media": "Add selected media", + "insert_media": "Insert Media", + "design_media": "Design Media", + "select": "Select", + "editor": "Editor", + "clear": "Clear", + "order_completed": "Order completed", + "the_order_has_been_completed": "The order has been completed", + "post_has_been_published": "post has been published", + "url_1": "URL:", + "new_offer": "New Offer", + "platform": "Platform", + "posts": "Posts", + "pay_accept_offer": "Pay & Accept Offer", + "accepted": "Accepted", + "post_draft": "Post Draft", + "revision_needed": "Revision Needed", + "approve": "Approve", + "preview": "Preview", + "revision_requested": "Revision Requested", + "accepted_1": "ACCEPTED", + "cancelled_by_the_seller": "Cancelled by the seller", + "please_select_your_country_where_your_business_is": "Please select your country where your business is.", + "select_country": "--SELECT COUNTRY--", + "connect_bank_account": "Connect Bank Account", + "seller_mode": "Seller Mode", + "active": "Active", + "details": "Details", + "audience_size": "Audience Size", + "add_another_platform": "Add another platform", + "send_an_offer_for": "Send an offer for $", + "complete_order_and_pay_early": "Complete order and pay early", + "order_in_progress": "Order in progress", + "create_a_new_offer": "Create a new offer", + "orders": "Orders", + "price": "Price", + "state": "State", + "showing": "Showing", + "to": "to", + "from": "from", + "results": "Results", + "content_writer": "Content Writer", + "influencer": "Influencer", + "request_service": "Request Service", + "the_marketplace_is_not_opened_yet": "The marketplace is not opened yet", + "check_again_soon": "Check again soon!", + "filter": "Filter", + "result": "Result", + "seller": "Seller", + "buyer": "Buyer", + "discord_support": "Discord Support", + "teams": "Teams", + "webhooks_1": "Webhooks", + "auto_post": "Auto Post", + "logout_from": "Logout from", + "join_10000_entrepreneurs_who_use_postiz": "Join 10,000+ Entrepreneurs Who Use Postiz", + "to_manage_all_your_social_media_channels": "To Manage All Your Social Media Channels", + "100_no_risk_trial": "100% no-risk trial", + "pay_nothing_for_the_first_7_days": "Pay nothing for the first 7 days", + "cancel_anytime_hassle_free": "Cancel anytime, hassle-free", + "add_free_subscription": "-- ADD FREE SUBSCRIPTION --", + "currently_impersonating": "Currently Impersonating", + "user_1": "user:", + "drag_n_drop_some_files_here": "Drag n drop some files here", + "add_time_slot": "Add Time Slot", + "add_slot": "Add Slot", + "cancel_publication": "Cancel publication", + "statistics": "Statistics", + "loading": "Loading", + "short_link": "Short Link", + "original_link": "Original Link", + "clicks": "Clicks", + "selected_customer": "Selected Customer", + "customer": "Customer:", + "repeat_post_every": "Repeat Post Every...", + "use_this_media": "Use this media", + "create_new_post": "Create Post", + "update_post": "Update Existing Post", + "merge_comments_into_one_post": "Merge comments into one post", + "accounts_that_will_engage": "Accounts that will engage:", + "day": "Day", + "week": "Week", + "month": "Month", + "remove_from_customer": "Remove from customer", + "show_more": "+ Show more", + "show_less": "- Show less", + "upload": "Upload", + "ai": "AI", + "add_channel": "Add Channel", + "add_platform": "Add platform", + "articles": "Articles", + "add_comment": "Add comment", + "add_post": "Add post in a thread", + "add_comment_or_post": "Add comment / post", + "you_are_in_global_editing_mode": "You are in global editing mode", + "the_post_should_be_at_least_6_characters_long": "The post should be at least 6 characters long", + "are_you_sure_you_want_to_delete_post": "Are you sure you want to delete this post?", + "post_deleted_successfully": "Post deleted successfully", + "delete_post": "Delete Post", + "save_as_draft": "Save as draft", + "post_now": "Post now", + "please_add": "Please add", + "to_your_telegram_group_channel_and_click_here": "to your\n telegram group / channel and click here:", + "connect_telegram": "Connect Telegram", + "please_add_the_following_command_in_your_chat": "Please add the following command in your chat:", + "copy": "Copy", + "settings": "Settings", + "integrations": "Integrations", + "add_integration": "Add Integration", + "you_are_now_editing_only": "You are now editing only", + "tag_a_company": "Tag a company", + "video_length_is_invalid_must_be_up_to": "Video length is invalid, must be up to", + "seconds": "seconds", + "this_feature_available_only_for_photos": "This feature available only for photos, it will add a default music that you can change later.", + "allow_user_to": "Allow User To:", + "your_video_will_be_labeled_promotional": "Your video will be labeled \"Promotional Content\".", + "this_cannot_be_changed_once_posted": "This cannot be changed once your video is posted.", + "turn_on_to_disclose_video_promotes": "Turn on to disclose that this video promotes goods or services in exchange for something of value. You video could promote yourself, a third party, or both.", + "you_are_promoting_yourself": "You are promoting yourself or your own brand.", + "this_video_will_be_classified_brand_organic": "This video will be classified as Brand Organic.", + "you_are_promoting_another_brand": "You are promoting another brand or a third party.", + "this_video_will_be_classified_branded_content": "This video will be classified as Branded Content.", + "by_posting_you_agree_to_tiktoks": "By posting, you agree to TikTok's", + "music_usage_confirmation": "Music Usage Confirmation", + "branded_content_policy": "Branded Content Policy", + "select_1": "--Select--", + "select_flair": "--Select Flair--", + "link": "Link", + "add_subreddit": "Add Subreddit", + "please_add_at_least_one_subreddit": "Please add at least one Subreddit", + "add_community": "Add Community", + "select_post_type": "Select Post Type...", + "we_couldn_t_find_any_business_connected_to_your_linkedin_page": "We couldn't find any business connected to your LinkedIn Page.", + "please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again": "Please close this dialog, create a new page, and add a new channel again.", + "select_linkedin_page": "Select Linkedin Page:", + "we_couldn_t_find_any_business_connected_to_the_selected_pages": "We couldn't find any business connected to the selected pages.", + "we_recommend_you_to_connect_all_the_pages_and_all_the_businesses": "We recommend you to connect all the pages and all the businesses.", + "please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again": "Please close this dialog, delete your integration and add a new channel\n again.", + "select_instagram_account": "Select Instagram Account:", + "select_page": "Select Page:", + "generate_image_with_ai": "Generate image with AI", + "reconnect_channel": "Reconnect channel", + "update_credentials": "Update Credentials", + "additional_settings": "Additional Settings", + "change_bot": "Change Bot", + "move_add_to_customer": "Move / add to customer", + "edit_time_slots": "Edit Time Slots", + "enable_channel": "Enable Channel", + "disable_channel": "Disable Channel", + "add": "Add", + "short_post": "Short post", + "long_post": "Long post", + "a_thread_with_short_posts": "A thread with short posts", + "a_thread_with_long_posts": "A thread with long posts", + "personal_voice_i_am_happy_to_announce": "Personal voice (\"I am happy to announce\")", + "company_voice_we_are_happy_to_announce": "Company voice (\"We are happy to announce\")", + "generate": "Generate", + "generate_posts": "Generate Posts", + "purchase_a_life_time_pro_account_with_sol_199": "Purchase a Life-time PRO account with SOL ($199), Please be advised that there is no refund for this purchase.", + "purchase_now": "Purchase now", + "pay_today": "Pay Today", + "we_are_sorry_to_see_you_go": "We are sorry to see you go :(", + "would_you_mind_shortly_tell_us_what_we_could_have_done_better": "Would you mind shortly tell us what we could have done better?", + "cancel_subscription": "Cancel Subscription", + "plans": "Plans", + "monthly": "MONTHLY", + "yearly": "YEARLY", + "reactivate_subscription": "Reactivate subscription", + "update_payment_method_invoices_history": "Update Payment Method / Invoices History", + "cancel_subscription_1": "Cancel subscription", + "your_subscription_will_be_canceled_at": "Your subscription will be canceled at", + "you_will_never_be_charged_again": "You will never be charged again", + "current_package": "Current Package:", + "next_package": "Next Package:", + "claim": "Claim", + "frequently_asked_questions": "Frequently Asked Questions", + "autopost": "Autopost", + "autopost_can_automatically_posts_your_rss_new_items_to_social_media": "Autopost can automatically posts your RSS new items to social media", + "title": "Title", + "add_an_autopost": "Add an autopost", + "post_content": "Post content", + "sign_up": "Sign Up", + "or": "OR", + "by_registering_you_agree_to_our": "By registering you agree to our", + "and": "and", + "terms_of_service": "Terms of Service", + "privacy_policy": "Privacy Policy", + "create_account": "Create Account", + "already_have_an_account": "Already Have An Account?", + "sign_in": "Sign In", + "sign_in_1": "Sign in", + "don_t_have_an_account": "Don't Have An Account?", + "forgot_password": "Forgot password", + "forgot_password_1": "Forgot Password", + "send_password_reset_email": "Send Password Reset Email", + "go_back_to_login": "Go back to login", + "we_have_send_you_an_email_with_a_link_to_reset_your_password": "We have send you an email with a link to reset your password.", + "change_password": "Change Password", + "we_successfully_reset_your_password_you_can_now_login_with_your": "We successfully reset your password. You can now login with your", + "click_here_to_go_back_to_login": "Click here to go back to login", + "activate_your_account": "Activate your account", + "thank_you_for_registering": "Thank you for registering!", + "please_check_your_email_to_activate_your_account": "Please check your email to activate your account.", + "sign_in_with": "Sign in with", + "continue_with_google": "Continue with Google", + "sign_in_with_github": "Sign in with GitHub", + "continue_with_farcaster": "Continue with Farcaster", + "continue_with_your_wallet": "Continue with your Wallet", + "stars_per_day": "Stars per day", + "media": "Media", + "check_launch": "Check Launch", + "load_your_github_repository_from_settings_to_see_analytics": "Load your GitHub repository from settings to see analytics", + "stars": "Stars", + "processing_stars": "Processing stars...", + "forks": "Forks", + "registration_is_disabled": "Registration is disabled", + "login_instead": "Login instead", + "gitroom": "Gitroom", + "select_a_conversation_and_chat_away": "Select a conversation and chat away.", + "adding_channel_redirecting_you": "Adding channel, Redirecting You", + "could_not_add_provider": "Could not add provider.", + "you_are_being_redirected_back": "You are being redirected back", + "we_are_experiencing_some_difficulty_try_to_refresh_the_page": "We are experiencing some difficulty, try to refresh the page", + "post_not_found": "Post not found", + "publication_date": "Publication Date:", + "analytics": "Analytics", + "launches": "Launches", + "plugs": "Plugs", + "billing": "Billing", + "affiliate": "Affiliate", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday", + "sunday": "Sunday", + "can_t_change_date_remove_post_from_publication": "Can't change date, remove post from publication", + "predicted_github_trending_change": "Predicted GitHub Trending Change", + "duplicate_post": "Duplicate Post", + "preview_post": "Preview Post", + "post_statistics": "Post Statistics", + "draft": "Draft", + "week_number": "Week {{number}}", + "top_title_edit_webhook": "Edit webhook", + "top_title_add_webhook": "Add webhook", + "top_title_oh_no": "Oh no", + "top_title_auto_plug": "Auto Plug: {{title}}", + "top_title_edit_autopost": "Edit autopost", + "top_title_add_autopost": "Add autopost", + "top_title_send_a_new_offer": "Send a new offer", + "top_title_media_library": "Media Library", + "top_title_add_signature": "Add signature", + "top_title_send_a_message_to": "Send a message to {{name}}", + "top_title_configure_provider": "Configure Provider", + "top_title_add_member": "Add Member", + "top_title_change_bot_picture": "Change Bot Picture", + "top_title_create_a_new_tag": "Create a new tag", + "top_title_select_company": "Select Company", + "top_title_additional_settings": "Additional Settings", + "top_title_time_table_slots": "Time Table Slots", + "top_title_design_media": "Design Media", + "top_title_edit_post": "Edit Post", + "top_title_create_post": "Create Post", + "top_title_move__add_to_customer": "Move / Add to customer", + "top_title_add_api_key_for": "Add API key for {{name}}", + "top_title_instance_url": "Instance URL", + "top_title_custom_url": "Custom URL", + "top_title_add_channel": "Add Channel", + "top_title_add_telegram": "Add Telegram", + "top_title_add_wrapcast": "Add Wrapcast", + "top_title_comments_for": "Comments for {{date}}", + "top_title_edit_signature": "Edit Signature", + "label_name": "Name", + "label_url": "URL", + "label_title": "Title", + "label_subtitle": "Subtitle", + "label_email": "Email", + "label_full_name": "Full Name", + "label_password": "Password", + "label_confirm_password": "Confirm Password", + "label_api_key": "API Key", + "label_instance_url": "Instance URL", + "label_custom_url": "Custom URL", + "label_feedback": "Feedback", + "label_bio": "Bio", + "label_role": "Role", + "label_country": "Country", + "label_audience_size": "Audience size on all platforms", + "label_pick_time": "Pick time", + "label_nickname": "Nickname", + "label_write_anything": "Write anything", + "label_output_format": "Output format", + "label_add_pictures": "Add pictures?", + "label_hour": "Hour", + "label_minutes": "Minutes", + "label_select_publication": "Select publication", + "label_canonical_link": "Canonical Link", + "label_cover_picture": "Cover picture", + "label_tags": "Tags", + "label_topics": "Topics", + "label_tags_maximum_4": "Tags (Maximum 4)", + "label_attachments": "Attachments", + "label_type": "Type", + "label_thumbnail": "Thumbnail", + "label_who_can_see_this_video": "Who can see this video?", + "label_content_posting_method": "Content posting method", + "label_auto_add_music": "Auto add music", + "label_duet": "Duet", + "label_stitch": "Stitch", + "label_comments": "Comments", + "label_disclose_video_content": "Disclose Video Content", + "label_your_brand": "Your brand", + "label_branded_content": "Branded content", + "label_subreddit": "Subreddit", + "label_flair": "Flair", + "label_media": "Media", + "label_search_subreddit": "Search Subreddit", + "label_delay": "Delay", + "label_post_type": "Post Type", + "label_collaborators": "Collaborators (max 3) - accounts can't be private", + "label_community": "Community", + "label_search_community": "Search Community", + "label_channel": "Channel", + "label_search_channel": "Search Channel", + "label_select_channel": "Select Channel", + "label_new_password": "New Password", + "label_repeat_password": "Repeat Password", + "label_platform": "Platform", + "label_price_per_post": "Price per post", + "label_integrations": "Integrations", + "label_code": "Code", + "label_should_sync_last_post": "Should we sync the current last post?", + "label_when_post": "When should we post it?", + "label_autogenerate_content": "Autogenerate content", + "label_generate_picture": "Generate Picture?", + "label_company": "Company", + "label_tag_color": "Tag Color", + "label_select_board": "Select board", + "label_select_organization": "Select organization", + "label_auto_add_signature": "Auto add signature?", + "enable_color_picker": "Enable color picker", + "cancel_the_color_picker": "Cancel the color picker", + "no_content_yet": "No Content Yet", + "write_your_reply": "Write your post...", + "add_a_tag": "Add a tag", + "add_to_calendar": "Add to Calendar", + "select_channels_from_circles": "Select channels from the circles above", + "not_matching_order": "Not matching order", + "submit_for_order": "Submit for order", + "schedule": "Schedule", + "update": "Update", + "attachments": "Attachments", + "tags": "Tags", + "public_to_everyone": "Public to everyone", + "mutual_follow_friends": "Mutual follow friends", + "follower_of_creator": "Follower of creator", + "self_only": "Self only", + "post_content_directly_to_tiktok": "Post content directly to TikTok", + "upload_content_to_tiktok_without_posting": "Upload content to TikTok without posting it", + "choose_upload_without_posting_description": "Choose upload without posting if you want to review and edit your content within TikTok's app before publishing. This gives you access to TikTok's built-in editing tools and lets you make final adjustments before posting.", + "faq_am_i_going_to_be_charged_by_postiz": "Am I going to be charged by Postiz?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "To confirm credit card information Postiz will hold $2 and release it immediately", + "faq_can_i_trust_postiz_gitroom": "Can I trust Postiz?", + "faq_postiz_gitroom_is_proudly_open_source": "Postiz is proudly open-source! We believe in an ethical and transparent culture, meaning that Postiz will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">click here</a>.", + "faq_what_are_channels": "What are channels?", + "faq_postiz_gitroom_allows_you_to_schedule_posts": "Postiz allows you to schedule your posts between different channels.\nA channel is a publishing platform where you can schedule your posts.\nFor example, you can schedule your posts on X, Facebook, Instagram, TikTok, YouTube, Reddit, Linkedin, Dribbble, Threads and Pinterest.", + "faq_what_are_team_members": "What are team members?", + "faq_if_you_have_a_team_with_multiple_members": "If you have a team with multiple members, you can invite them to your workspace to collaborate on your posts and add their personal channels", + "faq_what_is_ai_auto_complete": "What is AI auto-complete?", + "faq_we_automate_chatgpt_to_help_you_write": "We automate ChatGPT to help you write social posts and articles.", + "enter_email": "Enter email", + "are_you_sure": "Are you sure?", + "yes_delete_it": "Yes, delete it!", + "no_cancel": "No, cancel!", + "are_you_sure_you_want_to_delete": "Are you sure you want to delete {{name}}?", + "are_you_sure_you_want_to_delete_the_image": "Are you sure you want to delete the image?", + "are_you_sure_you_want_to_logout": "Are you sure you want to logout?", + "yes_logout": "Yes logout", + "are_you_sure_you_want_to_delete_this_slot": "Are you sure you want to delete this slot?", + "are_you_sure_you_want_to_delete_this_subreddit": "Are you sure you want to delete this Subreddit?", + "are_you_sure_you_want_to_close_the_window": "Are you sure you want to close the window?", + "yes_close": "Yes, close", + "link_copied_to_clipboard": "Link copied to clipboard", + "are_you_sure_you_want_to_close_this_modal_all_data_will_be_lost": "Are you sure you want to close this modal? (all data will be lost)", + "yes_close_it": "Yes, close it!", + "uploading_pictures": "Uploading pictures...", + "agent_starting": "Agent starting", + "researching_your_content": "Researching your content...", + "understanding_the_category": "Understanding the category...", + "finding_the_topic": "Finding the topic...", + "finding_popular_posts_to_match_with": "Finding popular posts to match with...", + "generating_hook": "Generating hook...", + "generating_content": "Generating content...", + "generating_pictures": "Generating pictures...", + "finding_time_to_post": "Finding time to post...", + "write_anything": "Write anything", + "you_can_write_anything_you_want_and_also_add_links_we_will_do_the_research_for_you": "You can write anything you want, and also add links, we will do the research for you...", + "output_format": "Output format", + "add_pictures": "Add pictures?", + "7_days": "7 Days", + "30_days": "30 Days", + "90_days": "90 Days", + "start_7_days_free_trial": "Start 7 days free trial", + "change_language": "Change Language", + "that_a_wrap": "That's a wrap!\n\nIf you enjoyed this thread:\n\n1. Follow me @{{username}} for more of these\n2. RT the tweet below to share this thread with your audience\n", + "post_as_images_carousel": "Post as images carousel", + "save_set": "Save Set", + "separate_post": "Separate post to multiple posts", + "label_who_can_reply_to_this_post": "Who can reply to this post?", + "delete_integration": "Delete Integration", + "start_writing_your_post": "Start writing your post for a preview", + "billing_join_over": "Join Over", + "billing_entrepreneurs_count": "18,000+ Entrepreneurs", + "billing_who_use": "who use", + "billing_postiz_grow_social": "Postiz To Grow Their Social Presence", + "billing_no_risk_trial": "100% No-Risk Free Trial", + "billing_pay_nothing_7_days": "Pay NOTHING for the first 7-days", + "billing_cancel_anytime": "Cancel anytime, hassle-free", + "billing_choose_plan": "Choose a Plan", + "billing_monthly": "Monthly", + "billing_yearly": "Yearly", + "billing_20_percent_off": "20% Off", + "billing_features": "Features", + "billing_channel": "channel", + "billing_channels": "channels", + "billing_unlimited": "Unlimited", + "billing_posts_per_month": "posts per month", + "billing_unlimited_team_members": "Unlimited team members", + "billing_ai_auto_complete": "AI auto-complete", + "billing_ai_copilots": "AI copilots", + "billing_ai_autocomplete": "AI Autocomplete", + "billing_advanced_picture_editor": "Advanced Picture Editor", + "billing_ai_images_per_month": "AI Images per month", + "billing_ai_videos_per_month": "AI Videos per month", + "billing_billing_address": "Billing Address", + "billing_payment": "Payment", + "billing_powered_by_stripe": "Secure payments processed by", + "billing_your_7_day_trial_is": "Your 7-day trial is", + "billing_100_percent_free": "100% free", + "billing_ending": "ending", + "billing_cancel_anytime_short": "Cancel anytime.", + "billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!", + "billing_pay_now": "Pay Now", + "billing_per_month": "/ month", + "select_channels": "Select Channels", + "start_a_new_chat": "Start a new chat", + "your_assistant": "Your Assistant", + "agent_welcome_message": "Hello, I am your Postiz agent 🙌🏻.\n\nI can schedule a post or multiple posts to multiple channels and generate pictures and videos.\n\nYou can select the channels you want to use from the left menu.\n\nYou can see your previous conversations from the right menu.\n\nYou can also use me as an MCP Server, check Settings >> Public API", + "last_github_trending": "Last Github Trending", + "next_predicted_github_trending": "Next Predicted GitHub Trending", + "repository": "Repository", + "date": "Date", + "total_stars": "Total Stars", + "total_forks": "Total Forks", + "continue_with": "Continue With", + "email_address": "Email Address", + "label_company": "Company", + "email_already_exists": "Email already exists", + "google": "Google", + "farcaster": "Farcaster", + "edit_autopost": "Edit Autopost", + "add_autopost_title": "Add Autopost", + "webhook_deleted_successfully": "Webhook deleted successfully", + "all_integrations": "All integrations", + "specific_integrations": "Specific integrations", + "post_on_next_available_slot": "Post on the next available slot", + "post_immediately": "Post Immediately", + "could_not_use_rss_feed": "Could not use this RSS feed", + "rss_valid": "RSS valid!", + "autopost_updated_successfully": "Autopost updated successfully", + "autopost_added_successfully": "Autopost added successfully", + "write_your_post_placeholder": "Write your post...", + "select_or_upload_pictures_max_5": "Select or upload pictures (maximum 5 at a time).", + "you_can_drag_drop_pictures": "You can also drag & drop pictures.", + "you_dont_have_any_media_yet": "You don't have any media yet", + "media_library": "Media Library", + "media_settings": "Media Settings", + "media_editor": "Media Editor", + "close": "Close", + "me": "Me", + "noname": "Noname", + "password_reset_link_expired": "Your password reset link has expired. Please try again.", + "invalid_api_key": "Invalid API key", + "could_not_connect_to_platform": "Could not connect to the platform", + "web3_provider": "Web3 provider", + "add_provider_title": "Add Provider", + "profile_updated": "Profile updated", + "sets": "Sets", + "email_address": "Email Address", + "invitation_link_sent": "Invitation link sent", + "send_invitation_link": "Send Invitation Link", + "copy_link": "Copy Link", + "are_you_sure_remove_team_member": "Are you sure you want to remove this team member?", + "admin": "Admin", + "super_admin": "Super Admin", + "update_webhook": "Update webhook", + "add_webhook": "Add webhook", + "webhook_updated_successfully": "Webhook updated successfully", + "webhook_added_successfully": "Webhook added successfully", + "webhook_sent": "Webhook send", + "today": "Today", + "channel_disconnected_click_to_reconnect": "Channel disconnected, click to reconnect.", + "channel_disabled_upgrade_plan": "This channel is disabled, please upgrade your plan to enable it.", + "channel_added": "Channel added", + "are_you_sure_disable_channel": "Are you sure you want to disable this channel?", + "disable_channel_title": "Disable Channel", + "channel_disabled": "Channel Disabled", + "are_you_sure_delete_channel": "Are you sure you want to delete this channel?", + "delete_channel_title": "Delete Channel", + "delete_posts_before_channel": "You have to delete all the posts associated with this channel before deleting it", + "channel_deleted": "Channel Deleted", + "channel_enabled": "Channel Enabled", + "time_table_slots": "Time Table Slots", + "channel_id_copied": "Channel ID copied to clipboard", + "settings_updated": "Settings Updated", + "customer_updated": "Customer Updated", + "custom_url": "Custom URL", + "picture": "Picture", + "upgrade_required": "You need to upgrade to use this feature", + "move_to_billing": "Move to billing", + "payment_required": "Payment Required", + "no_content": "no content", + "select_customer_tooltip": "Select Customer", + "customers": "Customers", + "hour": "Hour", + "minutes": "Minutes", + "updated": "Updated", + "change_bot_picture_title": "Change Bot Picture", + "select_customer_label": "Select Customer", + "start_typing": "Start typing...", + "choose_set_or_continue": "Choose a set or continue without one", + "continue_without_set": "Continue without set", + "select_set": "Select a Set", + "channel_settings": "Settings", + "post_needs_content_or_image": "Your post should have at least one character or one image.", + "please_fix_your_settings": "Please fix your settings", + "shortlink_urls_question": "Do you want to shortlink the URLs? it will let you get statistics over clicks", + "yes_shortlink_it": "Yes, shortlink it!", + "added_successfully": "Added successfully", + "updated_successfully": "Updated successfully", + "create_post_title": "Create Post", + "post_preview": "Post Preview", + "check_circles_above": "Check the circles above", + "create_output": "Create output", + "assistant_initial_message": "Hi! I can help you to refine your social media posts.", + "no_longer_global_mode": "No longer in global mode", + "two_days": "Two Days", + "three_days": "Three Days", + "four_days": "Four Days", + "five_days": "Five Days", + "six_days": "Six Days", + "two_weeks": "Two Weeks", + "repeat_post_every_label": "Repeat Post Every", + "add_new_tag": "Add New Tag", + "tag_name": "Name", + "post_is_too_long": "post is too long, please fix it", + "your_post_should_have_at_least_one_character_or_one_image": "Your post should have at least one character or one image.", + "internal_edit": "Internal Edit", + "are_you_sure_go_back_to_global_mode": "This action is irreversible. Are you sure you want to go back to global mode?", + "yes_go_back_to_global_mode": "Yes, go back to global mode", + "are_you_sure_delete_this_post": "Are you sure you want to delete this post?", + "yes_delete_it": "Yes, delete it!", + "cant_edit_networks_when_creating_set": "You can't edit networks when creating a set", + "click_to_exit_global_editing": "Click this button to exit global editing and customize the post for this channel", + "edit_content": "Edit content", + "editing_a_specific_network": "Editing a Specific Network", + "back_to_global": "Back to global", + "delete_post_tooltip": "Delete Post", + "drop_files_here_to_upload": "Drop your files here to upload", + "insert_emoji": "Insert Emoji", + "write_something": "Write something …" } From 2544e870aae9ff24a112ebe510c2eaa49958ab09 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 12:29:22 +0700 Subject: [PATCH 117/340] feat: delayed comments --- .../components/new-launch/add.edit.modal.tsx | 2 +- .../components/new-launch/delay.component.tsx | 134 ++++++- .../components/new-launch/manage.modal.tsx | 1 + .../src/activities/post.activity.ts | 3 +- apps/orchestrator/src/workflows/index.ts | 3 +- .../{ => post-workflows}/post.workflow.ts | 0 .../post-workflows/post.workflow.v1.0.1.ts | 375 ++++++++++++++++++ .../prisma/autopost/autopost.service.ts | 4 +- .../database/prisma/posts/posts.repository.ts | 1 + .../database/prisma/posts/posts.service.ts | 6 +- .../src/database/prisma/schema.prisma | 1 + .../src/dtos/posts/create.post.dto.ts | 24 +- 12 files changed, 529 insertions(+), 25 deletions(-) rename apps/orchestrator/src/workflows/{ => post-workflows}/post.workflow.ts (100%) create mode 100644 apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts diff --git a/apps/frontend/src/components/new-launch/add.edit.modal.tsx b/apps/frontend/src/components/new-launch/add.edit.modal.tsx index 42eef82c..b68b5d66 100644 --- a/apps/frontend/src/components/new-launch/add.edit.modal.tsx +++ b/apps/frontend/src/components/new-launch/add.edit.modal.tsx @@ -148,7 +148,7 @@ export const AddEditModalInnerInner: FC<AddEditModalProps> = (props) => { 0, existingData.integration, existingData.posts.map((post) => ({ - delay: 0, + delay: post.delay, content: post.content.indexOf('<p>') > -1 ? post.content diff --git a/apps/frontend/src/components/new-launch/delay.component.tsx b/apps/frontend/src/components/new-launch/delay.component.tsx index e8607e1d..bee7453c 100644 --- a/apps/frontend/src/components/new-launch/delay.component.tsx +++ b/apps/frontend/src/components/new-launch/delay.component.tsx @@ -1,17 +1,42 @@ 'use client'; -import React, { FC, useCallback } from 'react'; -import { DelayIcon } from '@gitroom/frontend/components/ui/icons'; +import React, { FC, useCallback, useEffect, useState } from 'react'; +import { DelayIcon, DropdownArrowIcon } from '@gitroom/frontend/components/ui/icons'; import clsx from 'clsx'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useClickOutside } from '@mantine/hooks'; + +const delayOptions = [ + { value: 1, label: '1m' }, + { value: 2, label: '2m' }, + { value: 5, label: '5m' }, + { value: 10, label: '10m' }, + { value: 15, label: '15m' }, + { value: 30, label: '30m' }, + { value: 60, label: '1h' }, + { value: 120, label: '2h' }, +]; export const DelayComponent: FC<{ currentIndex: number; currentDelay: number; }> = ({ currentIndex, currentDelay }) => { const t = useT(); + const [isOpen, setIsOpen] = useState(false); + const [customValue, setCustomValue] = useState(''); + + const isCustomDelay = currentDelay > 0 && !delayOptions.some((opt) => opt.value === currentDelay); + + useEffect(() => { + if (isOpen && isCustomDelay) { + setCustomValue(String(currentDelay)); + } else if (isOpen && !isCustomDelay) { + setCustomValue(''); + } + }, [isOpen, isCustomDelay, currentDelay]); + const { current, setInternalDelay, setGlobalDelay } = useLaunchStore( useShallow((state) => ({ current: state.current, @@ -20,6 +45,13 @@ export const DelayComponent: FC<{ })) ); + const ref = useClickOutside(() => { + if (!isOpen) { + return; + } + setIsOpen(false); + }); + const setDelay = useCallback( (index: number) => (minutes: number) => { if (current !== 'global') { @@ -31,20 +63,92 @@ export const DelayComponent: FC<{ [currentIndex, current] ); + const handleSelectDelay = useCallback( + (minutes: number) => { + setDelay(currentIndex)(minutes); + setIsOpen(false); + }, + [currentIndex, setDelay] + ); + + const getCurrentDelayLabel = () => { + if (!currentDelay) return null; + const option = delayOptions.find((opt) => opt.value === currentDelay); + return option?.label || `${currentDelay} min`; + }; + return ( - <DelayIcon - // move it into the modal - onClick={() => setDelay(currentIndex)(100)} - data-tooltip-id="tooltip" - data-tooltip-content={ - !currentDelay - ? t('delay_comment', 'Delay comment') - : `Comment delayed by ${currentDelay} minutes` - } - className={clsx( - 'cursor-pointer', - currentDelay > 0 && 'bg-[#D82D7E] text-white rounded-full' + <div ref={ref} className="relative"> + <div + onClick={() => setIsOpen(!isOpen)} + data-tooltip-id="tooltip" + data-tooltip-content={ + !currentDelay + ? t('delay_comment', 'Delay comment') + : `${t('delay_comment_by', 'Comment delayed by')} ${getCurrentDelayLabel()}` + } + className={clsx( + 'cursor-pointer flex items-center gap-[4px]', + currentDelay > 0 && 'bg-[#D82D7E] text-white rounded-full' + )} + > + <DelayIcon /> + </div> + {isOpen && ( + <div className="z-[300] absolute end-0 top-[100%] w-[200px] bg-newBgColorInner p-[8px] menu-shadow translate-y-[10px] flex flex-col rounded-[8px]"> + <div className="grid grid-cols-4 gap-[4px]"> + {delayOptions.map((option) => ( + <div + onClick={() => handleSelectDelay(option.value)} + key={option.value} + className={clsx( + 'h-[32px] flex items-center justify-center rounded-[4px] cursor-pointer hover:bg-newBgColor text-[13px]', + currentDelay === option.value && 'bg-[#612BD3] text-white hover:bg-[#612BD3]' + )} + > + {option.label} + </div> + ))} + </div> + <div className="border-t border-newTextColor/10 mt-[8px] pt-[8px]"> + <div className="flex gap-[4px]"> + <input + type="number" + min="1" + value={customValue} + onChange={(e) => setCustomValue(e.target.value)} + onClick={(e) => e.stopPropagation()} + placeholder="Custom min" + className={clsx( + 'flex-1 w-full h-[32px] px-[8px] rounded-[4px] bg-newBgColor border text-[13px] outline-none focus:border-[#612BD3]', + isCustomDelay ? 'border-[#612BD3]' : 'border-newTextColor/10' + )} + /> + <button + onClick={(e) => { + e.stopPropagation(); + const value = parseInt(customValue, 10); + if (value > 0) { + handleSelectDelay(value); + setCustomValue(''); + } + }} + className="h-[32px] px-[10px] rounded-[4px] bg-[#612BD3] text-white text-[12px] font-[600] hover:bg-[#612BD3]/80" + > + Set + </button> + </div> + </div> + {currentDelay > 0 && ( + <button + onClick={() => handleSelectDelay(0)} + className="mt-[8px] h-[32px] w-full rounded-[4px] text-[13px] text-red-400 hover:bg-red-400/10" + > + Remove delay + </button> + )} + </div> )} - /> + </div> ); }; diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 8f59d674..202daff9 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -315,6 +315,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { value: post.values.map((value: any) => ({ ...(value.id ? { id: value.id } : {}), content: value.content, + delay: value.delay || 0, image: (value?.media || []).map( ({ id, path, alt, thumbnail, thumbnailTimestamp }: any) => ({ diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index e6dc8fd7..f89ecaca 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -22,7 +22,6 @@ import { organizationId, postId as postIdSearchParam, } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; -import { postWorkflow } from '@gitroom/orchestrator/workflows'; @Injectable() @Activity() @@ -43,7 +42,7 @@ export class PostActivity { for (const post of list) { await this._temporalService.client .getRawClient() - .workflow.signalWithStart('postWorkflow', { + .workflow.signalWithStart('postWorkflowV101', { workflowId: `post_${post.id}`, taskQueue: 'main', signal: 'poke', diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index 3a865864..22f5fce3 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -1,4 +1,5 @@ -export * from './post.workflow'; +export * from './post-workflows/post.workflow'; +export * from './post-workflows/post.workflow.v1.0.1'; export * from './autopost.workflow'; export * from './digest.email.workflow'; export * from './missing.post.workflow'; diff --git a/apps/orchestrator/src/workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post-workflows/post.workflow.ts similarity index 100% rename from apps/orchestrator/src/workflows/post.workflow.ts rename to apps/orchestrator/src/workflows/post-workflows/post.workflow.ts diff --git a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts new file mode 100644 index 00000000..ed6a167f --- /dev/null +++ b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts @@ -0,0 +1,375 @@ +import { PostActivity } from '@gitroom/orchestrator/activities/post.activity'; +import { + ActivityFailure, + ApplicationFailure, + startChild, + proxyActivities, + sleep, + defineSignal, + setHandler, +} from '@temporalio/workflow'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { capitalize, sortBy } from 'lodash'; +import { PostResponse } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { TypedSearchAttributes } from '@temporalio/common'; +import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; + +const proxyTaskQueue = (taskQueue: string) => { + return proxyActivities<PostActivity>({ + startToCloseTimeout: '10 minute', + taskQueue, + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, + }); +}; + +const { + getPostsList, + inAppNotification, + changeState, + updatePost, + sendWebhooks, + isCommentable, +} = proxyActivities<PostActivity>({ + startToCloseTimeout: '10 minute', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, +}); + +const poke = defineSignal('poke'); + +export async function postWorkflowV101({ + taskQueue, + postId, + organizationId, + postNow = false, +}: { + taskQueue: string; + postId: string; + organizationId: string; + postNow?: boolean; +}) { + // Dynamic task queue, for concurrency + const { + postSocial, + postComment, + refreshToken, + internalPlugs, + globalPlugs, + processInternalPlug, + processPlug, + } = proxyTaskQueue(taskQueue); + + let poked = false; + setHandler(poke, () => { + poked = true; + }); + + const startTime = new Date(); + // get all the posts and comments to post + const postsList = await getPostsList(organizationId, postId); + const [post] = postsList; + + // in case doesn't exists for some reason, fail it + if (!post || (!postNow && post.state !== 'QUEUE')) { + return; + } + + // if it's a repeatable post, we should ignore this. + if (!postNow) { + await sleep( + dayjs(post.publishDate).isBefore(dayjs()) + ? 0 + : dayjs(post.publishDate).diff(dayjs(), 'millisecond') + ); + } + + // if refresh is needed from last time, let's inform the user + if (post.integration?.refreshNeeded) { + await inAppNotification( + post.organizationId, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name} because you need to reconnect it. Please enable it and try again.`, + true, + false, + 'info' + ); + return; + } + + // if it's disabled, inform the user + if (post.integration?.disabled) { + await inAppNotification( + post.organizationId, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name} because it's disabled. Please enable it and try again.`, + true, + false, + 'info' + ); + return; + } + + // Do we need to post comment for this social? + const toComment = + postsList.length === 1 ? false : await isCommentable(post.integration); + + // list of all the saved results + const postsResults: PostResponse[] = []; + + // iterate over the posts + for (let i = 0; i < postsList.length; i++) { + // this is a small trick to repeat an action in case of token refresh + while (true) { + try { + // first post the main post + if (i === 0) { + postsResults.push( + ...(await postSocial(post.integration as Integration, [ + postsList[i], + ])) + ); + + // then post the comments if any + } else { + if (!toComment) { + break; + } + + if (postsList[i].delay) { + await sleep(60000 * postsList[i].delay); + } + + postsResults.push( + ...(await postComment( + postsResults[0].postId, + postsResults.length === 1 + ? undefined + : postsResults[i - 1].postId, + post.integration, + [postsList[i]] + )) + ); + } + + // mark post as successful + await updatePost( + postsList[i].id, + postsResults[i].postId, + postsResults[i].releaseURL + ); + + if (i === 0) { + // send notification on a sucessful post + await inAppNotification( + post.integration.organizationId, + `Your post has been published on ${capitalize( + post.integration.providerIdentifier + )}`, + `Your post has been published on ${capitalize( + post.integration.providerIdentifier + )} at ${postsResults[0].releaseURL}`, + true, + true + ); + } + + // break the current while to move to the next post + break; + } catch (err) { + // if token refresh is needed, do it and repeat + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'refresh_token' + ) { + const refresh = await refreshToken(post.integration); + if (!refresh || !refresh.accessToken) { + await changeState(postsList[0].id, 'ERROR', err, postsList); + return false; + } + + post.integration.token = refresh.accessToken; + continue; + } + + // for other errors, change state and inform the user if needed + await changeState(postsList[0].id, 'ERROR', err, postsList); + + // specific case for bad body errors + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'bad_body' + ) { + await inAppNotification( + post.organizationId, + `Error posting${i === 0 ? ' ' : ' comments '}on ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `An error occurred while posting${i === 0 ? ' ' : ' comments '}on ${ + post.integration?.providerIdentifier + }${err?.cause?.message ? `: ${err?.cause?.message}` : ``}`, + true, + false, + 'fail' + ); + return false; + } + + return false; + } + } + } + + // send webhooks for the post + await sendWebhooks( + postsResults[0].postId, + post.organizationId, + post.integration.id + ); + + // load internal plugs like repost by other users + const internalPlugsList = await internalPlugs( + post.integration, + JSON.parse(post.settings) + ); + + // load global plugs, like repost a post if it gets to a certain number of likes + const globalPlugsList = (await globalPlugs(post.integration)).reduce( + (all, current) => { + for (let i = 1; i <= current.totalRuns; i++) { + all.push({ + ...current, + delay: current.delay * i, + }); + } + + return all; + }, + [] + ); + + // Check if the post is repeatable + const repeatPost = !post.intervalInDays + ? [] + : [ + { + type: 'repeat-post', + delay: + post.intervalInDays * 24 * 60 * 60 * 1000 - + (new Date().getTime() - startTime.getTime()), + }, + ]; + + // Sort all the actions by delay, so we can process them in order + const list = sortBy( + [...internalPlugsList, ...globalPlugsList, ...repeatPost], + 'delay' + ); + + // process all the plugs in order, we are using while because in some cases we need to remove items from the list + while (list.length > 0) { + // get the next to process + const todo = list.shift(); + + // wait for the delay + await sleep(todo.delay); + + // process internal plug + if (todo.type === 'internal-plug') { + while (true) { + try { + await processInternalPlug({ ...todo, post: postsResults[0].postId }); + } catch (err) { + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'refresh_token' + ) { + const refresh = await refreshToken(post.integration); + if (!refresh || !refresh.accessToken) { + await changeState(postsList[0].id, 'ERROR', err, postsList); + return false; + } + + post.integration.token = refresh.accessToken; + continue; + } + } + break; + } + } + + // process global plug + if (todo.type === 'global') { + while (true) { + try { + const process = await processPlug({ + ...todo, + postId: postsResults[0].postId, + }); + if (process) { + const toDelete = list + .reduce((all, current, index) => { + if (current.plugId === todo.plugId) { + all.push(index); + } + + return all; + }, []) + .reverse(); + + for (const index of toDelete) { + list.splice(index, 1); + } + } + } catch (err) { + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'refresh_token' + ) { + const refresh = await refreshToken(post.integration); + if (!refresh || !refresh.accessToken) { + await changeState(postsList[0].id, 'ERROR', err, postsList); + return false; + } + + post.integration.token = refresh.accessToken; + continue; + } + } + break; + } + } + + // process repeat post in a new workflow, this is important so the other plugs can keep running + if (todo.type === 'repeat-post') { + await startChild(postWorkflowV101, { + parentClosePolicy: 'ABANDON', + args: [ + { + taskQueue, + postId, + organizationId, + postNow: true, + }, + ], + workflowId: `post_${post.id}_${makeId(10)}`, + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: postId, + }, + ]), + }); + } + } +} diff --git a/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts b/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts index 68560944..e455aad3 100644 --- a/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts @@ -18,7 +18,6 @@ import { TemporalService } from 'nestjs-temporal-core'; import { TypedSearchAttributes } from '@temporalio/common'; import { organizationId, - postId as postIdSearchParam, } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; const parser = new Parser(); @@ -106,7 +105,7 @@ export class AutopostService { try { return this._temporalService.client .getRawClient() - ?.workflow.start('postWorkflow', { + ?.workflow.start('postWorkflowV101', { workflowId: `autopost-${id}`, taskQueue: 'main', args: [{ id, immediately: true }], @@ -286,6 +285,7 @@ export class AutopostService { value: [ { id: makeId(10), + delay: 0, content: state.description.replace(/\n/g, '\n\n') + '\n\n' + diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 937a9d87..cb2a647a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -383,6 +383,7 @@ export class PostsRepository { } : {}), content: value.content, + delay: value.delay || 0, group: uuid, intervalInDays: inter ? +inter : null, approvedSubmitForOrder: APPROVED_SUBMIT_FOR_ORDER.NO, diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 36f56392..fe92a0e9 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -465,7 +465,7 @@ export class PostsService { await this._temporalService.client .getRawClient() - ?.workflow.start('postWorkflow', { + ?.workflow.start('postWorkflowV101', { workflowId: `post_${posts[0].id}`, taskQueue: 'main', args: [ @@ -533,7 +533,7 @@ export class PostsService { await this._temporalService.client .getRawClient() - ?.workflow.start('postWorkflow', { + ?.workflow.start('postWorkflowV101', { workflowId: `post_${getPostById.id}`, taskQueue: 'main', args: [ @@ -622,10 +622,12 @@ export class PostsService { ...toPost.list.map((l) => ({ id: '', content: l.post, + delay: 0, image: [], })), { id: '', + delay: 0, content: `Check out the full story here:\n${ body.postId || body.url }`, diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 56be7279..d3c5d771 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -385,6 +385,7 @@ model Post { organizationId String integrationId String content String + delay Int @default(0) group String title String? description String? diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts index dd9a8471..18842f4e 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts @@ -1,9 +1,25 @@ import { - ArrayMinSize, IsArray, IsBoolean, IsDateString, IsDefined, IsIn, IsNumber, IsOptional, IsString, MinLength, Validate, ValidateIf, ValidateNested + ArrayMinSize, + IsArray, + IsBoolean, + IsDateString, + IsDefined, + IsIn, + IsNumber, + IsOptional, + IsString, + MinLength, + Validate, + ValidateIf, + ValidateNested, } from 'class-validator'; import { Type } from 'class-transformer'; import { MediaDto } from '@gitroom/nestjs-libraries/dtos/media/media.dto'; -import { allProviders, type AllProvidersSettings, EmptySettings } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/all.providers.settings'; +import { + allProviders, + type AllProvidersSettings, + EmptySettings, +} from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/all.providers.settings'; import { ValidContent } from '@gitroom/helpers/utils/valid.images'; export class Integration { @@ -22,6 +38,10 @@ export class PostContent { @IsString() id: string; + @IsOptional() + @IsNumber() + delay: number; + @IsArray() @Type(() => MediaDto) @ValidateNested({ each: true }) From 2cc639bbfc41d7522400b519bf4d0af42398ff00 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 13:57:45 +0700 Subject: [PATCH 118/340] fix: workflow termination --- .../src/database/prisma/autopost/autopost.service.ts | 2 +- .../src/database/prisma/posts/posts.service.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts b/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts index e455aad3..5964983a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/autopost/autopost.service.ts @@ -105,7 +105,7 @@ export class AutopostService { try { return this._temporalService.client .getRawClient() - ?.workflow.start('postWorkflowV101', { + ?.workflow.start('autoPostWorkflow', { workflowId: `autopost-${id}`, taskQueue: 'main', args: [{ id, immediately: true }], diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index fe92a0e9..deabc60b 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -384,7 +384,7 @@ export class PostsService { const workflows = this._temporalService.client .getRawClient() ?.workflow.list({ - query: `WorkflowType="postWorkflow" AND postId="${post.id}" AND ExecutionStatus="Running"`, + query: `postId="${post.id}" AND ExecutionStatus="Running"`, }); for await (const executionInfo of workflows) { @@ -444,7 +444,7 @@ export class PostsService { const workflows = this._temporalService.client .getRawClient() ?.workflow.list({ - query: `WorkflowType="postWorkflow" AND postId="${posts[0].id}" AND ExecutionStatus="Running"`, + query: `postId="${posts[0].id}" AND ExecutionStatus="Running"`, }); for await (const executionInfo of workflows) { @@ -513,7 +513,7 @@ export class PostsService { const workflows = this._temporalService.client .getRawClient() ?.workflow.list({ - query: `WorkflowType="postWorkflow" AND postId="${getPostById.id}" AND ExecutionStatus="Running"`, + query: `postId="${getPostById.id}" AND ExecutionStatus="Running"`, }); for await (const executionInfo of workflows) { From 82a92afabb50a7a46dcb1c5d6956536a488da610 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 15:31:51 +0700 Subject: [PATCH 119/340] fix: don't wait for workflow start, for faster creation --- .../database/prisma/posts/posts.service.ts | 90 ++++++++++--------- 1 file changed, 46 insertions(+), 44 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index deabc60b..c019bfd8 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -440,52 +440,54 @@ export class PostsService { return [] as any[]; } - try { - const workflows = this._temporalService.client + new Promise(async () => { + try { + const workflows = this._temporalService.client + .getRawClient() + ?.workflow.list({ + query: `postId="${posts[0].id}" AND ExecutionStatus="Running"`, + }); + + for await (const executionInfo of workflows) { + try { + const workflow = + await this._temporalService.client.getWorkflowHandle( + executionInfo.workflowId + ); + if ( + workflow && + (await workflow.describe()).status.name !== 'TERMINATED' + ) { + await workflow.terminate(); + } + } catch (err) {} + } + } catch (err) {} + + await this._temporalService.client .getRawClient() - ?.workflow.list({ - query: `postId="${posts[0].id}" AND ExecutionStatus="Running"`, + ?.workflow.start('postWorkflowV101', { + workflowId: `post_${posts[0].id}`, + taskQueue: 'main', + args: [ + { + taskQueue: post.settings.__type.split('-')[0].toLowerCase(), + postId: posts[0].id, + organizationId: orgId, + }, + ], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: posts[0].id, + }, + { + key: organizationId, + value: orgId, + }, + ]), }); - - for await (const executionInfo of workflows) { - try { - const workflow = - await this._temporalService.client.getWorkflowHandle( - executionInfo.workflowId - ); - if ( - workflow && - (await workflow.describe()).status.name !== 'TERMINATED' - ) { - await workflow.terminate(); - } - } catch (err) {} - } - } catch (err) {} - - await this._temporalService.client - .getRawClient() - ?.workflow.start('postWorkflowV101', { - workflowId: `post_${posts[0].id}`, - taskQueue: 'main', - args: [ - { - taskQueue: post.settings.__type.split('-')[0].toLowerCase(), - postId: posts[0].id, - organizationId: orgId, - }, - ], - typedSearchAttributes: new TypedSearchAttributes([ - { - key: postIdSearchParam, - value: posts[0].id, - }, - { - key: organizationId, - value: orgId, - }, - ]), - }); + }).catch((err) => {}); Sentry.metrics.count('post_created', 1); postList.push({ From bd24282d2bd23d11a63b156bd32db61bd2d79e00 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 15:40:31 +0700 Subject: [PATCH 120/340] fix: don't wait for workflow start, for faster creation --- .../database/prisma/posts/posts.service.ts | 154 +++++++----------- 1 file changed, 61 insertions(+), 93 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index c019bfd8..d8dfd75d 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -414,6 +414,57 @@ export class PostsService { getPostByForWebhookId(id: string) { return this._postRepository.getPostByForWebhookId(id); } + + async startWorkflow(taskQueue: string, postId: string, orgId: string) { + try { + const workflows = this._temporalService.client + .getRawClient() + ?.workflow.list({ + query: `postId="${postId}" AND ExecutionStatus="Running"`, + }); + + for await (const executionInfo of workflows) { + try { + const workflow = await this._temporalService.client.getWorkflowHandle( + executionInfo.workflowId + ); + if ( + workflow && + (await workflow.describe()).status.name !== 'TERMINATED' + ) { + await workflow.terminate(); + } + } catch (err) {} + } + } catch (err) {} + + try { + await this._temporalService.client + .getRawClient() + ?.workflow.start('postWorkflowV101', { + workflowId: `post_${postId}`, + taskQueue: 'main', + args: [ + { + taskQueue: taskQueue, + postId: postId, + organizationId: orgId, + }, + ], + typedSearchAttributes: new TypedSearchAttributes([ + { + key: postIdSearchParam, + value: postId, + }, + { + key: organizationId, + value: orgId, + }, + ]), + }); + } catch (err) {} + } + async createPost(orgId: string, body: CreatePostDto): Promise<any[]> { const postList = []; for (const post of body.posts) { @@ -440,54 +491,11 @@ export class PostsService { return [] as any[]; } - new Promise(async () => { - try { - const workflows = this._temporalService.client - .getRawClient() - ?.workflow.list({ - query: `postId="${posts[0].id}" AND ExecutionStatus="Running"`, - }); - - for await (const executionInfo of workflows) { - try { - const workflow = - await this._temporalService.client.getWorkflowHandle( - executionInfo.workflowId - ); - if ( - workflow && - (await workflow.describe()).status.name !== 'TERMINATED' - ) { - await workflow.terminate(); - } - } catch (err) {} - } - } catch (err) {} - - await this._temporalService.client - .getRawClient() - ?.workflow.start('postWorkflowV101', { - workflowId: `post_${posts[0].id}`, - taskQueue: 'main', - args: [ - { - taskQueue: post.settings.__type.split('-')[0].toLowerCase(), - postId: posts[0].id, - organizationId: orgId, - }, - ], - typedSearchAttributes: new TypedSearchAttributes([ - { - key: postIdSearchParam, - value: posts[0].id, - }, - { - key: organizationId, - value: orgId, - }, - ]), - }); - }).catch((err) => {}); + this.startWorkflow( + post.settings.__type.split('-')[0].toLowerCase(), + posts[0].id, + orgId + ).catch((err) => {}); Sentry.metrics.count('post_created', 1); postList.push({ @@ -512,53 +520,13 @@ export class PostsService { const newDate = await this._postRepository.changeDate(orgId, id, date); try { - const workflows = this._temporalService.client - .getRawClient() - ?.workflow.list({ - query: `postId="${getPostById.id}" AND ExecutionStatus="Running"`, - }); - - for await (const executionInfo of workflows) { - try { - const workflow = await this._temporalService.client.getWorkflowHandle( - executionInfo.workflowId - ); - if ( - workflow && - (await workflow.describe()).status.name !== 'TERMINATED' - ) { - await workflow.terminate(); - } - } catch (err) {} - } + await this.startWorkflow( + getPostById.integration.providerIdentifier.split('-')[0].toLowerCase(), + getPostById.id, + orgId + ); } catch (err) {} - await this._temporalService.client - .getRawClient() - ?.workflow.start('postWorkflowV101', { - workflowId: `post_${getPostById.id}`, - taskQueue: 'main', - args: [ - { - taskQueue: getPostById.integration.providerIdentifier - .split('-')[0] - .toLowerCase(), - postId: getPostById.id, - organizationId: orgId, - }, - ], - typedSearchAttributes: new TypedSearchAttributes([ - { - key: postIdSearchParam, - value: getPostById.id, - }, - { - key: organizationId, - value: orgId, - }, - ]), - }); - return newDate; } From 774d29d798477c960271bc7ea4e6c7304da14cae Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 18:38:08 +0700 Subject: [PATCH 121/340] feat: fetch post faster --- .../src/api/routes/posts.controller.ts | 10 ++++- .../src/components/launches/calendar.tsx | 2 +- .../database/prisma/posts/posts.repository.ts | 20 ++++++++- .../database/prisma/posts/posts.service.ts | 41 +++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index c51852ee..ce03df00 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -22,7 +22,10 @@ import { Response } from 'express'; import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; import { ShortLinkService } from '@gitroom/nestjs-libraries/short-linking/short.link.service'; import { CreateTagDto } from '@gitroom/nestjs-libraries/dtos/posts/create.tag.dto'; -import { AuthorizationActions, Sections } from '@gitroom/backend/services/auth/permissions/permission.exception.class'; +import { + AuthorizationActions, + Sections, +} from '@gitroom/backend/services/auth/permissions/permission.exception.class'; @ApiTags('Posts') @Controller('/posts') @@ -111,6 +114,11 @@ export class PostsController { return this._postsService.getOldPosts(org.id, date); } + @Get('/group/:group') + getPostsByGroup(@GetOrgFromRequest() org: Organization, @Param('group') group: string) { + return this._postsService.getPostsByGroup(org.id, group); + } + @Get('/:id') getPost(@GetOrgFromRequest() org: Organization, @Param('id') id: string) { return this._postsService.getPost(org.id, id); diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 997218f8..f0dbb4f6 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -454,7 +454,7 @@ export const CalendarColumn: FC<{ publishDate: loadPost.actualDate || loadPost.publishDate, }; - const data = await (await fetch(`/posts/${post.id}`)).json(); + const data = await (await fetch(`/posts/group/${post.group}`)).json(); const date = !isDuplicate ? null : (await (await fetch('/posts/find-slot')).json()).date; diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index cb2a647a..56efa445 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -49,7 +49,7 @@ export class PostsRepository { integration: { select: { providerIdentifier: true, - } + }, }, publishDate: true, }, @@ -235,6 +235,24 @@ export class PostsRepository { }); } + getPostsByGroup(orgId: string, group: string) { + return this._post.model.post.findMany({ + where: { + group, + ...(orgId ? { organizationId: orgId } : {}), + deletedAt: null, + }, + include: { + integration: true, + tags: { + select: { + tag: true, + }, + }, + }, + }); + } + getPost( id: string, includeIntegration = false, diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index d8dfd75d..98cc144b 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -252,6 +252,47 @@ export class PostsService { } } + async getPostsByGroup(orgId: string, group: string) { + const convertToJPEG = false; + const loadAll = await this._postRepository.getPostsByGroup(orgId, group); + const posts = this.arrangePostsByGroup(loadAll, undefined); + + return { + group: posts?.[0]?.group, + posts: await Promise.all( + (posts || []).map(async (post) => ({ + ...post, + image: await this.updateMedia( + post.id, + JSON.parse(post.image || '[]'), + convertToJPEG + ), + })) + ), + integrationPicture: posts[0]?.integration?.picture, + integration: posts[0].integrationId, + settings: JSON.parse(posts[0].settings || '{}'), + }; + } + + arrangePostsByGroup(all: any, parent?: string): PostWithConditionals[] { + const findAll = all + .filter((p: any) => + !parent ? !p.parentPostId : p.parentPostId === parent + ) + .map(({ integration, ...all }: any) => ({ + ...all, + ...(!parent ? { integration } : {}), + })); + + return [ + ...findAll, + ...(findAll.length + ? findAll.flatMap((p: any) => this.arrangePostsByGroup(all, p.id)) + : []), + ]; + } + async getPost(orgId: string, id: string, convertToJPEG = false) { const posts = await this.getPostsRecursively(id, true, orgId, true); const list = { From 96661cc30d0bb80d0b842f0519737c4e4a4d61c3 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 7 Jan 2026 20:39:15 +0700 Subject: [PATCH 122/340] fix: url --- libraries/helpers/src/utils/valid.url.path.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/helpers/src/utils/valid.url.path.ts b/libraries/helpers/src/utils/valid.url.path.ts index a9021ea5..aeedbe41 100644 --- a/libraries/helpers/src/utils/valid.url.path.ts +++ b/libraries/helpers/src/utils/valid.url.path.ts @@ -40,7 +40,7 @@ export class ValidUrlPath implements ValidatorConstraintInterface { defaultMessage(args: ValidationArguments) { // here you can provide default error message if validation failed return ( - 'URL must contain the domain: ' + process.env.RESTRICT_UPLOAD_DOMAINS + 'URL must contain the domain: ' + process.env.RESTRICT_UPLOAD_DOMAINS + ' Make sure you first use the upload API route.' ); } } From 07238a06a694a5eb75765477528735a728876e18 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 8 Jan 2026 21:26:29 +0700 Subject: [PATCH 123/340] feat: big workflow change --- .../src/activities/post.activity.ts | 5 ++ .../post-workflows/post.workflow.v1.0.1.ts | 48 ++++++++++++++----- 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index f89ecaca..c4514ec5 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -36,6 +36,11 @@ export class PostActivity { private _temporalService: TemporalService ) {} + @ActivityMethod() + async getIntegrationById(orgId: string, id: string) { + return this._integrationService.getIntegrationById(orgId, id); + } + @ActivityMethod() async searchForMissingThreeHoursPosts() { const list = await this._postService.searchForMissingThreeHoursPosts(); diff --git a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts index ed6a167f..7ca6eafa 100644 --- a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts +++ b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts @@ -46,6 +46,8 @@ const { const poke = defineSignal('poke'); +const iterate = ['post', 'afterRefresh', 'retry1', 'retry2', 'retry3']; + export async function postWorkflowV101({ taskQueue, postId, @@ -61,6 +63,7 @@ export async function postWorkflowV101({ const { postSocial, postComment, + getIntegrationById, refreshToken, internalPlugs, globalPlugs, @@ -128,7 +131,7 @@ export async function postWorkflowV101({ // iterate over the posts for (let i = 0; i < postsList.length; i++) { // this is a small trick to repeat an action in case of token refresh - while (true) { + for (const _ of iterate) { try { // first post the main post if (i === 0) { @@ -212,7 +215,9 @@ export async function postWorkflowV101({ ) { await inAppNotification( post.organizationId, - `Error posting${i === 0 ? ' ' : ' comments '}on ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, + `Error posting${i === 0 ? ' ' : ' comments '}on ${ + post.integration?.providerIdentifier + } for ${post?.integration?.name}`, `An error occurred while posting${i === 0 ? ' ' : ' comments '}on ${ post.integration?.providerIdentifier }${err?.cause?.message ? `: ${err?.cause?.message}` : ``}`, @@ -222,8 +227,6 @@ export async function postWorkflowV101({ ); return false; } - - return false; } } } @@ -284,7 +287,7 @@ export async function postWorkflowV101({ // process internal plug if (todo.type === 'internal-plug') { - while (true) { + for (const _ of iterate) { try { await processInternalPlug({ ...todo, post: postsResults[0].postId }); } catch (err) { @@ -293,15 +296,25 @@ export async function postWorkflowV101({ err.cause instanceof ApplicationFailure && err.cause.type === 'refresh_token' ) { - const refresh = await refreshToken(post.integration); + const refresh = await refreshToken( + await getIntegrationById(organizationId, todo.integration) + ); if (!refresh || !refresh.accessToken) { - await changeState(postsList[0].id, 'ERROR', err, postsList); - return false; + break; } - post.integration.token = refresh.accessToken; continue; } + + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'bad_body' + ) { + break; + } + + continue; } break; } @@ -309,7 +322,7 @@ export async function postWorkflowV101({ // process global plug if (todo.type === 'global') { - while (true) { + for (const _ of iterate) { try { const process = await processPlug({ ...todo, @@ -338,14 +351,23 @@ export async function postWorkflowV101({ ) { const refresh = await refreshToken(post.integration); if (!refresh || !refresh.accessToken) { - await changeState(postsList[0].id, 'ERROR', err, postsList); - return false; + break; } - post.integration.token = refresh.accessToken; continue; } + + if ( + err instanceof ActivityFailure && + err.cause instanceof ApplicationFailure && + err.cause.type === 'bad_body' + ) { + break; + } + + continue; } + break; } } From 3e8f5ddca6e3015b8f1a6f4e7d31d2f3b76e205b Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 8 Jan 2026 21:44:43 +0700 Subject: [PATCH 124/340] fix: email notifiations --- .../src/database/prisma/notifications/notification.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index fa5f036d..6608ba17 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -101,7 +101,7 @@ export class NotificationService { } async sendEmail(to: string, subject: string, html: string, replyTo?: string) { - await this._emailService.sendEmail(to, subject, html, 'bottom', replyTo); + await this._emailService.sendEmail(to, subject, html, 'top', replyTo); } hasEmailProvider() { From 631d7c15e875c2580d1aa740b6093c83afd8d816 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 9 Jan 2026 10:41:07 +0700 Subject: [PATCH 125/340] feat: better workflow logic --- apps/orchestrator/src/workflows/index.ts | 1 - .../workflows/post-workflows/post.workflow.ts | 368 ------------------ .../post-workflows/post.workflow.v1.0.1.ts | 28 +- 3 files changed, 17 insertions(+), 380 deletions(-) delete mode 100644 apps/orchestrator/src/workflows/post-workflows/post.workflow.ts diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index 22f5fce3..9796a976 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -1,4 +1,3 @@ -export * from './post-workflows/post.workflow'; export * from './post-workflows/post.workflow.v1.0.1'; export * from './autopost.workflow'; export * from './digest.email.workflow'; diff --git a/apps/orchestrator/src/workflows/post-workflows/post.workflow.ts b/apps/orchestrator/src/workflows/post-workflows/post.workflow.ts deleted file mode 100644 index 96e3b06d..00000000 --- a/apps/orchestrator/src/workflows/post-workflows/post.workflow.ts +++ /dev/null @@ -1,368 +0,0 @@ -import { PostActivity } from '@gitroom/orchestrator/activities/post.activity'; -import { - ActivityFailure, - ApplicationFailure, - startChild, - proxyActivities, - sleep, - defineSignal, - setHandler, -} from '@temporalio/workflow'; -import dayjs from 'dayjs'; -import { Integration } from '@prisma/client'; -import { capitalize, sortBy } from 'lodash'; -import { PostResponse } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; -import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; -import { TypedSearchAttributes } from '@temporalio/common'; -import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; - -const proxyTaskQueue = (taskQueue: string) => { - return proxyActivities<PostActivity>({ - startToCloseTimeout: '10 minute', - taskQueue, - retry: { - maximumAttempts: 3, - backoffCoefficient: 1, - initialInterval: '2 minutes', - }, - }); -}; - -const { - getPostsList, - inAppNotification, - changeState, - updatePost, - sendWebhooks, - isCommentable, -} = proxyActivities<PostActivity>({ - startToCloseTimeout: '10 minute', - retry: { - maximumAttempts: 3, - backoffCoefficient: 1, - initialInterval: '2 minutes', - }, -}); - -const poke = defineSignal('poke'); - -export async function postWorkflow({ - taskQueue, - postId, - organizationId, - postNow = false, -}: { - taskQueue: string; - postId: string; - organizationId: string; - postNow?: boolean; -}) { - // Dynamic task queue, for concurrency - const { - postSocial, - postComment, - refreshToken, - internalPlugs, - globalPlugs, - processInternalPlug, - processPlug, - } = proxyTaskQueue(taskQueue); - - let poked = false; - setHandler(poke, () => { - poked = true; - }); - - const startTime = new Date(); - // get all the posts and comments to post - const postsList = await getPostsList(organizationId, postId); - const [post] = postsList; - - // in case doesn't exists for some reason, fail it - if (!post || (!postNow && post.state !== 'QUEUE')) { - return; - } - - // if it's a repeatable post, we should ignore this. - if (!postNow) { - await sleep( - dayjs(post.publishDate).isBefore(dayjs()) - ? 0 - : dayjs(post.publishDate).diff(dayjs(), 'millisecond') - ); - } - - // if refresh is needed from last time, let's inform the user - if (post.integration?.refreshNeeded) { - await inAppNotification( - post.organizationId, - `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, - `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name} because you need to reconnect it. Please enable it and try again.`, - true, - false, - 'info' - ); - return; - } - - // if it's disabled, inform the user - if (post.integration?.disabled) { - await inAppNotification( - post.organizationId, - `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, - `We couldn't post to ${post.integration?.providerIdentifier} for ${post?.integration?.name} because it's disabled. Please enable it and try again.`, - true, - false, - 'info' - ); - return; - } - - // Do we need to post comment for this social? - const toComment = - postsList.length === 1 ? false : await isCommentable(post.integration); - - // list of all the saved results - const postsResults: PostResponse[] = []; - - // iterate over the posts - for (let i = 0; i < postsList.length; i++) { - // this is a small trick to repeat an action in case of token refresh - while (true) { - try { - // first post the main post - if (i === 0) { - postsResults.push( - ...(await postSocial(post.integration as Integration, [ - postsList[i], - ])) - ); - - // then post the comments if any - } else { - if (!toComment) { - break; - } - postsResults.push( - ...(await postComment( - postsResults[0].postId, - postsResults.length === 1 - ? undefined - : postsResults[i - 1].postId, - post.integration, - [postsList[i]] - )) - ); - } - - // mark post as successful - await updatePost( - postsList[i].id, - postsResults[i].postId, - postsResults[i].releaseURL - ); - - // break the current while to move to the next post - break; - } catch (err) { - // if token refresh is needed, do it and repeat - if ( - err instanceof ActivityFailure && - err.cause instanceof ApplicationFailure && - err.cause.type === 'refresh_token' - ) { - const refresh = await refreshToken(post.integration); - if (!refresh || !refresh.accessToken) { - await changeState(postsList[0].id, 'ERROR', err, postsList); - return false; - } - - post.integration.token = refresh.accessToken; - continue; - } - - // for other errors, change state and inform the user if needed - await changeState(postsList[0].id, 'ERROR', err, postsList); - - // specific case for bad body errors - if ( - err instanceof ActivityFailure && - err.cause instanceof ApplicationFailure && - err.cause.type === 'bad_body' - ) { - await inAppNotification( - post.organizationId, - `Error posting on ${post.integration?.providerIdentifier} for ${post?.integration?.name}`, - `An error occurred while posting on ${ - post.integration?.providerIdentifier - }${err?.cause?.message ? `: ${err?.cause?.message}` : ``}`, - true, - false, - 'fail' - ); - return false; - } - - return false; - } - } - } - - // send notification on a sucessful post - await inAppNotification( - post.integration.organizationId, - `Your post has been published on ${capitalize( - post.integration.providerIdentifier - )}`, - `Your post has been published on ${capitalize( - post.integration.providerIdentifier - )} at ${postsResults[0].releaseURL}`, - true, - true - ); - - // send webhooks for the post - await sendWebhooks( - postsResults[0].postId, - post.organizationId, - post.integration.id - ); - - // load internal plugs like repost by other users - const internalPlugsList = await internalPlugs( - post.integration, - JSON.parse(post.settings) - ); - - // load global plugs, like repost a post if it gets to a certain number of likes - const globalPlugsList = (await globalPlugs(post.integration)).reduce( - (all, current) => { - for (let i = 1; i <= current.totalRuns; i++) { - all.push({ - ...current, - delay: current.delay * i, - }); - } - - return all; - }, - [] - ); - - // Check if the post is repeatable - const repeatPost = !post.intervalInDays - ? [] - : [ - { - type: 'repeat-post', - delay: - post.intervalInDays * 24 * 60 * 60 * 1000 - - (new Date().getTime() - startTime.getTime()), - }, - ]; - - // Sort all the actions by delay, so we can process them in order - const list = sortBy( - [...internalPlugsList, ...globalPlugsList, ...repeatPost], - 'delay' - ); - - // process all the plugs in order, we are using while because in some cases we need to remove items from the list - while (list.length > 0) { - // get the next to process - const todo = list.shift(); - - // wait for the delay - await sleep(todo.delay); - - // process internal plug - if (todo.type === 'internal-plug') { - while (true) { - try { - await processInternalPlug({ ...todo, post: postsResults[0].postId }); - } catch (err) { - if ( - err instanceof ActivityFailure && - err.cause instanceof ApplicationFailure && - err.cause.type === 'refresh_token' - ) { - const refresh = await refreshToken(post.integration); - if (!refresh || !refresh.accessToken) { - await changeState(postsList[0].id, 'ERROR', err, postsList); - return false; - } - - post.integration.token = refresh.accessToken; - continue; - } - } - break; - } - } - - // process global plug - if (todo.type === 'global') { - while (true) { - try { - const process = await processPlug({ - ...todo, - postId: postsResults[0].postId, - }); - if (process) { - const toDelete = list - .reduce((all, current, index) => { - if (current.plugId === todo.plugId) { - all.push(index); - } - - return all; - }, []) - .reverse(); - - for (const index of toDelete) { - list.splice(index, 1); - } - } - } catch (err) { - if ( - err instanceof ActivityFailure && - err.cause instanceof ApplicationFailure && - err.cause.type === 'refresh_token' - ) { - const refresh = await refreshToken(post.integration); - if (!refresh || !refresh.accessToken) { - await changeState(postsList[0].id, 'ERROR', err, postsList); - return false; - } - - post.integration.token = refresh.accessToken; - continue; - } - } - break; - } - } - - // process repeat post in a new workflow, this is important so the other plugs can keep running - if (todo.type === 'repeat-post') { - await startChild(postWorkflow, { - parentClosePolicy: 'ABANDON', - args: [ - { - taskQueue, - postId, - organizationId, - postNow: true, - }, - ], - workflowId: `post_${post.id}_${makeId(10)}`, - typedSearchAttributes: new TypedSearchAttributes([ - { - key: postIdSearchParam, - value: postId, - }, - ]), - }); - } - } -} diff --git a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts index 7ca6eafa..b8fce6ac 100644 --- a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts +++ b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.1.ts @@ -46,7 +46,7 @@ const { const poke = defineSignal('poke'); -const iterate = ['post', 'afterRefresh', 'retry1', 'retry2', 'retry3']; +const iterate = Array.from({ length: 5 }); export async function postWorkflowV101({ taskQueue, @@ -78,8 +78,8 @@ export async function postWorkflowV101({ const startTime = new Date(); // get all the posts and comments to post - const postsList = await getPostsList(organizationId, postId); - const [post] = postsList; + const postsListBefore = await getPostsList(organizationId, postId); + const [post] = postsListBefore; // in case doesn't exists for some reason, fail it if (!post || (!postNow && post.state !== 'QUEUE')) { @@ -122,14 +122,19 @@ export async function postWorkflowV101({ } // Do we need to post comment for this social? - const toComment = - postsList.length === 1 ? false : await isCommentable(post.integration); + const toComment: boolean = + postsListBefore.length === 1 + ? false + : await isCommentable(post.integration); + + const postsList = toComment ? postsListBefore : [postsListBefore[0]]; // list of all the saved results const postsResults: PostResponse[] = []; // iterate over the posts for (let i = 0; i < postsList.length; i++) { + const before = postsResults.length; // this is a small trick to repeat an action in case of token refresh for (const _ of iterate) { try { @@ -143,12 +148,8 @@ export async function postWorkflowV101({ // then post the comments if any } else { - if (!toComment) { - break; - } - if (postsList[i].delay) { - await sleep(60000 * postsList[i].delay); + await sleep(60000 * Math.max(0, Number(postsList[i].delay ?? 0))); } postsResults.push( @@ -229,6 +230,11 @@ export async function postWorkflowV101({ } } } + + if (postsResults.length === before) { + // all retries exhausted without success + return false; + } } // send webhooks for the post @@ -283,7 +289,7 @@ export async function postWorkflowV101({ const todo = list.shift(); // wait for the delay - await sleep(todo.delay); + await sleep(Math.max(0, Number(todo.delay ?? 0))); // process internal plug if (todo.type === 'internal-plug') { From 46b12633040457376f0d16dd06f65e6de3582830 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 9 Jan 2026 15:04:16 +0700 Subject: [PATCH 126/340] feat: add comment from another user --- .../components/launches/internal.channels.tsx | 44 +++++++++++++ apps/orchestrator/src/main.ts | 2 + .../social/linkedin.page.provider.ts | 21 +++++- .../integrations/social/linkedin.provider.ts | 64 ++++++++++++++----- 4 files changed, 111 insertions(+), 20 deletions(-) diff --git a/apps/frontend/src/components/launches/internal.channels.tsx b/apps/frontend/src/components/launches/internal.channels.tsx index aa6f2af3..17c91bf2 100644 --- a/apps/frontend/src/components/launches/internal.channels.tsx +++ b/apps/frontend/src/components/launches/internal.channels.tsx @@ -7,6 +7,8 @@ import { PickPlatforms } from '@gitroom/frontend/components/launches/helpers/pic import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; import { Select } from '@gitroom/react/form/select'; import { Slider } from '@gitroom/react/form/slider'; +import { Input } from '@gitroom/react/form/input'; +import { Textarea } from '@gitroom/react/form/textarea'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import clsx from 'clsx'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; @@ -68,6 +70,37 @@ export const InternalChannels: FC<{ </div> ); }; +const PlugField: FC<{ + plugIdentifier: string; + field: { + name: string; + description: string; + type: string; + placeholder: string; + validation?: RegExp; + }; +}> = ({ plugIdentifier, field }) => { + const fieldName = `plug--${plugIdentifier}--${field.name}`; + + if (field.type === 'textarea') { + return ( + <Textarea + label={field.description} + name={fieldName} + placeholder={field.placeholder} + /> + ); + } + + return ( + <Input + label={field.description} + name={fieldName} + placeholder={field.placeholder} + /> + ); +}; + const Plug: FC<{ plug: { identifier: string; @@ -151,6 +184,17 @@ const Plug: FC<{ </option> ))} </Select> + {plug.fields.length > 0 && ( + <div className="flex flex-col gap-[10px]"> + {plug.fields.map((field) => ( + <PlugField + key={field.name} + plugIdentifier={plug.identifier} + field={field} + /> + ))} + </div> + )} <div> {t('accounts_that_will_engage', 'Accounts that will engage:')} </div> diff --git a/apps/orchestrator/src/main.ts b/apps/orchestrator/src/main.ts index 03bf80a2..dacc5743 100644 --- a/apps/orchestrator/src/main.ts +++ b/apps/orchestrator/src/main.ts @@ -5,6 +5,8 @@ dayjs.extend(utc); import { NestFactory } from '@nestjs/core'; import { AppModule } from '@gitroom/orchestrator/app.module'; +import * as dns from 'node:dns'; +dns.setDefaultResultOrder('ipv4first'); async function bootstrap() { // some comment again diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index 8aebb98c..6327a243 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -90,6 +90,21 @@ export class LinkedinPageProvider }; } + override async addComment( + integration: Integration, + originalIntegration: Integration, + postId: string, + information: any, + ) { + return super.addComment( + integration, + originalIntegration, + postId, + information, + false + ); + } + override async repostPostUsers( integration: Integration, originalIntegration: Integration, @@ -293,7 +308,7 @@ export class LinkedinPageProvider { headers: { Authorization: `Bearer ${accessToken}`, - 'Linkedin-Version': '202405', + 'Linkedin-Version': '202511', 'X-Restli-Protocol-Version': '2.0.0', }, } @@ -308,7 +323,7 @@ export class LinkedinPageProvider { headers: { Authorization: `Bearer ${accessToken}`, - 'Linkedin-Version': '202405', + 'Linkedin-Version': '202511', 'X-Restli-Protocol-Version': '2.0.0', }, } @@ -323,7 +338,7 @@ export class LinkedinPageProvider { headers: { Authorization: `Bearer ${accessToken}`, - 'Linkedin-Version': '202405', + 'Linkedin-Version': '202511', 'X-Restli-Protocol-Version': '2.0.0', }, } diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index d0415bfc..941aa8c9 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -188,7 +188,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'Content-Type': 'application/json', 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202511', Authorization: `Bearer ${token}`, }, } @@ -233,7 +233,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'Content-Type': 'application/json', 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202511', Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ @@ -266,7 +266,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { method: 'PUT', headers: { 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202511', Authorization: `Bearer ${accessToken}`, ...(isVideo ? { 'Content-Type': 'application/octet-stream' } @@ -298,7 +298,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { }), headers: { 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202511', 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, }, @@ -554,21 +554,10 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { isPdf ); - console.log({ + const response = await this.fetch(`https://api.linkedin.com/rest/posts`, { method: 'POST', headers: { - 'LinkedIn-Version': '202501', - 'X-Restli-Protocol-Version': '2.0.0', - 'Content-Type': 'application/json', - Authorization: `Bearer ${accessToken}`, - }, - body: JSON.stringify(postPayload), - }); - - const response = await this.fetch('https://api.linkedin.com/rest/posts', { - method: 'POST', - headers: { - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202511', 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, @@ -703,6 +692,47 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { return [this.createPostResponse(commentPostId, commentPost.id, false)]; } + @PostPlug({ + identifier: 'linkedin-add-comment', + title: 'Add comments by a different account', + description: 'Add accounts to comment on your post', + pickIntegration: ['linkedin', 'linkedin-page'], + fields: [ + { + name: 'comment', + description: 'The comment to add to the post', + type: 'textarea', + placeholder: 'Enter your comment here', + }, + ], + }) + async addComment( + integration: Integration, + originalIntegration: Integration, + postId: string, + information: any, + isPersonal = true + ) { + return this.comment( + integration.internalId, + postId, + undefined, + integration.token, + [ + { + id: makeId(10), + message: information.comment, + media: [], + settings: { + post_as_images_carousel: false, + }, + }, + ], + integration, + isPersonal ? 'personal' : 'company' + ); + } + @PostPlug({ identifier: 'linkedin-repost-post-users', title: 'Add Re-posters', From 38cbeb8d952dd612da579ee9a53a891c398aada7 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 13:49:59 +0700 Subject: [PATCH 127/340] feat: change social concurrency --- .../src/integrations/social/instagram.provider.ts | 2 +- .../src/integrations/social/youtube.provider.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index a167a612..e7b58e4d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -33,7 +33,7 @@ export class InstagramProvider 'instagram_manage_comments', 'instagram_manage_insights', ]; - override maxConcurrentJob = 10; + override maxConcurrentJob = 200; editor = 'normal' as const; dto = InstagramDto; maxLength() { diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts index 3b17e8e6..66760cf1 100644 --- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts @@ -50,7 +50,7 @@ const clientAndYoutube = () => { @Rules('YouTube must have on video attachment, it cannot be empty') export class YoutubeProvider extends SocialAbstract implements SocialProvider { - override maxConcurrentJob = 1; // YouTube has strict upload quotas + override maxConcurrentJob = 200; // YouTube has strict upload quotas identifier = 'youtube'; name = 'YouTube'; isBetweenSteps = true; From c5bacf7d4315f3f62618c6a3c3c2d67c82e08497 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 13:50:54 +0700 Subject: [PATCH 128/340] feat: change social concurrency --- .../src/integrations/social/instagram.standalone.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts index 0b853b86..c9413154 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts @@ -30,7 +30,7 @@ export class InstagramStandaloneProvider 'instagram_business_manage_comments', 'instagram_business_manage_insights', ]; - override maxConcurrentJob = 10; // Instagram standalone has stricter limits + override maxConcurrentJob = 200; // Instagram standalone has stricter limits dto = InstagramDto; editor = 'normal' as const; From ce94f9bcc455ebf59ef8765c00b108475a9f3afb Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 14:57:08 +0700 Subject: [PATCH 129/340] fix: dont try to post of parent is not null --- apps/orchestrator/src/activities/post.activity.ts | 7 ++++++- .../integrations/social/instagram.standalone.provider.ts | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index c4514ec5..71200476 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -83,7 +83,12 @@ export class PostActivity { @ActivityMethod() async getPostsList(orgId: string, postId: string) { - return this._postService.getPostsRecursively(postId, true, orgId); + const getPosts = await this._postService.getPostsRecursively(postId, true, orgId); + if (!getPosts || getPosts.length === 0 || getPosts[0].parentPostId) { + return []; + } + + return getPosts; } @ActivityMethod() diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts index c9413154..e0bac74f 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts @@ -30,7 +30,7 @@ export class InstagramStandaloneProvider 'instagram_business_manage_comments', 'instagram_business_manage_insights', ]; - override maxConcurrentJob = 200; // Instagram standalone has stricter limits + override maxConcurrentJob = 200; // Instagram standalone has stricter limits dto = InstagramDto; editor = 'normal' as const; From 31ccf8d3528ea0a7efe904038b60f130bdd08da6 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 15:46:41 +0700 Subject: [PATCH 130/340] fix: rolling id --- .../launches/general.preview.component.tsx | 4 +- .../src/components/new-launch/store.ts | 110 ++++++++++++++---- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/apps/frontend/src/components/launches/general.preview.component.tsx b/apps/frontend/src/components/launches/general.preview.component.tsx index 049891a7..6682b97c 100644 --- a/apps/frontend/src/components/launches/general.preview.component.tsx +++ b/apps/frontend/src/components/launches/general.preview.component.tsx @@ -60,7 +60,7 @@ export const GeneralPreviewComponent: FC<{ index === renderContent.length - 1 ? 'pb-[12px]' : 'pb-[24px]' )} > - <div className="w-[40px] flex flex-col items-center"> + <div className="min-w-[40px] h-[40px] min-h-[40px] w-[40px] flex flex-col items-center"> <div className="relative"> <img src={ @@ -75,7 +75,7 @@ export const GeneralPreviewComponent: FC<{ {current !== 'global' && ( <Image src={`/icons/platforms/${integration?.identifier}.png`} - className="rounded-full absolute z-10 -bottom-[5px] -end-[5px] border border-fifth" + className="min-w-[20px] min-h-[20px] rounded-full absolute z-10 -bottom-[5px] -end-[5px] border border-fifth" alt={integration.identifier} width={20} height={20} diff --git a/apps/frontend/src/components/new-launch/store.ts b/apps/frontend/src/components/new-launch/store.ts index e54585c2..879526ea 100644 --- a/apps/frontend/src/components/new-launch/store.ts +++ b/apps/frontend/src/components/new-launch/store.ts @@ -4,7 +4,6 @@ import { create } from 'zustand'; import dayjs from 'dayjs'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; import { createRef, RefObject } from 'react'; -import { arrayMoveImmutable } from 'array-move'; import { PostComment } from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; @@ -257,22 +256,45 @@ export const useLaunchStore = create<StoreState>()((set) => ({ }; }), deleteGlobalValue: (index: number) => - set((state) => ({ - global: state.global.filter((_, i) => i !== index), - })), + set((state) => { + // Preserve the IDs at their current positions + const ids = state.global.map((item) => item.id); + + // Get remaining data (content, delay, media) after filtering out deleted index + const remainingData = state.global + .filter((_, i) => i !== index) + .map(({ id, ...rest }) => rest); + + // Reconstruct with preserved IDs + return { + global: remainingData.map((data, i) => ({ + id: ids[i], + ...data, + })), + }; + }), deleteInternalValue: (integrationId: string, index: number) => set((state) => { return { - internal: state.internal.map((i) => { - if (i.integration.id === integrationId) { + internal: state.internal.map((item) => { + if (item.integration.id === integrationId) { + // Preserve the IDs at their current positions + const ids = item.integrationValue.map((v) => v.id); + + // Get remaining data after filtering out deleted index + const remainingData = item.integrationValue + .filter((_, idx) => idx !== index) + .map(({ id, ...rest }) => rest); + return { - ...i, - integrationValue: i.integrationValue.filter( - (_, idx) => idx !== index - ), + ...item, + integrationValue: remainingData.map((data, i) => ({ + id: ids[i], + ...data, + })), }; } - return i; + return item; }), }; }), @@ -305,12 +327,35 @@ export const useLaunchStore = create<StoreState>()((set) => ({ }), changeOrderGlobal: (index: number, direction: 'up' | 'down') => set((state) => { + const targetIndex = direction === 'up' ? index - 1 : index + 1; + + if (targetIndex < 0 || targetIndex >= state.global.length) { + return { global: state.global }; + } + + const currentItem = state.global[index]; + const targetItem = state.global[targetIndex]; + return { - global: arrayMoveImmutable( - state.global, - index, - direction === 'up' ? index - 1 : index + 1 - ), + global: state.global.map((item, i) => { + if (i === index) { + return { + id: item.id, + content: targetItem.content, + delay: targetItem.delay, + media: targetItem.media, + }; + } + if (i === targetIndex) { + return { + id: item.id, + content: currentItem.content, + delay: currentItem.delay, + media: currentItem.media, + }; + } + return item; + }), }; }), changeOrderInternal: ( @@ -322,13 +367,36 @@ export const useLaunchStore = create<StoreState>()((set) => ({ return { internal: state.internal.map((item) => { if (item.integration.id === integrationId) { + const targetIndex = direction === 'up' ? index - 1 : index + 1; + + if (targetIndex < 0 || targetIndex >= item.integrationValue.length) { + return item; + } + + const currentValue = item.integrationValue[index]; + const targetValue = item.integrationValue[targetIndex]; + return { ...item, - integrationValue: arrayMoveImmutable( - item.integrationValue, - index, - direction === 'up' ? index - 1 : index + 1 - ), + integrationValue: item.integrationValue.map((v, i) => { + if (i === index) { + return { + id: v.id, + content: targetValue.content, + delay: targetValue.delay, + media: targetValue.media, + }; + } + if (i === targetIndex) { + return { + id: v.id, + content: currentValue.content, + delay: currentValue.delay, + media: currentValue.media, + }; + } + return v; + }), }; } From fca16b5c48af95594e9e22e7dbe1600ea0989b7f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 16:39:45 +0700 Subject: [PATCH 131/340] fix: rolling id --- .../nestjs-libraries/src/services/email.service.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index a6063135..a0dc8311 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -33,7 +33,13 @@ export class EmailService { } } - async sendEmail(to: string, subject: string, html: string, sendTo: 'top' | 'bottom', replyTo?: string) { + async sendEmail( + to: string, + subject: string, + html: string, + sendTo: 'top' | 'bottom', + replyTo?: string + ) { return this._temporalService.client .getRawClient() ?.workflow.signalWithStart('sendEmailWorkflow', { @@ -108,6 +114,9 @@ export class EmailService { color: #1f2937; margin: 0; ">${process.env.EMAIL_FROM_NAME}</h2> + <div style="font-size: 12px"> + You can change your notification preferences in your <a href="${process.env.FRONTEND_URL}/settings">account settings.</a> + </div> </div> </div> </div> From 05b6b2ec003b23f1a1e3cf473d741f8871ee2f40 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 18:22:18 +0700 Subject: [PATCH 132/340] fix: encode instead of decode --- .../src/integrations/social/linkedin.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index 941aa8c9..fdb65685 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -583,7 +583,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { type === 'personal' ? `urn:li:person:${id}` : `urn:li:organization:${id}`; const response = await this.fetch( - `https://api.linkedin.com/v2/socialActions/${decodeURIComponent( + `https://api.linkedin.com/v2/socialActions/${encodeURIComponent( parentPostId )}/comments`, { From fddf61f052b0434e064c9d8ee8df99d5d27bee8d Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 11 Jan 2026 19:30:21 +0700 Subject: [PATCH 133/340] fix: logo --- apps/frontend/src/components/new-layout/logo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/new-layout/logo.tsx b/apps/frontend/src/components/new-layout/logo.tsx index 3c9113ca..b38e6039 100644 --- a/apps/frontend/src/components/new-layout/logo.tsx +++ b/apps/frontend/src/components/new-layout/logo.tsx @@ -8,7 +8,7 @@ export const Logo = () => { height="60" viewBox="0 0 60 60" fill="none" - className="mt-[8px]" + className="mt-[8px] min-w-[60px] min-h-[60px]" > <path d="M12.8816 11.4648C12.9195 12.0781 12.9731 12.7698 13.0342 13.5594L15.2408 42.0719C15.4874 45.2588 15.6107 46.8522 16.3251 48.0214C16.9535 49.0498 17.8913 49.853 19.0042 50.3156C20.2694 50.8416 21.8629 50.7183 25.0497 50.4717L46.8877 48.7817C48.9537 48.6218 50.3501 48.5137 51.3952 48.2628C51.0447 49.0858 50.5039 49.8189 49.8121 50.3992C48.7623 51.2797 47.205 51.6389 44.0905 52.3574L22.7476 57.2805C19.633 57.9989 18.0757 58.3581 16.7463 58.0264C15.5769 57.7346 14.5299 57.0801 13.7554 56.1566C12.8749 55.1069 12.5156 53.5496 11.7972 50.435L5.36942 22.569C4.651 19.4544 4.29178 17.8972 4.62351 16.5677C4.91531 15.3983 5.56982 14.3513 6.49325 13.5768C7.54303 12.6963 9.10032 12.3371 12.2149 11.6186L12.8816 11.4648Z" From b2677649df414aa01490849f387d3a7bda890a13 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 12 Jan 2026 15:53:47 +0700 Subject: [PATCH 134/340] fix: don't modify gif --- .../src/components/media/new.uploader.tsx | 35 ++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index f5d5e2b5..6ad53838 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; // @ts-ignore -import Uppy, { UploadResult } from '@uppy/core'; +import Uppy, { BasePlugin, UploadResult, UppyFile } from '@uppy/core'; // @ts-ignore import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { getUppyUploadPlugin } from '@gitroom/react/helpers/uppy.upload'; @@ -14,6 +14,27 @@ import { useToaster } from '@gitroom/react/toaster/toaster'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { uniq } from 'lodash'; +export class CompressionWrapper<M = any, B = any> extends Compressor<M, B> { + override async prepareUpload(fileIDs: string[]) { + const { files } = this.uppy.getState(); + + // 1) Skip GIFs (and anything missing) + const filteredIDs = fileIDs.filter((id) => { + const f = files[id]; + if (!f) return false; + + const type = f.type ?? ''; + const name = (f.name ?? '').toLowerCase(); + const isGif = type === 'image/gif' || name.endsWith('.gif'); + + return !isGif; + }); + + // 2) Let @uppy/compressor do its work (convert/resize/etc) + return super.prepareUpload(filteredIDs); + } +} + export function MultipartFileUploader({ onUploadSuccess, allowedFileTypes, @@ -88,7 +109,13 @@ export function useUppyUploader(props: { // Expand generic types to specific ones const expandedTypes = allowedTypes.flatMap((type) => { if (type === 'image/*') { - return ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/webp']; + return [ + 'image/png', + 'image/jpeg', + 'image/jpg', + 'image/gif', + 'image/webp', + ]; } if (type === 'video/*') { return ['video/mp4', 'video/mpeg']; @@ -182,8 +209,8 @@ export function useUppyUploader(props: { uppy2.use(plugin, options); if (!disableImageCompression) { - uppy2.use(Compressor, { - convertTypes: ['image/jpeg'], + uppy2.use(CompressionWrapper, { + convertTypes: ['image/jpeg', 'image/png', 'image/webp'], maxWidth: 1000, maxHeight: 1000, quality: 1, From 82d91fd8ed24c24afe09133fc57c4a2562b75048 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 12 Jan 2026 15:54:12 +0700 Subject: [PATCH 135/340] fix: don't modify gif --- apps/frontend/src/components/media/new.uploader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index 6ad53838..540b9e10 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -14,7 +14,7 @@ import { useToaster } from '@gitroom/react/toaster/toaster'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { uniq } from 'lodash'; -export class CompressionWrapper<M = any, B = any> extends Compressor<M, B> { +export class CompressionWrapper<M = any, B = any> extends Compressor<any, any> { override async prepareUpload(fileIDs: string[]) { const { files } = this.uppy.getState(); From 6859f0d04942886da68271c861aaa1d610034828 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 12 Jan 2026 16:15:39 +0700 Subject: [PATCH 136/340] feat: retry no activity --- .../integrations/social/linkedin.provider.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index fdb65685..b07b361c 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -15,6 +15,7 @@ import { LinkedinDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-sett import imageToPDF from 'image-to-pdf'; import { Readable } from 'stream'; import { Rules } from '@gitroom/nestjs-libraries/chat/rules.description.decorator'; +import { string } from 'yup'; @Rules( 'LinkedIn can have maximum one attachment when selecting video, when choosing a carousel on LinkedIn minimum amount of attachment must be two, and only pictures, if uploading a video, LinkedIn can have only one attachment' @@ -40,6 +41,22 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { maxLength() { return 3000; } + + override handleErrors( + body: string + ): + | { type: 'refresh-token' | 'bad-body' | 'retry'; value: string } + | undefined { + if (body.indexOf('Unable to obtain activity') > -1) { + return { + type: 'retry', + value: 'Unable to obtain activity', + }; + } + + return undefined; + } + async refreshToken(refresh_token: string): Promise<AuthTokenDetails> { const { access_token: accessToken, From 56657742606d6995be53ff0e25186ad2e2839e05 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 12 Jan 2026 18:55:24 +0700 Subject: [PATCH 137/340] resend activation email --- .../backend/src/api/routes/auth.controller.ts | 16 +++ .../backend/src/services/auth/auth.service.ts | 23 +++ .../frontend/src/components/auth/activate.tsx | 136 +++++++++++++++++- apps/frontend/src/components/auth/login.tsx | 29 +++- .../src/dtos/auth/resend-activation.dto.ts | 9 ++ 5 files changed, 209 insertions(+), 4 deletions(-) create mode 100644 libraries/nestjs-libraries/src/dtos/auth/resend-activation.dto.ts diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts index 338bf962..fb41f672 100644 --- a/apps/backend/src/api/routes/auth.controller.ts +++ b/apps/backend/src/api/routes/auth.controller.ts @@ -15,6 +15,7 @@ import { LoginUserDto } from '@gitroom/nestjs-libraries/dtos/auth/login.user.dto import { AuthService } from '@gitroom/backend/services/auth/auth.service'; import { ForgotReturnPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot-return.password.dto'; import { ForgotPasswordDto } from '@gitroom/nestjs-libraries/dtos/auth/forgot.password.dto'; +import { ResendActivationDto } from '@gitroom/nestjs-libraries/dtos/auth/resend-activation.dto'; import { ApiTags } from '@nestjs/swagger'; import { getCookieUrlFromDomain } from '@gitroom/helpers/subdomain/subdomain.management'; import { EmailService } from '@gitroom/nestjs-libraries/services/email.service'; @@ -234,6 +235,21 @@ export class AuthController { return response.status(200).json({ can: true }); } + @Post('/resend-activation') + async resendActivation(@Body() body: ResendActivationDto) { + try { + await this._authService.resendActivationEmail(body.email); + return { + success: true, + }; + } catch (e: any) { + return { + success: false, + message: e.message, + }; + } + } + @Post('/oauth/:provider/exists') async oauthExists( @Body('code') code: string, diff --git a/apps/backend/src/services/auth/auth.service.ts b/apps/backend/src/services/auth/auth.service.ts index d2468d0d..c01c2ac5 100644 --- a/apps/backend/src/services/auth/auth.service.ts +++ b/apps/backend/src/services/auth/auth.service.ts @@ -222,6 +222,29 @@ export class AuthService { return false; } + async resendActivationEmail(email: string) { + const user = await this._userService.getUserByEmail(email); + + if (!user) { + throw new Error('User not found'); + } + + if (user.activated) { + throw new Error('Account is already activated'); + } + + const jwt = await this.jwt(user); + + await this._emailService.sendEmail( + user.email, + 'Activate your account', + `Click <a href="${process.env.FRONTEND_URL}/auth/activate/${jwt}">here</a> to activate your account`, + 'top' + ); + + return true; + } + oauthLink(provider: string, query?: any) { const providerInstance = ProvidersFactory.loadProvider( provider as Provider diff --git a/apps/frontend/src/components/auth/activate.tsx b/apps/frontend/src/components/auth/activate.tsx index 3ffbd423..3f3f3cf1 100644 --- a/apps/frontend/src/components/auth/activate.tsx +++ b/apps/frontend/src/components/auth/activate.tsx @@ -1,11 +1,73 @@ 'use client'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { Button } from '@gitroom/react/form/button'; +import { Input } from '@gitroom/react/form/input'; +import { useState, useEffect, useCallback } from 'react'; +import Link from 'next/link'; + +type ResendInputs = { + email: string; +}; + +type ResendStatus = 'idle' | 'sent' | 'already_activated'; + +const COOLDOWN_SECONDS = 60; export function Activate() { const t = useT(); + const fetch = useFetch(); + const [loading, setLoading] = useState(false); + const [status, setStatus] = useState<ResendStatus>('idle'); + const [cooldown, setCooldown] = useState(0); + const form = useForm<ResendInputs>(); + + useEffect(() => { + if (cooldown <= 0) return; + + const timer = setInterval(() => { + setCooldown((prev) => prev - 1); + }, 1000); + + return () => clearInterval(timer); + }, [cooldown]); + + const resetToForm = useCallback(() => { + setStatus('idle'); + setCooldown(COOLDOWN_SECONDS); + }, []); + + const onSubmit: SubmitHandler<ResendInputs> = async (data) => { + setLoading(true); + try { + const response = await fetch('/auth/resend-activation', { + method: 'POST', + body: JSON.stringify(data), + }); + const result = await response.json(); + if (result.success) { + setStatus('sent'); + setCooldown(COOLDOWN_SECONDS); + } else if (result.message === 'Account is already activated') { + setStatus('already_activated'); + } else { + form.setError('email', { + message: result.message || t('failed_to_resend', 'Failed to resend activation email'), + }); + } + } catch (e) { + form.setError('email', { + message: t('error_occurred', 'An error occurred. Please try again.'), + }); + } finally { + setLoading(false); + } + }; + return ( - <div className="flex flex-col"> + <div className="flex flex-col flex-1"> <div> <h1 className="text-3xl font-bold text-start mb-4 cursor-pointer"> {t('activate_your_account', 'Activate your account')} @@ -19,6 +81,78 @@ export function Activate() { 'Please check your email to activate your account.' )} </div> + + <div className="mt-8 border-t border-fifth pt-6"> + <h2 className="text-lg font-semibold mb-4"> + {t('didnt_receive_email', "Didn't receive the email?")} + </h2> + {status === 'sent' ? ( + <div className="flex flex-col gap-4"> + <div className="text-green-400"> + {t( + 'activation_email_sent', + 'Activation email has been sent! Please check your inbox.' + )} + </div> + {cooldown > 0 ? ( + <p className="text-sm text-textColor"> + {t('resend_available_in', 'You can resend in')} {cooldown}s + </p> + ) : ( + <Button + onClick={resetToForm} + className="rounded-[10px] !h-[52px]" + > + {t('send_again', 'Send Again')} + </Button> + )} + </div> + ) : status === 'already_activated' ? ( + <div className="flex flex-col gap-4"> + <div className="text-green-400"> + {t( + 'account_already_activated', + 'Great news! Your account is already activated.' + )} + </div> + <Link href="/auth/login"> + <Button className="rounded-[10px] !h-[52px] w-full"> + {t('go_to_login', 'Go to Login')} + </Button> + </Link> + </div> + ) : ( + <FormProvider {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-4"> + <Input + label={t('label_email', 'Email')} + translationKey="label_email" + {...form.register('email', { required: true })} + type="email" + placeholder={t('email_address', 'Email Address')} + /> + <Button + type="submit" + className="rounded-[10px] !h-[52px]" + loading={loading} + disabled={cooldown > 0} + > + {cooldown > 0 + ? `${t('resend_available_in', 'You can resend in')} ${cooldown}s` + : t('resend_activation_email', 'Resend Activation Email')} + </Button> + </form> + </FormProvider> + )} + {status !== 'already_activated' && ( + <p className="mt-4 text-sm text-textColor"> + {t('already_activated', 'Already activated?')}  + <Link href="/auth/login" className="underline cursor-pointer"> + {t('sign_in', 'Sign In')} + </Link> + </p> + )} + </div> </div> ); } diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index cb194be0..793fc92e 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -24,6 +24,7 @@ type Inputs = { export function Login() { const t = useT(); const [loading, setLoading] = useState(false); + const [notActivated, setNotActivated] = useState(false); const { isGeneral, neynarClientId, billingEnabled, genericOauth } = useVariables(); const resolver = useMemo(() => { @@ -39,6 +40,7 @@ export function Login() { const fetchData = useFetch(); const onSubmit: SubmitHandler<Inputs> = async (data) => { setLoading(true); + setNotActivated(false); const login = await fetchData('/auth/login', { method: 'POST', body: JSON.stringify({ @@ -47,9 +49,14 @@ export function Login() { }), }); if (login.status === 400) { - form.setError('email', { - message: await login.text(), - }); + const errorMessage = await login.text(); + if (errorMessage === 'User is not activated') { + setNotActivated(true); + } else { + form.setError('email', { + message: errorMessage, + }); + } setLoading(false); } }; @@ -103,6 +110,22 @@ export function Login() { placeholder={t('label_password', 'Password')} /> </div> + {notActivated && ( + <div className="bg-amber-500/10 border border-amber-500/30 rounded-[10px] p-4 mb-4"> + <p className="text-amber-400 text-sm mb-2"> + {t( + 'account_not_activated', + 'Your account is not activated yet. Please check your email for the activation link.' + )} + </p> + <Link + href="/auth/activate" + className="text-amber-400 underline hover:font-bold text-sm" + > + {t('resend_activation_email', 'Resend Activation Email')} + </Link> + </div> + )} <div className="text-center mt-6"> <div className="w-full flex"> <Button diff --git a/libraries/nestjs-libraries/src/dtos/auth/resend-activation.dto.ts b/libraries/nestjs-libraries/src/dtos/auth/resend-activation.dto.ts new file mode 100644 index 00000000..5002acf2 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/auth/resend-activation.dto.ts @@ -0,0 +1,9 @@ +import { IsDefined, IsEmail, IsString } from 'class-validator'; + +export class ResendActivationDto { + @IsString() + @IsDefined() + @IsEmail() + email: string; +} + From d7a92e0b926f5cc45c5b7b9ad8e95306012fb2da Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 13 Jan 2026 19:33:25 +0700 Subject: [PATCH 138/340] feat: safe stringify --- .../src/integrations/social.abstract.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 150d642a..4d96e605 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -30,6 +30,20 @@ export class NotEnoughScopes { constructor(public message = 'Not enough scopes') {} } +function safeStringify(obj: any) { + const seen = new WeakSet(); + + return JSON.stringify(obj, (key, value) => { + if (typeof value === 'object' && value !== null) { + if (seen.has(value)) { + return '[Circular]'; + } + seen.add(value); + } + return value; + }); +} + export abstract class SocialAbstract { abstract identifier: string; maxConcurrentJob = 1; @@ -62,7 +76,7 @@ export abstract class SocialAbstract { try { value = await func(); } catch (err) { - const handle = this.handleErrors(JSON.stringify(err)); + const handle = this.handleErrors(safeStringify(err)); value = { err: true, value: 'Unknown Error', ...(handle || {}) }; } @@ -70,12 +84,12 @@ export abstract class SocialAbstract { if (value.type === 'refresh-token') { throw new RefreshToken( '', - JSON.stringify({}), + safeStringify({}), {} as any, value.value || '' ); } - throw new BadBody('', JSON.stringify({}), {} as any, value.value || ''); + throw new BadBody('', safeStringify({}), {} as any, value.value || ''); } return value; From 563e7a257b37b65e44824ef1b047b5f50a4f8947 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 14 Jan 2026 14:10:20 +0700 Subject: [PATCH 139/340] fix: docker-compose for temporal --- Dockerfile.dev | 19 ++- docker-compose.dev.yaml | 1 + docker-compose.yaml | 268 ++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- 4 files changed, 283 insertions(+), 7 deletions(-) create mode 100644 docker-compose.yaml diff --git a/Dockerfile.dev b/Dockerfile.dev index 80d439dc..01ad14d6 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,11 +1,18 @@ -FROM node:22.20-alpine +FROM node:22.20-bookworm-slim ARG NEXT_PUBLIC_VERSION ENV NEXT_PUBLIC_VERSION=$NEXT_PUBLIC_VERSION -RUN apk add --no-cache g++ make py3-pip bash nginx -RUN adduser -D -g 'www' www -RUN mkdir /www -RUN chown -R www:www /var/lib/nginx -RUN chown -R www:www /www +RUN apt-get update && apt-get install -y --no-install-recommends \ + g++ \ + make \ + python3-pip \ + bash \ + nginx \ +&& rm -rf /var/lib/apt/lists/* + +RUN addgroup --system www \ + && adduser --system --ingroup www --home /www --shell /usr/sbin/nologin www \ + && mkdir -p /www \ + && chown -R www:www /www /var/lib/nginx RUN npm --no-update-notifier --no-fund --global install pnpm@10.6.1 pm2 diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml index 38ee985b..91fb1c99 100644 --- a/docker-compose.dev.yaml +++ b/docker-compose.dev.yaml @@ -12,6 +12,7 @@ services: POSTGRES_PASSWORD: postiz-local-pwd POSTGRES_USER: postiz-local POSTGRES_DB: postiz-db-local + TEMPORAL_ADDRESS: "temporal:7233" volumes: - postgres-volume:/var/lib/postgresql/data ports: diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000..7bf244d7 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,268 @@ +services: + postiz: + image: ghcr.io/gitroomhq/postiz-app:latest + container_name: postiz + restart: always + environment: + # === Required Settings + MAIN_URL: 'http://localhost:4007' + FRONTEND_URL: 'http://localhost:4007' + NEXT_PUBLIC_BACKEND_URL: 'http://localhost:4007/api' + JWT_SECRET: 'random string that is unique to every install - just type random characters here!' + DATABASE_URL: 'postgresql://postiz-user:postiz-password@postiz-postgres:5432/postiz-db-local' + REDIS_URL: 'redis://postiz-redis:6379' + BACKEND_INTERNAL_URL: 'http://localhost:3000' + TEMPORAL_ADDRESS: "temporal:7233" + IS_GENERAL: 'true' + DISABLE_REGISTRATION: 'false' + + # === Storage Settings + STORAGE_PROVIDER: 'local' + UPLOAD_DIRECTORY: '/uploads' + NEXT_PUBLIC_UPLOAD_DIRECTORY: '/uploads' + + # === Cloudflare (R2) Settings + # STORAGE_PROVIDER: 'cloudflare' + # CLOUDFLARE_ACCOUNT_ID: 'your-account-id' + # CLOUDFLARE_ACCESS_KEY: 'your-access-key' + # CLOUDFLARE_SECRET_ACCESS_KEY: 'your-secret-access-key' + # CLOUDFLARE_BUCKETNAME: 'your-bucket-name' + # CLOUDFLARE_BUCKET_URL: 'https://your-bucket-url.r2.cloudflarestorage.com/' + # CLOUDFLARE_REGION: 'auto' + + # === Social Media API Settings + X_API_KEY: '' + X_API_SECRET: '' + LINKEDIN_CLIENT_ID: '' + LINKEDIN_CLIENT_SECRET: '' + REDDIT_CLIENT_ID: '' + REDDIT_CLIENT_SECRET: '' + GITHUB_CLIENT_ID: '' + GITHUB_CLIENT_SECRET: '' + BEEHIIVE_API_KEY: '' + BEEHIIVE_PUBLICATION_ID: '' + THREADS_APP_ID: '' + THREADS_APP_SECRET: '' + FACEBOOK_APP_ID: '' + FACEBOOK_APP_SECRET: '' + YOUTUBE_CLIENT_ID: '' + YOUTUBE_CLIENT_SECRET: '' + TIKTOK_CLIENT_ID: '' + TIKTOK_CLIENT_SECRET: '' + PINTEREST_CLIENT_ID: '' + PINTEREST_CLIENT_SECRET: '' + DRIBBBLE_CLIENT_ID: '' + DRIBBBLE_CLIENT_SECRET: '' + DISCORD_CLIENT_ID: '' + DISCORD_CLIENT_SECRET: '' + DISCORD_BOT_TOKEN_ID: '' + SLACK_ID: '' + SLACK_SECRET: '' + SLACK_SIGNING_SECRET: '' + MASTODON_URL: 'https://mastodon.social' + MASTODON_CLIENT_ID: '' + MASTODON_CLIENT_SECRET: '' + + # === OAuth & Authentik Settings + # NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME: 'Authentik' + # NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL: 'https://raw.githubusercontent.com/walkxcode/dashboard-icons/master/png/authentik.png' + # POSTIZ_GENERIC_OAUTH: 'false' + # POSTIZ_OAUTH_URL: 'https://auth.example.com' + # POSTIZ_OAUTH_AUTH_URL: 'https://auth.example.com/application/o/authorize' + # POSTIZ_OAUTH_TOKEN_URL: 'https://auth.example.com/application/o/token' + # POSTIZ_OAUTH_USERINFO_URL: 'https://authentik.example.com/application/o/userinfo' + # POSTIZ_OAUTH_CLIENT_ID: '' + # POSTIZ_OAUTH_CLIENT_SECRET: '' + # POSTIZ_OAUTH_SCOPE: "openid profile email" # Optional: uncomment to override default scope + + # === Sentry + + # NEXT_PUBLIC_SENTRY_DSN: 'http://spotlight:8969/stream' + # SENTRY_SPOTLIGHT: '1' + + # === Misc Settings + OPENAI_API_KEY: '' + NEXT_PUBLIC_DISCORD_SUPPORT: '' + NEXT_PUBLIC_POLOTNO: '' + API_LIMIT: 30 + + # === Payment / Stripe Settings + FEE_AMOUNT: 0.05 + STRIPE_PUBLISHABLE_KEY: '' + STRIPE_SECRET_KEY: '' + STRIPE_SIGNING_KEY: '' + STRIPE_SIGNING_KEY_CONNECT: '' + + # === Developer Settings + NX_ADD_PLUGINS: false + + # === Short Link Service Settings (Optional - leave blank if unused) + # DUB_TOKEN: "" + # DUB_API_ENDPOINT: "https://api.dub.co" + # DUB_SHORT_LINK_DOMAIN: "dub.sh" + # SHORT_IO_SECRET_KEY: "" + # KUTT_API_KEY: "" + # KUTT_API_ENDPOINT: "https://kutt.it/api/v2" + # KUTT_SHORT_LINK_DOMAIN: "kutt.it" + # LINK_DRIP_API_KEY: "" + # LINK_DRIP_API_ENDPOINT: "https://api.linkdrip.com/v1/" + # LINK_DRIP_SHORT_LINK_DOMAIN: "dripl.ink" + + volumes: + - postiz-config:/config/ + - postiz-uploads:/uploads/ + ports: + - "4007:5000" + networks: + - postiz-network + - temporal-network + depends_on: + postiz-postgres: + condition: service_healthy + postiz-redis: + condition: service_healthy + + postiz-postgres: + image: postgres:17-alpine + container_name: postiz-postgres + restart: always + environment: + POSTGRES_PASSWORD: postiz-password + POSTGRES_USER: postiz-user + POSTGRES_DB: postiz-db-local + volumes: + - postgres-volume:/var/lib/postgresql/data + networks: + - postiz-network + healthcheck: + test: pg_isready -U postiz-user -d postiz-db-local + interval: 10s + timeout: 3s + retries: 3 + postiz-redis: + image: redis:7.2 + container_name: postiz-redis + restart: always + healthcheck: + test: redis-cli ping + interval: 10s + timeout: 3s + retries: 3 + volumes: + - postiz-redis-data:/data + networks: + - postiz-network + + # For Application Monitoring / Debugging + spotlight: + pull_policy: always + container_name: spotlight + ports: + - 8969:8969/tcp + image: ghcr.io/getsentry/spotlight:latest + networks: + - postiz-network + + # ----------------------- + # Temporal Stack + # ----------------------- + temporal-elasticsearch: + container_name: temporal-elasticsearch + image: elasticsearch:7.17.27 + environment: + - cluster.routing.allocation.disk.threshold_enabled=true + - cluster.routing.allocation.disk.watermark.low=512mb + - cluster.routing.allocation.disk.watermark.high=256mb + - cluster.routing.allocation.disk.watermark.flood_stage=128mb + - discovery.type=single-node + - ES_JAVA_OPTS=-Xms256m -Xmx256m + - xpack.security.enabled=false + networks: + - temporal-network + expose: + - 9200 + volumes: + - /var/lib/elasticsearch/data + + temporal-postgresql: + container_name: temporal-postgresql + image: postgres:16 + environment: + POSTGRES_PASSWORD: temporal + POSTGRES_USER: temporal + networks: + - temporal-network + expose: + - 5432 + volumes: + - /var/lib/postgresql/data + + temporal: + container_name: temporal + ports: + - '7233:7233' + image: temporalio/auto-setup:1.28.1 + depends_on: + - temporal-postgresql + - temporal-elasticsearch + environment: + - DB=postgres12 + - DB_PORT=5432 + - POSTGRES_USER=temporal + - POSTGRES_PWD=temporal + - POSTGRES_SEEDS=temporal-postgresql + - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml + - ENABLE_ES=true + - ES_SEEDS=temporal-elasticsearch + - ES_VERSION=v7 + - TEMPORAL_NAMESPACE=default + networks: + - temporal-network + volumes: + - ./dynamicconfig:/etc/temporal/config/dynamicconfig + labels: + kompose.volume.type: configMap + + temporal-admin-tools: + container_name: temporal-admin-tools + image: temporalio/admin-tools:1.28.1-tctl-1.18.4-cli-1.4.1 + environment: + - TEMPORAL_ADDRESS=temporal:7233 + - TEMPORAL_CLI_ADDRESS=temporal:7233 + networks: + - temporal-network + stdin_open: true + depends_on: + - temporal + tty: true + + temporal-ui: + container_name: temporal-ui + image: temporalio/ui:2.34.0 + environment: + - TEMPORAL_ADDRESS=temporal:7233 + - TEMPORAL_CORS_ORIGINS=http://127.0.0.1:3000 + networks: + - temporal-network + ports: + - '8080:8080' + +volumes: + postgres-volume: + external: false + + postiz-redis-data: + external: false + + postiz-config: + external: false + + postiz-uploads: + external: false + +networks: + postiz-network: + external: false + temporal-network: + driver: bridge + name: temporal-network diff --git a/package.json b/package.json index fad2eab8..5973b71f 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "dev:docker": "docker compose -f ./docker-compose.dev.yaml up -d", "commands:build:development": "pnpm --filter ./apps/commands run build", "prisma-generate": "pnpm dlx prisma@6.5.0 generate --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", - "prisma-db-push": "pnpm dlx prisma@6.5.0 db push --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", + "prisma-db-push": "pnpm dlx prisma@6.5.0 db push --accept-data-loss --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-db-pull": "pnpm dlx prisma@6.5.0 db pull --schema ./libraries/nestjs-libraries/src/database/prisma/schema.prisma", "prisma-reset": "cd ./libraries/nestjs-libraries/src/database/prisma && pnpm dlx prisma@6.5.0 db push --force-reset && pnpx prisma@6.5.0 db push", "docker-build": "./var/docker/docker-build.sh", From 0af727e0cfb067695f97156fa72fe1438307656c Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:32:05 +0700 Subject: [PATCH 140/340] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 635e3640..bee16081 100644 --- a/README.md +++ b/README.md @@ -88,11 +88,11 @@ ## Tech Stack -- NX (Monorepo) +- Pnpm workspaces (Monorepo) - NextJS (React) - NestJS - Prisma (Default to PostgreSQL) -- Redis (BullMQ) +- Temporal - Resend (email notifications) ## Quick Start From abd73039c3f753300cf52c1b63d85317ee4a3137 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 16 Jan 2026 18:01:18 +0700 Subject: [PATCH 141/340] fix: support much smaller resolutions --- apps/frontend/src/components/billing/embedded.billing.tsx | 2 +- .../src/components/billing/first.billing.component.tsx | 2 +- apps/frontend/src/components/layout/top.menu.tsx | 4 ++-- apps/frontend/src/components/new-layout/billing.after.tsx | 2 +- .../src/components/new-layout/layout.component.tsx | 2 +- apps/frontend/src/components/new-layout/menu-item.tsx | 4 ++-- apps/frontend/tailwind.config.js | 3 +++ i18n.lock | 7 ++++--- .../src/translation/locales/ar/translation.json | 7 ++++--- .../src/translation/locales/bn/translation.json | 5 +++-- .../src/translation/locales/de/translation.json | 5 +++-- .../src/translation/locales/en/translation.json | 6 +++--- .../src/translation/locales/es/translation.json | 5 +++-- .../src/translation/locales/fr/translation.json | 5 +++-- .../src/translation/locales/he/translation.json | 5 +++-- .../src/translation/locales/it/translation.json | 5 +++-- .../src/translation/locales/ja/translation.json | 7 ++++--- .../src/translation/locales/ko/translation.json | 7 ++++--- .../src/translation/locales/pt/translation.json | 5 +++-- .../src/translation/locales/ru/translation.json | 5 +++-- .../src/translation/locales/tr/translation.json | 7 ++++--- .../src/translation/locales/vi/translation.json | 7 ++++--- .../src/translation/locales/zh/translation.json | 5 +++-- 23 files changed, 65 insertions(+), 47 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index 713c57e8..b0c26104 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -191,7 +191,7 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { —{' '} </span> <span className="text-textColor font-[600]"> - {t('billing_cancel_anytime_short', 'Cancel anytime.')} + {t('billing_cancel_anytime_short', 'Cancel anytime from settings')} </span> </div> ) : null} diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index 14316ae0..2cda0593 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -125,7 +125,7 @@ export const FirstBillingComponent = () => { <CheckIconComponent /> </div> <div> - {t('billing_cancel_anytime', 'Cancel anytime, hassle-free')} + {t('billing_cancel_anytime', 'Cancel anytime, from settings')} </div> </div> </div> diff --git a/apps/frontend/src/components/layout/top.menu.tsx b/apps/frontend/src/components/layout/top.menu.tsx index ed7a454a..43f02215 100644 --- a/apps/frontend/src/components/layout/top.menu.tsx +++ b/apps/frontend/src/components/layout/top.menu.tsx @@ -271,7 +271,7 @@ export const TopMenu: FC = () => { const { isGeneral, billingEnabled } = useVariables(); return ( <> - <div className="flex flex-1 flex-col gap-[16px] blurMe"> + <div className="flex flex-1 flex-col minCustom:gap-[16px] blurMe"> { // @ts-ignore user?.orgId && @@ -303,7 +303,7 @@ export const TopMenu: FC = () => { )) } </div> - <div className="flex flex-col gap-[16px] blurMe"> + <div className="flex flex-col minCustom:gap-[16px] blurMe"> {secondMenu .filter((f) => { if (f.hide) { diff --git a/apps/frontend/src/components/new-layout/billing.after.tsx b/apps/frontend/src/components/new-layout/billing.after.tsx index 45628527..c10e2f3a 100644 --- a/apps/frontend/src/components/new-layout/billing.after.tsx +++ b/apps/frontend/src/components/new-layout/billing.after.tsx @@ -89,7 +89,7 @@ export const BillingAfter = () => { </svg> </div> <div> - {t('cancel_anytime_hassle_free', 'Cancel anytime, hassle-free')} + {t('cancel_anytime_hassle_free', 'Cancel anytime, from settings')} </div> </div> </div> diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index 8272e535..2d0d070e 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -100,7 +100,7 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => { <div className={clsx( 'fixed h-full w-[64px] start-[17px] flex flex-1 top-0', - user?.admin && 'pt-[60px]' + user?.admin && 'pt-[60px] max-h-[1000px]:w-[500px]' )} > <div className="flex flex-col h-full gap-[32px] flex-1 py-[12px]"> diff --git a/apps/frontend/src/components/new-layout/menu-item.tsx b/apps/frontend/src/components/new-layout/menu-item.tsx index 9c860b7c..cb9e21f1 100644 --- a/apps/frontend/src/components/new-layout/menu-item.tsx +++ b/apps/frontend/src/components/new-layout/menu-item.tsx @@ -17,11 +17,11 @@ export const MenuItem: FC<{ label: string; icon: ReactNode; path: string }> = ({ prefetch={true} href={path} className={clsx( - 'w-full h-[54px] py-[8px] px-[6px] gap-[4px] flex flex-col text-[10px] font-[600] items-center justify-center rounded-[12px] hover:text-textItemFocused hover:bg-boxFocused', + 'w-full minCustom:h-[54px] custom:h-[30px] py-[8px] px-[6px] gap-[4px] flex flex-col custom:flex-row text-[10px] font-[600] items-center minCustom:justify-center rounded-[12px] hover:text-textItemFocused hover:bg-boxFocused', isActive ? 'text-textItemFocused bg-boxFocused' : 'text-textItemBlur' )} > - <div>{icon}</div> + <div className="custom:hidden">{icon}</div> <div className="text-[10px]">{label}</div> </Link> ); diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index 4eb7bb39..cb2ccce8 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -254,6 +254,9 @@ module.exports = { maxMedia: { raw: '(max-width: 1400px)', }, + minCustom: { + raw: '(min-height: 800px)', + }, custom: { raw: '(max-height: 800px)', }, diff --git a/i18n.lock b/i18n.lock index a7b7dcb3..c7aa8a64 100644 --- a/i18n.lock +++ b/i18n.lock @@ -27,6 +27,7 @@ checksums: you_can_add_signatures_to_your_account_to_be_used_in_your_posts: 14ee7a89e90ddbca11da6abf70a25ab0 content: 669b337031ef165af577f04f2b7fd54f auto_add: 35002db73ce495c51c5e7ebb9ea6c480 + delay_comment: 1e29aaf362d5a0b52c57b692f3c18064 actions: c46571856723b03262fd33f511116298 use_signature: 362135c08a0566c7cbfdf331c67c0b21 add_a_signature: 341a71b74ca1907f7d87a4a34232e95d @@ -154,7 +155,7 @@ checksums: to_manage_all_your_social_media_channels: 155f69562b7a791691489f6c13f8ce50 100_no_risk_trial: 4d8cf3bef45c64d898a678c8d2d7e32f pay_nothing_for_the_first_7_days: f85904a77eed00c8009f59b214e15ea1 - cancel_anytime_hassle_free: f69ae39eec3fb9eb6114481d67c8481e + cancel_anytime_hassle_free: 91355fdc2e1f62fcb28b1d74ed342a90 add_free_subscription: 3cf6d7072004d151e88aafd3f227d9dd currently_impersonating: a116d1c0c69a389131357a418589f372 user_1: aaf867586dbad4bbeb4af66612694ca7 @@ -513,7 +514,7 @@ checksums: billing_postiz_grow_social: 3cf5ab166df9cb65e17b9a039ccbbbad billing_no_risk_trial: 6e5c60d9ddf3affa8ba8870272f9f9ab billing_pay_nothing_7_days: 2db15615cad39981069b65e6a0852b18 - billing_cancel_anytime: f69ae39eec3fb9eb6114481d67c8481e + billing_cancel_anytime: 91355fdc2e1f62fcb28b1d74ed342a90 billing_choose_plan: 6eacca8177c43945435ac9c97a1e9734 billing_monthly: 818f1192e32bb855597f930d3e78806e billing_yearly: 87f43e016c19cb25860f456549a2f431 @@ -536,7 +537,7 @@ checksums: billing_your_7_day_trial_is: 4b59fb559f5fd520668974e909e3479c billing_100_percent_free: 6616fd6ee152264c06dd2537f6347e66 billing_ending: f66133a14aa7d86ea2453df97de12cd5 - billing_cancel_anytime_short: 5b1b4a998fd56b18e4153dd83c84022c + billing_cancel_anytime_short: b170ff9c26b8f0263a11f8e292d43da3 billing_pay_0_start_trial: 28e72154e6cce7541e707b35f3a67309 billing_pay_now: 50cb14454e1b2df4a2f83bf1ac799819 billing_per_month: 6293d01c3d13f6938d47285122bd1a48 diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index 54d69655..89c82b7c 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "يمكنك إضافة تواقيع إلى حسابك لاستخدامها في منشوراتك.", "content": "المحتوى", "auto_add": "إضافة تلقائية؟", + "delay_comment": "تعليق التأخير", "actions": "الإجراءات", "use_signature": "استخدم التوقيع", "add_a_signature": "أضف توقيعًا", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "لإدارة جميع قنوات التواصل الاجتماعي الخاصة بك", "100_no_risk_trial": "تجربة خالية من المخاطر 100%", "pay_nothing_for_the_first_7_days": "لا تدفع شيئًا لأول 7 أيام", - "cancel_anytime_hassle_free": "يمكنك الإلغاء في أي وقت وبدون عناء", + "cancel_anytime_hassle_free": "يمكنك الإلغاء في أي وقت من الإعدادات", "add_free_subscription": "-- أضف اشتراكًا مجانيًا --", "currently_impersonating": "يتم الآن الانتحال", "user_1": "المستخدم:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz لتنمية حضورهم على وسائل التواصل الاجتماعي", "billing_no_risk_trial": "تجربة مجانية بدون أي مخاطرة 100%", "billing_pay_nothing_7_days": "لا تدفع شيئًا لأول 7 أيام", - "billing_cancel_anytime": "يمكنك الإلغاء في أي وقت، بدون متاعب", + "billing_cancel_anytime": "يمكنك الإلغاء في أي وقت من الإعدادات", "billing_choose_plan": "اختر خطة", "billing_monthly": "شهريًا", "billing_yearly": "سنويًا", @@ -532,7 +533,7 @@ "billing_your_7_day_trial_is": "فترة التجربة المجانية لمدة 7 أيام الخاصة بك هي", "billing_100_percent_free": "مجانية 100%", "billing_ending": "تنتهي", - "billing_cancel_anytime_short": "يمكنك الإلغاء في أي وقت.", + "billing_cancel_anytime_short": "يمكنك الإلغاء في أي وقت من الإعدادات", "billing_pay_0_start_trial": "ادفع 0 دولار اليوم - ابدأ تجربتك المجانية!", "billing_pay_now": "ادفع الآن", "billing_per_month": "/ شهريًا", diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index b57d2b4d..88069dd5 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "আপনি আপনার পোস্টে ব্যবহারের জন্য আপনার অ্যাকাউন্টে স্বাক্ষর যোগ করতে পারেন।", "content": "বিষয়বস্তু", "auto_add": "স্বয়ংক্রিয় যোগ?", + "delay_comment": "বিলম্ব মন্তব্য", "actions": "কার্যক্রম", "use_signature": "স্বাক্ষর ব্যবহার করুন", "add_a_signature": "একটি স্বাক্ষর যোগ করুন", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "আপনার সব সোশ্যাল মিডিয়া চ্যানেল পরিচালনা করতে", "100_no_risk_trial": "১০০% ঝুঁকিমুক্ত ট্রায়াল", "pay_nothing_for_the_first_7_days": "প্রথম ৭ দিনের জন্য কিছুই পেমেন্ট করুন না", - "cancel_anytime_hassle_free": "যেকোনো সময় ঝামেলামুক্তভাবে বাতিল করুন", + "cancel_anytime_hassle_free": "যেকোনো সময়, সেটিংস থেকে বাতিল করুন", "add_free_subscription": "-- বিনামূল্যে সাবস্ক্রিপশন যোগ করুন --", "currently_impersonating": "বর্তমানে ছদ্মবেশ ধারণ করছেন", "user_1": "ব্যবহারকারী:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz তাদের সামাজিক উপস্থিতি বাড়াতে", "billing_no_risk_trial": "১০০% ঝুঁকিমুক্ত ফ্রি ট্রায়াল", "billing_pay_nothing_7_days": "প্রথম ৭ দিন কিছুই দিতে হবে না", - "billing_cancel_anytime": "যেকোনো সময় বাতিল করুন, ঝামেলা ছাড়াই", + "billing_cancel_anytime": "যেকোনো সময়, সেটিংস থেকে বাতিল করুন", "billing_choose_plan": "একটি প্ল্যান বেছে নিন", "billing_monthly": "মাসিক", "billing_yearly": "বার্ষিক", diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index 15e28284..23b5a293 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Sie können ihrem Konto Signaturen hinzufügen, die in Ihren Beiträgen verwendet werden können.", "content": "Inhalt", "auto_add": "Automatisch hinzufügen?", + "delay_comment": "Verzögerungskommentar", "actions": "Aktionen", "use_signature": "Signatur verwenden", "add_a_signature": "Signatur hinzufügen", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Um all deine Social-Media-Kanäle zu verwalten", "100_no_risk_trial": "100% risikofreie Testphase", "pay_nothing_for_the_first_7_days": "Zahle in den ersten 7 Tagen nichts", - "cancel_anytime_hassle_free": "Jederzeit kündbar, ganz ohne Aufwand", + "cancel_anytime_hassle_free": "Jederzeit kündigen, in den Einstellungen", "add_free_subscription": "-- KOSTENLOSES ABONNEMENT HINZUFÜGEN --", "currently_impersonating": "Derzeit als jemand anderes angemeldet", "user_1": "Benutzer:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz, um ihre Social-Media-Präsenz zu steigern", "billing_no_risk_trial": "100% risikofreie Testphase", "billing_pay_nothing_7_days": "Zahlen Sie NICHTS in den ersten 7 Tagen", - "billing_cancel_anytime": "Jederzeit kündbar, ganz ohne Aufwand", + "billing_cancel_anytime": "Jederzeit kündigen, in den Einstellungen", "billing_choose_plan": "Wählen Sie einen Plan", "billing_monthly": "Monatlich", "billing_yearly": "Jährlich", diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index 0cad5c0d..6c0b78be 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -151,7 +151,7 @@ "to_manage_all_your_social_media_channels": "To Manage All Your Social Media Channels", "100_no_risk_trial": "100% no-risk trial", "pay_nothing_for_the_first_7_days": "Pay nothing for the first 7 days", - "cancel_anytime_hassle_free": "Cancel anytime, hassle-free", + "cancel_anytime_hassle_free": "Cancel anytime, from settings", "add_free_subscription": "-- ADD FREE SUBSCRIPTION --", "currently_impersonating": "Currently Impersonating", "user_1": "user:", @@ -510,7 +510,7 @@ "billing_postiz_grow_social": "Postiz To Grow Their Social Presence", "billing_no_risk_trial": "100% No-Risk Free Trial", "billing_pay_nothing_7_days": "Pay NOTHING for the first 7-days", - "billing_cancel_anytime": "Cancel anytime, hassle-free", + "billing_cancel_anytime": "Cancel anytime, from settings", "billing_choose_plan": "Choose a Plan", "billing_monthly": "Monthly", "billing_yearly": "Yearly", @@ -533,7 +533,7 @@ "billing_your_7_day_trial_is": "Your 7-day trial is", "billing_100_percent_free": "100% free", "billing_ending": "ending", - "billing_cancel_anytime_short": "Cancel anytime.", + "billing_cancel_anytime_short": "Cancel anytime from settings", "billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!", "billing_pay_now": "Pay Now", "billing_per_month": "/ month", diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index c9b6afe6..c8b9fd37 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Puedes agregar firmas a tu cuenta para usarlas en tus publicaciones.", "content": "Contenido", "auto_add": "¿Agregar automáticamente?", + "delay_comment": "Comentario de demora", "actions": "Acciones", "use_signature": "Usar firma", "add_a_signature": "Agregar una firma", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Para gestionar todos tus canales de redes sociales", "100_no_risk_trial": "Prueba sin riesgo 100%", "pay_nothing_for_the_first_7_days": "No pagues nada durante los primeros 7 días", - "cancel_anytime_hassle_free": "Cancela en cualquier momento, sin complicaciones", + "cancel_anytime_hassle_free": "Cancela en cualquier momento, desde la configuración", "add_free_subscription": "-- AÑADIR SUSCRIPCIÓN GRATUITA --", "currently_impersonating": "Actualmente suplantando", "user_1": "usuario:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz para hacer crecer su presencia en redes sociales", "billing_no_risk_trial": "Prueba gratuita 100% sin riesgo", "billing_pay_nothing_7_days": "No pagues NADA durante los primeros 7 días", - "billing_cancel_anytime": "Cancela en cualquier momento, sin complicaciones", + "billing_cancel_anytime": "Cancela en cualquier momento, desde la configuración", "billing_choose_plan": "Elige un plan", "billing_monthly": "Mensual", "billing_yearly": "Anual", diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index 5fa04566..a542ad60 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Vous pouvez ajouter des signatures à votre compte pour les utiliser dans vos publications.", "content": "Contenu", "auto_add": "Ajout automatique ?", + "delay_comment": "Commentaire de retard", "actions": "Actions", "use_signature": "Utiliser la signature", "add_a_signature": "Ajouter une signature", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Pour gérer tous vos réseaux sociaux", "100_no_risk_trial": "Essai 100% sans risque", "pay_nothing_for_the_first_7_days": "Ne payez rien pendant les 7 premiers jours", - "cancel_anytime_hassle_free": "Annulez à tout moment, sans tracas", + "cancel_anytime_hassle_free": "Annulez à tout moment, depuis les paramètres", "add_free_subscription": "-- AJOUTER UN ABONNEMENT GRATUIT --", "currently_impersonating": "Actuellement en mode usurpation", "user_1": "utilisateur :", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz pour développer leur présence sur les réseaux sociaux", "billing_no_risk_trial": "Essai gratuit 100% sans risque", "billing_pay_nothing_7_days": "Ne payez RIEN pendant les 7 premiers jours", - "billing_cancel_anytime": "Annulez à tout moment, sans tracas", + "billing_cancel_anytime": "Annulez à tout moment, depuis les paramètres", "billing_choose_plan": "Choisissez un forfait", "billing_monthly": "Mensuel", "billing_yearly": "Annuel", diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index 6456456c..bec164b6 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "ניתן להוסיף חתימות לחשבונך לשימוש בפוסטים שלך.", "content": "תוכן", "auto_add": "הוספה אוטומטית?", + "delay_comment": "הערת עיכוב", "actions": "פעולות", "use_signature": "השתמש בחתימה", "add_a_signature": "הוסף חתימה", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "לניהול כל ערוצי המדיה החברתית שלך", "100_no_risk_trial": "100% ניסיון ללא סיכון", "pay_nothing_for_the_first_7_days": "לא תשלם כלום ב-7 הימים הראשונים", - "cancel_anytime_hassle_free": "ביטול בכל עת, ללא טרחה", + "cancel_anytime_hassle_free": "ניתן לבטל בכל עת, מההגדרות", "add_free_subscription": "-- הוסף מנוי חינמי --", "currently_impersonating": "מתחזה כרגע", "user_1": "משתמש:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz כדי להגדיל את הנוכחות החברתית שלהם", "billing_no_risk_trial": "ניסיון חינם ללא סיכון ב-100%", "billing_pay_nothing_7_days": "לא משלמים כלום ב-7 הימים הראשונים", - "billing_cancel_anytime": "ניתן לבטל בכל עת, ללא טרחה", + "billing_cancel_anytime": "ניתן לבטל בכל עת, מההגדרות", "billing_choose_plan": "בחרו תוכנית", "billing_monthly": "חודשי", "billing_yearly": "שנתי", diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index 63357a4d..27c7e7bf 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Puoi aggiungere firme al tuo account da utilizzare nei tuoi post.", "content": "Contenuto", "auto_add": "Aggiunta automatica?", + "delay_comment": "Commento sul ritardo", "actions": "Azioni", "use_signature": "Usa firma", "add_a_signature": "Aggiungi una firma", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Per gestire tutti i tuoi canali social", "100_no_risk_trial": "Prova senza rischi al 100%", "pay_nothing_for_the_first_7_days": "Non paghi nulla per i primi 7 giorni", - "cancel_anytime_hassle_free": "Annulla in qualsiasi momento, senza problemi", + "cancel_anytime_hassle_free": "Annulla in qualsiasi momento, dalle impostazioni", "add_free_subscription": "-- AGGIUNGI ABBONAMENTO GRATUITO --", "currently_impersonating": "Attualmente stai impersonando", "user_1": "utente:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz per far crescere la loro presenza sui social", "billing_no_risk_trial": "Prova gratuita senza rischi al 100%", "billing_pay_nothing_7_days": "Non paghi NULLA per i primi 7 giorni", - "billing_cancel_anytime": "Annulla in qualsiasi momento, senza problemi", + "billing_cancel_anytime": "Annulla in qualsiasi momento, dalle impostazioni", "billing_choose_plan": "Scegli un piano", "billing_monthly": "Mensile", "billing_yearly": "Annuale", diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index 3d157ec0..5e3c732a 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "投稿で使用するための署名をアカウントに追加できます。", "content": "内容", "auto_add": "自動追加?", + "delay_comment": "遅延コメント", "actions": "操作", "use_signature": "署名を使用", "add_a_signature": "署名を追加", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "すべてのソーシャルメディアチャンネルを管理するために", "100_no_risk_trial": "100%リスクなしのトライアル", "pay_nothing_for_the_first_7_days": "最初の7日間は無料", - "cancel_anytime_hassle_free": "いつでも簡単にキャンセル可能", + "cancel_anytime_hassle_free": "いつでも設定からキャンセルできます", "add_free_subscription": "-- 無料サブスクリプションを追加 --", "currently_impersonating": "現在なりすまし中", "user_1": "ユーザー:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postizでソーシャルプレゼンスを成長させる", "billing_no_risk_trial": "100%リスクなしの無料トライアル", "billing_pay_nothing_7_days": "最初の7日間は一切料金不要", - "billing_cancel_anytime": "いつでもキャンセル可能、手間なし", + "billing_cancel_anytime": "いつでも設定からキャンセルできます", "billing_choose_plan": "プランを選択", "billing_monthly": "月額", "billing_yearly": "年額", @@ -532,7 +533,7 @@ "billing_your_7_day_trial_is": "7日間の無料トライアルは", "billing_100_percent_free": "完全に無料", "billing_ending": "終了します", - "billing_cancel_anytime_short": "いつでもキャンセル可能。", + "billing_cancel_anytime_short": "設定からいつでもキャンセル可能", "billing_pay_0_start_trial": "本日のお支払いは0円 - 無料トライアルを始めましょう!", "billing_pay_now": "今すぐ支払う", "billing_per_month": "/月", diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index 7212ad82..783dfb07 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "게시물에 사용할 수 있도록 계정에 서명을 추가할 수 있습니다.", "content": "내용", "auto_add": "자동 추가?", + "delay_comment": "지연 코멘트", "actions": "작업", "use_signature": "서명 사용", "add_a_signature": "서명 추가", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "모든 소셜 미디어 채널을 관리하세요", "100_no_risk_trial": "100% 무위험 체험", "pay_nothing_for_the_first_7_days": "처음 7일 동안은 결제되지 않습니다", - "cancel_anytime_hassle_free": "언제든지 간편하게 취소 가능", + "cancel_anytime_hassle_free": "언제든지 설정에서 취소할 수 있습니다", "add_free_subscription": "-- 무료 구독 추가 --", "currently_impersonating": "현재 가장 중", "user_1": "사용자:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz로 소셜 존재감을 키우세요", "billing_no_risk_trial": "100% 무위험 무료 체험", "billing_pay_nothing_7_days": "처음 7일 동안은 결제 없이 이용하세요", - "billing_cancel_anytime": "언제든지 간편하게 해지 가능", + "billing_cancel_anytime": "언제든지 설정에서 취소할 수 있습니다", "billing_choose_plan": "요금제 선택", "billing_monthly": "월간", "billing_yearly": "연간", @@ -532,7 +533,7 @@ "billing_your_7_day_trial_is": "7일 무료 체험이", "billing_100_percent_free": "100% 무료", "billing_ending": "종료됩니다", - "billing_cancel_anytime_short": "언제든지 취소 가능합니다.", + "billing_cancel_anytime_short": "설정에서 언제든지 취소할 수 있습니다", "billing_pay_0_start_trial": "오늘 $0 결제 - 무료 체험을 시작하세요!", "billing_pay_now": "지금 결제", "billing_per_month": "/월", diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index 3906d742..09d37edc 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Você pode adicionar assinaturas à sua conta para serem usadas em suas postagens.", "content": "Conteúdo", "auto_add": "Adicionar automaticamente?", + "delay_comment": "Comentário de atraso", "actions": "Ações", "use_signature": "Usar assinatura", "add_a_signature": "Adicionar uma assinatura", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Para gerenciar todos os seus canais de mídia social", "100_no_risk_trial": "Teste 100% sem risco", "pay_nothing_for_the_first_7_days": "Não pague nada nos primeiros 7 dias", - "cancel_anytime_hassle_free": "Cancele a qualquer momento, sem complicações", + "cancel_anytime_hassle_free": "Cancele a qualquer momento, nas configurações", "add_free_subscription": "-- ADICIONAR ASSINATURA GRATUITA --", "currently_impersonating": "Atualmente personificando", "user_1": "usuário:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz para aumentar sua presença social", "billing_no_risk_trial": "Teste gratuito 100% sem risco", "billing_pay_nothing_7_days": "Não pague NADA nos primeiros 7 dias", - "billing_cancel_anytime": "Cancele a qualquer momento, sem complicações", + "billing_cancel_anytime": "Cancele a qualquer momento, nas configurações", "billing_choose_plan": "Escolha um plano", "billing_monthly": "Mensal", "billing_yearly": "Anual", diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index b03b3aad..146b5d25 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Вы можете добавить подписи к своему аккаунту для использования в ваших публикациях.", "content": "Содержание", "auto_add": "Добавлять автоматически?", + "delay_comment": "Комментарий к задержке", "actions": "Действия", "use_signature": "Использовать подпись", "add_a_signature": "Добавить подпись", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Для управления всеми вашими социальными сетями", "100_no_risk_trial": "100% безрисковый пробный период", "pay_nothing_for_the_first_7_days": "Не платите ничего первые 7 дней", - "cancel_anytime_hassle_free": "Отменяйте в любое время, без проблем", + "cancel_anytime_hassle_free": "Отменяйте в любое время через настройки", "add_free_subscription": "-- ДОБАВИТЬ БЕСПЛАТНУЮ ПОДПИСКУ --", "currently_impersonating": "В данный момент имитируется", "user_1": "пользователь:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz для роста своей социальной активности", "billing_no_risk_trial": "100% бесплатный пробный период без риска", "billing_pay_nothing_7_days": "Платите НИЧЕГО первые 7 дней", - "billing_cancel_anytime": "Отменить можно в любое время, без хлопот", + "billing_cancel_anytime": "Отменяйте в любое время через настройки", "billing_choose_plan": "Выберите тариф", "billing_monthly": "Ежемесячно", "billing_yearly": "Ежегодно", diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index f831d6ce..406a7403 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Gönderilerinizde kullanılmak üzere hesabınıza imzalar ekleyebilirsiniz.", "content": "İçerik", "auto_add": "Otomatik Ekle?", + "delay_comment": "Gecikme yorumu", "actions": "Eylemler", "use_signature": "İmzayı Kullan", "add_a_signature": "Bir imza ekle", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Tüm sosyal medya kanallarınızı yönetmek için", "100_no_risk_trial": "%100 risksiz deneme", "pay_nothing_for_the_first_7_days": "İlk 7 gün hiçbir ücret ödeme", - "cancel_anytime_hassle_free": "İstediğin zaman zahmetsizce iptal et", + "cancel_anytime_hassle_free": "İstediğiniz zaman, ayarlardan iptal edin", "add_free_subscription": "-- ÜCRETSİZ ABONELİK EKLE --", "currently_impersonating": "Şu anda taklit ediliyor", "user_1": "kullanıcı:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Sosyal Varlıklarını Büyütmek İçin Postiz", "billing_no_risk_trial": "%100 Risksiz Ücretsiz Deneme", "billing_pay_nothing_7_days": "İlk 7 gün boyunca HİÇBİR ŞEY ödemeyin", - "billing_cancel_anytime": "İstediğiniz zaman, zahmetsizce iptal edin", + "billing_cancel_anytime": "İstediğiniz zaman, ayarlardan iptal edin", "billing_choose_plan": "Bir Plan Seçin", "billing_monthly": "Aylık", "billing_yearly": "Yıllık", @@ -532,7 +533,7 @@ "billing_your_7_day_trial_is": "7 günlük deneme süreniz", "billing_100_percent_free": "%100 ücretsiz", "billing_ending": "sona eriyor", - "billing_cancel_anytime_short": "İstediğiniz zaman iptal edin.", + "billing_cancel_anytime_short": "İstediğiniz zaman ayarlardan iptal edin", "billing_pay_0_start_trial": "Bugün 0$ ödeyin - Ücretsiz denemenizi başlatın!", "billing_pay_now": "Şimdi Öde", "billing_per_month": "/ ay", diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 05aa3f23..8c4473fe 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "Bạn có thể thêm chữ ký vào tài khoản của mình để sử dụng trong các bài đăng.", "content": "Nội dung", "auto_add": "Tự động thêm?", + "delay_comment": "Bình luận bị trì hoãn", "actions": "Hành động", "use_signature": "Sử dụng chữ ký", "add_a_signature": "Thêm chữ ký", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "Để quản lý tất cả các kênh mạng xã hội của bạn", "100_no_risk_trial": "Dùng thử 100% không rủi ro", "pay_nothing_for_the_first_7_days": "Không mất phí trong 7 ngày đầu tiên", - "cancel_anytime_hassle_free": "Hủy bất cứ lúc nào, không rắc rối", + "cancel_anytime_hassle_free": "Hủy bất cứ lúc nào trong phần cài đặt", "add_free_subscription": "-- THÊM GÓI MIỄN PHÍ --", "currently_impersonating": "Đang giả lập tài khoản", "user_1": "người dùng:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz để phát triển sự hiện diện trên mạng xã hội của họ", "billing_no_risk_trial": "Dùng thử miễn phí 100% không rủi ro", "billing_pay_nothing_7_days": "Không phải trả gì trong 7 ngày đầu tiên", - "billing_cancel_anytime": "Hủy bất cứ lúc nào, không ràng buộc", + "billing_cancel_anytime": "Hủy bất cứ lúc nào trong phần cài đặt", "billing_choose_plan": "Chọn gói", "billing_monthly": "Hàng tháng", "billing_yearly": "Hàng năm", @@ -532,7 +533,7 @@ "billing_your_7_day_trial_is": "Dùng thử 7 ngày của bạn là", "billing_100_percent_free": "Miễn phí 100%", "billing_ending": "kết thúc", - "billing_cancel_anytime_short": "Hủy bất cứ lúc nào.", + "billing_cancel_anytime_short": "Hủy bất cứ lúc nào trong phần cài đặt", "billing_pay_0_start_trial": "Thanh toán $0 hôm nay - Bắt đầu dùng thử miễn phí!", "billing_pay_now": "Thanh toán ngay", "billing_per_month": "/ tháng", diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index ba3e416c..3d128ea3 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -23,6 +23,7 @@ "you_can_add_signatures_to_your_account_to_be_used_in_your_posts": "你可以为你的账户添加签名,以便在你的帖子中使用。", "content": "内容", "auto_add": "自动添加?", + "delay_comment": "延迟评论", "actions": "操作", "use_signature": "使用签名", "add_a_signature": "添加签名", @@ -150,7 +151,7 @@ "to_manage_all_your_social_media_channels": "管理你所有的社交媒体渠道", "100_no_risk_trial": "100% 无风险试用", "pay_nothing_for_the_first_7_days": "前 7 天免费", - "cancel_anytime_hassle_free": "随时取消,无需担忧", + "cancel_anytime_hassle_free": "随时可在设置中取消", "add_free_subscription": "-- 添加免费订阅 --", "currently_impersonating": "当前模拟身份", "user_1": "用户:", @@ -509,7 +510,7 @@ "billing_postiz_grow_social": "Postiz来提升他们的社交影响力", "billing_no_risk_trial": "100%无风险免费试用", "billing_pay_nothing_7_days": "前7天完全免费", - "billing_cancel_anytime": "随时取消,无需担心", + "billing_cancel_anytime": "随时可在设置中取消", "billing_choose_plan": "选择一个方案", "billing_monthly": "按月", "billing_yearly": "按年", From 07b0c2e85d64ff0f41f1d4b11c79d81a6393ca2e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 16 Jan 2026 20:41:29 +0700 Subject: [PATCH 142/340] feat: refresh token before expiration for specific platforms --- .../src/api/routes/integrations.controller.ts | 57 +++++++++++-------- .../src/activities/integrations.activity.ts | 23 ++++++++ apps/orchestrator/src/app.module.ts | 8 ++- apps/orchestrator/src/workflows/index.ts | 1 + .../src/workflows/refresh.token.workflow.ts | 55 ++++++++++++++++++ .../refresh.integration.service.ts | 19 ++++++- .../social/instagram.standalone.provider.ts | 5 +- .../social/social.integrations.interface.ts | 1 + .../integrations/social/threads.provider.ts | 7 ++- 9 files changed, 145 insertions(+), 31 deletions(-) create mode 100644 apps/orchestrator/src/activities/integrations.activity.ts create mode 100644 apps/orchestrator/src/workflows/refresh.token.workflow.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 78179693..7f3c31b1 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -488,30 +488,39 @@ export class IntegrationsController { throw new HttpException('', 412); } - return this._integrationService.createOrUpdateIntegration( - additionalSettings, - !!integrationProvider.oneTimeToken, - org.id, - validName.trim(), - picture, - 'social', - String(id), - integration, - accessToken, - refreshToken, - expiresIn, - username, - refresh ? false : integrationProvider.isBetweenSteps, - body.refresh, - +body.timezone, - details - ? AuthService.fixedEncryption(details) - : integrationProvider.customFields - ? AuthService.fixedEncryption( - Buffer.from(body.code, 'base64').toString() - ) - : undefined - ); + const createUpdate = + await this._integrationService.createOrUpdateIntegration( + additionalSettings, + !!integrationProvider.oneTimeToken, + org.id, + validName.trim(), + picture, + 'social', + String(id), + integration, + accessToken, + refreshToken, + expiresIn, + username, + refresh ? false : integrationProvider.isBetweenSteps, + body.refresh, + +body.timezone, + details + ? AuthService.fixedEncryption(details) + : integrationProvider.customFields + ? AuthService.fixedEncryption( + Buffer.from(body.code, 'base64').toString() + ) + : undefined + ); + + this._refreshIntegrationService + .startRefreshWorkflow(org.id, createUpdate.id, integrationProvider) + .catch((err) => { + console.log(err); + }); + + return createUpdate; } @Post('/disable') diff --git a/apps/orchestrator/src/activities/integrations.activity.ts b/apps/orchestrator/src/activities/integrations.activity.ts new file mode 100644 index 00000000..4a668bb6 --- /dev/null +++ b/apps/orchestrator/src/activities/integrations.activity.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@nestjs/common'; +import { Activity, ActivityMethod } from 'nestjs-temporal-core'; +import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; +import { Integration } from '@prisma/client'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; + +@Injectable() +@Activity() +export class IntegrationsActivity { + constructor( + private _integrationService: IntegrationService, + private _refreshIntegrationService: RefreshIntegrationService + ) {} + + @ActivityMethod() + async getIntegrationsById(id: string, orgId: string) { + return this._integrationService.getIntegrationById(orgId, id); + } + + async refreshToken(integration: Integration) { + return this._refreshIntegrationService.refresh(integration); + } +} diff --git a/apps/orchestrator/src/app.module.ts b/apps/orchestrator/src/app.module.ts index 1d2acf0b..a0c277db 100644 --- a/apps/orchestrator/src/app.module.ts +++ b/apps/orchestrator/src/app.module.ts @@ -4,8 +4,14 @@ import { getTemporalModule } from '@gitroom/nestjs-libraries/temporal/temporal.m import { DatabaseModule } from '@gitroom/nestjs-libraries/database/prisma/database.module'; import { AutopostService } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.service'; import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; +import { IntegrationsActivity } from '@gitroom/orchestrator/activities/integrations.activity'; -const activities = [PostActivity, AutopostService, EmailActivity]; +const activities = [ + PostActivity, + AutopostService, + EmailActivity, + IntegrationsActivity, +]; @Module({ imports: [ DatabaseModule, diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index 9796a976..72f10d68 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -3,3 +3,4 @@ export * from './autopost.workflow'; export * from './digest.email.workflow'; export * from './missing.post.workflow'; export * from './send.email.workflow'; +export * from './refresh.token.workflow'; diff --git a/apps/orchestrator/src/workflows/refresh.token.workflow.ts b/apps/orchestrator/src/workflows/refresh.token.workflow.ts new file mode 100644 index 00000000..dc189ff0 --- /dev/null +++ b/apps/orchestrator/src/workflows/refresh.token.workflow.ts @@ -0,0 +1,55 @@ +import { proxyActivities, sleep } from '@temporalio/workflow'; +import { IntegrationsActivity } from '@gitroom/orchestrator/activities/integrations.activity'; + +const { getIntegrationsById, refreshToken } = + proxyActivities<IntegrationsActivity>({ + startToCloseTimeout: '10 minute', + retry: { + maximumAttempts: 3, + backoffCoefficient: 1, + initialInterval: '2 minutes', + }, + }); + +export async function refreshTokenWorkflow({ + organizationId, + integrationId, +}: { + integrationId: string; + organizationId: string; +}) { + while (true) { + let integration = await getIntegrationsById(integrationId, organizationId); + if ( + !integration || + integration.deletedAt || + integration.inBetweenSteps || + integration.refreshNeeded + ) { + return false; + } + + const today = new Date(); + const endDate = new Date(integration.tokenExpiration); + + const minMax = Math.max(0, endDate.getTime() - today.getTime()); + if (!minMax) { + return false; + } + + await sleep(minMax as number); + + // while we were sleeping, the integration might have been deleted + integration = await getIntegrationsById(integrationId, organizationId); + if ( + !integration || + integration.deletedAt || + integration.inBetweenSteps || + integration.refreshNeeded + ) { + return false; + } + + await refreshToken(integration); + } +} diff --git a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts index 1966aed8..a14d990b 100644 --- a/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts +++ b/libraries/nestjs-libraries/src/integrations/refresh.integration.service.ts @@ -6,13 +6,15 @@ import { AuthTokenDetails, SocialProvider, } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { TemporalService } from 'nestjs-temporal-core'; @Injectable() export class RefreshIntegrationService { constructor( private _integrationManager: IntegrationManager, @Inject(forwardRef(() => IntegrationService)) - private _integrationService: IntegrationService + private _integrationService: IntegrationService, + private _temporalService: TemporalService ) {} async refresh(integration: Integration): Promise<false | AuthTokenDetails> { const socialProvider = this._integrationManager.getSocialIntegration( @@ -50,6 +52,21 @@ export class RefreshIntegrationService { ); } + public async startRefreshWorkflow(orgId: string, id: string, integration: SocialProvider) { + if (!integration.refreshCron) { + return false; + } + + return this._temporalService.client + .getRawClient() + ?.workflow.start(`refreshTokenWorkflow`, { + workflowId: `refresh_${id}`, + args: [{integrationId: id, organizationId: orgId}], + taskQueue: 'main', + workflowIdConflictPolicy: 'TERMINATE_EXISTING', + }); + } + private async refreshProcess( integration: Integration, socialProvider: SocialProvider diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts index e0bac74f..fecd0470 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts @@ -24,6 +24,7 @@ export class InstagramStandaloneProvider identifier = 'instagram-standalone'; name = 'Instagram\n(Standalone)'; isBetweenSteps = false; + refreshCron = true; scopes = [ 'instagram_business_basic', 'instagram_business_content_publish', @@ -69,7 +70,7 @@ export class InstagramStandaloneProvider name, accessToken: access_token, refreshToken: access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), + expiresIn: dayjs().add(58, 'days').unix() - dayjs().unix(), picture: profile_picture_url || '', username, }; @@ -144,7 +145,7 @@ export class InstagramStandaloneProvider name, accessToken: access_token, refreshToken: access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), + expiresIn: dayjs().add(58, 'days').unix() - dayjs().unix(), picture: profile_picture_url, username, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 8ade3a81..3ecfba88 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -130,6 +130,7 @@ export interface SocialProvider identifier: string; refreshWait?: boolean; convertToJPEG?: boolean; + refreshCron?: boolean; dto?: any; maxLength: (additionalSettings?: any) => number; isWeb3?: boolean; diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index fa8e0dc2..0087c34a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -26,6 +26,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { // 'threads_profile_discovery', ]; override maxConcurrentJob = 2; // Threads has moderate rate limits + refreshCron = true; editor = 'normal' as const; maxLength() { @@ -61,7 +62,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { name, accessToken: access_token, refreshToken: access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), + expiresIn: dayjs().add(58, 'days').unix() - dayjs().unix(), picture: picture || '', username: '', }; @@ -114,7 +115,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { 'https://graph.threads.net/access_token' + '?grant_type=th_exchange_token' + `&client_secret=${process.env.THREADS_APP_SECRET}` + - `&access_token=${getAccessToken.access_token}&fields=access_token,expires_in` + `&access_token=${getAccessToken.access_token}` ) ).json(); @@ -127,7 +128,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { name, accessToken: access_token, refreshToken: access_token, - expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(), + expiresIn: dayjs().add(58, 'days').unix() - dayjs().unix(), picture: picture || '', username: username, }; From ba4ad5deb230166ad9fd52374cf9b2ac79bf3194 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 11:42:12 +0700 Subject: [PATCH 143/340] feat: stripe changes --- .../components/billing/embedded.billing.tsx | 333 +++++++++++++++++- .../src/components/billing/faq.component.tsx | 10 +- .../billing/first.billing.component.tsx | 14 +- .../src/services/stripe.service.ts | 60 +++- 4 files changed, 395 insertions(+), 22 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index b0c26104..c556b890 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -20,7 +20,9 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; export const EmbeddedBilling: FC<{ stripe: Promise<Stripe>; secret: string; -}> = ({ stripe, secret }) => { + showCoupon?: boolean; + autoApplyCoupon?: string; +}> = ({ stripe, secret, showCoupon = false, autoApplyCoupon }) => { const [saveSecret, setSaveSecret] = useState(secret); const [loading, setLoading] = useState(false); const [mode, setMode] = useCookie('mode', 'dark'); @@ -80,18 +82,24 @@ export const EmbeddedBilling: FC<{ }, }} > - <FormWrapper /> + <FormWrapper + showCoupon={showCoupon} + autoApplyCoupon={autoApplyCoupon} + /> </CheckoutProvider> </div> ); }; -const FormWrapper = () => { +const FormWrapper: FC<{ showCoupon?: boolean; autoApplyCoupon?: string }> = ({ + showCoupon = false, + autoApplyCoupon, +}) => { const checkoutState = useCheckout(); const toaster = useToaster(); const [loading, setLoading] = useState(false); - if (checkoutState.type === 'loading' || checkoutState.type === 'error') { + if (checkoutState.type !== 'success') { return null; } @@ -112,15 +120,19 @@ const FormWrapper = () => { return ( <form onSubmit={handleSubmit} className="flex flex-col flex-1"> - <StripeInputs /> + <StripeInputs showCoupon={showCoupon} autoApplyCoupon={autoApplyCoupon} /> <SubmitBar loading={loading} /> </form> ); }; -const StripeInputs = () => { +const StripeInputs: FC<{ showCoupon: boolean; autoApplyCoupon?: string }> = ({ + showCoupon, + autoApplyCoupon, +}) => { const checkout = useCheckout(); const t = useT(); + const [ready, setReady] = useState(false); return ( <> <div> @@ -135,14 +147,12 @@ const StripeInputs = () => { <h4 className="mt-[40px] mb-[32px] text-[24px] font-[700]"> {checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')} </h4> - <PaymentElement id="payment-element" options={{ layout: 'tabs' }} /> + <PaymentElement id="payment-element" options={{ layout: 'tabs' }} onReady={() => setReady(true)} /> + {showCoupon && ready && <CouponInput autoApplyCoupon={autoApplyCoupon} />} {checkout.type === 'loading' ? null : ( <div className="mt-[24px] text-[16px] font-[600] flex gap-[4px] items-center"> <div> - {t( - 'billing_powered_by_stripe', - 'Secure payments processed by' - )} + {t('billing_powered_by_stripe', 'Secure payments processed by')} </div> <svg className="mt-[4px]" @@ -166,6 +176,302 @@ const StripeInputs = () => { ); }; +const AppliedCouponDisplay: FC<{ + appliedCode: string; + checkout: any; + isApplying: boolean; + onRemove: () => void; +}> = ({ appliedCode, checkout, isApplying, onRemove }) => { + const t = useT(); + + // Get discount display from checkout state + const getDiscountDisplay = (): string | null => { + // Try to get percentage from discountAmounts + const percentOff = checkout?.discountAmounts?.[0]?.percentOff; + if (percentOff && typeof percentOff === 'number' && percentOff > 0) { + return `-${percentOff}%`; + } + + // Try to get actual discount amount from recurring.dueNext.discount + const recurringDiscount = + checkout?.recurring?.dueNext?.discount?.minorUnitsAmount; + if ( + recurringDiscount && + typeof recurringDiscount === 'number' && + recurringDiscount > 0 + ) { + return `-$${(recurringDiscount / 100).toFixed(2)}`; + } + + // Try lineItems discount + const lineItemDiscount = + checkout?.lineItems?.[0]?.discountAmounts?.[0]?.percentOff; + if ( + lineItemDiscount && + typeof lineItemDiscount === 'number' && + lineItemDiscount > 0 + ) { + return `-${lineItemDiscount}%`; + } + + return null; + }; + + // Get expiration date from checkout state (if available) + const getExpirationDate = (): string | null => { + const discount = checkout?.discountAmounts?.[0]; + const lineItemDiscount = checkout?.lineItems?.[0]?.discountAmounts?.[0]; + + // Check for expiresAt in various locations (Unix timestamp) + const expiresAt = + discount?.expiresAt || + discount?.expires_at || + lineItemDiscount?.expiresAt || + lineItemDiscount?.expires_at || + checkout?.promotionCode?.expiresAt || + checkout?.promotionCode?.expires_at; + + if (expiresAt && typeof expiresAt === 'number') { + const date = new Date(expiresAt * 1000); + return dayjs(date).format('MMMM D, YYYY'); + } + + if (expiresAt && typeof expiresAt === 'string') { + return dayjs(expiresAt).format('MMMM D, YYYY'); + } + + return null; + }; + + const discountDisplay = getDiscountDisplay(); + const expirationDate = getExpirationDate(); + + return ( + <div className="flex flex-col gap-[8px]"> + <div className="flex items-center gap-[12px] p-[16px] rounded-[12px] border border-[#AA0FA4]/30 bg-[#AA0FA4]/10"> + <div className="flex-1"> + <div className="flex items-center gap-[8px] flex-wrap"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 24 24" + fill="none" + stroke="#FC69FF" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + > + <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" /> + <polyline points="22 4 12 14.01 9 11.01" /> + </svg> + <span className="font-[600] text-[#FC69FF]">{appliedCode}</span> + <span className="text-[14px] text-textColor/70"> + {t('billing_discount_applied', 'applied')} + {discountDisplay && ` (${discountDisplay})`} + </span> + </div> + </div> + <button + type="button" + onClick={onRemove} + disabled={isApplying} + className="text-[14px] text-textColor/50 hover:text-textColor font-[500] disabled:opacity-50" + > + {t('billing_remove', 'Remove')} + </button> + </div> + {expirationDate && ( + <p className="text-[13px] text-textColor/50 flex items-center gap-[6px]"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="14" + height="14" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + > + <circle cx="12" cy="12" r="10" /> + <polyline points="12 6 12 12 16 14" /> + </svg> + {t('billing_coupon_expires', 'Coupon expires on')} {expirationDate} + </p> + )} + </div> + ); +}; + +export const CouponInput: FC<{ autoApplyCoupon?: string }> = ({ + autoApplyCoupon, +}) => { + const checkoutState = useCheckout(); + const t = useT(); + const toaster = useToaster(); + const [couponCode, setCouponCode] = useState(''); + const [isApplying, setIsApplying] = useState(false); + const [appliedCode, setAppliedCode] = useState<string | null>(null); + const [showInput, setShowInput] = useState(false); + + const { checkout } = + checkoutState.type === 'success' ? checkoutState : { checkout: null }; + + // Auto-apply coupon from backend when checkout is ready + useEffect(() => { + if (autoApplyCoupon) { + handleApplyCoupon(undefined, autoApplyCoupon); + } + }, []); + + // Check if a coupon is already pre-applied (e.g., auto-apply coupon from backend) + const preAppliedCode = checkout?.discountAmounts?.[0]?.promotionCode; + const effectiveAppliedCode = appliedCode || preAppliedCode || null; + + const handleApplyCoupon = async (e?: any, coupon?: string) => { + if (!coupon && !couponCode.trim()) return; + + setIsApplying(true); + try { + const result = await checkout.applyPromotionCode( + coupon || couponCode.trim() + ); + if (result.type === 'error') { + toaster.show( + result.error.message || + t('billing_invalid_coupon', 'Invalid coupon code'), + 'warning' + ); + } else { + setAppliedCode(coupon || couponCode.trim()); + setCouponCode(''); + setShowInput(false); + toaster.show( + t('billing_coupon_applied', 'Coupon applied successfully!'), + 'success' + ); + } + } catch (err: any) { + toaster.show( + err.message || t('billing_invalid_coupon', 'Invalid coupon code'), + 'warning' + ); + } + setIsApplying(false); + }; + + const handleRemoveCoupon = async () => { + setIsApplying(true); + try { + await checkout.removePromotionCode(); + setAppliedCode(null); + toaster.show(t('billing_coupon_removed', 'Coupon removed'), 'success'); + } catch (err: any) { + toaster.show( + err.message || + t('billing_error_removing_coupon', 'Error removing coupon'), + 'warning' + ); + } + setIsApplying(false); + }; + + // Show applied coupon (either manually applied or pre-applied from backend) + if (effectiveAppliedCode) { + return ( + <div className="mt-[40px]"> + <AppliedCouponDisplay + appliedCode={effectiveAppliedCode} + checkout={checkout} + isApplying={isApplying} + onRemove={handleRemoveCoupon} + /> + </div> + ); + } + + // Show "Have a promo code?" link + if (!showInput) { + return ( + <div className="mt-[40px]"> + <button + type="button" + onClick={() => setShowInput(true)} + className="text-[16px] text-textColor/60 hover:text-textColor font-[500] flex items-center gap-[8px] transition-colors" + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="18" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + > + <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" /> + </svg> + {t('billing_have_discount_coupon', 'Have a discount coupon?')} + </button> + </div> + ); + } + + // Show input field + return ( + <div className="mt-[40px]"> + <div className="flex items-center gap-[12px] mb-[12px]"> + <h4 className="text-[18px] font-[600] text-textColor"> + {t('billing_discount_coupon', 'Discount Coupon')} + </h4> + <button + type="button" + onClick={() => { + setShowInput(false); + setCouponCode(''); + }} + className="text-[14px] text-textColor/50 hover:text-textColor transition-colors" + > + {t('billing_cancel', 'Cancel')} + </button> + </div> + <div className="flex gap-[12px]"> + <input + type="text" + value={couponCode} + onChange={(e) => setCouponCode(e.target.value)} + placeholder={t('billing_enter_coupon_code', 'Enter coupon code')} + disabled={isApplying} + autoFocus + className="flex-1 h-[44px] px-[16px] rounded-[8px] border border-newColColor bg-newBgColor text-textColor placeholder:text-textColor/50 focus:outline-none focus:border-boxFocused disabled:opacity-50" + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleApplyCoupon(); + } + if (e.key === 'Escape') { + setShowInput(false); + setCouponCode(''); + } + }} + /> + <button + type="button" + onClick={() => handleApplyCoupon()} + disabled={isApplying || !couponCode.trim()} + className="h-[44px] px-[24px] rounded-[8px] bg-boxFocused text-textItemFocused font-[600] hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed transition-all" + > + {isApplying + ? t('billing_applying', 'Applying...') + : t('billing_apply', 'Apply')} + </button> + </div> + </div> + ); +}; + const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { const checkout = useCheckout(); const t = useT(); @@ -191,7 +497,10 @@ const SubmitBar: FC<{ loading: boolean }> = ({ loading }) => { —{' '} </span> <span className="text-textColor font-[600]"> - {t('billing_cancel_anytime_short', 'Cancel anytime from settings')} + {t( + 'billing_cancel_anytime_short', + 'Cancel anytime from settings' + )} </span> </div> ) : null} diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index b5eac193..bd5fceb2 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -120,7 +120,7 @@ export const FAQSection: FC<{ onClick={(e) => { e.stopPropagation(); }} - className={`mt-[16px] w-full text-wrap font-[400] text-[16px] text-customColor17 select-text`} + className={`mt-[16px] w-full text-wrap font-[400] text-[16px] text-customColor17 select-text max-w-[450px]`} dangerouslySetInnerHTML={{ __html: description, }} @@ -134,10 +134,10 @@ export const FAQComponent: FC = () => { const list = useFaqList(); return ( <div> - <h3 className="text-[24px] mt-[48px] mb-[40px] tablet:mt-[80px]"> - {t('frequently_asked_questions', 'Frequently Asked Questions')} - </h3> - <div className="gap-[24px] flex-col flex select-none"> + {/*<h3 className="text-[24px] mt-[48px] mb-[40px] tablet:mt-[80px]">*/} + {/* {t('frequently_asked_questions', 'Frequently Asked Questions')}*/} + {/*</h3>*/} + <div className="gap-[24px] flex-col flex select-none mt-[48px] mb-[40px] tablet:mt-[80px]"> {list.map((item, index) => ( <FAQSection key={index} {...item} /> ))} diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index 2cda0593..8e18bbdf 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -160,10 +160,12 @@ export const FirstBillingComponent = () => { <JoinOver /> </div> {!isLoading && data && stripe ? ( - <> - <EmbeddedBilling stripe={stripe} secret={data.client_secret} /> - <FAQComponent /> - </> + <EmbeddedBilling + stripe={stripe} + secret={data.client_secret} + showCoupon={period === 'MONTHLY'} + autoApplyCoupon={data.auto_apply_coupon} + /> ) : ( <LoadingComponent /> )} @@ -245,6 +247,10 @@ export const FirstBillingComponent = () => { </div> <BillingFeatures tier={tier} /> </div> + <div className="flex flex-col mobile:hidden tablet:hidden"> + {/*<div>asd</div>*/} + <FAQComponent /> + </div> </div> </div> </div> diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index 6462b317..4a9bbb88 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -346,6 +346,54 @@ export class StripeService { }); } + /** + * Find an active promotion code with autoapply: true metadata + * Only returns codes that are active and not expired + * Returns the promotion code string (not the ID) for frontend auto-apply + */ + private async findAutoApplyPromotionCode(): Promise<string | null> { + try { + const promotionCodes = await stripe.promotionCodes.list({ + active: true, + limit: 100, + }); + + const now = Math.floor(Date.now() / 1000); + + for (const promoCode of promotionCodes.data) { + // Check if it has autoapply metadata set to true (check both promo and coupon metadata) + const autoApply = Object.assign( + {}, + promoCode.metadata, + promoCode.coupon.metadata + )?.autoapply; + if (autoApply !== 'true') continue; + + // Check if the promotion code has expired + if (promoCode.expires_at && promoCode.expires_at < now) continue; + + // Check if the coupon has expired (redeem_by) + if (promoCode.coupon.redeem_by && promoCode.coupon.redeem_by < now) + continue; + + // Check if max redemptions reached + if ( + promoCode.max_redemptions && + promoCode.times_redeemed >= promoCode.max_redemptions + ) + continue; + + // Found a valid auto-apply promotion code - return the code string for frontend + return promoCode.code; + } + + return null; + } catch (err) { + console.error('Error finding auto-apply promotion code:', err); + return null; + } + } + private async createEmbeddedCheckout( ud: string, uniqueId: string, @@ -376,6 +424,12 @@ export class StripeService { }); } catch (err) {} + // Check for auto-apply promotion code (only for monthly plans) + let autoApplyPromoCode: string | null = null; + if (body.period === 'MONTHLY') { + autoApplyPromoCode = await this.findAutoApplyPromotionCode(); + } + const isUtm = body.utm ? `&utm_source=${body.utm}` : ''; // @ts-ignore const { client_secret } = await stripeCustom.checkout.sessions.create({ @@ -405,7 +459,11 @@ export class StripeService { ], }); - return { client_secret }; + // Return auto-apply promo code for frontend to apply + return { + client_secret, + ...(autoApplyPromoCode ? { auto_apply_coupon: autoApplyPromoCode } : {}), + }; } private async createCheckoutSession( From 47e7c843c9bf76ab71dc3c4de204ee825c745880 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 12:03:28 +0700 Subject: [PATCH 144/340] feat: better biling --- .../components/billing/embedded.billing.tsx | 141 +++++++++++++++++- .../src/components/billing/faq.component.tsx | 6 +- i18n.lock | 27 +++- .../translation/locales/ar/translation.json | 27 +++- .../translation/locales/bn/translation.json | 27 +++- .../translation/locales/de/translation.json | 27 +++- .../translation/locales/en/translation.json | 26 +++- .../translation/locales/es/translation.json | 27 +++- .../translation/locales/fr/translation.json | 27 +++- .../translation/locales/he/translation.json | 27 +++- .../translation/locales/it/translation.json | 27 +++- .../translation/locales/ja/translation.json | 27 +++- .../translation/locales/ko/translation.json | 27 +++- .../translation/locales/pt/translation.json | 27 +++- .../translation/locales/ru/translation.json | 27 +++- .../translation/locales/tr/translation.json | 27 +++- .../translation/locales/vi/translation.json | 27 +++- .../translation/locales/zh/translation.json | 27 +++- 18 files changed, 518 insertions(+), 60 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index c556b890..0dc5cb8d 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -120,16 +120,20 @@ const FormWrapper: FC<{ showCoupon?: boolean; autoApplyCoupon?: string }> = ({ return ( <form onSubmit={handleSubmit} className="flex flex-col flex-1"> - <StripeInputs showCoupon={showCoupon} autoApplyCoupon={autoApplyCoupon} /> - <SubmitBar loading={loading} /> + <StripeInputs + showCoupon={showCoupon} + autoApplyCoupon={autoApplyCoupon} + loading={loading} + /> </form> ); }; -const StripeInputs: FC<{ showCoupon: boolean; autoApplyCoupon?: string }> = ({ - showCoupon, - autoApplyCoupon, -}) => { +const StripeInputs: FC<{ + showCoupon: boolean; + autoApplyCoupon?: string; + loading: boolean; +}> = ({ showCoupon, autoApplyCoupon, loading }) => { const checkout = useCheckout(); const t = useT(); const [ready, setReady] = useState(false); @@ -147,8 +151,16 @@ const StripeInputs: FC<{ showCoupon: boolean; autoApplyCoupon?: string }> = ({ <h4 className="mt-[40px] mb-[32px] text-[24px] font-[700]"> {checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')} </h4> - <PaymentElement id="payment-element" options={{ layout: 'tabs' }} onReady={() => setReady(true)} /> - {showCoupon && ready && <CouponInput autoApplyCoupon={autoApplyCoupon} />} + <PaymentElement + id="payment-element" + options={{ layout: 'tabs' }} + onReady={() => setReady(true)} + /> + {ready && <PriceBreakdown />} + {showCoupon && ready && ( + <CouponInput autoApplyCoupon={autoApplyCoupon} /> + )} + {ready && <SubmitBar loading={loading} />} {checkout.type === 'loading' ? null : ( <div className="mt-[24px] text-[16px] font-[600] flex gap-[4px] items-center"> <div> @@ -176,6 +188,119 @@ const StripeInputs: FC<{ showCoupon: boolean; autoApplyCoupon?: string }> = ({ ); }; +const PriceBreakdown: FC = () => { + const checkoutState = useCheckout(); + const t = useT(); + + if (checkoutState.type !== 'success') { + return null; + } + + const { checkout } = checkoutState; + const lineItem = checkout?.lineItems?.[0]; + const recurring = checkout?.recurring; + const discountAmounts = checkout?.discountAmounts; + const hasDiscount = discountAmounts && discountAmounts.length > 0; + + // Get values + const planName = lineItem?.name || t('billing_subscription', 'Subscription'); + const unitAmount = lineItem?.unitAmount?.amount || '$0.00'; + const discountDisplay = hasDiscount ? discountAmounts[0] : null; + const dueToday = checkout?.total?.total?.amount || '$0.00'; + const nextBillingTotal = recurring?.dueNext?.total?.amount; + const nextBillingDate = recurring?.trial?.trialEnd + ? dayjs(recurring.trial.trialEnd * 1000).format('MMMM D, YYYY') + : null; + const billingInterval = + recurring?.interval === 'month' + ? t('billing_monthly', 'Monthly') + : t('billing_yearly', 'Yearly'); + + return ( + <div className="mt-[40px]"> + <h4 className="mb-[16px] text-[24px] font-[700]"> + {t('billing_order_summary', 'Order Summary')} + </h4> + <div className="rounded-[12px] border border-newColColor p-[20px] flex flex-col gap-[12px]"> + {/* Plan */} + <div className="flex justify-between items-center"> + <div className="flex flex-col"> + <span className="font-[600] text-textColor">{planName}</span> + <span className="text-[13px] text-textColor/60"> + {billingInterval} + </span> + </div> + <span className="font-[500] text-textColor">{unitAmount}</span> + </div> + + {/* Discount */} + {discountDisplay && ( + <div className="flex justify-between items-center font-[600]"> + <div className="flex items-center gap-[6px]"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + > + <path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" /> + <line x1="7" y1="7" x2="7.01" y2="7" /> + </svg> + <span className="font-[500]"> + {discountDisplay.displayName || discountDisplay.promotionCode} + {discountDisplay.percentOff && + ` (${discountDisplay.percentOff}% off)`} + </span> + </div> + <span className="font-[500]"> + {discountDisplay.amount !== '$0.00' + ? `-${discountDisplay.amount}` + : t('billing_applied', 'Applied')} + </span> + </div> + )} + + {/* Divider */} + <div className="border-t border-newColColor my-[4px]" /> + + {/* Due today */} + <div className="flex justify-between items-center"> + <span className="font-[600] text-textColor"> + {t('billing_due_today', 'Due today')} + </span> + <span className="font-[700] text-[18px] text-textColor"> + {dueToday} + </span> + </div> + + {/* Next billing info */} + {nextBillingTotal && nextBillingDate && ( + <div className="flex justify-between items-center text-[13px] text-textColor/60"> + <span> + {t('billing_then', 'Then')} {nextBillingTotal}{' '} + {t('billing_on', 'on')} {nextBillingDate} + </span> + </div> + )} + + <div className="text-[12px]"> + <strong> + {t( + 'billing_cancel_notice', + 'Cancel anytime from settings without talking to a person and never be charged.' + )} + </strong> + </div> + </div> + </div> + ); +}; + const AppliedCouponDisplay: FC<{ appliedCode: string; checkout: any; diff --git a/apps/frontend/src/components/billing/faq.component.tsx b/apps/frontend/src/components/billing/faq.component.tsx index bd5fceb2..e47f488e 100644 --- a/apps/frontend/src/components/billing/faq.component.tsx +++ b/apps/frontend/src/components/billing/faq.component.tsx @@ -19,7 +19,7 @@ const useFaqList = () => { ), description: t( 'faq_to_confirm_credit_card_information_postiz_will_hold', - 'To confirm credit card information Postiz will hold $2 and release it immediately' + 'To confirm credit card information Postiz will hold $2 and release it immediately, you can cancel your subscription anytime from settings without talking to a person' ), }, ] @@ -72,9 +72,7 @@ export const FAQSection: FC<{ className="bg-sixth p-[24px] border border-tableBorder rounded-[8px] flex flex-col" onClick={changeShow} > - <div - className={`text-[20px] cursor-pointer flex justify-center`} - > + <div className={`text-[20px] cursor-pointer flex justify-center`}> <div className="flex-1">{title}</div> <div className="flex items-center justify-center w-[32px]"> {!show ? ( diff --git a/i18n.lock b/i18n.lock index c7aa8a64..c82d5269 100644 --- a/i18n.lock +++ b/i18n.lock @@ -458,7 +458,7 @@ checksums: upload_content_to_tiktok_without_posting: 6c3943e4d796b720933b90d250bd611c choose_upload_without_posting_description: 223c59be260eeaafe420791b76e2523c faq_am_i_going_to_be_charged_by_postiz: f4aad17d35bcaf45427216a432f23af3 - faq_to_confirm_credit_card_information_postiz_will_hold: 5087b85985bbb6f46ea869d4796fb891 + faq_to_confirm_credit_card_information_postiz_will_hold: ec475e644e3b5db2550c5bc06320b1cd faq_can_i_trust_postiz_gitroom: 9ee3c694bc392c339333047484d36003 faq_postiz_gitroom_is_proudly_open_source: 17658906aec47f1557f84c71efadee03 faq_what_are_channels: 22bc7aebd017e487d9a5ac8946a2a08d @@ -469,7 +469,6 @@ checksums: faq_we_automate_chatgpt_to_help_you_write: e19d331417449c114f03b2b6ad862f93 enter_email: 57d0675d7ff70e463dd0389652f35b20 are_you_sure: 6d5cd13628a7887711fd0c29f1123652 - yes_delete_it: e028575f88f6dc8e25f7a02fde480df9 no_cancel: 9ebf9f7c2d2591e603861ef4c0c7c559 are_you_sure_you_want_to_delete: f41210d5066ca7ae76a171fbe4b085a3 are_you_sure_you_want_to_delete_the_image: 5fc0522337ba24f14a15d5f35b19e1d3 @@ -509,7 +508,7 @@ checksums: delete_integration: ccc879ccfcf7f85bcfe09f2bc3fa0dd3 start_writing_your_post: 471efc4f2a7e2cf02a065a2de34e7213 billing_join_over: 6e1c237241ba00ddbb07fd603344c5c3 - billing_entrepreneurs_count: 05164f2ca2e8e20de3f63977c07d39fe + billing_entrepreneurs_count: 1e90206cb8ea01d0a1084d3af1a4cbed billing_who_use: 63bdc59ef443e193eca87889e83ea07a billing_postiz_grow_social: 3cf5ab166df9cb65e17b9a039ccbbbad billing_no_risk_trial: 6e5c60d9ddf3affa8ba8870272f9f9ab @@ -541,6 +540,27 @@ checksums: billing_pay_0_start_trial: 28e72154e6cce7541e707b35f3a67309 billing_pay_now: 50cb14454e1b2df4a2f83bf1ac799819 billing_per_month: 6293d01c3d13f6938d47285122bd1a48 + billing_per_year: 7d37200832af52329220e7431cf1a0e2 + billing_order_summary: 77352aa7eeded06ca26b3fe72e4bcfda + billing_applied: a79bcd1e53b38db7b03b506ff2e05904 + billing_due_today: a14cb9dd0003485894d328bb803c80e5 + billing_then: 5e941fb7dd51a18651fcfb865edd5ba6 + billing_on: 221bc27170a3baba6741482c0b28baa1 + billing_discount_applied: 66ca13dec03ff95bf7461fb5e9e3a3c5 + billing_remove: dba2fe5fe9f83f8078c687f28cba4b52 + billing_coupon_expires: 91d8dcbb9a0ec894dd769f99528f0768 + billing_invalid_coupon: 07fdf7218d84836632d60d2819089587 + billing_coupon_applied: 53d5f95a8b47b6771ac06fb48578367a + billing_coupon_removed: ebef384172a4632e325ddfa5de48d370 + billing_error_removing_coupon: aedc7fb1697d846a707828e02059cd61 + billing_have_discount_coupon: 7f3e2b5a65a2901492ea3d185003dc80 + billing_discount_coupon: 417a5576094621af534c5b3ba1898f1b + billing_cancel: 2e2a849c2223911717de8caa2c71bade + billing_enter_coupon_code: 4abd909f35156b0964bd5993f14d7b78 + billing_applying: 0a321e775629314323ab227340e1a919 + billing_apply: 0f03e8e3752eb120bf8ea0b8f8f5d244 + billing_subscription: ba9f3675e18987d067d48533c8897343 + billing_cancel_notice: bc2edd49b3c0a51f8e11bb93f56c5571 select_channels: 3aab82dc90ccafe55059c32c5a33c222 start_a_new_chat: 65b9067206fa4e4929d48ce242d879a6 your_assistant: bd6422d1c2a2ba461e1da169b082bf7b @@ -656,6 +676,7 @@ checksums: are_you_sure_go_back_to_global_mode: e7e0913ac6a8bdecd5a9349ad84d2f3c yes_go_back_to_global_mode: 50484cd2f72d76bd6a02d5ac956ba466 are_you_sure_delete_this_post: 25e67cb71ee64bf67af09ee20257655f + yes_delete_it: e028575f88f6dc8e25f7a02fde480df9 cant_edit_networks_when_creating_set: 10c389a15c85e82b69f3202bcd8fc527 click_to_exit_global_editing: fec20b458bec4dcabf3efe65d1e5777b edit_content: 13b729cc6459ecdc3ea84d476c6dcddd diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index 89c82b7c..39b6794a 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "ارفع المحتوى إلى تيك توك دون نشره", "choose_upload_without_posting_description": "اختر الرفع دون النشر إذا كنت ترغب في مراجعة وتحرير المحتوى الخاص بك داخل تطبيق تيك توك قبل النشر. هذا يمنحك إمكانية الوصول إلى أدوات التحرير المدمجة في تيك توك ويسمح لك بإجراء التعديلات النهائية قبل النشر.", "faq_am_i_going_to_be_charged_by_postiz": "هل سيتم تحميلي رسوم من قبل Postiz؟", - "faq_to_confirm_credit_card_information_postiz_will_hold": "لتأكيد معلومات بطاقة الائتمان، سيقوم Postiz بحجز مبلغ 2 دولار وإطلاقه فوراً", + "faq_to_confirm_credit_card_information_postiz_will_hold": "لتأكيد معلومات بطاقة الائتمان، سيحتجز Postiz مبلغ 2 دولار ويعيده فوراً، يمكنك إلغاء اشتراكك في أي وقت من الإعدادات دون الحاجة للتحدث مع أي شخص.", "faq_can_i_trust_postiz_gitroom": "هل يمكنني الوثوق بـ Postiz؟", "faq_postiz_gitroom_is_proudly_open_source": "Postiz مفتوح المصدر بكل فخر! نحن نؤمن بثقافة أخلاقية وشفافة، مما يعني أن Postiz سيبقى للأبد. يمكنك الاطلاع على الكود بالكامل أو استخدامه في مشاريعك الشخصية. لعرض مستودع المصدر المفتوح، <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">اضغط هنا</a>.", "faq_what_are_channels": "ما هي القنوات؟", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "نحن نستخدم ChatGPT لمساعدتك في كتابة المنشورات والمقالات الاجتماعية.", "enter_email": "أدخل البريد الإلكتروني", "are_you_sure": "هل أنت متأكد؟", - "yes_delete_it": "نعم، احذفه!", "no_cancel": "لا، إلغاء!", "are_you_sure_you_want_to_delete": "هل أنت متأكد أنك تريد حذف {{name}}؟", "are_you_sure_you_want_to_delete_the_image": "هل أنت متأكد أنك تريد حذف الصورة؟", @@ -505,7 +504,7 @@ "delete_integration": "حذف التكامل", "start_writing_your_post": "ابدأ بكتابة منشورك لمعاينة", "billing_join_over": "انضم إلى أكثر من", - "billing_entrepreneurs_count": "18,000+ رائد أعمال", + "billing_entrepreneurs_count": "أكثر من 20,000 رائد أعمال", "billing_who_use": "الذين يستخدمون", "billing_postiz_grow_social": "Postiz لتنمية حضورهم على وسائل التواصل الاجتماعي", "billing_no_risk_trial": "تجربة مجانية بدون أي مخاطرة 100%", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "ادفع 0 دولار اليوم - ابدأ تجربتك المجانية!", "billing_pay_now": "ادفع الآن", "billing_per_month": "/ شهريًا", + "billing_per_year": "/ سنوياً", + "billing_order_summary": "ملخص الطلب", + "billing_applied": "تم التطبيق", + "billing_due_today": "المستحق اليوم", + "billing_then": "ثم", + "billing_on": "في", + "billing_discount_applied": "تم تطبيق الخصم", + "billing_remove": "إزالة", + "billing_coupon_expires": "تنتهي صلاحية القسيمة في", + "billing_invalid_coupon": "رمز القسيمة غير صالح", + "billing_coupon_applied": "تم تطبيق القسيمة بنجاح!", + "billing_coupon_removed": "تمت إزالة القسيمة", + "billing_error_removing_coupon": "حدث خطأ أثناء إزالة القسيمة", + "billing_have_discount_coupon": "هل لديك قسيمة خصم؟", + "billing_discount_coupon": "قسيمة خصم", + "billing_cancel": "إلغاء", + "billing_enter_coupon_code": "أدخل رمز القسيمة", + "billing_applying": "جارٍ التطبيق...", + "billing_apply": "تطبيق", + "billing_subscription": "الاشتراك", + "billing_cancel_notice": "يمكنك الإلغاء في أي وقت من الإعدادات دون الحاجة للتحدث مع أي شخص ولن يتم خصم أي مبلغ منك.", "select_channels": "اختر القنوات", "start_a_new_chat": "ابدأ محادثة جديدة", "your_assistant": "مساعدك", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "هذا الإجراء لا يمكن التراجع عنه. هل أنت متأكد أنك تريد العودة إلى الوضع العام؟", "yes_go_back_to_global_mode": "نعم، العودة إلى الوضع العام", "are_you_sure_delete_this_post": "هل أنت متأكد أنك تريد حذف هذا المنشور؟", + "yes_delete_it": "نعم، احذفه!", "cant_edit_networks_when_creating_set": "لا يمكنك تعديل الشبكات أثناء إنشاء مجموعة", "click_to_exit_global_editing": "انقر على هذا الزر للخروج من التحرير العام وتخصيص المنشور لهذه القناة", "edit_content": "تعديل المحتوى", diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index 88069dd5..74554aa1 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "পোস্ট করার প্রয়োজন ছাড়া টিকটকে কন্টেন্ট আপলোড করা", "choose_upload_without_posting_description": "পোস্ট করার প্রয়োজন ছাড়া টিকটকে কন্টেন্ট আপলোড করার বর্ণনা", "faq_am_i_going_to_be_charged_by_postiz": "পোস্টিজ দ্বারা চার্জ করা হবে কি?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "পোস্টিজ ক্রেডিট কার্ড তথ্য নিশ্চিত করার জন্য কি?", + "faq_to_confirm_credit_card_information_postiz_will_hold": "ক্রেডিট কার্ডের তথ্য নিশ্চিত করার জন্য Postiz $2 ধরে রাখবে এবং তা সঙ্গে সঙ্গে মুক্তি দেবে, আপনি সেটিংস থেকে যেকোনো সময় কাউকে না বলেই আপনার সাবস্ক্রিপশন বাতিল করতে পারেন", "faq_can_i_trust_postiz_gitroom": "আমি কি Postiz-এ বিশ্বাস করতে পারি?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz গর্বের সাথে ওপেন-সোর্স! আমরা নৈতিক ও স্বচ্ছ সংস্কৃতিতে বিশ্বাস করি, যার মানে Postiz চিরকাল টিকে থাকবে। আপনি চাইলে সম্পূর্ণ কোড দেখতে পারেন বা ব্যক্তিগত প্রকল্পে ব্যবহার করতে পারেন। ওপেন-সোর্স রিপোজিটরি দেখতে, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">এখানে ক্লিক করুন</a>।", "faq_what_are_channels": "চ্যানেল কী?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "আমরা আপনাকে লিখতে সাহায্য করার জন্য ChatGPT স্বয়ংক্রিয় করি", "enter_email": "ইমেইল প্রবেশ করান", "are_you_sure": "আপনি কি নিশ্চিত?", - "yes_delete_it": "হ্যাঁ, মুছে ফেলুন", "no_cancel": "না, বাতিল করুন", "are_you_sure_you_want_to_delete": "আপনি কি নিশ্চিত যে আপনি মুছে ফেলতে চান?", "are_you_sure_you_want_to_delete_the_image": "আপনি কি নিশ্চিত যে আপনি ছবিটি মুছে ফেলতে চান?", @@ -505,7 +504,7 @@ "delete_integration": "ইন্টিগ্রেশন মুছে ফেলুন", "start_writing_your_post": "প্রিভিউর জন্য আপনার পোস্ট লেখা শুরু করুন", "billing_join_over": "যোগ দিন", - "billing_entrepreneurs_count": "১৮,০০০+ উদ্যোক্তা", + "billing_entrepreneurs_count": "২০,০০০+ উদ্যোক্তা", "billing_who_use": "যারা ব্যবহার করেন", "billing_postiz_grow_social": "Postiz তাদের সামাজিক উপস্থিতি বাড়াতে", "billing_no_risk_trial": "১০০% ঝুঁকিমুক্ত ফ্রি ট্রায়াল", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "আজ $0 দিন - আপনার ফ্রি ট্রায়াল শুরু করুন!", "billing_pay_now": "এখনই পেমেন্ট করুন", "billing_per_month": "/ মাস", + "billing_per_year": "/ বছর", + "billing_order_summary": "অর্ডার সারাংশ", + "billing_applied": "প্রয়োগ হয়েছে", + "billing_due_today": "আজকের বাকি", + "billing_then": "তারপর", + "billing_on": "তারিখে", + "billing_discount_applied": "ছাড় প্রয়োগ হয়েছে", + "billing_remove": "সরান", + "billing_coupon_expires": "কুপন মেয়াদ শেষ হবে", + "billing_invalid_coupon": "অবৈধ কুপন কোড", + "billing_coupon_applied": "কুপন সফলভাবে প্রয়োগ হয়েছে!", + "billing_coupon_removed": "কুপন সরানো হয়েছে", + "billing_error_removing_coupon": "কুপন সরাতে ত্রুটি", + "billing_have_discount_coupon": "আপনার কি ছাড়ের কুপন আছে?", + "billing_discount_coupon": "ছাড়ের কুপন", + "billing_cancel": "বাতিল করুন", + "billing_enter_coupon_code": "কুপন কোড লিখুন", + "billing_applying": "প্রয়োগ হচ্ছে...", + "billing_apply": "প্রয়োগ করুন", + "billing_subscription": "সাবস্ক্রিপশন", + "billing_cancel_notice": "সেটিংস থেকে যেকোনো সময় কাউকে না বলেই বাতিল করুন এবং কখনোই চার্জ হবে না।", "select_channels": "চ্যানেল নির্বাচন করুন", "start_a_new_chat": "নতুন চ্যাট শুরু করুন", "your_assistant": "আপনার সহকারী", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "এই কাজটি অপরিবর্তনীয়। আপনি কি নিশ্চিত যে আপনি গ্লোবাল মোডে ফিরে যেতে চান?", "yes_go_back_to_global_mode": "হ্যাঁ, গ্লোবাল মোডে ফিরে যান", "are_you_sure_delete_this_post": "আপনি কি নিশ্চিত যে আপনি এই পোস্টটি মুছে ফেলতে চান?", + "yes_delete_it": "হ্যাঁ, মুছে ফেলুন", "cant_edit_networks_when_creating_set": "সেট তৈরি করার সময় আপনি নেটওয়ার্ক সম্পাদনা করতে পারবেন না", "click_to_exit_global_editing": "গ্লোবাল সম্পাদনা থেকে বেরিয়ে এই চ্যানেলের জন্য পোস্টটি কাস্টমাইজ করতে এই বোতামে ক্লিক করুন", "edit_content": "বিষয়বস্তু সম্পাদনা করুন", diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index 23b5a293..fc8062c4 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Inhalt auf TikTok hochladen, ohne zu posten", "choose_upload_without_posting_description": "Wähle 'Hochladen ohne Posten', wenn du deinen Inhalt in der TikTok-App vor der Veröffentlichung überprüfen und bearbeiten möchtest. So hast du Zugriff auf die integrierten Bearbeitungstools von TikTok und kannst vor dem Posten letzte Anpassungen vornehmen.", "faq_am_i_going_to_be_charged_by_postiz": "Werde ich von Postiz Gebühren berechnet bekommen?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Um die Kreditkarteninformationen zu bestätigen, wird Postiz 2$ reservieren und diesen Betrag sofort wieder freigeben.", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Um die Kreditkarteninformationen zu bestätigen, wird Postiz 2 $ vorübergehend einbehalten und sofort wieder freigeben. Sie können Ihr Abonnement jederzeit in den Einstellungen kündigen, ohne mit einer Person sprechen zu müssen.", "faq_can_i_trust_postiz_gitroom": "Kann ich Postiz vertrauen?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz ist stolz darauf, Open Source zu sein! Wir glauben an eine ethische und transparente Kultur, was bedeutet, dass Postiz für immer bestehen wird. Du kannst den gesamten Code einsehen oder für persönliche Projekte nutzen. Um das Open-Source-Repository anzusehen, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">klicke hier</a>.", "faq_what_are_channels": "Was sind Kanäle?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Wir automatisieren ChatGPT, um dir beim Schreiben von Social-Media-Beiträgen und Artikeln zu helfen.", "enter_email": "E-Mail eingeben", "are_you_sure": "Bist du sicher?", - "yes_delete_it": "Ja, löschen!", "no_cancel": "Nein, abbrechen!", "are_you_sure_you_want_to_delete": "Bist du sicher, dass du {{name}} löschen möchtest?", "are_you_sure_you_want_to_delete_the_image": "Bist du sicher, dass du das Bild löschen möchtest?", @@ -505,7 +504,7 @@ "delete_integration": "Integration löschen", "start_writing_your_post": "Beginne, deinen Beitrag für eine Vorschau zu schreiben", "billing_join_over": "Schließen Sie sich über", - "billing_entrepreneurs_count": "18.000+ Unternehmern an", + "billing_entrepreneurs_count": "20.000+ Unternehmer", "billing_who_use": "die nutzen", "billing_postiz_grow_social": "Postiz, um ihre Social-Media-Präsenz zu steigern", "billing_no_risk_trial": "100% risikofreie Testphase", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Heute 0 $ zahlen – Starten Sie Ihre kostenlose Testphase!", "billing_pay_now": "Jetzt bezahlen", "billing_per_month": "/ Monat", + "billing_per_year": "/ Jahr", + "billing_order_summary": "Bestellübersicht", + "billing_applied": "Angewendet", + "billing_due_today": "Heute fällig", + "billing_then": "Dann", + "billing_on": "am", + "billing_discount_applied": "angewendet", + "billing_remove": "Entfernen", + "billing_coupon_expires": "Gutschein läuft ab am", + "billing_invalid_coupon": "Ungültiger Gutscheincode", + "billing_coupon_applied": "Gutschein erfolgreich angewendet!", + "billing_coupon_removed": "Gutschein entfernt", + "billing_error_removing_coupon": "Fehler beim Entfernen des Gutscheins", + "billing_have_discount_coupon": "Haben Sie einen Rabattgutschein?", + "billing_discount_coupon": "Rabattgutschein", + "billing_cancel": "Abbrechen", + "billing_enter_coupon_code": "Gutscheincode eingeben", + "billing_applying": "Wird angewendet...", + "billing_apply": "Anwenden", + "billing_subscription": "Abonnement", + "billing_cancel_notice": "Sie können jederzeit in den Einstellungen kündigen, ohne mit einer Person sprechen zu müssen, und werden nie belastet.", "select_channels": "Kanäle auswählen", "start_a_new_chat": "Neuen Chat starten", "your_assistant": "Ihr Assistent", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Diese Aktion ist unwiderruflich. Bist du sicher, dass du in den globalen Modus zurückkehren möchtest?", "yes_go_back_to_global_mode": "Ja, zum globalen Modus zurückkehren", "are_you_sure_delete_this_post": "Bist du sicher, dass du diesen Beitrag löschen möchtest?", + "yes_delete_it": "Ja, löschen!", "cant_edit_networks_when_creating_set": "Du kannst Netzwerke beim Erstellen eines Sets nicht bearbeiten.", "click_to_exit_global_editing": "Klicke auf diese Schaltfläche, um die globale Bearbeitung zu beenden und den Beitrag für diesen Kanal anzupassen.", "edit_content": "Inhalt bearbeiten", diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index 6c0b78be..4537bb6c 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Upload content to TikTok without posting it", "choose_upload_without_posting_description": "Choose upload without posting if you want to review and edit your content within TikTok's app before publishing. This gives you access to TikTok's built-in editing tools and lets you make final adjustments before posting.", "faq_am_i_going_to_be_charged_by_postiz": "Am I going to be charged by Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "To confirm credit card information Postiz will hold $2 and release it immediately", + "faq_to_confirm_credit_card_information_postiz_will_hold": "To confirm credit card information Postiz will hold $2 and release it immediately, you can cancel your subscription anytime from settings without talking to a person", "faq_can_i_trust_postiz_gitroom": "Can I trust Postiz?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz is proudly open-source! We believe in an ethical and transparent culture, meaning that Postiz will live forever. You can check out the entire code or use it for personal projects. To view the open-source repository, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">click here</a>.", "faq_what_are_channels": "What are channels?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "We automate ChatGPT to help you write social posts and articles.", "enter_email": "Enter email", "are_you_sure": "Are you sure?", - "yes_delete_it": "Yes, delete it!", "no_cancel": "No, cancel!", "are_you_sure_you_want_to_delete": "Are you sure you want to delete {{name}}?", "are_you_sure_you_want_to_delete_the_image": "Are you sure you want to delete the image?", @@ -505,7 +504,7 @@ "delete_integration": "Delete Integration", "start_writing_your_post": "Start writing your post for a preview", "billing_join_over": "Join Over", - "billing_entrepreneurs_count": "18,000+ Entrepreneurs", + "billing_entrepreneurs_count": "20,000+ Entrepreneurs", "billing_who_use": "who use", "billing_postiz_grow_social": "Postiz To Grow Their Social Presence", "billing_no_risk_trial": "100% No-Risk Free Trial", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Pay $0 Today - Start your free trial!", "billing_pay_now": "Pay Now", "billing_per_month": "/ month", + "billing_per_year": "/ year", + "billing_order_summary": "Order Summary", + "billing_applied": "Applied", + "billing_due_today": "Due today", + "billing_then": "Then", + "billing_on": "on", + "billing_discount_applied": "applied", + "billing_remove": "Remove", + "billing_coupon_expires": "Coupon expires on", + "billing_invalid_coupon": "Invalid coupon code", + "billing_coupon_applied": "Coupon applied successfully!", + "billing_coupon_removed": "Coupon removed", + "billing_error_removing_coupon": "Error removing coupon", + "billing_have_discount_coupon": "Have a discount coupon?", + "billing_discount_coupon": "Discount Coupon", + "billing_cancel": "Cancel", + "billing_enter_coupon_code": "Enter coupon code", + "billing_applying": "Applying...", + "billing_apply": "Apply", + "billing_subscription": "Subscription", + "billing_cancel_notice": "Cancel anytime from settings without talking to a person and never be charged.", "select_channels": "Select Channels", "start_a_new_chat": "Start a new chat", "your_assistant": "Your Assistant", diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index c8b9fd37..db286261 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Subir contenido a TikTok sin publicarlo", "choose_upload_without_posting_description": "Elige subir sin publicar si quieres revisar y editar tu contenido en la aplicación de TikTok antes de publicarlo. Esto te da acceso a las herramientas de edición integradas de TikTok y te permite hacer ajustes finales antes de publicar.", "faq_am_i_going_to_be_charged_by_postiz": "¿Me va a cobrar Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar la información de la tarjeta de crédito, Postiz retendrá $2 y los liberará inmediatamente", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar la información de la tarjeta de crédito, Postiz retendrá $2 y los liberará de inmediato. Puedes cancelar tu suscripción en cualquier momento desde la configuración sin hablar con una persona.", "faq_can_i_trust_postiz_gitroom": "¿Puedo confiar en Postiz?", "faq_postiz_gitroom_is_proudly_open_source": "¡Postiz es orgullosamente de código abierto! Creemos en una cultura ética y transparente, lo que significa que Postiz vivirá para siempre. Puedes revisar todo el código o usarlo para proyectos personales. Para ver el repositorio de código abierto, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">haz clic aquí</a>.", "faq_what_are_channels": "¿Qué son los canales?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Automatizamos ChatGPT para ayudarte a escribir publicaciones sociales y artículos.", "enter_email": "Introduce el correo electrónico", "are_you_sure": "¿Estás seguro?", - "yes_delete_it": "¡Sí, bórralo!", "no_cancel": "No, cancelar", "are_you_sure_you_want_to_delete": "¿Estás seguro de que quieres eliminar {{name}}?", "are_you_sure_you_want_to_delete_the_image": "¿Estás seguro de que quieres eliminar la imagen?", @@ -505,7 +504,7 @@ "delete_integration": "Eliminar integración", "start_writing_your_post": "Comienza a escribir tu publicación para obtener una vista previa", "billing_join_over": "Únete a más de", - "billing_entrepreneurs_count": "18,000+ emprendedores", + "billing_entrepreneurs_count": "Más de 20,000 emprendedores", "billing_who_use": "que usan", "billing_postiz_grow_social": "Postiz para hacer crecer su presencia en redes sociales", "billing_no_risk_trial": "Prueba gratuita 100% sin riesgo", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "¡Paga $0 hoy - Comienza tu prueba gratis!", "billing_pay_now": "Pagar ahora", "billing_per_month": "/ mes", + "billing_per_year": "/ año", + "billing_order_summary": "Resumen del pedido", + "billing_applied": "Aplicado", + "billing_due_today": "A pagar hoy", + "billing_then": "Luego", + "billing_on": "el", + "billing_discount_applied": "descuento aplicado", + "billing_remove": "Eliminar", + "billing_coupon_expires": "El cupón vence el", + "billing_invalid_coupon": "Código de cupón inválido", + "billing_coupon_applied": "¡Cupón aplicado con éxito!", + "billing_coupon_removed": "Cupón eliminado", + "billing_error_removing_coupon": "Error al eliminar el cupón", + "billing_have_discount_coupon": "¿Tienes un cupón de descuento?", + "billing_discount_coupon": "Cupón de descuento", + "billing_cancel": "Cancelar", + "billing_enter_coupon_code": "Ingresa el código del cupón", + "billing_applying": "Aplicando...", + "billing_apply": "Aplicar", + "billing_subscription": "Suscripción", + "billing_cancel_notice": "Cancela en cualquier momento desde la configuración sin hablar con una persona y nunca se te cobrará.", "select_channels": "Seleccionar canales", "start_a_new_chat": "Iniciar un nuevo chat", "your_assistant": "Tu Asistente", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Esta acción es irreversible. ¿Estás seguro de que quieres volver al modo global?", "yes_go_back_to_global_mode": "Sí, volver al modo global", "are_you_sure_delete_this_post": "¿Estás seguro de que quieres eliminar esta publicación?", + "yes_delete_it": "¡Sí, bórralo!", "cant_edit_networks_when_creating_set": "No puedes editar redes al crear un conjunto", "click_to_exit_global_editing": "Haz clic en este botón para salir de la edición global y personalizar la publicación para este canal", "edit_content": "Editar contenido", diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index a542ad60..2792bd5b 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Télécharger le contenu sur TikTok sans le publier", "choose_upload_without_posting_description": "Choisissez de télécharger sans publier si vous souhaitez revoir et modifier votre contenu dans l’application TikTok avant de le publier. Cela vous donne accès aux outils d’édition intégrés de TikTok et vous permet de faire des ajustements finaux avant la publication.", "faq_am_i_going_to_be_charged_by_postiz": "Vais-je être facturé par Postiz ?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Pour confirmer les informations de carte de crédit, Postiz retiendra 2 $ et les libérera immédiatement", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Pour confirmer les informations de carte de crédit, Postiz retiendra 2 $ et les libérera immédiatement. Vous pouvez annuler votre abonnement à tout moment depuis les paramètres, sans avoir à parler à quelqu'un.", "faq_can_i_trust_postiz_gitroom": "Puis-je faire confiance à Postiz ?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz est fièrement open source ! Nous croyons en une culture éthique et transparente, ce qui signifie que Postiz existera toujours. Vous pouvez consulter l'intégralité du code ou l'utiliser pour des projets personnels. Pour voir le dépôt open source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">cliquez ici</a>.", "faq_what_are_channels": "Que sont les canaux ?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Nous automatisons ChatGPT pour vous aider à rédiger des publications sociales et des articles.", "enter_email": "Saisissez l’e-mail", "are_you_sure": "Êtes-vous sûr ?", - "yes_delete_it": "Oui, supprimez-le !", "no_cancel": "Non, annuler !", "are_you_sure_you_want_to_delete": "Êtes-vous sûr de vouloir supprimer {{name}} ?", "are_you_sure_you_want_to_delete_the_image": "Êtes-vous sûr de vouloir supprimer l'image ?", @@ -505,7 +504,7 @@ "delete_integration": "Supprimer l'intégration", "start_writing_your_post": "Commencez à écrire votre post pour un aperçu", "billing_join_over": "Rejoignez plus de", - "billing_entrepreneurs_count": "18 000+ entrepreneurs", + "billing_entrepreneurs_count": "Plus de 20 000 entrepreneurs", "billing_who_use": "qui utilisent", "billing_postiz_grow_social": "Postiz pour développer leur présence sur les réseaux sociaux", "billing_no_risk_trial": "Essai gratuit 100% sans risque", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Payez 0 $ aujourd'hui - Commencez votre essai gratuit !", "billing_pay_now": "Payer maintenant", "billing_per_month": "/ mois", + "billing_per_year": "/ an", + "billing_order_summary": "Récapitulatif de la commande", + "billing_applied": "Appliqué", + "billing_due_today": "À payer aujourd'hui", + "billing_then": "Ensuite", + "billing_on": "le", + "billing_discount_applied": "appliqué", + "billing_remove": "Supprimer", + "billing_coupon_expires": "Le coupon expire le", + "billing_invalid_coupon": "Code coupon invalide", + "billing_coupon_applied": "Coupon appliqué avec succès !", + "billing_coupon_removed": "Coupon supprimé", + "billing_error_removing_coupon": "Erreur lors de la suppression du coupon", + "billing_have_discount_coupon": "Vous avez un coupon de réduction ?", + "billing_discount_coupon": "Coupon de réduction", + "billing_cancel": "Annuler", + "billing_enter_coupon_code": "Entrez le code du coupon", + "billing_applying": "Application en cours...", + "billing_apply": "Appliquer", + "billing_subscription": "Abonnement", + "billing_cancel_notice": "Annulez à tout moment depuis les paramètres sans avoir à parler à quelqu'un et ne soyez jamais facturé.", "select_channels": "Sélectionner les canaux", "start_a_new_chat": "Démarrer une nouvelle conversation", "your_assistant": "Votre assistant", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Cette action est irréversible. Êtes-vous sûr de vouloir revenir au mode global ?", "yes_go_back_to_global_mode": "Oui, revenir au mode global", "are_you_sure_delete_this_post": "Êtes-vous sûr de vouloir supprimer cette publication ?", + "yes_delete_it": "Oui, supprimez-le !", "cant_edit_networks_when_creating_set": "Vous ne pouvez pas modifier les réseaux lors de la création d’un ensemble", "click_to_exit_global_editing": "Cliquez sur ce bouton pour quitter l’édition globale et personnaliser la publication pour ce canal", "edit_content": "Modifier le contenu", diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index bec164b6..54f0d5de 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "העלה תוכן ל-TikTok מבלי לפרסם אותו", "choose_upload_without_posting_description": "בחר העלאה ללא פרסום אם ברצונך לעיין ולערוך את התוכן שלך באפליקציית TikTok לפני הפרסום. זה יאפשר לך להשתמש בכלי העריכה המובנים של TikTok ולבצע התאמות אחרונות לפני הפרסום.", "faq_am_i_going_to_be_charged_by_postiz": "האם אחויב על ידי Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "כדי לאמת את פרטי כרטיס האשראי, Postiz תחזיק $2 ותשחרר אותם מיד", + "faq_to_confirm_credit_card_information_postiz_will_hold": "כדי לאמת את פרטי כרטיס האשראי, פוסטיז תחזיק בסכום של 2$ ותשחרר אותו מיד. ניתן לבטל את המנוי בכל עת מההגדרות מבלי לדבר עם נציג.", "faq_can_i_trust_postiz_gitroom": "האם אפשר לסמוך על Postiz?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz היא קוד פתוח בגאווה! אנו מאמינים בתרבות אתית ושקופה, מה שאומר ש-Postiz תמשיך להתקיים לנצח. אתם יכולים לעיין בכל הקוד או להשתמש בו לפרויקטים אישיים. לצפייה במאגר הקוד הפתוח, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">לחצו כאן</a>.", "faq_what_are_channels": "מהם ערוצים?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "אנו משתמשים ב-ChatGPT כדי לעזור לך לכתוב פוסטים ומאמרים לרשתות החברתיות.", "enter_email": "הזן אימייל", "are_you_sure": "האם אתה בטוח?", - "yes_delete_it": "כן, מחק את זה!", "no_cancel": "לא, בטל!", "are_you_sure_you_want_to_delete": "האם אתה בטוח שברצונך למחוק את {{name}}?", "are_you_sure_you_want_to_delete_the_image": "האם אתה בטוח שברצונך למחוק את התמונה?", @@ -505,7 +504,7 @@ "delete_integration": "מחק אינטגרציה", "start_writing_your_post": "התחל לכתוב את הפוסט שלך לתצוגה מקדימה", "billing_join_over": "הצטרפו ליותר מ-", - "billing_entrepreneurs_count": "18,000+ יזמים", + "billing_entrepreneurs_count": "20,000+ יזמים", "billing_who_use": "שמשתמשים ב-", "billing_postiz_grow_social": "Postiz כדי להגדיל את הנוכחות החברתית שלהם", "billing_no_risk_trial": "ניסיון חינם ללא סיכון ב-100%", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "שלם 0$ היום - התחל את תקופת הניסיון שלך!", "billing_pay_now": "שלם עכשיו", "billing_per_month": "/ חודש", + "billing_per_year": "/ שנה", + "billing_order_summary": "סיכום הזמנה", + "billing_applied": "הוחל", + "billing_due_today": "לתשלום היום", + "billing_then": "לאחר מכן", + "billing_on": "בתאריך", + "billing_discount_applied": "ההנחה הוחלה", + "billing_remove": "הסר", + "billing_coupon_expires": "הקופון פג בתאריך", + "billing_invalid_coupon": "קוד קופון לא תקין", + "billing_coupon_applied": "הקופון הוחל בהצלחה!", + "billing_coupon_removed": "הקופון הוסר", + "billing_error_removing_coupon": "שגיאה בהסרת הקופון", + "billing_have_discount_coupon": "יש לך קופון הנחה?", + "billing_discount_coupon": "קופון הנחה", + "billing_cancel": "ביטול", + "billing_enter_coupon_code": "הזן קוד קופון", + "billing_applying": "מוחל...", + "billing_apply": "החל", + "billing_subscription": "מנוי", + "billing_cancel_notice": "ניתן לבטל בכל עת מההגדרות מבלי לדבר עם נציג ולעולם לא תחויב.", "select_channels": "בחר ערוצים", "start_a_new_chat": "התחל שיחה חדשה", "your_assistant": "העוזר שלך", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "פעולה זו אינה ניתנת לביטול. האם אתה בטוח שברצונך לחזור למצב גלובלי?", "yes_go_back_to_global_mode": "כן, חזור למצב גלובלי", "are_you_sure_delete_this_post": "האם אתה בטוח שברצונך למחוק את הפוסט הזה?", + "yes_delete_it": "כן, מחק את זה!", "cant_edit_networks_when_creating_set": "לא ניתן לערוך רשתות בעת יצירת סט", "click_to_exit_global_editing": "לחץ על כפתור זה כדי לצאת מעריכה גלובלית ולהתאים את הפוסט לערוץ זה", "edit_content": "ערוך תוכן", diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index 27c7e7bf..b4e918df 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Carica contenuto su TikTok senza pubblicarlo", "choose_upload_without_posting_description": "Scegli 'carica senza pubblicare' se vuoi rivedere e modificare il tuo contenuto nell'app di TikTok prima di pubblicarlo. Questo ti dà accesso agli strumenti di modifica integrati di TikTok e ti permette di fare le ultime modifiche prima della pubblicazione.", "faq_am_i_going_to_be_charged_by_postiz": "Mi verrà addebitato qualcosa da Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Per confermare le informazioni della carta di credito, Postiz tratterrà 2$ e li rilascerà immediatamente", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Per confermare le informazioni della carta di credito, Postiz tratterrà 2$ e li rilascerà immediatamente. Puoi annullare l'abbonamento in qualsiasi momento dalle impostazioni senza parlare con nessuno.", "faq_can_i_trust_postiz_gitroom": "Posso fidarmi di Postiz?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz è orgogliosamente open-source! Crediamo in una cultura etica e trasparente, il che significa che Postiz vivrà per sempre. Puoi consultare tutto il codice o usarlo per progetti personali. Per visualizzare il repository open-source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">clicca qui</a>.", "faq_what_are_channels": "Cosa sono i canali?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Automatizziamo ChatGPT per aiutarti a scrivere post social e articoli.", "enter_email": "Inserisci email", "are_you_sure": "Sei sicuro?", - "yes_delete_it": "Sì, eliminalo!", "no_cancel": "No, annulla!", "are_you_sure_you_want_to_delete": "Sei sicuro di voler eliminare {{name}}?", "are_you_sure_you_want_to_delete_the_image": "Sei sicuro di voler eliminare l'immagine?", @@ -505,7 +504,7 @@ "delete_integration": "Elimina integrazione", "start_writing_your_post": "Inizia a scrivere il tuo post per un'anteprima", "billing_join_over": "Unisciti a oltre", - "billing_entrepreneurs_count": "18.000+ imprenditori", + "billing_entrepreneurs_count": "Oltre 20.000 imprenditori", "billing_who_use": "che usano", "billing_postiz_grow_social": "Postiz per far crescere la loro presenza sui social", "billing_no_risk_trial": "Prova gratuita senza rischi al 100%", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Paga 0€ oggi - Inizia la tua prova gratuita!", "billing_pay_now": "Paga ora", "billing_per_month": "/ mese", + "billing_per_year": "/ anno", + "billing_order_summary": "Riepilogo ordine", + "billing_applied": "Applicato", + "billing_due_today": "Dovuto oggi", + "billing_then": "Poi", + "billing_on": "il", + "billing_discount_applied": "applicato", + "billing_remove": "Rimuovi", + "billing_coupon_expires": "Il coupon scade il", + "billing_invalid_coupon": "Codice coupon non valido", + "billing_coupon_applied": "Coupon applicato con successo!", + "billing_coupon_removed": "Coupon rimosso", + "billing_error_removing_coupon": "Errore durante la rimozione del coupon", + "billing_have_discount_coupon": "Hai un coupon sconto?", + "billing_discount_coupon": "Coupon sconto", + "billing_cancel": "Annulla", + "billing_enter_coupon_code": "Inserisci il codice coupon", + "billing_applying": "Applicazione in corso...", + "billing_apply": "Applica", + "billing_subscription": "Abbonamento", + "billing_cancel_notice": "Annulla in qualsiasi momento dalle impostazioni senza parlare con nessuno e non ti verrà mai addebitato nulla.", "select_channels": "Seleziona canali", "start_a_new_chat": "Inizia una nuova chat", "your_assistant": "Il tuo assistente", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Questa azione è irreversibile. Sei sicuro di voler tornare alla modalità globale?", "yes_go_back_to_global_mode": "Sì, torna alla modalità globale", "are_you_sure_delete_this_post": "Sei sicuro di voler eliminare questo post?", + "yes_delete_it": "Sì, eliminalo!", "cant_edit_networks_when_creating_set": "Non puoi modificare le reti durante la creazione di un set", "click_to_exit_global_editing": "Clicca su questo pulsante per uscire dalla modifica globale e personalizzare il post per questo canale", "edit_content": "Modifica contenuto", diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index 5e3c732a..300929b1 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "投稿せずにコンテンツをTikTokにアップロードする", "choose_upload_without_posting_description": "公開前にTikTokアプリ内でコンテンツを確認・編集したい場合は「投稿せずにアップロード」を選択してください。これにより、TikTokの編集ツールを利用でき、投稿前に最終調整が可能です。", "faq_am_i_going_to_be_charged_by_postiz": "Postizから料金が請求されますか?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "クレジットカード情報の確認のため、Postizは2ドルを一時的に保持し、すぐに解除します", + "faq_to_confirm_credit_card_information_postiz_will_hold": "クレジットカード情報を確認するために、Postizは2ドルを一時的に保持し、すぐに返金します。設定からいつでも人と話すことなくサブスクリプションをキャンセルできます。", "faq_can_i_trust_postiz_gitroom": "Postizは信頼できますか?", "faq_postiz_gitroom_is_proudly_open_source": "Postizは誇りを持ってオープンソースです!私たちは倫理的で透明性のある文化を信じており、Postizは永遠に存続します。全てのコードを確認したり、個人プロジェクトで利用したりできます。オープンソースリポジトリを見るには、<a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">こちらをクリック</a>してください。", "faq_what_are_channels": "チャンネルとは何ですか?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "ChatGPTを自動化して、SNS投稿や記事の作成をサポートします。", "enter_email": "メールアドレスを入力", "are_you_sure": "本当に宜しいですか?", - "yes_delete_it": "はい、削除します!", "no_cancel": "いいえ、キャンセルします", "are_you_sure_you_want_to_delete": "{{name}} を削除してもよろしいですか?", "are_you_sure_you_want_to_delete_the_image": "この画像を削除してもよろしいですか?", @@ -505,7 +504,7 @@ "delete_integration": "連携を削除", "start_writing_your_post": "プレビュー用に投稿を書き始めてください", "billing_join_over": "すでに参加している", - "billing_entrepreneurs_count": "18,000人以上の起業家", + "billing_entrepreneurs_count": "20,000人以上の起業家", "billing_who_use": "が利用している", "billing_postiz_grow_social": "Postizでソーシャルプレゼンスを成長させる", "billing_no_risk_trial": "100%リスクなしの無料トライアル", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "本日のお支払いは0円 - 無料トライアルを始めましょう!", "billing_pay_now": "今すぐ支払う", "billing_per_month": "/月", + "billing_per_year": "/年", + "billing_order_summary": "ご注文内容", + "billing_applied": "適用済み", + "billing_due_today": "本日のお支払い額", + "billing_then": "その後", + "billing_on": "日付", + "billing_discount_applied": "割引適用済み", + "billing_remove": "削除", + "billing_coupon_expires": "クーポンの有効期限:", + "billing_invalid_coupon": "無効なクーポンコードです", + "billing_coupon_applied": "クーポンが正常に適用されました!", + "billing_coupon_removed": "クーポンが削除されました", + "billing_error_removing_coupon": "クーポンの削除中にエラーが発生しました", + "billing_have_discount_coupon": "割引クーポンをお持ちですか?", + "billing_discount_coupon": "割引クーポン", + "billing_cancel": "キャンセル", + "billing_enter_coupon_code": "クーポンコードを入力してください", + "billing_applying": "適用中...", + "billing_apply": "適用", + "billing_subscription": "サブスクリプション", + "billing_cancel_notice": "設定からいつでも人と話すことなくキャンセルでき、料金は一切発生しません。", "select_channels": "チャンネルを選択", "start_a_new_chat": "新しいチャットを開始", "your_assistant": "あなたのアシスタント", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "この操作は元に戻せません。本当にグローバルモードに戻りますか?", "yes_go_back_to_global_mode": "はい、グローバルモードに戻ります", "are_you_sure_delete_this_post": "本当にこの投稿を削除しますか?", + "yes_delete_it": "はい、削除します!", "cant_edit_networks_when_creating_set": "セット作成中はネットワークを編集できません", "click_to_exit_global_editing": "このボタンをクリックしてグローバル編集を終了し、このチャンネル用に投稿をカスタマイズします", "edit_content": "内容を編集", diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index 783dfb07..881cc319 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "게시하지 않고 TikTok에 콘텐츠 업로드하기", "choose_upload_without_posting_description": "게시하지 않고 업로드를 선택하면 TikTok 앱 내에서 콘텐츠를 검토하고 편집한 후 게시할 수 있습니다. TikTok의 내장 편집 도구를 사용할 수 있으며, 게시 전에 최종 수정을 할 수 있습니다.", "faq_am_i_going_to_be_charged_by_postiz": "Postiz에서 요금이 청구되나요?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "신용카드 정보를 확인하기 위해 Postiz는 $2를 보류했다가 즉시 해제합니다.", + "faq_to_confirm_credit_card_information_postiz_will_hold": "신용카드 정보를 확인하기 위해 Postiz는 $2를 보류했다가 즉시 해제합니다. 설정에서 언제든지 사람과 대화하지 않고 구독을 취소할 수 있습니다.", "faq_can_i_trust_postiz_gitroom": "Postiz를 신뢰할 수 있나요?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz는 자랑스럽게 오픈소스입니다! 우리는 윤리적이고 투명한 문화를 믿으며, Postiz는 영원히 존재할 것입니다. 전체 코드를 확인하거나 개인 프로젝트에 사용할 수 있습니다. 오픈소스 저장소를 보려면 <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">여기를 클릭하세요</a>.", "faq_what_are_channels": "채널이란 무엇인가요?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "ChatGPT를 자동화하여 소셜 게시물과 글쓰기를 도와드립니다.", "enter_email": "이메일 입력", "are_you_sure": "정말로 하시겠습니까?", - "yes_delete_it": "네, 삭제하세요!", "no_cancel": "아니요, 취소하세요!", "are_you_sure_you_want_to_delete": "{{name}}을(를) 삭제하시겠습니까?", "are_you_sure_you_want_to_delete_the_image": "이 이미지를 삭제하시겠습니까?", @@ -505,7 +504,7 @@ "delete_integration": "통합 삭제", "start_writing_your_post": "미리보기를 위해 게시글 작성을 시작하세요", "billing_join_over": "이미 가입한", - "billing_entrepreneurs_count": "18,000+ 명의 창업가", + "billing_entrepreneurs_count": "20,000+명의 창업가", "billing_who_use": "이(가) 사용하는", "billing_postiz_grow_social": "Postiz로 소셜 존재감을 키우세요", "billing_no_risk_trial": "100% 무위험 무료 체험", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "오늘 $0 결제 - 무료 체험을 시작하세요!", "billing_pay_now": "지금 결제", "billing_per_month": "/월", + "billing_per_year": "/ 연", + "billing_order_summary": "주문 요약", + "billing_applied": "적용됨", + "billing_due_today": "오늘 결제 금액", + "billing_then": "그 다음", + "billing_on": "~에", + "billing_discount_applied": "할인 적용됨", + "billing_remove": "제거", + "billing_coupon_expires": "쿠폰 만료일:", + "billing_invalid_coupon": "유효하지 않은 쿠폰 코드", + "billing_coupon_applied": "쿠폰이 성공적으로 적용되었습니다!", + "billing_coupon_removed": "쿠폰이 제거되었습니다", + "billing_error_removing_coupon": "쿠폰 제거 중 오류가 발생했습니다", + "billing_have_discount_coupon": "할인 쿠폰이 있으신가요?", + "billing_discount_coupon": "할인 쿠폰", + "billing_cancel": "취소", + "billing_enter_coupon_code": "쿠폰 코드를 입력하세요", + "billing_applying": "적용 중...", + "billing_apply": "적용", + "billing_subscription": "구독", + "billing_cancel_notice": "설정에서 언제든지 사람과 대화하지 않고 구독을 취소할 수 있으며, 결제가 청구되지 않습니다.", "select_channels": "채널 선택", "start_a_new_chat": "새 채팅 시작", "your_assistant": "당신의 어시스턴트", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "이 작업은 되돌릴 수 없습니다. 정말로 글로벌 모드로 돌아가시겠습니까?", "yes_go_back_to_global_mode": "네, 글로벌 모드로 돌아갑니다", "are_you_sure_delete_this_post": "이 게시물을 삭제하시겠습니까?", + "yes_delete_it": "네, 삭제하세요!", "cant_edit_networks_when_creating_set": "세트를 생성할 때는 네트워크를 편집할 수 없습니다.", "click_to_exit_global_editing": "이 버튼을 클릭하면 글로벌 편집을 종료하고 이 채널에 맞게 게시물을 맞춤 설정할 수 있습니다.", "edit_content": "내용 편집", diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index 09d37edc..1b60edc7 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Enviar conteúdo para o TikTok sem publicar", "choose_upload_without_posting_description": "Escolha enviar sem publicar se quiser revisar e editar seu conteúdo no aplicativo do TikTok antes de publicar. Isso lhe dá acesso às ferramentas de edição do próprio TikTok e permite fazer ajustes finais antes de postar.", "faq_am_i_going_to_be_charged_by_postiz": "Vou ser cobrado pelo Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar as informações do cartão de crédito, o Postiz irá reter R$2 e liberá-lo imediatamente", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Para confirmar as informações do cartão de crédito, a Postiz irá reter R$2 e liberá-lo imediatamente. Você pode cancelar sua assinatura a qualquer momento nas configurações, sem precisar falar com ninguém.", "faq_can_i_trust_postiz_gitroom": "Posso confiar no Postiz?", "faq_postiz_gitroom_is_proudly_open_source": "O Postiz é orgulhosamente open-source! Acreditamos em uma cultura ética e transparente, o que significa que o Postiz viverá para sempre. Você pode conferir todo o código ou usá-lo em projetos pessoais. Para ver o repositório open-source, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">clique aqui</a>.", "faq_what_are_channels": "O que são canais?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Automatizamos o ChatGPT para ajudar você a escrever postagens e artigos para redes sociais.", "enter_email": "Digite o e-mail", "are_you_sure": "Tem certeza?", - "yes_delete_it": "Sim, exclua!", "no_cancel": "Não, cancelar!", "are_you_sure_you_want_to_delete": "Tem certeza de que deseja excluir {{name}}?", "are_you_sure_you_want_to_delete_the_image": "Tem certeza de que deseja excluir a imagem?", @@ -505,7 +504,7 @@ "delete_integration": "Excluir integração", "start_writing_your_post": "Comece a escrever sua postagem para visualizar", "billing_join_over": "Junte-se a mais de", - "billing_entrepreneurs_count": "18.000+ empreendedores", + "billing_entrepreneurs_count": "20.000+ Empreendedores", "billing_who_use": "que usam", "billing_postiz_grow_social": "Postiz para aumentar sua presença social", "billing_no_risk_trial": "Teste gratuito 100% sem risco", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Pague R$0 hoje - Comece seu teste gratuito!", "billing_pay_now": "Pagar agora", "billing_per_month": "/ mês", + "billing_per_year": "/ ano", + "billing_order_summary": "Resumo do Pedido", + "billing_applied": "Aplicado", + "billing_due_today": "Vencimento hoje", + "billing_then": "Depois", + "billing_on": "em", + "billing_discount_applied": "aplicado", + "billing_remove": "Remover", + "billing_coupon_expires": "O cupom expira em", + "billing_invalid_coupon": "Código de cupom inválido", + "billing_coupon_applied": "Cupom aplicado com sucesso!", + "billing_coupon_removed": "Cupom removido", + "billing_error_removing_coupon": "Erro ao remover o cupom", + "billing_have_discount_coupon": "Possui um cupom de desconto?", + "billing_discount_coupon": "Cupom de Desconto", + "billing_cancel": "Cancelar", + "billing_enter_coupon_code": "Digite o código do cupom", + "billing_applying": "Aplicando...", + "billing_apply": "Aplicar", + "billing_subscription": "Assinatura", + "billing_cancel_notice": "Cancele a qualquer momento nas configurações sem falar com ninguém e nunca será cobrado.", "select_channels": "Selecionar canais", "start_a_new_chat": "Iniciar um novo chat", "your_assistant": "Seu Assistente", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Esta ação é irreversível. Tem certeza de que deseja voltar para o modo global?", "yes_go_back_to_global_mode": "Sim, voltar para o modo global", "are_you_sure_delete_this_post": "Tem certeza de que deseja excluir esta postagem?", + "yes_delete_it": "Sim, exclua!", "cant_edit_networks_when_creating_set": "Você não pode editar redes ao criar um conjunto", "click_to_exit_global_editing": "Clique neste botão para sair da edição global e personalizar a postagem para este canal", "edit_content": "Editar conteúdo", diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index 146b5d25..6b913a8e 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Загрузить контент в TikTok без публикации", "choose_upload_without_posting_description": "Выберите загрузку без публикации, если хотите просмотреть и отредактировать свой контент в приложении TikTok перед публикацией. Это даст вам доступ к встроенным инструментам редактирования TikTok и позволит внести финальные изменения перед публикацией.", "faq_am_i_going_to_be_charged_by_postiz": "Будет ли с меня взиматься плата Postiz?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Для подтверждения информации о кредитной карте Postiz удержит $2 и сразу же их вернет", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Для подтверждения информации о кредитной карте Postiz временно удержит $2 и сразу же их вернет. Вы можете отменить подписку в любое время через настройки без необходимости разговаривать с оператором.", "faq_can_i_trust_postiz_gitroom": "Можно ли доверять Postiz?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz — это полностью open-source! Мы верим в этичную и прозрачную культуру, а значит, Postiz будет существовать всегда. Вы можете ознакомиться с полным кодом или использовать его для личных проектов. Чтобы просмотреть open-source репозиторий, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">нажмите здесь</a>.", "faq_what_are_channels": "Что такое каналы?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Мы автоматизируем ChatGPT, чтобы помочь вам писать посты и статьи для соцсетей.", "enter_email": "Введите email", "are_you_sure": "Вы уверены?", - "yes_delete_it": "Да, удалить!", "no_cancel": "Нет, отмена!", "are_you_sure_you_want_to_delete": "Вы уверены, что хотите удалить {{name}}?", "are_you_sure_you_want_to_delete_the_image": "Вы уверены, что хотите удалить изображение?", @@ -505,7 +504,7 @@ "delete_integration": "Удалить интеграцию", "start_writing_your_post": "Начните писать свой пост для предварительного просмотра", "billing_join_over": "Присоединяйтесь к", - "billing_entrepreneurs_count": "18 000+ предпринимателей", + "billing_entrepreneurs_count": "20 000+ предпринимателей", "billing_who_use": "которые используют", "billing_postiz_grow_social": "Postiz для роста своей социальной активности", "billing_no_risk_trial": "100% бесплатный пробный период без риска", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Заплатите $0 сегодня — начните бесплатный пробный период!", "billing_pay_now": "Оплатить сейчас", "billing_per_month": "/ месяц", + "billing_per_year": "/ год", + "billing_order_summary": "Сводка заказа", + "billing_applied": "Применено", + "billing_due_today": "К оплате сегодня", + "billing_then": "Затем", + "billing_on": "в", + "billing_discount_applied": "скидка применена", + "billing_remove": "Удалить", + "billing_coupon_expires": "Купон истекает", + "billing_invalid_coupon": "Недействительный промокод", + "billing_coupon_applied": "Промокод успешно применён!", + "billing_coupon_removed": "Промокод удалён", + "billing_error_removing_coupon": "Ошибка при удалении промокода", + "billing_have_discount_coupon": "Есть промокод на скидку?", + "billing_discount_coupon": "Промокод на скидку", + "billing_cancel": "Отмена", + "billing_enter_coupon_code": "Введите промокод", + "billing_applying": "Применение...", + "billing_apply": "Применить", + "billing_subscription": "Подписка", + "billing_cancel_notice": "Вы можете отменить подписку в любое время через настройки без необходимости разговаривать с оператором и больше не будете платить.", "select_channels": "Выберите каналы", "start_a_new_chat": "Начать новый чат", "your_assistant": "Ваш помощник", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Это действие необратимо. Вы уверены, что хотите вернуться в глобальный режим?", "yes_go_back_to_global_mode": "Да, вернуться в глобальный режим", "are_you_sure_delete_this_post": "Вы уверены, что хотите удалить эту публикацию?", + "yes_delete_it": "Да, удалить!", "cant_edit_networks_when_creating_set": "Вы не можете редактировать сети при создании набора", "click_to_exit_global_editing": "Нажмите эту кнопку, чтобы выйти из глобального редактирования и настроить публикацию для этого канала", "edit_content": "Редактировать содержимое", diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index 406a7403..53ad9552 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "İçeriği TikTok'a paylaşmadan yükle", "choose_upload_without_posting_description": "İçeriğinizi yayınlamadan önce TikTok uygulamasında gözden geçirmek ve düzenlemek istiyorsanız, paylaşmadan yüklemeyi seçin. Bu, TikTok'un yerleşik düzenleme araçlarına erişmenizi sağlar ve paylaşmadan önce son düzenlemeleri yapmanıza olanak tanır.", "faq_am_i_going_to_be_charged_by_postiz": "Postiz tarafından ücretlendirilecek miyim?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Kredi kartı bilgilerinizi doğrulamak için Postiz 2$ tutarında bir provizyon alacak ve hemen serbest bırakacaktır", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Kredi kartı bilgilerini doğrulamak için Postiz $2 tutacak ve hemen serbest bırakacaktır, ayarlarından herhangi bir kişiyle konuşmadan aboneliğinizi istediğiniz zaman iptal edebilirsiniz.", "faq_can_i_trust_postiz_gitroom": "Postiz'e güvenebilir miyim?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz gururla açık kaynaklıdır! Etik ve şeffaf bir kültüre inanıyoruz, bu da Postiz'in sonsuza dek yaşayacağı anlamına gelir. Tüm kodu inceleyebilir veya kişisel projelerinizde kullanabilirsiniz. Açık kaynak deposunu görüntülemek için <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">buraya tıklayın</a>.", "faq_what_are_channels": "Kanallar nedir?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Sosyal medya gönderileri ve makaleler yazmanıza yardımcı olmak için ChatGPT'yi otomatikleştiriyoruz.", "enter_email": "E-posta girin", "are_you_sure": "Emin misiniz?", - "yes_delete_it": "Evet, sil!", "no_cancel": "Hayır, iptal et!", "are_you_sure_you_want_to_delete": "{{name}}'i silmek istediğinizden emin misiniz?", "are_you_sure_you_want_to_delete_the_image": "Görseli silmek istediğinizden emin misiniz?", @@ -505,7 +504,7 @@ "delete_integration": "Entegrasyonu Sil", "start_writing_your_post": "Önizleme için gönderinizi yazmaya başlayın", "billing_join_over": "Katılan", - "billing_entrepreneurs_count": "18.000+ Girişimci", + "billing_entrepreneurs_count": "20.000+ Girişimci", "billing_who_use": "kullanan", "billing_postiz_grow_social": "Sosyal Varlıklarını Büyütmek İçin Postiz", "billing_no_risk_trial": "%100 Risksiz Ücretsiz Deneme", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Bugün 0$ ödeyin - Ücretsiz denemenizi başlatın!", "billing_pay_now": "Şimdi Öde", "billing_per_month": "/ ay", + "billing_per_year": "/ yıl", + "billing_order_summary": "Sipariş Özeti", + "billing_applied": "Uygulandı", + "billing_due_today": "Bugün ödenecek", + "billing_then": "Sonra", + "billing_on": "tarihinde", + "billing_discount_applied": "uygulandı", + "billing_remove": "Kaldır", + "billing_coupon_expires": "Kuponun son kullanma tarihi", + "billing_invalid_coupon": "Geçersiz kupon kodu", + "billing_coupon_applied": "Kupon başarıyla uygulandı!", + "billing_coupon_removed": "Kupon kaldırıldı", + "billing_error_removing_coupon": "Kupon kaldırılırken hata oluştu", + "billing_have_discount_coupon": "İndirim kuponunuz mu var?", + "billing_discount_coupon": "İndirim Kuponu", + "billing_cancel": "İptal Et", + "billing_enter_coupon_code": "Kupon kodunu girin", + "billing_applying": "Uygulanıyor...", + "billing_apply": "Uygula", + "billing_subscription": "Abonelik", + "billing_cancel_notice": "Ayarlar kısmından herhangi bir kişiyle konuşmadan istediğiniz zaman iptal edebilir ve asla ücretlendirilmezsiniz.", "select_channels": "Kanalları Seç", "start_a_new_chat": "Yeni bir sohbet başlat", "your_assistant": "Asistanınız", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Bu işlem geri alınamaz. Global moda dönmek istediğinizden emin misiniz?", "yes_go_back_to_global_mode": "Evet, global moda dön", "are_you_sure_delete_this_post": "Bu gönderiyi silmek istediğinizden emin misiniz?", + "yes_delete_it": "Evet, sil!", "cant_edit_networks_when_creating_set": "Bir set oluştururken ağları düzenleyemezsiniz", "click_to_exit_global_editing": "Global düzenlemeden çıkmak ve bu kanal için gönderiyi özelleştirmek için bu butona tıklayın", "edit_content": "İçeriği düzenle", diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 8c4473fe..390e8fb5 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "Tải nội dung lên TikTok mà không đăng", "choose_upload_without_posting_description": "Chọn tải lên mà không đăng nếu bạn muốn xem lại và chỉnh sửa nội dung trong ứng dụng TikTok trước khi xuất bản. Điều này giúp bạn sử dụng các công cụ chỉnh sửa tích hợp của TikTok và thực hiện các điều chỉnh cuối cùng trước khi đăng.", "faq_am_i_going_to_be_charged_by_postiz": "Tôi có bị Postiz tính phí không?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "Để xác nhận thông tin thẻ tín dụng, Postiz sẽ giữ $2 và hoàn trả ngay lập tức", + "faq_to_confirm_credit_card_information_postiz_will_hold": "Để xác nhận thông tin thẻ tín dụng, Postiz sẽ giữ $2 và hoàn trả ngay lập tức, bạn có thể hủy đăng ký bất cứ lúc nào trong phần cài đặt mà không cần nói chuyện với ai", "faq_can_i_trust_postiz_gitroom": "Tôi có thể tin tưởng Postiz không?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz tự hào là mã nguồn mở! Chúng tôi tin vào một văn hóa đạo đức và minh bạch, nghĩa là Postiz sẽ tồn tại mãi mãi. Bạn có thể xem toàn bộ mã nguồn hoặc sử dụng cho các dự án cá nhân. Để xem kho mã nguồn mở, <a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">bấm vào đây</a>.", "faq_what_are_channels": "Kênh là gì?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "Chúng tôi tự động hóa ChatGPT để giúp bạn viết bài đăng mạng xã hội và bài viết.", "enter_email": "Nhập email", "are_you_sure": "Bạn có chắc không?", - "yes_delete_it": "Vâng, xóa nó đi!", "no_cancel": "Không, hủy bỏ!", "are_you_sure_you_want_to_delete": "Bạn có chắc muốn xóa {{name}} không?", "are_you_sure_you_want_to_delete_the_image": "Bạn có chắc muốn xóa hình ảnh này không?", @@ -505,7 +504,7 @@ "delete_integration": "Xóa tích hợp", "start_writing_your_post": "Bắt đầu viết bài của bạn để xem trước", "billing_join_over": "Tham gia cùng hơn", - "billing_entrepreneurs_count": "18.000+ doanh nhân", + "billing_entrepreneurs_count": "Hơn 20.000 doanh nhân", "billing_who_use": "đang sử dụng", "billing_postiz_grow_social": "Postiz để phát triển sự hiện diện trên mạng xã hội của họ", "billing_no_risk_trial": "Dùng thử miễn phí 100% không rủi ro", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "Thanh toán $0 hôm nay - Bắt đầu dùng thử miễn phí!", "billing_pay_now": "Thanh toán ngay", "billing_per_month": "/ tháng", + "billing_per_year": "/ năm", + "billing_order_summary": "Tóm tắt đơn hàng", + "billing_applied": "Đã áp dụng", + "billing_due_today": "Thanh toán hôm nay", + "billing_then": "Sau đó", + "billing_on": "vào ngày", + "billing_discount_applied": "đã áp dụng", + "billing_remove": "Xóa", + "billing_coupon_expires": "Mã giảm giá hết hạn vào", + "billing_invalid_coupon": "Mã giảm giá không hợp lệ", + "billing_coupon_applied": "Áp dụng mã giảm giá thành công!", + "billing_coupon_removed": "Đã xóa mã giảm giá", + "billing_error_removing_coupon": "Lỗi khi xóa mã giảm giá", + "billing_have_discount_coupon": "Bạn có mã giảm giá?", + "billing_discount_coupon": "Mã giảm giá", + "billing_cancel": "Hủy", + "billing_enter_coupon_code": "Nhập mã giảm giá", + "billing_applying": "Đang áp dụng...", + "billing_apply": "Áp dụng", + "billing_subscription": "Đăng ký", + "billing_cancel_notice": "Bạn có thể hủy bất cứ lúc nào trong phần cài đặt mà không cần nói chuyện với ai và sẽ không bị tính phí.", "select_channels": "Chọn kênh", "start_a_new_chat": "Bắt đầu cuộc trò chuyện mới", "your_assistant": "Trợ lý của bạn", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "Hành động này không thể hoàn tác. Bạn có chắc chắn muốn quay lại chế độ toàn cục không?", "yes_go_back_to_global_mode": "Vâng, quay lại chế độ toàn cục", "are_you_sure_delete_this_post": "Bạn có chắc chắn muốn xóa bài đăng này không?", + "yes_delete_it": "Vâng, xóa nó đi!", "cant_edit_networks_when_creating_set": "Bạn không thể chỉnh sửa các mạng khi đang tạo một bộ", "click_to_exit_global_editing": "Nhấn vào nút này để thoát chỉnh sửa toàn cục và tùy chỉnh bài đăng cho kênh này", "edit_content": "Chỉnh sửa nội dung", diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index 3d128ea3..0431a116 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -454,7 +454,7 @@ "upload_content_to_tiktok_without_posting": "上传内容到 TikTok 但不发布", "choose_upload_without_posting_description": "如果你希望在 TikTok 应用内审核和编辑内容后再发布,请选择“上传但不发布”。这样你可以使用 TikTok 内置的编辑工具,并在发布前进行最终调整。", "faq_am_i_going_to_be_charged_by_postiz": "我会被 Postiz 收费吗?", - "faq_to_confirm_credit_card_information_postiz_will_hold": "为确认信用卡信息,Postiz 会暂时预授权 2 美元并立即释放", + "faq_to_confirm_credit_card_information_postiz_will_hold": "为确认信用卡信息,Postiz将暂时预授权2美元并立即释放,您可以随时在设置中取消订阅,无需与任何人交流。", "faq_can_i_trust_postiz_gitroom": "我可以信任Postiz吗?", "faq_postiz_gitroom_is_proudly_open_source": "Postiz自豪地开源!我们相信道德和透明的文化,这意味着Postiz将永远存在。您可以查看全部代码或将其用于个人项目。要查看开源仓库,<a href=\"https://github.com/gitroomhq/postiz-app\" target=\"_blank\" style=\"text-decoration: underline;\">请点击这里</a>。", "faq_what_are_channels": "什么是频道?", @@ -465,7 +465,6 @@ "faq_we_automate_chatgpt_to_help_you_write": "我们自动化集成了 ChatGPT,帮助你撰写社交帖子和文章。", "enter_email": "请输入邮箱", "are_you_sure": "你确定吗?", - "yes_delete_it": "是的,删除它!", "no_cancel": "不,取消!", "are_you_sure_you_want_to_delete": "你确定要删除{{name}}吗?", "are_you_sure_you_want_to_delete_the_image": "你确定要删除这张图片吗?", @@ -505,7 +504,7 @@ "delete_integration": "删除集成", "start_writing_your_post": "开始写你的帖子以预览", "billing_join_over": "已有超过", - "billing_entrepreneurs_count": "18,000+位创业者", + "billing_entrepreneurs_count": "20,000+ 位创业者", "billing_who_use": "正在使用", "billing_postiz_grow_social": "Postiz来提升他们的社交影响力", "billing_no_risk_trial": "100%无风险免费试用", @@ -537,6 +536,27 @@ "billing_pay_0_start_trial": "今日支付$0 - 开始您的免费试用!", "billing_pay_now": "立即支付", "billing_per_month": "每月", + "billing_per_year": "/ 年", + "billing_order_summary": "订单摘要", + "billing_applied": "已应用", + "billing_due_today": "今日应付", + "billing_then": "之后", + "billing_on": "在", + "billing_discount_applied": "已应用折扣", + "billing_remove": "移除", + "billing_coupon_expires": "优惠券有效期至", + "billing_invalid_coupon": "无效的优惠码", + "billing_coupon_applied": "优惠券应用成功!", + "billing_coupon_removed": "优惠券已移除", + "billing_error_removing_coupon": "移除优惠券时出错", + "billing_have_discount_coupon": "有优惠券吗?", + "billing_discount_coupon": "优惠券", + "billing_cancel": "取消", + "billing_enter_coupon_code": "请输入优惠码", + "billing_applying": "正在应用...", + "billing_apply": "应用", + "billing_subscription": "订阅", + "billing_cancel_notice": "您可以随时在设置中取消订阅,无需与任何人交流,且不会被收费。", "select_channels": "选择频道", "start_a_new_chat": "开始新聊天", "your_assistant": "你的助手", @@ -652,6 +672,7 @@ "are_you_sure_go_back_to_global_mode": "此操作不可逆。您确定要返回全局模式吗?", "yes_go_back_to_global_mode": "是的,返回全局模式", "are_you_sure_delete_this_post": "您确定要删除此帖子吗?", + "yes_delete_it": "是的,删除它!", "cant_edit_networks_when_creating_set": "创建集合时无法编辑网络", "click_to_exit_global_editing": "点击此按钮退出全局编辑,并为该频道自定义帖子", "edit_content": "编辑内容", From ba0a2498a45dfc01868dd16b4a4af85cc64f89f7 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 13:28:09 +0700 Subject: [PATCH 145/340] fix: remove billing address --- .../components/billing/embedded.billing.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/billing/embedded.billing.tsx b/apps/frontend/src/components/billing/embedded.billing.tsx index 0dc5cb8d..63a1a267 100644 --- a/apps/frontend/src/components/billing/embedded.billing.tsx +++ b/apps/frontend/src/components/billing/embedded.billing.tsx @@ -139,21 +139,24 @@ const StripeInputs: FC<{ const [ready, setReady] = useState(false); return ( <> + {/*<div>*/} + {/* <h4 className="mb-[32px] text-[24px] font-[700]">*/} + {/* {checkout.type === 'loading'*/} + {/* ? ''*/} + {/* : t('billing_billing_address', 'Billing Address')}*/} + {/* </h4>*/} + {/* <BillingAddressElement />*/} + {/*</div>*/} <div> <h4 className="mb-[32px] text-[24px] font-[700]"> - {checkout.type === 'loading' - ? '' - : t('billing_billing_address', 'Billing Address')} - </h4> - <BillingAddressElement /> - </div> - <div> - <h4 className="mt-[40px] mb-[32px] text-[24px] font-[700]"> {checkout.type === 'loading' ? '' : t('billing_payment', 'Payment')} </h4> <PaymentElement id="payment-element" - options={{ layout: 'tabs' }} + options={{ + fields: { billingDetails: { address: 'if_required' } }, + layout: 'tabs', + }} onReady={() => setReady(true)} /> {ready && <PriceBreakdown />} From 5bfb97e9a423c622a9749f026d3717a8fc6c7eab Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 13:53:54 +0700 Subject: [PATCH 146/340] fix: remove billing address --- apps/frontend/src/app/(app)/auth/layout.tsx | 2 +- .../frontend/src/components/billing/first.billing.component.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/app/(app)/auth/layout.tsx b/apps/frontend/src/app/(app)/auth/layout.tsx index 729e7aa6..9fcbfa8a 100644 --- a/apps/frontend/src/app/(app)/auth/layout.tsx +++ b/apps/frontend/src/app/(app)/auth/layout.tsx @@ -26,7 +26,7 @@ export default async function AuthLayout({ </div> <div className="text-[36px] flex-1 pt-[88px] hidden lg:flex flex-col items-center"> <div className="text-center"> - Over <span className="text-[42px] text-[#FC69FF]">18,000+</span>{' '} + Over <span className="text-[42px] text-[#FC69FF]">20,000+</span>{' '} Entrepreneurs use <br /> Postiz To Grow Their Social Presence diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index 8e18bbdf..cc8c07e9 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -92,7 +92,7 @@ export const FirstBillingComponent = () => { <div className="text-[46px] font-[600] leading-[110%] tablet:text-[36px] mobile:!text-[30px] whitespace-pre-line text-balance"> {t('billing_join_over', 'Join Over')}{' '} <span className="text-[#FC69FF]"> - {t('billing_entrepreneurs_count', '18,000+ Entrepreneurs')} + {t('billing_entrepreneurs_count', '20,000+ Entrepreneurs')} </span>{' '} {t('billing_who_use', 'who use')}{' '} {t( From 7eeb1cb044a88e193d41204afbbfd36da7f0cd7d Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 16:27:02 +0700 Subject: [PATCH 147/340] fix: slack urgent fix for posting --- .../src/integrations/social/slack.provider.ts | 153 ++++++++++++++---- 1 file changed, 120 insertions(+), 33 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts b/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts index c29b9b3c..bcbe01db 100644 --- a/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/slack.provider.ts @@ -137,6 +137,10 @@ export class SlackProvider extends SocialAbstract implements SocialProvider { postDetails: PostDetails[], integration: Integration ): Promise<PostResponse[]> { + const [firstPost] = postDetails; + const channel = firstPost.settings.channel; + + // Join the channel first await fetch(`https://slack.com/api/conversations.join`, { method: 'POST', headers: { @@ -144,48 +148,131 @@ export class SlackProvider extends SocialAbstract implements SocialProvider { 'Content-Type': 'application/json', }, body: JSON.stringify({ - channel: postDetails[0].settings.channel, + channel, }), }); - let lastId = ''; - for (const post of postDetails) { - const { ts } = await ( - await fetch(`https://slack.com/api/chat.postMessage`, { - method: 'POST', + // Post the main message + const { ts, channel: responseChannel } = await ( + await fetch(`https://slack.com/api/chat.postMessage`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + channel, + username: integration.name, + icon_url: integration.picture, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: firstPost.message, + }, + }, + ...(firstPost.media?.length + ? firstPost.media.map((m) => ({ + type: 'image', + image_url: m.path, + alt_text: '', + })) + : []), + ], + }), + }) + ).json(); + + // Get permalink for the message + const { permalink } = await ( + await fetch( + `https://slack.com/api/chat.getPermalink?channel=${responseChannel}&message_ts=${ts}`, + { + method: 'GET', headers: { Authorization: `Bearer ${accessToken}`, - 'Content-Type': 'application/json', }, - body: JSON.stringify({ - channel: postDetails[0].settings.channel, - username: integration.name, - icon_url: integration.picture, - ...(lastId ? { thread_ts: lastId } : {}), - blocks: [ - { - type: 'section', - text: { - type: 'mrkdwn', - text: post.message, - }, + } + ) + ).json(); + + return [ + { + id: firstPost.id, + postId: ts, + releaseURL: permalink || '', + status: 'posted', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const channel = commentPost.settings.channel; + const threadTs = lastCommentId || postId; + + // Post the threaded reply + const { ts, channel: responseChannel } = await ( + await fetch(`https://slack.com/api/chat.postMessage`, { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + channel, + username: integration.name, + icon_url: integration.picture, + thread_ts: threadTs, + blocks: [ + { + type: 'section', + text: { + type: 'mrkdwn', + text: commentPost.message, }, - ...(post.media?.length - ? post.media.map((m) => ({ - type: 'image', - image_url: m.path, - alt_text: '', - })) - : []), - ], - }), - }) - ).json(); + }, + ...(commentPost.media?.length + ? commentPost.media.map((m) => ({ + type: 'image', + image_url: m.path, + alt_text: '', + })) + : []), + ], + }), + }) + ).json(); - lastId = ts; - } + // Get permalink for the comment + const { permalink } = await ( + await fetch( + `https://slack.com/api/chat.getPermalink?channel=${responseChannel}&message_ts=${ts}`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ) + ).json(); - return []; + return [ + { + id: commentPost.id, + postId: ts, + releaseURL: permalink || '', + status: 'posted', + }, + ]; } async changeProfilePicture(id: string, accessToken: string, url: string) { From e29812501ea5fe22ce6626619a227b0104ca0d08 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 16:35:56 +0700 Subject: [PATCH 148/340] fix: urgent providers fix --- .../src/integrations/social/lemmy.provider.ts | 133 ++++++---- .../src/integrations/social/nostr.provider.ts | 91 +++++-- .../integrations/social/telegram.provider.ts | 251 +++++++++++------- .../src/integrations/social/vk.provider.ts | 247 +++++++++-------- 4 files changed, 429 insertions(+), 293 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts b/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts index 6234eb0f..465a0d86 100644 --- a/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts @@ -10,7 +10,6 @@ import dayjs from 'dayjs'; import { Integration } from '@prisma/client'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; import { LemmySettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/lemmy.dto'; -import { groupBy } from 'lodash'; import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; export class LemmyProvider extends SocialAbstract implements SocialProvider { @@ -121,14 +120,7 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider { } } - async post( - id: string, - accessToken: string, - postDetails: PostDetails<LemmySettingsDto>[], - integration: Integration - ): Promise<PostResponse[]> { - const [firstPost, ...restPosts] = postDetails; - + private async getJwtAndService(integration: Integration): Promise<{ jwt: string; service: string }> { const body = JSON.parse( AuthService.fixedDecryption(integration.customInstanceDetails!) ); @@ -146,6 +138,18 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider { }) ).json(); + return { jwt, service: body.service }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails<LemmySettingsDto>[], + integration: Integration + ): Promise<PostResponse[]> { + const [firstPost] = postDetails; + const { jwt, service } = await this.getJwtAndService(integration); + const valueArray: PostResponse[] = []; for (const lemmy of firstPost.settings.subreddit) { @@ -159,8 +163,8 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider { : {}), nsfw: false, }); - const { post_view, ...all } = await ( - await fetch(body.service + '/api/v3/post', { + const { post_view } = await ( + await fetch(service + '/api/v3/post', { body: JSON.stringify({ community_id: +lemmy.value.id, name: lemmy.value.title, @@ -188,41 +192,68 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider { valueArray.push({ postId: post_view.post.id, - releaseURL: body.service + '/post/' + post_view.post.id, + releaseURL: service + '/post/' + post_view.post.id, id: firstPost.id, status: 'published', }); - - for (const comment of restPosts) { - const { comment_view } = await ( - await fetch(body.service + '/api/v3/comment', { - body: JSON.stringify({ - post_id: post_view.post.id, - content: comment.message, - }), - method: 'POST', - headers: { - Authorization: `Bearer ${jwt}`, - 'Content-Type': 'application/json', - }, - }) - ).json(); - - valueArray.push({ - postId: comment_view.post.id, - releaseURL: body.service + '/comment/' + comment_view.comment.id, - id: comment.id, - status: 'published', - }); - } } - return Object.values(groupBy(valueArray, (p) => p.id)).map((p) => ({ - id: p[0].id, - postId: p.map((p) => String(p.postId)).join(','), - releaseURL: p.map((p) => p.releaseURL).join(','), - status: 'published', - })); + return [ + { + id: firstPost.id, + postId: valueArray.map((p) => String(p.postId)).join(','), + releaseURL: valueArray.map((p) => p.releaseURL).join(','), + status: 'published', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<LemmySettingsDto>[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const { jwt, service } = await this.getJwtAndService(integration); + + // postId can be comma-separated if posted to multiple communities + const postIds = postId.split(','); + const valueArray: PostResponse[] = []; + + for (const singlePostId of postIds) { + const { comment_view } = await ( + await fetch(service + '/api/v3/comment', { + body: JSON.stringify({ + post_id: +singlePostId, + content: commentPost.message, + }), + method: 'POST', + headers: { + Authorization: `Bearer ${jwt}`, + 'Content-Type': 'application/json', + }, + }) + ).json(); + + valueArray.push({ + postId: String(comment_view.comment.id), + releaseURL: service + '/comment/' + comment_view.comment.id, + id: commentPost.id, + status: 'published', + }); + } + + return [ + { + id: commentPost.id, + postId: valueArray.map((p) => p.postId).join(','), + releaseURL: valueArray.map((p) => p.releaseURL).join(','), + status: 'published', + }, + ]; } @Tool({ @@ -241,27 +272,11 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider { id: string, integration: Integration ) { - const body = JSON.parse( - AuthService.fixedDecryption(integration.customInstanceDetails!) - ); - - const { jwt } = await ( - await fetch(body.service + '/api/v3/user/login', { - body: JSON.stringify({ - username_or_email: body.identifier, - password: body.password, - }), - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - }) - ).json(); + const { jwt, service } = await this.getJwtAndService(integration); const { communities } = await ( await fetch( - body.service + - `/api/v3/search?type_=Communities&sort=Active&q=${data.word}`, + service + `/api/v3/search?type_=Communities&sort=Active&q=${data.word}`, { headers: { Authorization: `Bearer ${jwt}`, diff --git a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts index 8a7921e2..22c8a987 100644 --- a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts @@ -11,6 +11,7 @@ import { getPublicKey, Relay, finalizeEvent, SimplePool } from 'nostr-tools'; import WebSocket from 'ws'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; +import { Integration } from '@prisma/client'; // @ts-ignore global.WebSocket = WebSocket; @@ -158,43 +159,77 @@ export class NostrProvider extends SocialAbstract implements SocialProvider { } } + private buildContent(post: PostDetails): string { + const mediaContent = post.media?.map((m) => m.path).join('\n\n') || ''; + return mediaContent + ? `${post.message}\n\n${mediaContent}` + : post.message; + } + async post( id: string, accessToken: string, postDetails: PostDetails[] ): Promise<PostResponse[]> { const { password } = AuthService.verifyJWT(accessToken) as any; + const [firstPost] = postDetails; - let lastId = ''; - const ids: PostResponse[] = []; - for (const post of postDetails) { - const textEvent = finalizeEvent( - { - kind: 1, // Text note - content: - post.message + '\n\n' + post.media?.map((m) => m.path).join('\n\n'), - tags: [ - ...(lastId - ? [ - ['e', lastId, '', 'reply'], - ['p', id], - ] - : []), - ], // Include delegation token in the event - created_at: Math.floor(Date.now() / 1000), - }, - password - ); + const textEvent = finalizeEvent( + { + kind: 1, // Text note + content: this.buildContent(firstPost), + tags: [], + created_at: Math.floor(Date.now() / 1000), + }, + password + ); - lastId = await this.publish(id, textEvent); - ids.push({ - id: post.id, - postId: String(lastId), - releaseURL: `https://primal.net/e/${lastId}`, + const eventId = await this.publish(id, textEvent); + + return [ + { + id: firstPost.id, + postId: String(eventId), + releaseURL: `https://primal.net/e/${eventId}`, status: 'completed', - }); - } + }, + ]; + } - return ids; + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const { password } = AuthService.verifyJWT(accessToken) as any; + const [commentPost] = postDetails; + const replyToId = lastCommentId || postId; + + const textEvent = finalizeEvent( + { + kind: 1, // Text note + content: this.buildContent(commentPost), + tags: [ + ['e', replyToId, '', 'reply'], + ['p', id], + ], + created_at: Math.floor(Date.now() / 1000), + }, + password + ); + + const eventId = await this.publish(id, textEvent); + + return [ + { + id: commentPost.id, + postId: String(eventId), + releaseURL: `https://primal.net/e/${eventId}`, + status: 'completed', + }, + ]; } } diff --git a/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts index ba07ba9b..9b1313bc 100644 --- a/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts @@ -140,118 +140,173 @@ export class TelegramProvider extends SocialAbstract implements SocialProvider { : {}; } + private processMedia(mediaFiles: PostDetails['media']) { + return (mediaFiles || []).map((media) => { + let mediaUrl = media.path; + if (mediaStorage === 'local' && mediaUrl.startsWith(frontendURL)) { + mediaUrl = mediaUrl.replace(frontendURL, ''); + } + //get mime type to pass contentType to telegram api. + //some photos and videos might not pass telegram api restrictions, so they are sent as documents instead of returning errors + const mimeType = mime.getType(mediaUrl); // Detect MIME type + let mediaType: 'photo' | 'video' | 'document'; + + if (mimeType?.startsWith('image/')) { + mediaType = 'photo'; + } else if (mimeType?.startsWith('video/')) { + mediaType = 'video'; + } else { + mediaType = 'document'; + } + + return { + type: mediaType, + media: mediaUrl, + fileOptions: { + filename: media.path.split('/').pop(), + contentType: mimeType || 'application/octet-stream', + }, + }; + }); + } + + private async sendMessage( + accessToken: string, + message: PostDetails, + replyToMessageId?: number + ): Promise<number | null> { + let messageId: number | null = null; + const mediaFiles = message.media || []; + const text = striptags(message.message || '', ['u', 'strong', 'p']) + .replace(/<strong>/g, '<b>') + .replace(/<\/strong>/g, '</b>') + .replace(/<p>(.*?)<\/p>/g, '$1\n'); + + console.log(text); + const processedMedia = this.processMedia(mediaFiles); + + // if there's no media, bot sends a text message only + if (processedMedia.length === 0) { + const response = await telegramBot.sendMessage(accessToken, text, { + parse_mode: 'HTML', + ...(replyToMessageId ? { reply_to_message_id: replyToMessageId } : {}), + }); + messageId = response.message_id; + } + // if there's only one media, bot sends the media with the text message as caption + else if (processedMedia.length === 1) { + const media = processedMedia[0]; + const options = { + caption: text, + parse_mode: 'HTML' as const, + ...(replyToMessageId ? { reply_to_message_id: replyToMessageId } : {}), + }; + const response = + media.type === 'video' + ? await telegramBot.sendVideo( + accessToken, + media.media, + options, + media.fileOptions + ) + : media.type === 'photo' + ? await telegramBot.sendPhoto( + accessToken, + media.media, + options, + media.fileOptions + ) + : await telegramBot.sendDocument( + accessToken, + media.media, + options, + media.fileOptions + ); + messageId = response.message_id; + } + // if there are multiple media, bot sends them as a media group - max 10 media per group - with the text as a caption (if there are more than 1 group, the caption will only be sent with the first group) + else { + const mediaGroups = this.chunkMedia(processedMedia, 10); + for (let i = 0; i < mediaGroups.length; i++) { + const mediaGroup = mediaGroups[i].map((m, index) => ({ + type: m.type === 'document' ? 'document' : m.type, // Documents are not allowed in media groups + media: m.media, + caption: i === 0 && index === 0 ? text : undefined, + parse_mode: 'HTML', + })); + + const response = await telegramBot.sendMediaGroup( + accessToken, + mediaGroup as any[], + { + ...(replyToMessageId && i === 0 + ? { reply_to_message_id: replyToMessageId } + : {}), + } + ); + if (i === 0) { + messageId = response[0].message_id; + } + } + } + + return messageId; + } + async post( id: string, accessToken: string, postDetails: PostDetails[] ): Promise<PostResponse[]> { - const ids: PostResponse[] = []; + const [firstPost] = postDetails; - for (const message of postDetails) { - let messageId: number | null = null; - const mediaFiles = message.media || []; - const text = striptags(message.message || '', ['u', 'strong', 'p']) - .replace(/<strong>/g, '<b>') - .replace(/<\/strong>/g, '</b>') - .replace(/<p>(.*?)<\/p>/g, '$1\n'); + const messageId = await this.sendMessage(accessToken, firstPost); - console.log(text); - // check if media is local to modify url - const processedMedia = mediaFiles.map((media) => { - let mediaUrl = media.path; - if (mediaStorage === 'local' && mediaUrl.startsWith(frontendURL)) { - mediaUrl = mediaUrl.replace(frontendURL, ''); - } - //get mime type to pass contentType to telegram api. - //some photos and videos might not pass telegram api restrictions, so they are sent as documents instead of returning errors - const mimeType = mime.getType(mediaUrl); // Detect MIME type - let mediaType: 'photo' | 'video' | 'document'; - - if (mimeType?.startsWith('image/')) { - mediaType = 'photo'; - } else if (mimeType?.startsWith('video/')) { - mediaType = 'video'; - } else { - mediaType = 'document'; - } - - return { - type: mediaType, - media: mediaUrl, - fileOptions: { - filename: media.path.split('/').pop(), - contentType: mimeType || 'application/octet-stream', - }, - }; - }); - // if there's no media, bot sends a text message only - if (processedMedia.length === 0) { - const response = await telegramBot.sendMessage(accessToken, text, { - parse_mode: 'HTML', - }); - messageId = response.message_id; - } - // if there's only one media, bot sends the media with the text message as caption - else if (processedMedia.length === 1) { - const media = processedMedia[0]; - const response = - media.type === 'video' - ? await telegramBot.sendVideo( - accessToken, - media.media, - { caption: text, parse_mode: 'HTML' }, - media.fileOptions - ) - : media.type === 'photo' - ? await telegramBot.sendPhoto( - accessToken, - media.media, - { caption: text, parse_mode: 'HTML' }, - media.fileOptions - ) - : await telegramBot.sendDocument( - accessToken, - media.media, - { caption: text, parse_mode: 'HTML' }, - media.fileOptions - ); - messageId = response.message_id; - } - // if there are multiple media, bot sends them as a media group - max 10 media per group - with the text as a caption (if there are more than 1 group, the caption will only be sent with the first group) - else { - const mediaGroups = this.chunkMedia(processedMedia, 10); - for (let i = 0; i < mediaGroups.length; i++) { - const mediaGroup = mediaGroups[i].map((m, index) => ({ - type: m.type === 'document' ? 'document' : m.type, // Documents are not allowed in media groups - media: m.media, - caption: i === 0 && index === 0 ? text : undefined, - parse_mode: 'HTML', - })); - - const response = await telegramBot.sendMediaGroup( - accessToken, - mediaGroup as any[] - ); - if (i === 0) { - messageId = response[0].message_id; - } - } - } - // for private groups/channels message.id is undefined so the link generated by Postiz will be unusable "https://t.me/c/undefined/16" - // to avoid that, we use accessToken instead of message.id and we generate the link manually removing the -100 from the start. - if (messageId) { - ids.push({ - id: message.id, + // for private groups/channels message.id is undefined so the link generated by Postiz will be unusable "https://t.me/c/undefined/16" + // to avoid that, we use accessToken instead of message.id and we generate the link manually removing the -100 from the start. + if (messageId) { + return [ + { + id: firstPost.id, postId: String(messageId), releaseURL: `https://t.me/${ id !== 'undefined' ? id : `c/${accessToken.replace('-100', '')}` }/${messageId}`, status: 'completed', - }); - } + }, + ]; } - return ids; + return []; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const replyToId = Number(lastCommentId || postId); + + const messageId = await this.sendMessage(accessToken, commentPost, replyToId); + + if (messageId) { + return [ + { + id: commentPost.id, + postId: String(messageId), + releaseURL: `https://t.me/${ + id !== 'undefined' ? id : `c/${accessToken.replace('-100', '')}` + }/${messageId}`, + status: 'completed', + }, + ]; + } + + return []; } // chunkMedia is used to split media into groups of "size". 10 is used here because telegram api allows a maximum of 10 media per group private chunkMedia(media: { type: string; media: string }[], size: number) { diff --git a/libraries/nestjs-libraries/src/integrations/social/vk.provider.ts b/libraries/nestjs-libraries/src/integrations/social/vk.provider.ts index adbc8600..c871e1b6 100644 --- a/libraries/nestjs-libraries/src/integrations/social/vk.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/vk.provider.ts @@ -11,6 +11,7 @@ import { createHash, randomBytes } from 'crypto'; import axios from 'axios'; import FormDataNew from 'form-data'; import mime from 'mime-types'; +import { Integration } from '@prisma/client'; export class VkProvider extends SocialAbstract implements SocialProvider { override maxConcurrentJob = 2; // VK has moderate API limits @@ -158,123 +159,153 @@ export class VkProvider extends SocialAbstract implements SocialProvider { }; } + private async uploadMedia( + userId: string, + accessToken: string, + post: PostDetails + ): Promise<{ id: string; type: string }[]> { + return await Promise.all( + (post?.media || []).map(async (media) => { + const all = await ( + await this.fetch( + media.path.indexOf('mp4') > -1 + ? `https://api.vk.com/method/video.save?access_token=${accessToken}&v=5.251` + : `https://api.vk.com/method/photos.getWallUploadServer?owner_id=${userId}&access_token=${accessToken}&v=5.251` + ) + ).json(); + + const { data } = await axios.get(media.path!, { + responseType: 'stream', + }); + + const slash = media.path.split('/').at(-1); + + const formData = new FormDataNew(); + formData.append('photo', data, { + filename: slash, + contentType: mime.lookup(slash!) || '', + }); + const value = ( + await axios.post(all.response.upload_url, formData, { + headers: { + ...formData.getHeaders(), + }, + }) + ).data; + + if (media.path.indexOf('mp4') > -1) { + return { + id: all.response.video_id, + type: 'video', + }; + } + + const formSend = new FormData(); + formSend.append('photo', value.photo); + formSend.append('server', value.server); + formSend.append('hash', value.hash); + + const { id } = ( + await ( + await fetch( + `https://api.vk.com/method/photos.saveWallPhoto?access_token=${accessToken}&v=5.251`, + { + method: 'POST', + body: formSend, + } + ) + ).json() + ).response[0]; + + return { + id, + type: 'photo', + }; + }) + ); + } + async post( userId: string, accessToken: string, postDetails: PostDetails[] ): Promise<PostResponse[]> { - let replyTo = ''; - const values: PostResponse[] = []; + const [firstPost] = postDetails; - const uploading = await Promise.all( - postDetails.map(async (post) => { - return await Promise.all( - (post?.media || []).map(async (media) => { - const all = await ( - await this.fetch( - media.path.indexOf('mp4') > -1 - ? `https://api.vk.com/method/video.save?access_token=${accessToken}&v=5.251` - : `https://api.vk.com/method/photos.getWallUploadServer?owner_id=${userId}&access_token=${accessToken}&v=5.251` - ) - ).json(); + // Upload media for the first post + const mediaList = await this.uploadMedia(userId, accessToken, firstPost); - const { data } = await axios.get(media.path!, { - responseType: 'stream', - }); + const body = new FormData(); + body.append('message', firstPost.message); - const slash = media.path.split('/').at(-1); - - const formData = new FormDataNew(); - formData.append('photo', data, { - filename: slash, - contentType: mime.lookup(slash!) || '', - }); - const value = ( - await axios.post(all.response.upload_url, formData, { - headers: { - ...formData.getHeaders(), - }, - }) - ).data; - - if (media.path.indexOf('mp4') > -1) { - return { - id: all.response.video_id, - type: 'video', - }; - } - - const formSend = new FormData(); - formSend.append('photo', value.photo); - formSend.append('server', value.server); - formSend.append('hash', value.hash); - - const { id } = ( - await ( - await fetch( - `https://api.vk.com/method/photos.saveWallPhoto?access_token=${accessToken}&v=5.251`, - { - method: 'POST', - body: formSend, - } - ) - ).json() - ).response[0]; - - return { - id, - type: 'photo', - }; - }) - ); - }) - ); - - let i = 0; - for (const post of postDetails) { - const list = uploading?.[i] || []; - - const body = new FormData(); - body.append('message', post.message); - if (replyTo) { - body.append('post_id', replyTo); - } - - if (list.length) { - body.append( - 'attachments', - list.map((p) => `${p.type}${userId}_${p.id}`).join(',') - ); - } - - const { response, ...all } = await ( - await this.fetch( - `https://api.vk.com/method/${ - replyTo ? 'wall.createComment' : 'wall.post' - }?v=5.251&access_token=${accessToken}&client_id=${process.env.VK_ID}`, - { - method: 'POST', - body, - } - ) - ).json(); - - values.push({ - id: post.id, - postId: String(response?.post_id || response?.comment_id), - releaseURL: `https://vk.com/feed?w=wall${userId}_${ - response?.post_id || replyTo - }`, - status: 'completed', - }); - - if (!replyTo) { - replyTo = response.post_id; - } - - i++; + if (mediaList.length) { + body.append( + 'attachments', + mediaList.map((p) => `${p.type}${userId}_${p.id}`).join(',') + ); } - return values; + const { response } = await ( + await this.fetch( + `https://api.vk.com/method/wall.post?v=5.251&access_token=${accessToken}&client_id=${process.env.VK_ID}`, + { + method: 'POST', + body, + } + ) + ).json(); + + return [ + { + id: firstPost.id, + postId: String(response?.post_id), + releaseURL: `https://vk.com/feed?w=wall${userId}_${response?.post_id}`, + status: 'completed', + }, + ]; + } + + async comment( + userId: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + + // Upload media for the comment + const mediaList = await this.uploadMedia(userId, accessToken, commentPost); + + const body = new FormData(); + body.append('message', commentPost.message); + body.append('post_id', postId); + + if (mediaList.length) { + body.append( + 'attachments', + mediaList.map((p) => `${p.type}${userId}_${p.id}`).join(',') + ); + } + + const { response } = await ( + await this.fetch( + `https://api.vk.com/method/wall.createComment?v=5.251&access_token=${accessToken}&client_id=${process.env.VK_ID}`, + { + method: 'POST', + body, + } + ) + ).json(); + + return [ + { + id: commentPost.id, + postId: String(response?.comment_id), + releaseURL: `https://vk.com/feed?w=wall${userId}_${postId}`, + status: 'completed', + }, + ]; } } From 005e3f753b09bbf08f275a9095aa2e62c58d1212 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 16:49:40 +0700 Subject: [PATCH 149/340] fix: urgent providers fix --- .../integrations/social/farcaster.provider.ts | 109 ++++++++++++------ 1 file changed, 71 insertions(+), 38 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts index 32e68bfa..ac9fd256 100644 --- a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts @@ -9,8 +9,6 @@ import dayjs from 'dayjs'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { NeynarAPIClient } from '@neynar/nodejs-sdk'; import { Integration } from '@prisma/client'; -import { AuthService } from '@gitroom/helpers/auth/auth.service'; -import { groupBy } from 'lodash'; import { FarcasterDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/farcaster.dto'; import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; import { Rules } from '@gitroom/nestjs-libraries/chat/rules.description.decorator'; @@ -81,48 +79,83 @@ export class FarcasterProvider accessToken: string, postDetails: PostDetails<FarcasterDto>[] ): Promise<PostResponse[]> { - const ids = []; - const subreddit = - !postDetails?.[0]?.settings?.subreddit || - postDetails?.[0]?.settings?.subreddit.length === 0 + const [firstPost] = postDetails; + const ids: { releaseURL: string; postId: string }[] = []; + + const channels = + !firstPost?.settings?.subreddit || + firstPost?.settings?.subreddit.length === 0 ? [undefined] - : postDetails?.[0]?.settings?.subreddit; + : firstPost?.settings?.subreddit; - for (const channel of subreddit) { - let idHash = ''; - for (const post of postDetails) { - const data = await client.publishCast({ - embeds: - post?.media?.map((media) => ({ - url: media.path, - })) || [], - signerUuid: accessToken, - text: post.message, - ...(idHash ? { parent: idHash } : {}), - ...(channel?.value?.id ? { channelId: channel?.value?.id } : {}), - }); - idHash = data.cast.hash; + for (const channel of channels) { + const data = await client.publishCast({ + embeds: + firstPost?.media?.map((media) => ({ + url: media.path, + })) || [], + signerUuid: accessToken, + text: firstPost.message, + ...(channel?.value?.id ? { channelId: channel?.value?.id } : {}), + }); - ids.push({ - // @ts-ignore - releaseURL: `https://warpcast.com/${data.cast.author.username}/${idHash}`, - status: 'success', - id: post.id, - postId: data.cast.hash, - // @ts-ignore - author: data.cast.author.username, - }); - } + ids.push({ + // @ts-ignore + releaseURL: `https://warpcast.com/${data.cast.author.username}/${data.cast.hash}`, + postId: data.cast.hash, + }); } - const list = Object.values(groupBy(ids, (p) => p.id)).map((p) => ({ - id: p[0].id, - postId: p.map((p) => String(p.postId)).join(','), - releaseURL: p.map((p) => p.releaseURL).join(','), - status: 'published', - })); + return [ + { + id: firstPost.id, + postId: ids.map((p) => p.postId).join(','), + releaseURL: ids.map((p) => p.releaseURL).join(','), + status: 'published', + }, + ]; + } - return list; + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails<FarcasterDto>[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const ids: { releaseURL: string; postId: string }[] = []; + + // postId can be comma-separated if posted to multiple channels + const parentIds = (lastCommentId || postId).split(','); + + for (const parentHash of parentIds) { + const data = await client.publishCast({ + embeds: + commentPost?.media?.map((media) => ({ + url: media.path, + })) || [], + signerUuid: accessToken, + text: commentPost.message, + parent: parentHash, + }); + + ids.push({ + // @ts-ignore + releaseURL: `https://warpcast.com/${data.cast.author.username}/${data.cast.hash}`, + postId: data.cast.hash, + }); + } + + return [ + { + id: commentPost.id, + postId: ids.map((p) => p.postId).join(','), + releaseURL: ids.map((p) => p.releaseURL).join(','), + status: 'published', + }, + ]; } @Tool({ From 34b465aaa1e88dfefa921dc196c4393a67a45289 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 16:53:54 +0700 Subject: [PATCH 150/340] fix: urgent providers fix --- .../web3/providers/nostr.provider.tsx | 58 ------------------- .../web3/providers/telegram.provider.tsx | 31 +--------- .../web3/providers/wrapcaster.provider.tsx | 43 ++++---------- .../integrations/social/farcaster.provider.ts | 2 +- 4 files changed, 14 insertions(+), 120 deletions(-) delete mode 100644 apps/frontend/src/components/launches/web3/providers/nostr.provider.tsx diff --git a/apps/frontend/src/components/launches/web3/providers/nostr.provider.tsx b/apps/frontend/src/components/launches/web3/providers/nostr.provider.tsx deleted file mode 100644 index 45deac21..00000000 --- a/apps/frontend/src/components/launches/web3/providers/nostr.provider.tsx +++ /dev/null @@ -1,58 +0,0 @@ -'use client'; - -import '@neynar/react/dist/style.css'; -import React, { FC, useMemo, useState, useCallback, useEffect } from 'react'; -import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; -import { useVariables } from '@gitroom/react/helpers/variable.context'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; -import { ButtonCaster } from '@gitroom/frontend/components/auth/providers/farcaster.provider'; -export const WrapcasterProvider: FC<Web3ProviderInterface> = (props) => { - const [_, state] = props.nonce.split('||'); - const modal = useModals(); - const [hide, setHide] = useState(false); - const auth = useCallback( - (code: string) => { - setHide(true); - return props.onComplete(code, state); - }, - [state] - ); - return ( - <div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative w-full"> - <TopTitle title={`Add Wrapcast`} /> - <button - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - onClick={() => modal.closeAll()} - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - <div className="justify-center items-center flex"> - {hide ? ( - <div className="justify-center items-center flex -mt-[90px]"> - <LoadingComponent width={100} height={100} /> - </div> - ) : ( - <div className="justify-center items-center py-[20px] flex-col w-[500px]"> - <ButtonCaster login={auth} /> - </div> - )} - </div> - </div> - ); -}; diff --git a/apps/frontend/src/components/launches/web3/providers/telegram.provider.tsx b/apps/frontend/src/components/launches/web3/providers/telegram.provider.tsx index a177f239..8ade7653 100644 --- a/apps/frontend/src/components/launches/web3/providers/telegram.provider.tsx +++ b/apps/frontend/src/components/launches/web3/providers/telegram.provider.tsx @@ -3,8 +3,6 @@ import '@neynar/react/dist/style.css'; import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; -import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { timer } from '@gitroom/helpers/utils/timer'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; @@ -17,7 +15,6 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; export const TelegramProvider: FC<Web3ProviderInterface> = (props) => { const { onComplete, nonce } = props; const { telegramBotName } = useVariables(); - const modal = useModals(); const fetch = useFetch(); const word = useRef(makeId(4)); const stop = useRef(false); @@ -66,32 +63,10 @@ export const TelegramProvider: FC<Web3ProviderInterface> = (props) => { }; }, []); return ( - <div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative w-[700px]"> - <TopTitle title={`Add Telegram`} /> - <button - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - onClick={() => modal.closeAll()} - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> + <> <div className="justify-center items-center flex flex-col pt-[16px]"> <div> - {t('please_add', 'Please add')}{' '} - <strong>@{telegramBotName}</strong>{' '} + {t('please_add', 'Please add')} <strong>@{telegramBotName}</strong>{' '} {t( 'to_your_telegram_group_channel_and_click_here', 'to your\n telegram group / channel and click here:' @@ -145,6 +120,6 @@ export const TelegramProvider: FC<Web3ProviderInterface> = (props) => { </div> )} </div> - </div> + </> ); }; diff --git a/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx b/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx index 4d6ffd9a..43ca5bed 100644 --- a/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx +++ b/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx @@ -27,39 +27,16 @@ export const WrapcasterProvider: FC<Web3ProviderInterface> = (props) => { [state] ); return ( - <div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative w-full"> - <TopTitle title={`Add Wrapcast`} /> - <button - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - onClick={() => modal.closeAll()} - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - <div className="justify-center items-center flex"> - {hide ? ( - <div className="justify-center items-center flex -mt-[90px]"> - <LoadingComponent width={100} height={100} /> - </div> - ) : ( - <div className="justify-center items-center py-[20px] flex-col w-[500px]"> - <ButtonCaster login={auth} /> - </div> - )} - </div> + <div className="justify-center items-center flex"> + {hide ? ( + <div className="justify-center items-center flex -mt-[90px]"> + <LoadingComponent width={100} height={100} /> + </div> + ) : ( + <div className="justify-center items-center py-[20px] flex-col w-[500px]"> + <ButtonCaster login={auth} /> + </div> + )} </div> ); }; diff --git a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts index ac9fd256..fbadbeec 100644 --- a/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/farcaster.provider.ts @@ -25,7 +25,7 @@ export class FarcasterProvider implements SocialProvider { identifier = 'wrapcast'; - name = 'Warpcast'; + name = 'Farcaster'; isBetweenSteps = false; isWeb3 = true; scopes = [] as string[]; From 88e05853c1bccce4180c76dd9fdf65ad97facd71 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 16:55:43 +0700 Subject: [PATCH 151/340] fix: urgent providers fix --- .../nestjs-libraries/src/integrations/social/bluesky.provider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts index bebd448a..1f28c38d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts @@ -149,6 +149,7 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { override maxConcurrentJob = 2; // Bluesky has moderate rate limits identifier = 'bluesky'; name = 'Bluesky'; + toolTip = "We don’t currently support two-factor authentication. If it’s enabled on Bluesky, you’ll need to disable it." isBetweenSteps = false; scopes = ['write:statuses', 'profile', 'write:media']; editor = 'normal' as const; From 87eca91b9bec9b5ada2cd4856b9582a056b729d2 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 17 Jan 2026 17:01:54 +0700 Subject: [PATCH 152/340] fix: urgent providers fix --- .../frontend/src/components/launches/add.provider.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index ec03832a..264658f3 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -344,7 +344,7 @@ export const AddProviderComponent: FC<{ ).json(); modal.openModal({ title: t('web3_provider', 'Web3 provider'), - withCloseButton: false, + withCloseButton: true, classNames: { modal: 'bg-transparent text-textColor', }, From 604b029a9d7980199016543d381bfc67b93993c0 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 18 Jan 2026 09:44:22 +0700 Subject: [PATCH 153/340] feat: start button --- .../components/launches/web3/providers/wrapcaster.provider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx b/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx index 43ca5bed..c3814ac9 100644 --- a/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx +++ b/apps/frontend/src/components/launches/web3/providers/wrapcaster.provider.tsx @@ -34,6 +34,7 @@ export const WrapcasterProvider: FC<Web3ProviderInterface> = (props) => { </div> ) : ( <div className="justify-center items-center py-[20px] flex-col w-[500px]"> + <div>Click on the bottom below to start the process</div> <ButtonCaster login={auth} /> </div> )} From 0d5b45cd11aaad38c6b933cfab91b56a67fffc38 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 18 Jan 2026 11:51:40 +0700 Subject: [PATCH 154/340] fix: statistics --- .../nestjs-libraries/src/short-linking/short.link.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/short-linking/short.link.service.ts b/libraries/nestjs-libraries/src/short-linking/short.link.service.ts index b3706713..b97a0b7f 100644 --- a/libraries/nestjs-libraries/src/short-linking/short.link.service.ts +++ b/libraries/nestjs-libraries/src/short-linking/short.link.service.ts @@ -6,6 +6,7 @@ import { ShortIo } from './providers/short.io'; import { Kutt } from './providers/kutt'; import { LinkDrip } from './providers/linkdrip'; import { uniq } from 'lodash'; +import striptags from 'striptags'; const getProvider = (): ShortLinking => { if (process.env.DUB_TOKEN) { @@ -139,7 +140,7 @@ export class ShortLinkService { )}/[^\\s]*`, 'g' ); - const urls = mergeMessages.match(regex); + const urls = striptags(mergeMessages).match(regex); if (!urls) { // No URLs found, return the original text return []; From 809476aa1d509c2d021c0b879256a16fc433711f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 18 Jan 2026 15:30:09 +0700 Subject: [PATCH 155/340] fix: postiz agent --- .../src/chat/tools/integration.schedule.post.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/chat/tools/integration.schedule.post.ts b/libraries/nestjs-libraries/src/chat/tools/integration.schedule.post.ts index d10608f9..078bea72 100644 --- a/libraries/nestjs-libraries/src/chat/tools/integration.schedule.post.ts +++ b/libraries/nestjs-libraries/src/chat/tools/integration.schedule.post.ts @@ -201,11 +201,14 @@ If the tools return errors, you would need to rerun it with the right parameters ...acc, [s.key]: s.value, }), - {} as AllProvidersSettings + { + __type: integration.providerIdentifier, + } as AllProvidersSettings ), value: post.postsAndComments.map((p) => ({ content: p.content, id: makeId(10), + delay: 0, image: p.attachments.map((p) => ({ id: makeId(10), path: p, From 8a577dea735c598de2c49b452a4c36bea4a7208b Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 19 Jan 2026 14:55:07 +0700 Subject: [PATCH 156/340] feat: time table improvements --- .../src/components/launches/menu/menu.tsx | 2 +- .../src/components/launches/time.table.tsx | 178 +++++++++++------- 2 files changed, 115 insertions(+), 65 deletions(-) diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index 647c9165..e3d92045 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -168,7 +168,7 @@ export const Menu: FC<{ (integration) => integration.id === id ); modal.openModal({ - withCloseButton: false, + withCloseButton: true, closeOnEscape: false, closeOnClickOutside: false, askClose: true, diff --git a/apps/frontend/src/components/launches/time.table.tsx b/apps/frontend/src/components/launches/time.table.tsx index ea2fca3b..3e878a52 100644 --- a/apps/frontend/src/components/launches/time.table.tsx +++ b/apps/frontend/src/components/launches/time.table.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; +import React, { FC, useCallback, useMemo, useState } from 'react'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; import dayjs from 'dayjs'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; @@ -16,14 +16,24 @@ import { sortBy } from 'lodash'; import { usePreventWindowUnload } from '@gitroom/react/helpers/use.prevent.window.unload'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; +import clsx from 'clsx'; +import { + TrashIcon, + PlusIcon, + DelayIcon, +} from '@gitroom/frontend/components/ui/icons'; + dayjs.extend(utc); dayjs.extend(timezone); -const hours = [...Array(24).keys()].map((i, index) => ({ - value: index, + +const hours = [...Array(24).keys()].map((i) => ({ + value: i, })); -const minutes = [...Array(60).keys()].map((i, index) => ({ - value: index, + +const minutes = [...Array(60).keys()].map((i) => ({ + value: i, })); + export const TimeTable: FC<{ integration: Integrations; mutate: () => void; @@ -39,6 +49,7 @@ export const TimeTable: FC<{ const fetch = useFetch(); const modal = useModals(); usePreventWindowUnload(true); + const askClose = useCallback(async () => { if ( !(await deleteDialog( @@ -53,7 +64,9 @@ export const TimeTable: FC<{ } modal.closeAll(); }, []); + useKeypress('Escape', askClose); + const removeSlot = useCallback( (index: number) => async () => { if ( @@ -70,6 +83,7 @@ export const TimeTable: FC<{ }, [] ); + const addHour = useCallback(() => { const calculateMinutes = newDayjs() @@ -77,7 +91,8 @@ export const TimeTable: FC<{ .startOf('day') .add(hour, 'hours') .add(minute, 'minutes') - .diff(newDayjs().utc().startOf('day'), 'minutes') - dayjs.tz().utcOffset(); + .diff(newDayjs().utc().startOf('day'), 'minutes') - + dayjs.tz().utcOffset(); setCurrentTimes((prev) => [ ...prev, { @@ -85,6 +100,7 @@ export const TimeTable: FC<{ }, ]); }, [hour, minute]); + const times = useMemo(() => { return sortBy( currentTimes.map(({ time }) => ({ @@ -99,6 +115,7 @@ export const TimeTable: FC<{ (p) => p.value ); }, [currentTimes]); + const save = useCallback(async () => { await fetch(`/integrations/${props.integration.id}/time`, { method: 'POST', @@ -109,72 +126,105 @@ export const TimeTable: FC<{ mutate(); modal.closeAll(); }, [currentTimes]); + return ( - <div className="relative w-full"> - <div> - <div className="text-[16px] font-bold mt-[16px]"> + <div className="relative w-full max-w-[400px] mx-auto"> + {/* Add Time Slot Section */} + <div className="bg-newBgColorInner rounded-[12px] p-[20px] border border-newTableBorder"> + <div className="text-[15px] font-semibold mb-[16px] flex items-center gap-[8px]"> + <DelayIcon size={18} className="text-[#612BD3]" /> {t('add_time_slot', 'Add Time Slot')} </div> - <div className="flex flex-col"> - <div className="mt-[16px] flex justify-center gap-[16px]"> - <div className="w-[100px]"> - <Select - label={t('hour', 'Hour')} - name="hour" - disableForm={true} - className="w-[100px] mt-[8px]" - value={hour} - onChange={(e) => setHour(Number(e.target.value))} - > - {hours.map((hour) => ( - <option key={hour.value} value={hour.value}> - {hour.value.toString().length === 1 ? '0' : ''} - {hour.value} - </option> - ))} - </Select> - </div> - <div className="w-[100px]"> - <Select - label={t('minutes', 'Minutes')} - name="minutes" - disableForm={true} - className="w-[100px] mt-[8px]" - value={minute} - onChange={(e) => setMinute(Number(e.target.value))} - > - {minutes.map((minute) => ( - <option key={minute.value} value={minute.value}> - {minute.value.toString().length === 1 ? '0' : ''} - {minute.value} - </option> - ))} - </Select> - </div> + + <div className="flex gap-[12px] items-end"> + <div className="flex-1"> + <Select + label={t('hour', 'Hour')} + name="hour" + disableForm={true} + hideErrors={true} + value={hour} + onChange={(e) => setHour(Number(e.target.value))} + > + {hours.map((h) => ( + <option key={h.value} value={h.value}> + {h.value.toString().padStart(2, '0')} + </option> + ))} + </Select> </div> - <div className="flex w-[215px] mx-auto justify-center mb-[50px]"> - <Button type="button" className="w-full" onClick={addHour}> - {t('add_slot', 'Add Slot')} - </Button> + <div className="flex-1"> + <Select + label={t('minutes', 'Minutes')} + name="minutes" + disableForm={true} + hideErrors={true} + value={minute} + onChange={(e) => setMinute(Number(e.target.value))} + > + {minutes.map((m) => ( + <option key={m.value} value={m.value}> + {m.value.toString().padStart(2, '0')} + </option> + ))} + </Select> </div> + <button + type="button" + onClick={addHour} + className="h-[42px] px-[16px] bg-[#612BD3] hover:bg-[#7640e0] transition-colors rounded-[8px] flex items-center gap-[6px] text-white text-[14px] font-medium" + > + <PlusIcon size={14} /> + {t('add', 'Add')} + </button> </div> </div> - <div className="mt-[16px] grid grid-cols-2 place-items-center w-[100px] mx-auto"> - {times.map((timeSlot, index) => ( - <Fragment key={timeSlot.formatted}> - <div className="text-start w-full">{timeSlot.formatted}</div> - <div - className="cursor-pointer text-red-400 text-start w-full" - onClick={removeSlot(index)} - > - X - </div> - </Fragment> - ))} + + {/* Time Slots List */} + <div className="mt-[20px]"> + <div className="text-[14px] text-newTextColor/60 mb-[12px]"> + {t('scheduled_times', 'Scheduled Times')} ({times.length}) + </div> + + {times.length === 0 ? ( + <div className="text-center py-[32px] text-newTextColor/40 text-[14px] border border-dashed border-newTableBorder rounded-[12px]"> + {t('no_time_slots', 'No time slots added yet')} + </div> + ) : ( + <div className="flex flex-col gap-[8px]"> + {times.map((timeSlot, index) => ( + <div + key={`${timeSlot.value}-${index}`} + className={clsx( + 'group flex items-center justify-between', + 'h-[48px] px-[16px] rounded-[8px]', + 'bg-newBgColorInner border border-newTableBorder', + 'hover:border-[#612BD3]/40 transition-colors' + )} + > + <div className="flex items-center gap-[12px]"> + <div className="w-[8px] h-[8px] rounded-full bg-[#612BD3]" /> + <span className="text-[15px] font-medium tabular-nums"> + {timeSlot.formatted} + </span> + </div> + <button + type="button" + onClick={removeSlot(index)} + className="opacity-0 group-hover:opacity-100 transition-opacity p-[8px] hover:bg-red-500/10 rounded-[6px] text-red-400 hover:text-red-500" + > + <TrashIcon size={16} /> + </button> + </div> + ))} + </div> + )} </div> - <div className="flex w-[215px] mx-auto justify-center mb-[50px]"> - <Button type="button" className="w-full" onClick={save}> - {t('save', 'Save')} + + {/* Save Button */} + <div className="mt-[24px]"> + <Button type="button" className="w-full rounded-[8px]" onClick={save}> + {t('save_changes', 'Save Changes')} </Button> </div> </div> From d8b8b3d629486ed101c727cfc960550abf5f7e0e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 19 Jan 2026 16:08:17 +0700 Subject: [PATCH 157/340] feat: maximize media --- .../src/components/layout/new-modal.tsx | 25 ++++++++-- .../src/components/media/media.component.tsx | 47 ++++++++++++++++++- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/components/layout/new-modal.tsx b/apps/frontend/src/components/layout/new-modal.tsx index bcaaf61e..651e3e77 100644 --- a/apps/frontend/src/components/layout/new-modal.tsx +++ b/apps/frontend/src/components/layout/new-modal.tsx @@ -21,6 +21,7 @@ interface OpenModalInterface { closeOnClickOutside?: boolean; removeLayout?: boolean; fullScreen?: boolean; + top?: string | number; closeOnEscape?: boolean; withCloseButton?: boolean; askClose?: boolean; @@ -175,12 +176,21 @@ export const Component: FC<{ > <div className="relative flex-1"> <div + style={ + modal.top + ? { paddingTop: modal.top, paddingBottom: modal.top } + : {} + } className={clsx( 'absolute min-w-full', !modal.fullScreen - ? 'min-h-full pt-[100px] pb-[100px]' + ? modal.top + ? '' + : 'min-h-full pt-[100px] pb-[100px]' : 'h-screen', - modal.size && modal.height ? 'flex justify-center items-center' : 'top-0 left-0' + modal.size && modal.height + ? 'flex justify-center items-center' + : 'top-0 left-0' )} > <div @@ -188,7 +198,7 @@ export const Component: FC<{ !modal.removeLayout && 'gap-[40px] p-[32px]', 'bg-newBgColorInner mx-auto flex flex-col w-fit rounded-[24px] relative', modal.size ? '' : 'min-w-[600px]', - modal.fullScreen && 'h-full', + modal.fullScreen && 'h-full' )} {...((!!modal.size || !!modal.height) && { style: { @@ -228,7 +238,14 @@ export const Component: FC<{ </div> ) : null} </div> - <div className={clsx("whitespace-pre-line", !!modal.height && !!modal.size && 'flex flex-1 flex-col')}>{RenderComponent}</div> + <div + className={clsx( + 'whitespace-pre-line', + !!modal.height && !!modal.size && 'flex flex-1 flex-col' + )} + > + {RenderComponent} + </div> </div> </div> </div> diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 7d7605dd..e0c7c1fd 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -290,6 +290,32 @@ export const MediaBox: FC<{ [] ); + const maximize = useCallback( + (media: Media) => async (e: any) => { + e.stopPropagation(); + modals.openModal({ + title: '', + top: 10, + children: ( + <div className="w-full h-full p-[50px]"> + {media.path.indexOf('mp4') > -1 ? ( + <VideoFrame url={mediaDirectory.set(media.path)} /> + ) : ( + <img + width="100%" + height="100%" + className="w-full h-full max-h-[100%] max-w-[100%] object-cover" + src={mediaDirectory.set(media.path)} + alt="media" + /> + )} + </div> + ), + }); + }, + [] + ); + const deleteImage = useCallback( (media: Media) => async (e: any) => { e.stopPropagation(); @@ -459,7 +485,26 @@ export const MediaBox: FC<{ onClick={deleteImage(media)} /> )} - <div className="w-full h-full rounded-[6px] overflow-hidden"> + <div className="w-full h-full rounded-[6px] overflow-hidden relative"> + <div className="absolute left-[50%] top-[50%] -translate-x-[50%] -translate-y-[50%]"> + <div + onClick={maximize(media)} + className="cursor-pointer p-[4px] bg-black/40 hidden group-hover:block hover:scale-150 transition-all" + > + <svg + width="30" + height="30" + viewBox="0 0 14 14" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M2 9H0V14H5V12H2V9ZM0 5H2V2H5V0H0V5ZM12 12H9V14H14V9H12V12ZM9 0V2H12V5H14V0H9Z" + fill="#F1F5F9" + /> + </svg> + </div> + </div> {media.path.indexOf('mp4') > -1 ? ( <VideoFrame url={mediaDirectory.set(media.path)} /> ) : ( From 5bad88daa9d904c489442e61f5e6e4880e54cd33 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 19 Jan 2026 16:33:24 +0700 Subject: [PATCH 158/340] feat: ask for shortlinking --- .../src/api/routes/settings.controller.ts | 18 +++ .../components/new-launch/manage.modal.tsx | 56 +++++---- .../components/settings/global.settings.tsx | 2 + .../shortlink-preference.component.tsx | 117 ++++++++++++++++++ .../organizations/organization.repository.ts | 24 +++- .../organizations/organization.service.ts | 13 +- .../src/database/prisma/schema.prisma | 17 ++- .../dtos/settings/shortlink-preference.dto.ts | 8 ++ 8 files changed, 226 insertions(+), 29 deletions(-) create mode 100644 apps/frontend/src/components/settings/shortlink-preference.component.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/settings/shortlink-preference.dto.ts diff --git a/apps/backend/src/api/routes/settings.controller.ts b/apps/backend/src/api/routes/settings.controller.ts index c92537b4..b99044bf 100644 --- a/apps/backend/src/api/routes/settings.controller.ts +++ b/apps/backend/src/api/routes/settings.controller.ts @@ -4,6 +4,7 @@ import { Organization } from '@prisma/client'; import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; import { AddTeamMemberDto } from '@gitroom/nestjs-libraries/dtos/settings/add.team.member.dto'; +import { ShortlinkPreferenceDto } from '@gitroom/nestjs-libraries/dtos/settings/shortlink-preference.dto'; import { ApiTags } from '@nestjs/swagger'; import { AuthorizationActions, Sections } from '@gitroom/backend/services/auth/permissions/permission.exception.class'; @@ -46,4 +47,21 @@ export class SettingsController { ) { return this._organizationService.deleteTeamMember(org, id); } + + @Get('/shortlink') + async getShortlinkPreference(@GetOrgFromRequest() org: Organization) { + return this._organizationService.getShortlinkPreference(org.id); + } + + @Post('/shortlink') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async updateShortlinkPreference( + @GetOrgFromRequest() org: Organization, + @Body() body: ShortlinkPreferenceDto + ) { + return this._organizationService.updateShortlinkPreference( + org.id, + body.shortlink + ); + } } diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 202daff9..ca4e5a3c 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -41,6 +41,7 @@ import { DropdownArrowSmallIcon, } from '@gitroom/frontend/components/ui/icons'; import { useHasScroll } from '@gitroom/frontend/components/ui/is.scroll.hook'; +import { useShortlinkPreference } from '@gitroom/frontend/components/settings/shortlink-preference.component'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -58,6 +59,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const toaster = useToaster(); const modal = useModals(); const [showSettings, setShowSettings] = useState(false); + const { data: shortlinkPreferenceData } = useShortlinkPreference(); const { addEditSets, mutate, customClose, dummy } = props; @@ -276,28 +278,38 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { } } - const shortLinkUrl = dummy - ? { ask: false } - : await ( - await fetch('/posts/should-shortlink', { - method: 'POST', - body: JSON.stringify({ - messages: checkAllValid.flatMap((p: any) => - p.values.flatMap((a: any) => a.content) - ), - }), - }) - ).json(); + const shortlinkPreference = shortlinkPreferenceData?.shortlink || 'ASK'; - const shortLink = !shortLinkUrl.ask - ? false - : await deleteDialog( - t( - 'shortlink_urls_question', - 'Do you want to shortlink the URLs? it will let you get statistics over clicks' - ), - t('yes_shortlink_it', 'Yes, shortlink it!') - ); + let shortLink = false; + + if (!dummy && shortlinkPreference !== 'NO') { + const shortLinkUrl = await ( + await fetch('/posts/should-shortlink', { + method: 'POST', + body: JSON.stringify({ + messages: checkAllValid.flatMap((p: any) => + p.values.flatMap((a: any) => a.content) + ), + }), + }) + ).json(); + + if (shortLinkUrl.ask) { + if (shortlinkPreference === 'YES') { + // Automatically shortlink without asking + shortLink = true; + } else { + // ASK: Show the dialog + shortLink = await deleteDialog( + t( + 'shortlink_urls_question', + 'Do you want to shortlink the URLs? it will let you get statistics over clicks' + ), + t('yes_shortlink_it', 'Yes, shortlink it!') + ); + } + } + } const group = existingData.group || makeId(10); const data = { @@ -373,7 +385,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { } } }, - [ref, repeater, tags, date, addEditSets, dummy] + [ref, repeater, tags, date, addEditSets, dummy, shortlinkPreferenceData] ); return ( diff --git a/apps/frontend/src/components/settings/global.settings.tsx b/apps/frontend/src/components/settings/global.settings.tsx index 970a960a..6fc56cd8 100644 --- a/apps/frontend/src/components/settings/global.settings.tsx +++ b/apps/frontend/src/components/settings/global.settings.tsx @@ -4,6 +4,7 @@ import React from 'react'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import dynamic from 'next/dynamic'; import EmailNotificationsComponent from '@gitroom/frontend/components/settings/email-notifications.component'; +import ShortlinkPreferenceComponent from '@gitroom/frontend/components/settings/shortlink-preference.component'; const MetricComponent = dynamic( () => import('@gitroom/frontend/components/settings/metric.component'), @@ -19,6 +20,7 @@ export const GlobalSettings = () => { <h3 className="text-[20px]">{t('global_settings', 'Global Settings')}</h3> <MetricComponent /> <EmailNotificationsComponent /> + <ShortlinkPreferenceComponent /> </div> ); }; diff --git a/apps/frontend/src/components/settings/shortlink-preference.component.tsx b/apps/frontend/src/components/settings/shortlink-preference.component.tsx new file mode 100644 index 00000000..fa622695 --- /dev/null +++ b/apps/frontend/src/components/settings/shortlink-preference.component.tsx @@ -0,0 +1,117 @@ +'use client'; + +import React, { useCallback, useEffect, useState } from 'react'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import useSWR from 'swr'; +import { Select } from '@gitroom/react/form/select'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +type ShortLinkPreference = 'ASK' | 'YES' | 'NO'; + +interface ShortlinkPreferenceResponse { + shortlink: ShortLinkPreference; +} + +export const useShortlinkPreference = () => { + const fetch = useFetch(); + + const load = useCallback(async () => { + return (await fetch('/settings/shortlink')).json(); + }, []); + + return useSWR<ShortlinkPreferenceResponse>('shortlink-preference', load, { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + revalidateOnMount: true, + refreshWhenHidden: false, + refreshWhenOffline: false, + }); +}; + +const ShortlinkPreferenceComponent = () => { + const t = useT(); + const fetch = useFetch(); + const toaster = useToaster(); + const { data, isLoading, mutate } = useShortlinkPreference(); + + const [localValue, setLocalValue] = useState<ShortLinkPreference>('ASK'); + + // Sync local state with fetched data + useEffect(() => { + if (data?.shortlink) { + setLocalValue(data.shortlink); + } + }, [data]); + + const handleChange = useCallback( + async (event: React.ChangeEvent<HTMLSelectElement>) => { + const newValue = event.target.value as ShortLinkPreference; + + // Update local state immediately + setLocalValue(newValue); + + await fetch('/settings/shortlink', { + method: 'POST', + body: JSON.stringify({ shortlink: newValue }), + }); + + mutate({ shortlink: newValue }); + toaster.show(t('settings_updated', 'Settings updated'), 'success'); + }, + [fetch, mutate, toaster, t] + ); + + if (isLoading) { + return ( + <div className="my-[16px] mt-[16px] bg-sixth border-fifth border rounded-[4px] p-[24px]"> + <div className="animate-pulse">{t('loading', 'Loading...')}</div> + </div> + ); + } + + return ( + <div className="my-[16px] mt-[16px] bg-sixth border-fifth border rounded-[4px] p-[24px] flex flex-col gap-[24px]"> + <div className="mt-[4px]"> + {t('shortlink_settings', 'Shortlink Settings')} + </div> + <div className="flex items-center justify-between gap-[24px]"> + <div className="flex flex-col flex-1"> + <div className="text-[14px]"> + {t('shortlink_preference', 'Shortlink Preference')} + </div> + <div className="text-[12px] text-customColor18"> + {t( + 'shortlink_preference_description', + 'Control how URLs in your posts are handled. Shortlinks provide click statistics.' + )} + </div> + </div> + <div className="w-[200px]"> + <Select + name="shortlink" + label="" + disableForm={true} + hideErrors={true} + value={localValue} + onChange={handleChange} + > + <option value="ASK"> + {t('shortlink_ask', 'Ask every time')} + </option> + <option value="YES"> + {t('shortlink_yes', 'Always shortlink')} + </option> + <option value="NO"> + {t('shortlink_no', 'Never shortlink')} + </option> + </Select> + </div> + </div> + </div> + ); +}; + +export default ShortlinkPreferenceComponent; + diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index 6da496ca..57bae74e 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -1,5 +1,5 @@ import { PrismaRepository } from '@gitroom/nestjs-libraries/database/prisma/prisma.service'; -import { Role, SubscriptionTier } from '@prisma/client'; +import { Role, ShortLinkPreference, SubscriptionTier } from '@prisma/client'; import { Injectable } from '@nestjs/common'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; import { CreateOrgUserDto } from '@gitroom/nestjs-libraries/dtos/auth/create.org.user.dto'; @@ -329,4 +329,26 @@ export class OrganizationRepository { }, }); } + + getShortlinkPreference(orgId: string) { + return this._organization.model.organization.findUnique({ + where: { + id: orgId, + }, + select: { + shortlink: true, + }, + }); + } + + updateShortlinkPreference(orgId: string, shortlink: ShortLinkPreference) { + return this._organization.model.organization.update({ + where: { + id: orgId, + }, + data: { + shortlink, + }, + }); + } } diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts index f523ea8f..c9fca065 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts @@ -6,7 +6,7 @@ import { AddTeamMemberDto } from '@gitroom/nestjs-libraries/dtos/settings/add.te import { AuthService } from '@gitroom/helpers/auth/auth.service'; import dayjs from 'dayjs'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; -import { Organization } from '@prisma/client'; +import { Organization, ShortLinkPreference } from '@prisma/client'; import { AutopostService } from '@gitroom/nestjs-libraries/database/prisma/autopost/autopost.service'; @Injectable() @@ -111,4 +111,15 @@ export class OrganizationService { disable ); } + + getShortlinkPreference(orgId: string) { + return this._organizationRepository.getShortlinkPreference(orgId); + } + + updateShortlinkPreference(orgId: string, shortlink: ShortLinkPreference) { + return this._organizationRepository.updateShortlinkPreference( + orgId, + shortlink + ); + } } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index d3c5d771..32310bb9 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -9,15 +9,16 @@ datasource db { } model Organization { - id String @id @default(uuid()) + id String @id @default(uuid()) name String description String? apiKey String? paymentId String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - allowTrial Boolean @default(false) - isTrailing Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + allowTrial Boolean @default(false) + isTrailing Boolean @default(false) + shortlink ShortLinkPreference @default(ASK) autoPost AutoPost[] Comments Comments[] credits Credits[] @@ -879,3 +880,9 @@ enum APPROVED_SUBMIT_FOR_ORDER { WAITING_CONFIRMATION YES } + +enum ShortLinkPreference { + ASK + YES + NO +} diff --git a/libraries/nestjs-libraries/src/dtos/settings/shortlink-preference.dto.ts b/libraries/nestjs-libraries/src/dtos/settings/shortlink-preference.dto.ts new file mode 100644 index 00000000..0cc17941 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/settings/shortlink-preference.dto.ts @@ -0,0 +1,8 @@ +import { IsEnum } from 'class-validator'; +import { ShortLinkPreference } from '@prisma/client'; + +export class ShortlinkPreferenceDto { + @IsEnum(ShortLinkPreference) + shortlink: ShortLinkPreference; +} + From c8f6cdb643b57110e8eb37a72160122cdb8cd89b Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 19 Jan 2026 17:25:13 +0700 Subject: [PATCH 159/340] fix: z-index --- apps/frontend/src/components/media/media.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index e0c7c1fd..22769abc 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -486,7 +486,7 @@ export const MediaBox: FC<{ /> )} <div className="w-full h-full rounded-[6px] overflow-hidden relative"> - <div className="absolute left-[50%] top-[50%] -translate-x-[50%] -translate-y-[50%]"> + <div className="absolute z-[20] left-[50%] top-[50%] -translate-x-[50%] -translate-y-[50%]"> <div onClick={maximize(media)} className="cursor-pointer p-[4px] bg-black/40 hidden group-hover:block hover:scale-150 transition-all" From 99889d59e2f65bd45c544409f8a6b909c24176bb Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 19 Jan 2026 17:31:14 +0700 Subject: [PATCH 160/340] fix: autoplay video --- apps/frontend/src/components/media/media.component.tsx | 2 +- libraries/react-shared-libraries/src/helpers/video.frame.tsx | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 22769abc..9a289958 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -299,7 +299,7 @@ export const MediaBox: FC<{ children: ( <div className="w-full h-full p-[50px]"> {media.path.indexOf('mp4') > -1 ? ( - <VideoFrame url={mediaDirectory.set(media.path)} /> + <VideoFrame autoplay={true} url={mediaDirectory.set(media.path)} /> ) : ( <img width="100%" diff --git a/libraries/react-shared-libraries/src/helpers/video.frame.tsx b/libraries/react-shared-libraries/src/helpers/video.frame.tsx index ec2fbd85..06376938 100644 --- a/libraries/react-shared-libraries/src/helpers/video.frame.tsx +++ b/libraries/react-shared-libraries/src/helpers/video.frame.tsx @@ -3,6 +3,7 @@ import { FC } from 'react'; export const VideoFrame: FC<{ url: string; + autoplay?: boolean; }> = (props) => { const { url } = props; return ( @@ -10,7 +11,7 @@ export const VideoFrame: FC<{ className="w-full h-full object-cover rounded-[4px]" src={url + '#t=0.1'} preload="metadata" - autoPlay={false} - ></video> + autoPlay={!!props?.autoplay} + /> ); }; From bfa552401ef19fd42e10c6c24d4b02bead96f4be Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Mon, 19 Jan 2026 22:05:19 +0700 Subject: [PATCH 161/340] Add video link for Postiz features in README Updated README to include a video link showcasing Postiz features. --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index bee16081..de079adf 100644 --- a/README.md +++ b/README.md @@ -67,8 +67,12 @@ <br /> +## 🔌 See the leading Postiz features + <p align="center"> - <video src="https://github.com/user-attachments/assets/05436a01-19c8-4827-b57f-05a5e7637a67" width="100%" /> + <a href="https://www.youtube.com/watch?v=BdsCVvEYgHU" target="_blank"> + <img alt="Postiz" src="https://github.com/user-attachments/assets/8b9b7939-da1a-4be5-95be-42c6fce772de" /> + </a> </p> ## ✨ Features From 5cca81e0029313fde34e7b248d6d2ac2d5565ff4 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 21 Jan 2026 15:11:29 +0700 Subject: [PATCH 162/340] feat: post analytics --- .../src/api/routes/analytics.controller.ts | 15 +- .../src/components/launches/calendar.tsx | 2 +- .../src/components/launches/statistics.tsx | 150 ++++++++++++++--- .../database/prisma/posts/posts.repository.ts | 3 +- .../database/prisma/posts/posts.service.ts | 87 +++++++++- .../integrations/social/dribbble.provider.ts | 10 ++ .../integrations/social/facebook.provider.ts | 75 +++++++++ .../src/integrations/social/gmb.provider.ts | 11 ++ .../integrations/social/instagram.provider.ts | 69 ++++++++ .../social/instagram.standalone.provider.ts | 15 ++ .../social/linkedin.page.provider.ts | 153 ++++++++++++++++++ .../integrations/social/pinterest.provider.ts | 74 +++++++++ .../social/social.integrations.interface.ts | 7 + .../integrations/social/threads.provider.ts | 62 +++++++ .../src/integrations/social/x.provider.ts | 90 +++++++++++ .../integrations/social/youtube.provider.ts | 67 ++++++++ 16 files changed, 865 insertions(+), 25 deletions(-) diff --git a/apps/backend/src/api/routes/analytics.controller.ts b/apps/backend/src/api/routes/analytics.controller.ts index 8c3201ef..a9bbcc2f 100644 --- a/apps/backend/src/api/routes/analytics.controller.ts +++ b/apps/backend/src/api/routes/analytics.controller.ts @@ -3,11 +3,15 @@ import { Organization } from '@prisma/client'; import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; import { ApiTags } from '@nestjs/swagger'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; +import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; @ApiTags('Analytics') @Controller('/analytics') export class AnalyticsController { - constructor(private _integrationService: IntegrationService) {} + constructor( + private _integrationService: IntegrationService, + private _postsService: PostsService + ) {} @Get('/:integration') async getIntegration( @@ -17,4 +21,13 @@ export class AnalyticsController { ) { return this._integrationService.checkAnalytics(org, integration, date); } + + @Get('/post/:postId') + async getPostAnalytics( + @GetOrgFromRequest() org: Organization, + @Param('postId') postId: string, + @Query('date') date: string + ) { + return this._postsService.checkPostAnalytics(org.id, postId, +date); + } } diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index f0dbb4f6..c2143821 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -876,7 +876,7 @@ const CalendarItem: FC<{ > <Preview /> </div>{' '} - {post.integration.providerIdentifier === 'x' && disableXAnalytics ? ( + {((post.integration.providerIdentifier === 'x' && disableXAnalytics) || !post.releaseId) ? ( <></> ) : ( <div diff --git a/apps/frontend/src/components/launches/statistics.tsx b/apps/frontend/src/components/launches/statistics.tsx index d5d7c2a7..9054ec52 100644 --- a/apps/frontend/src/components/launches/statistics.tsx +++ b/apps/frontend/src/components/launches/statistics.tsx @@ -1,36 +1,138 @@ -import React, { FC, Fragment, useCallback } from 'react'; -import { useModals } from '@gitroom/frontend/components/layout/new-modal'; +import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { ChartSocial } from '@gitroom/frontend/components/analytics/chart-social'; +import { Select } from '@gitroom/react/form/select'; +import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; + +interface AnalyticsData { + label: string; + data: Array<{ total: number; date: string }>; + percentageChange: number; + average?: boolean; +} + export const StatisticsModal: FC<{ postId: string; }> = (props) => { const { postId } = props; - const modals = useModals(); const t = useT(); const fetch = useFetch(); + const [dateRange, setDateRange] = useState(7); + const loadStatistics = useCallback(async () => { return (await fetch(`/posts/${postId}/statistics`)).json(); - }, [postId]); - const closeAll = useCallback(() => { - modals.closeAll(); - }, []); - const { data, isLoading } = useSWR( + }, [postId, fetch]); + + const loadPostAnalytics = useCallback(async () => { + return (await fetch(`/analytics/post/${postId}?date=${dateRange}`)).json(); + }, [postId, dateRange, fetch]); + + const { data: statisticsData, isLoading: isLoadingStatistics } = useSWR( `/posts/${postId}/statistics`, loadStatistics ); + + const { data: analyticsData, isLoading: isLoadingAnalytics } = useSWR( + `/analytics/post/${postId}?date=${dateRange}`, + loadPostAnalytics, + { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + revalidateOnMount: true, + refreshWhenHidden: false, + refreshWhenOffline: false, + } + ); + + const dateOptions = useMemo(() => { + return [ + { key: 7, value: t('7_days', '7 Days') }, + { key: 30, value: t('30_days', '30 Days') }, + { key: 90, value: t('90_days', '90 Days') }, + ]; + }, [t]); + + const totals = useMemo(() => { + if (!analyticsData || !Array.isArray(analyticsData)) return []; + return analyticsData.map((p: AnalyticsData) => { + const value = + (p?.data?.reduce((acc: number, curr: any) => acc + Number(curr.total), 0) || 0) / + (p.average ? p.data.length : 1); + if (p.average) { + return value.toFixed(2) + '%'; + } + return Math.round(value); + }); + }, [analyticsData]); + + const isLoading = isLoadingStatistics || isLoadingAnalytics; + return ( - <div className="relative"> + <div className="relative min-h-[200px]"> {isLoading ? ( - <div>{t('loading', 'Loading')}</div> + <div className="flex items-center justify-center py-[40px]"> + <LoadingComponent /> + </div> ) : ( - <> - {data.clicks.length === 0 ? ( - 'No Results' - ) : ( - <> - <div className="grid grid-cols-3 mt-[20px]"> + <div className="flex flex-col gap-[24px]"> + {/* Post Analytics Section */} + {analyticsData && Array.isArray(analyticsData) && analyticsData.length > 0 && ( + <div className="flex flex-col gap-[14px]"> + <div className="flex items-center justify-between"> + <h3 className="text-[18px] font-[500]"> + {t('post_analytics', 'Post Analytics')} + </h3> + <div className="max-w-[150px]"> + <Select + label="" + name="date" + disableForm={true} + hideErrors={true} + value={dateRange} + onChange={(e) => setDateRange(+e.target.value)} + > + {dateOptions.map((option) => ( + <option key={option.key} value={option.key}> + {option.value} + </option> + ))} + </Select> + </div> + </div> + <div className="grid grid-cols-3 gap-[20px]"> + {analyticsData.map((p: AnalyticsData, index: number) => ( + <div key={`analytics-${index}`} className="flex"> + <div className="flex-1 bg-newTableHeader rounded-[8px] py-[10px] px-[16px] gap-[10px] flex flex-col"> + <div className="flex items-center gap-[14px]"> + <div className="text-[20px]">{p.label}</div> + </div> + <div className="flex-1"> + <div className="h-[156px] relative"> + <ChartSocial data={p.data} key={`chart-${index}`} /> + </div> + </div> + <div className="text-[50px] leading-[60px]">{totals[index]}</div> + </div> + </div> + ))} + </div> + </div> + )} + + {/* Short Links Statistics Section */} + <div className="flex flex-col gap-[14px]"> + <h3 className="text-[18px] font-[500]"> + {t('short_links_statistics', 'Short Links Statistics')} + </h3> + {statisticsData?.clicks?.length === 0 ? ( + <div className="text-gray-400"> + {t('no_short_link_results', 'No short link results')} + </div> + ) : ( + <div className="grid grid-cols-3"> <div className="bg-forth p-[4px] rounded-tl-lg"> {t('short_link', 'Short Link')} </div> @@ -40,7 +142,7 @@ export const StatisticsModal: FC<{ <div className="bg-forth p-[4px] rounded-tr-lg"> {t('clicks', 'Clicks')} </div> - {data.clicks.map((p: any) => ( + {statisticsData?.clicks?.map((p: any) => ( <Fragment key={p.short}> <div className="p-[4px] py-[10px] bg-customColor6"> {p.short} @@ -54,9 +156,17 @@ export const StatisticsModal: FC<{ </Fragment> ))} </div> - </> - )} - </> + )} + </div> + + {/* No analytics available message */} + {(!analyticsData || !Array.isArray(analyticsData) || analyticsData.length === 0) && + (!statisticsData?.clicks || statisticsData.clicks.length === 0) && ( + <div className="text-center text-gray-400 py-[20px]"> + {t('no_statistics_available', 'No statistics available for this post')} + </div> + )} + </div> )} </div> ); diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 56efa445..5b735cdf 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -168,8 +168,7 @@ export class PostsRepository { content: true, publishDate: true, releaseURL: true, - submittedForOrganizationId: true, - submittedForOrderId: true, + releaseId: true, state: true, intervalInDays: true, group: true, diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 98cc144b..aa7abd79 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -30,6 +30,11 @@ import { organizationId, postId as postIdSearchParam, } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; +import { AnalyticsData } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { timer } from '@gitroom/helpers/utils/timer'; +import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; +import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; type PostWithConditionals = Post & { integration?: Integration; @@ -46,7 +51,8 @@ export class PostsService { private _mediaService: MediaService, private _shortLinkService: ShortLinkService, private _openaiService: OpenaiService, - private _temporalService: TemporalService + private _temporalService: TemporalService, + private _refreshIntegrationService: RefreshIntegrationService ) {} searchForMissingThreeHoursPosts() { @@ -57,6 +63,85 @@ export class PostsService { return this._postRepository.updatePost(id, postId, releaseURL); } + async checkPostAnalytics( + orgId: string, + postId: string, + date: number, + forceRefresh = false + ): Promise<AnalyticsData[]> { + const post = await this._postRepository.getPostById(postId, orgId); + if (!post || !post.releaseId) { + return []; + } + + const integrationProvider = this._integrationManager.getSocialIntegration( + post.integration.providerIdentifier + ); + + if (!integrationProvider.postAnalytics) { + return []; + } + + const getIntegration = post.integration!; + + if ( + dayjs(getIntegration?.tokenExpiration).isBefore(dayjs()) || + forceRefresh + ) { + const data = await this._refreshIntegrationService.refresh( + getIntegration + ); + if (!data) { + return []; + } + + const { accessToken } = data; + + if (accessToken) { + getIntegration.token = accessToken; + + if (integrationProvider.refreshWait) { + await timer(10000); + } + } else { + await this._integrationService.disconnectChannel(orgId, getIntegration); + return []; + } + } + + const getIntegrationData = await ioRedis.get( + `integration:${orgId}:${post.id}:${date}` + ); + if (getIntegrationData) { + return JSON.parse(getIntegrationData); + } + + try { + const loadAnalytics = await integrationProvider.postAnalytics( + getIntegration.internalId, + getIntegration.token, + post.releaseId, + date + ); + await ioRedis.set( + `integration:${orgId}:${post.id}:${date}`, + JSON.stringify(loadAnalytics), + 'EX', + !process.env.NODE_ENV || process.env.NODE_ENV === 'development' + ? 1 + : 3600 + ); + return loadAnalytics; + } catch (e) { + console.log(e); + if (e instanceof RefreshToken) { + return this.checkPostAnalytics(orgId, postId, date, true); + } + } + + return []; + } + async getStatistics(orgId: string, id: string) { const getPost = await this.getPostsRecursively(id, true, orgId, true); const content = getPost.map((p) => p.content); diff --git a/libraries/nestjs-libraries/src/integrations/social/dribbble.provider.ts b/libraries/nestjs-libraries/src/integrations/social/dribbble.provider.ts index 4b2574ed..ba5968ea 100644 --- a/libraries/nestjs-libraries/src/integrations/social/dribbble.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/dribbble.provider.ts @@ -187,4 +187,14 @@ export class DribbbleProvider extends SocialAbstract implements SocialProvider { ): Promise<AnalyticsData[]> { return Promise.resolve([]); } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + // Dribbble doesn't provide detailed post-level analytics via their API + return []; + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index ffd89c2e..56ebfd35 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -462,4 +462,79 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { })) || [] ); } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + + try { + // Fetch post insights from Facebook Graph API + const { data } = await ( + await this.fetch( + `https://graph.facebook.com/v20.0/${postId}/insights?metric=post_impressions,post_impressions_unique,post_engaged_users,post_clicks,post_reactions_by_type_total&access_token=${accessToken}` + ) + ).json(); + + if (!data || data.length === 0) { + return []; + } + + const result: AnalyticsData[] = []; + + for (const metric of data) { + const value = metric.values?.[0]?.value; + if (value === undefined) continue; + + let label = ''; + let total = ''; + + switch (metric.name) { + case 'post_impressions': + label = 'Impressions'; + total = String(value); + break; + case 'post_impressions_unique': + label = 'Reach'; + total = String(value); + break; + case 'post_engaged_users': + label = 'Engaged Users'; + total = String(value); + break; + case 'post_clicks': + label = 'Clicks'; + total = String(value); + break; + case 'post_reactions_by_type_total': + // This returns an object with reaction types + if (typeof value === 'object') { + const totalReactions = Object.values(value as Record<string, number>).reduce( + (sum: number, v: number) => sum + v, + 0 + ); + label = 'Reactions'; + total = String(totalReactions); + } + break; + } + + if (label) { + result.push({ + label, + percentageChange: 0, + data: [{ total, date: today }], + }); + } + } + + return result; + } catch (err) { + console.error('Error fetching Facebook post analytics:', err); + return []; + } + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts index 3fa7de95..6c472da2 100644 --- a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -560,4 +560,15 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { return []; } } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + // Google My Business local posts don't have detailed individual post analytics + // The API focuses on location-level metrics rather than post-level metrics + return []; + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index e7b58e4d..9d509f2f 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -741,4 +741,73 @@ export class InstagramProvider )}&access_token=${accessToken}` ); } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number, + type = 'graph.facebook.com' + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + + try { + // Fetch media insights from Instagram Graph API + const { data } = await ( + await this.fetch( + `https://${type}/v21.0/${postId}/insights?metric=impressions,reach,engagement,saved,likes,comments,shares&access_token=${accessToken}` + ) + ).json(); + + if (!data || data.length === 0) { + return []; + } + + const result: AnalyticsData[] = []; + + for (const metric of data) { + const value = metric.values?.[0]?.value; + if (value === undefined) continue; + + let label = ''; + + switch (metric.name) { + case 'impressions': + label = 'Impressions'; + break; + case 'reach': + label = 'Reach'; + break; + case 'engagement': + label = 'Engagement'; + break; + case 'saved': + label = 'Saves'; + break; + case 'likes': + label = 'Likes'; + break; + case 'comments': + label = 'Comments'; + break; + case 'shares': + label = 'Shares'; + break; + } + + if (label) { + result.push({ + label, + percentageChange: 0, + data: [{ total: String(value), date: today }], + }); + } + } + + return result; + } catch (err) { + console.error('Error fetching Instagram post analytics:', err); + return []; + } + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts index fecd0470..5681d09e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.standalone.provider.ts @@ -193,4 +193,19 @@ export class InstagramStandaloneProvider 'graph.instagram.com' ); } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ) { + return instagramProvider.postAnalytics( + integrationId, + accessToken, + postId, + date, + 'graph.instagram.com' + ); + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index 6327a243..f60944d3 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -417,6 +417,132 @@ export class LinkedinPageProvider })); } + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + const endDate = dayjs().unix() * 1000; + const startDate = dayjs().subtract(date, 'days').unix() * 1000; + + // Fetch share statistics for the specific post + const shareStatsUrl = `https://api.linkedin.com/v2/organizationalEntityShareStatistics?q=organizationalEntity&organizationalEntity=${encodeURIComponent( + `urn:li:organization:${integrationId}` + )}&shares=List(${encodeURIComponent(postId)})&timeIntervals=(timeRange:(start:${startDate},end:${endDate}),timeGranularityType:DAY)`; + + const { elements: shareElements }: { elements: PostShareStatElement[] } = + await ( + await this.fetch(shareStatsUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'LinkedIn-Version': '202511', + 'X-Restli-Protocol-Version': '2.0.0', + }, + }) + ).json(); + + // Also fetch social actions (likes, comments, shares) for the specific post + let socialActions: SocialActionsResponse | null = null; + try { + const socialActionsUrl = `https://api.linkedin.com/v2/socialActions/${encodeURIComponent( + postId + )}`; + socialActions = await ( + await this.fetch(socialActionsUrl, { + headers: { + Authorization: `Bearer ${accessToken}`, + 'LinkedIn-Version': '202511', + 'X-Restli-Protocol-Version': '2.0.0', + }, + }) + ).json(); + } catch (e) { + // Social actions may not be available for all posts + } + + // Process share statistics into time series data + const analytics = (shareElements || []).reduce( + (all, current) => { + if (typeof current?.totalShareStatistics !== 'undefined') { + const dateStr = dayjs(current.timeRange.start).format('YYYY-MM-DD'); + + all['Impressions'].push({ + total: current.totalShareStatistics.impressionCount || 0, + date: dateStr, + }); + + all['Unique Impressions'].push({ + total: current.totalShareStatistics.uniqueImpressionsCount || 0, + date: dateStr, + }); + + all['Clicks'].push({ + total: current.totalShareStatistics.clickCount || 0, + date: dateStr, + }); + + all['Likes'].push({ + total: current.totalShareStatistics.likeCount || 0, + date: dateStr, + }); + + all['Comments'].push({ + total: current.totalShareStatistics.commentCount || 0, + date: dateStr, + }); + + all['Shares'].push({ + total: current.totalShareStatistics.shareCount || 0, + date: dateStr, + }); + + all['Engagement'].push({ + total: current.totalShareStatistics.engagement || 0, + date: dateStr, + }); + } + return all; + }, + { + Impressions: [] as { total: number; date: string }[], + 'Unique Impressions': [] as { total: number; date: string }[], + Clicks: [] as { total: number; date: string }[], + Likes: [] as { total: number; date: string }[], + Comments: [] as { total: number; date: string }[], + Shares: [] as { total: number; date: string }[], + Engagement: [] as { total: number; date: string }[], + } + ); + + // If no time series data but we have social actions, create a single data point + if ( + Object.values(analytics).every((arr) => arr.length === 0) && + socialActions + ) { + const today = dayjs().format('YYYY-MM-DD'); + analytics['Likes'].push({ + total: socialActions.likesSummary?.totalLikes || 0, + date: today, + }); + analytics['Comments'].push({ + total: socialActions.commentsSummary?.totalFirstLevelComments || 0, + date: today, + }); + } + + // Filter out empty analytics + const result = Object.entries(analytics) + .filter(([_, data]) => data.length > 0) + .map(([label, data]) => ({ + label, + data, + percentageChange: 0, + })); + + return result as any; + } + @Plug({ identifier: 'linkedin-page-autoRepostPost', title: 'Auto Repost Posts', @@ -764,3 +890,30 @@ export interface TimeRange { start: number; end: number; } + +// Post analytics interfaces +export interface PostShareStatElement { + organizationalEntity: string; + share: string; + totalShareStatistics: { + uniqueImpressionsCount: number; + shareCount: number; + engagement: number; + clickCount: number; + likeCount: number; + impressionCount: number; + commentCount: number; + }; + timeRange: TimeRange; +} + +export interface SocialActionsResponse { + likesSummary?: { + totalLikes: number; + likedByCurrentUser: boolean; + }; + commentsSummary?: { + totalFirstLevelComments: number; + commentsState: string; + }; +} diff --git a/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts b/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts index f9779010..8f4a0684 100644 --- a/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts @@ -358,4 +358,78 @@ export class PinterestProvider ] ); } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + const since = dayjs().subtract(date, 'day').format('YYYY-MM-DD'); + + try { + // Fetch pin analytics from Pinterest API + const response = await this.fetch( + `https://api.pinterest.com/v5/pins/${postId}/analytics?start_date=${since}&end_date=${today}&metric_types=IMPRESSION,PIN_CLICK,OUTBOUND_CLICK,SAVE`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + } + ); + + const data = await response.json(); + + if (!data || !data.all) { + return []; + } + + const result: AnalyticsData[] = []; + const metrics = data.all; + + if (metrics.lifetime_metrics) { + const lifetimeMetrics = metrics.lifetime_metrics; + + if (lifetimeMetrics.IMPRESSION !== undefined) { + result.push({ + label: 'Impressions', + percentageChange: 0, + data: [{ total: String(lifetimeMetrics.IMPRESSION), date: today }], + }); + } + + if (lifetimeMetrics.PIN_CLICK !== undefined) { + result.push({ + label: 'Pin Clicks', + percentageChange: 0, + data: [{ total: String(lifetimeMetrics.PIN_CLICK), date: today }], + }); + } + + if (lifetimeMetrics.OUTBOUND_CLICK !== undefined) { + result.push({ + label: 'Outbound Clicks', + percentageChange: 0, + data: [{ total: String(lifetimeMetrics.OUTBOUND_CLICK), date: today }], + }); + } + + if (lifetimeMetrics.SAVE !== undefined) { + result.push({ + label: 'Saves', + percentageChange: 0, + data: [{ total: String(lifetimeMetrics.SAVE), date: today }], + }); + } + } + + return result; + } catch (err) { + console.error('Error fetching Pinterest post analytics:', err); + return []; + } + } } diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 3ecfba88..377621fb 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -28,6 +28,12 @@ export interface IAuthenticator { accessToken: string, date: number ): Promise<AnalyticsData[]>; + postAnalytics?( + integrationId: string, + accessToken: string, + postId: string, + fromDate: number, + ): Promise<AnalyticsData[]>; changeNickname?( id: string, accessToken: string, @@ -46,6 +52,7 @@ export interface AnalyticsData { percentageChange: number; } + export type GenerateAuthUrlResponse = { url: string; codeVerifier: string; diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index 0087c34a..eca2307d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -523,6 +523,68 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { return false; } + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + + try { + // Fetch thread insights from Threads API + const { data } = await ( + await this.fetch( + `https://graph.threads.net/v1.0/${postId}/insights?metric=views,likes,replies,reposts,quotes&access_token=${accessToken}` + ) + ).json(); + + if (!data || data.length === 0) { + return []; + } + + const result: AnalyticsData[] = []; + + for (const metric of data) { + const value = metric.values?.[0]?.value ?? metric.total_value?.value; + if (value === undefined) continue; + + let label = ''; + + switch (metric.name) { + case 'views': + label = 'Views'; + break; + case 'likes': + label = 'Likes'; + break; + case 'replies': + label = 'Replies'; + break; + case 'reposts': + label = 'Reposts'; + break; + case 'quotes': + label = 'Quotes'; + break; + } + + if (label) { + result.push({ + label, + percentageChange: 0, + data: [{ total: String(value), date: today }], + }); + } + } + + return result; + } catch (err) { + console.error('Error fetching Threads post analytics:', err); + return []; + } + } + // override async mention( // token: string, // data: { query: string }, diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index 95e7559c..fcfaa45b 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -590,6 +590,96 @@ export class XProvider extends SocialAbstract implements SocialProvider { return []; } + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + if (process.env.DISABLE_X_ANALYTICS) { + return []; + } + + const today = dayjs().format('YYYY-MM-DD'); + + const [accessTokenSplit, accessSecretSplit] = accessToken.split(':'); + const client = new TwitterApi({ + appKey: process.env.X_API_KEY!, + appSecret: process.env.X_API_SECRET!, + accessToken: accessTokenSplit, + accessSecret: accessSecretSplit, + }); + + try { + // Fetch the specific tweet with public metrics + const tweet = await client.v2.singleTweet(postId, { + 'tweet.fields': ['public_metrics', 'created_at'], + }); + + if (!tweet?.data?.public_metrics) { + return []; + } + + const metrics = tweet.data.public_metrics; + + const result: AnalyticsData[] = []; + + if (metrics.impression_count !== undefined) { + result.push({ + label: 'Impressions', + percentageChange: 0, + data: [{ total: String(metrics.impression_count), date: today }], + }); + } + + if (metrics.like_count !== undefined) { + result.push({ + label: 'Likes', + percentageChange: 0, + data: [{ total: String(metrics.like_count), date: today }], + }); + } + + if (metrics.retweet_count !== undefined) { + result.push({ + label: 'Retweets', + percentageChange: 0, + data: [{ total: String(metrics.retweet_count), date: today }], + }); + } + + if (metrics.reply_count !== undefined) { + result.push({ + label: 'Replies', + percentageChange: 0, + data: [{ total: String(metrics.reply_count), date: today }], + }); + } + + if (metrics.quote_count !== undefined) { + result.push({ + label: 'Quotes', + percentageChange: 0, + data: [{ total: String(metrics.quote_count), date: today }], + }); + } + + if (metrics.bookmark_count !== undefined) { + result.push({ + label: 'Bookmarks', + percentageChange: 0, + data: [{ total: String(metrics.bookmark_count), date: today }], + }); + } + + return result; + } catch (err) { + console.log('Error fetching X post analytics:', err); + } + + return []; + } + override async mention(token: string, d: { query: string }) { const [accessTokenSplit, accessSecretSplit] = token.split(':'); const client = new TwitterApi({ diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts index 66760cf1..0254c429 100644 --- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts @@ -449,4 +449,71 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider { return []; } } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + date: number + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + + try { + const { client, youtube } = clientAndYoutube(); + client.setCredentials({ access_token: accessToken }); + const youtubeClient = youtube(client); + + // Fetch video statistics + const response = await youtubeClient.videos.list({ + part: ['statistics', 'snippet'], + id: [postId], + }); + + const video = response.data.items?.[0]; + + if (!video || !video.statistics) { + return []; + } + + const stats = video.statistics; + const result: AnalyticsData[] = []; + + if (stats.viewCount !== undefined) { + result.push({ + label: 'Views', + percentageChange: 0, + data: [{ total: String(stats.viewCount), date: today }], + }); + } + + if (stats.likeCount !== undefined) { + result.push({ + label: 'Likes', + percentageChange: 0, + data: [{ total: String(stats.likeCount), date: today }], + }); + } + + if (stats.commentCount !== undefined) { + result.push({ + label: 'Comments', + percentageChange: 0, + data: [{ total: String(stats.commentCount), date: today }], + }); + } + + if (stats.favoriteCount !== undefined) { + result.push({ + label: 'Favorites', + percentageChange: 0, + data: [{ total: String(stats.favoriteCount), date: today }], + }); + } + + return result; + } catch (err) { + console.error('Error fetching YouTube post analytics:', err); + return []; + } + } } From 2346223a04f08144bbde059e34007eaf630bfb04 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 21 Jan 2026 15:54:37 +0700 Subject: [PATCH 163/340] feat: fix facebook impressions --- .../integrations/social/facebook.provider.ts | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index 56ebfd35..6a062af6 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -475,7 +475,7 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { // Fetch post insights from Facebook Graph API const { data } = await ( await this.fetch( - `https://graph.facebook.com/v20.0/${postId}/insights?metric=post_impressions,post_impressions_unique,post_engaged_users,post_clicks,post_reactions_by_type_total&access_token=${accessToken}` + `https://graph.facebook.com/v20.0/${postId}/insights?metric=post_impressions_unique,post_reactions_by_type_total,post_clicks,post_clicks_by_type&access_token=${accessToken}` ) ).json(); @@ -493,22 +493,25 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { let total = ''; switch (metric.name) { - case 'post_impressions': - label = 'Impressions'; - total = String(value); - break; case 'post_impressions_unique': - label = 'Reach'; - total = String(value); - break; - case 'post_engaged_users': - label = 'Engaged Users'; + label = 'Impressions'; total = String(value); break; case 'post_clicks': label = 'Clicks'; total = String(value); break; + case 'post_clicks_by_type': + // This returns an object with click types + if (typeof value === 'object') { + const totalClicks = Object.values(value as Record<string, number>).reduce( + (sum: number, v: number) => sum + v, + 0 + ); + label = 'Clicks by Type'; + total = String(totalClicks); + } + break; case 'post_reactions_by_type_total': // This returns an object with reaction types if (typeof value === 'object') { From 98a6fb101313214ef1cc5788a673f837e29b5d7f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 21 Jan 2026 16:01:02 +0700 Subject: [PATCH 164/340] feat: analytics changes --- .../src/database/prisma/posts/posts.service.ts | 12 ++++++------ .../src/integrations/social/pinterest.provider.ts | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index aa7abd79..c2b5a780 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -109,12 +109,12 @@ export class PostsService { } } - const getIntegrationData = await ioRedis.get( - `integration:${orgId}:${post.id}:${date}` - ); - if (getIntegrationData) { - return JSON.parse(getIntegrationData); - } + // const getIntegrationData = await ioRedis.get( + // `integration:${orgId}:${post.id}:${date}` + // ); + // if (getIntegrationData) { + // return JSON.parse(getIntegrationData); + // } try { const loadAnalytics = await integrationProvider.postAnalytics( diff --git a/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts b/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts index 8f4a0684..377b93db 100644 --- a/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/pinterest.provider.ts @@ -366,7 +366,8 @@ export class PinterestProvider date: number ): Promise<AnalyticsData[]> { const today = dayjs().format('YYYY-MM-DD'); - const since = dayjs().subtract(date, 'day').format('YYYY-MM-DD'); + // Use a very long date range (2 years) to capture lifetime metrics for older posts + const since = dayjs().subtract(2, 'year').format('YYYY-MM-DD'); try { // Fetch pin analytics from Pinterest API From 9e54e315fdf9a6d38bddadf8f67f3860b924877c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 21 Jan 2026 17:56:38 +0700 Subject: [PATCH 165/340] fix: settings on the global edit --- .../components/new-launch/manage.modal.tsx | 31 +++++++++---- .../providers/high.order.provider.tsx | 46 ++++++++++++++++--- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index ca4e5a3c..8d1f9708 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -105,7 +105,19 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { const currentIntegrationText = useMemo(() => { if (current === 'global') { - return ''; + return ( + <div className="flex items-center gap-[10px]"> + <div className="relative"> + <SettingsIcon + size={15} + className="text-white" + /> + </div> + <div> + Settings + </div> + </div> + ); } const currentIntegration = integrations.find((p) => p.id === current)!; @@ -425,8 +437,8 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { <div id="social-empty" className={clsx( - 'pb-[16px]', - current !== 'global' && 'hidden' + 'pb-[16px]' + // current !== 'global' && 'hidden' )} /> </div> @@ -436,11 +448,11 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { id="wrapper-settings" className={clsx( 'pb-[20px] px-[20px] select-none', - current === 'global' && 'hidden', - showSettings && 'flex-1 flex pt-[20px]' + showSettings && 'flex-1 flex pt-[20px]', + current === 'global' && 'hidden' )} > - <div className="bg-newSettings flex-1 flex flex-col rounded-[12px] gap-[12px] overflow-hidden"> + <div className="flex-1 flex flex-col rounded-[12px] gap-[12px] overflow-hidden bg-newSettings"> <div onClick={() => setShowSettings(!showSettings)} className={clsx( @@ -465,9 +477,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { )} > <div - id="social-settings" - className="px-[12px] pb-[12px] absolute left-0 top-0 w-full h-full overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor" - /> + className="absolute left-0 top-0 w-full h-full flex flex-col overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor" + > + <div id="social-settings" className="flex flex-col gap-[20px] bg-newBgColor" /> + </div> </div> <style> {`#social-settings [data-id="${current}"] {display: block !important;}`} diff --git a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx index 5e9803d5..a8a6e0f3 100644 --- a/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/high.order.provider.tsx @@ -21,6 +21,7 @@ import useSWR from 'swr'; import { InternalChannels } from '@gitroom/frontend/components/launches/internal.channels'; import { createPortal } from 'react-dom'; import clsx from 'clsx'; +import Image from 'next/image'; class Empty { @IsOptional() @@ -91,7 +92,7 @@ export const withProvider = function <T extends object>(params: { dummy, setChars, setComments, - setHide + setHide, } = useLaunchStore( useShallow((state) => ({ date: state.date, @@ -141,7 +142,9 @@ export const withProvider = function <T extends object>(params: { } if (current) { - setComments(typeof params.comments === 'undefined' ? true : params.comments); + setComments( + typeof params.comments === 'undefined' ? true : params.comments + ); setEditor(selectedIntegration?.integration.editor); setPostComment(postComment); setTotalChars( @@ -258,7 +261,12 @@ export const withProvider = function <T extends object>(params: { }} > <FormProvider {...form}> - <div className={clsx('border border-borderPreview rounded-[12px] shadow-previewShadow', !current && 'hidden')}> + <div + className={clsx( + 'border border-borderPreview rounded-[12px] shadow-previewShadow', + !current && 'hidden' + )} + > {current && (tab === 0 || (!SettingsComponent && !data?.internalPlugs?.length)) && @@ -303,19 +311,45 @@ export const withProvider = function <T extends object>(params: { ))} {(SettingsComponent || !!data?.internalPlugs?.length) && createPortal( - <div data-id={props.id} className="hidden"> + <div data-id={props.id} className={isGlobal ? 'bg-newSettings pb-[12px] px-[12px]' : 'hidden bg-newSettings px-[12px] pb-[12px]'}> + {isGlobal && ( + <style>{`#wrapper-settings {display: flex !important} #social-empty {display: block !important;}`}</style> + )} + {isGlobal && ( + <div className="flex py-[20px] items-center gap-[15px]"> + <div className="relative"> + <Image + alt={selectedIntegration?.integration.name!} + width={42} + height={42} + className="min-w-[42px] min-h-[42px] w-[42px] h-[42px] rounded-full" + src={selectedIntegration?.integration.picture} + /> + <Image + alt={selectedIntegration?.integration.identifier} + width={16} + height={16} + className="rounded-[16px] min-w-[16px] min-h-[16px] w-[16px] h-[16px] absolute bottom-0 end-0" + src={`/icons/platforms/${selectedIntegration?.integration.identifier}.png`} + /> + </div> + <div className="text-[20px]">{selectedIntegration?.integration.name}</div> + </div> + )} <SettingsComponent /> {!!data?.internalPlugs?.length && !dummy && ( <InternalChannels plugs={data?.internalPlugs} /> )} </div>, - document.querySelector('#social-settings') || document.createElement('div') + document.querySelector('#social-settings') || + document.createElement('div') )} {current && !SettingsComponent && createPortal( <style>{`#wrapper-settings {display: none !important;} #social-empty {display: block !important;}`}</style>, - document.querySelector('#social-settings') || document.createElement('div') + document.querySelector('#social-settings') || + document.createElement('div') )} </div> </FormProvider> From baea8b28ee9a19c141ec4b5547d8038888ac99f4 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 23 Jan 2026 11:38:18 +0700 Subject: [PATCH 166/340] feat: nostr instructions --- .../nestjs-libraries/src/integrations/social/nostr.provider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts index 22c8a987..04d90475 100644 --- a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts @@ -27,12 +27,13 @@ const list = [ const pool = new SimplePool(); export class NostrProvider extends SocialAbstract implements SocialProvider { - override maxConcurrentJob = 5; // Nostr relays typically have generous limits + override maxConcurrentJob = 5; identifier = 'nostr'; name = 'Nostr'; isBetweenSteps = false; scopes = [] as string[]; editor = 'normal' as const; + toolTip = 'Make sure you private a HEX key of your Nostr private key, you can get it from websites like iris.to' maxLength() { return 100000; From 5cb69667b39e665c415434c7c8dc5a69bbae3cd9 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 23 Jan 2026 13:01:09 +0700 Subject: [PATCH 167/340] feat: drag and drop files --- .../src/components/layout/drop.files.tsx | 11 +- .../src/components/media/media.component.tsx | 89 +++++++--- .../src/components/media/new.uploader.tsx | 162 ++++++------------ .../src/components/new-launch/editor.tsx | 56 +++++- 4 files changed, 184 insertions(+), 134 deletions(-) diff --git a/apps/frontend/src/components/layout/drop.files.tsx b/apps/frontend/src/components/layout/drop.files.tsx index 746c1585..e84b9c7a 100644 --- a/apps/frontend/src/components/layout/drop.files.tsx +++ b/apps/frontend/src/components/layout/drop.files.tsx @@ -2,15 +2,24 @@ import { useDropzone } from 'react-dropzone'; import { FC, ReactNode } from 'react'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import clsx from 'clsx'; +import { useToaster } from '@gitroom/react/toaster/toaster'; export const DropFiles: FC<{ children: ReactNode; className?: string; onDrop: (files: File[]) => void; + disabled?: boolean; }> = (props) => { const t = useT(); + const toaster = useToaster(); const { getRootProps, isDragActive } = useDropzone({ - onDrop: props.onDrop, + onDrop: (files) => { + if (props.disabled) { + toaster.show('Upload current in progress, please wait and then try again.', 'warning'); + return ; + } + props.onDrop(files); + }, }); return ( <div {...getRootProps()} className={clsx("relative", props.className)}> diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 9a289958..97a10637 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -18,6 +18,7 @@ import { Media } from '@prisma/client'; import { useMediaDirectory } from '@gitroom/react/helpers/use.media.directory'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import EventEmitter from 'events'; +import { useToaster } from '@gitroom/react/toaster/toaster'; import clsx from 'clsx'; import { VideoFrame } from '@gitroom/react/helpers/video.frame'; import { useUppyUploader } from '@gitroom/frontend/components/media/new.uploader'; @@ -48,6 +49,7 @@ import { } from '@gitroom/frontend/components/ui/icons'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; +import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; const Polonto = dynamic( () => import('@gitroom/frontend/components/launches/polonto') ); @@ -194,6 +196,7 @@ export const showMediaBox = ( showModalEmitter.emit('show-modal', callback); }; const CHUNK_SIZE = 1024 * 1024; +const MAX_UPLOAD_SIZE = 1024 * 1024 * 1024; // 1 GB export const MediaBox: FC<{ setMedia: (params: { id: string; path: string }[]) => void; standalone?: boolean; @@ -203,6 +206,7 @@ export const MediaBox: FC<{ const [page, setPage] = useState(0); const fetch = useFetch(); const modals = useModals(); + const toaster = useToaster(); const loadMedia = useCallback(async () => { return (await fetch(`/media?page=${page + 1}`)).json(); }, [page]); @@ -211,6 +215,7 @@ export const MediaBox: FC<{ const t = useT(); const uploaderRef = useRef<any>(null); const mediaDirectory = useMediaDirectory(); + const [loading, setLoading] = useState(false); const uppy = useUppyUploader({ allowedFileTypes: @@ -220,7 +225,6 @@ export const MediaBox: FC<{ ? 'video/mp4' : 'image/*,video/mp4', onUploadSuccess: async (arr) => { - uppy.clear(); await mutate(); if (standalone) { return; @@ -229,6 +233,8 @@ export const MediaBox: FC<{ return [...prevSelected, ...arr]; }); }, + onStart: () => setLoading(true), + onEnd: () => setLoading(false), }); const addRemoveSelected = useCallback( @@ -255,11 +261,29 @@ export const MediaBox: FC<{ modals.closeCurrent(); }, [selected]); - const addToUpload = useCallback(async (e: ChangeEvent<HTMLInputElement>) => { - const files = Array.from(e.target.files).slice(0, 5); - // @ts-ignore - uppy.addFiles(files); - }, []); + const addToUpload = useCallback( + async (e: ChangeEvent<HTMLInputElement>) => { + const files = Array.from(e.target.files || []); + const totalSize = files.reduce((acc, file) => acc + file.size, 0); + + if (totalSize > MAX_UPLOAD_SIZE) { + toaster.show( + t( + 'upload_size_limit_exceeded', + 'Upload size limit exceeded. Maximum 1 GB per upload session.' + ), + 'warning' + ); + return; + } + + setLoading(true); + + // @ts-ignore + uppy.addFiles(files); + }, + [toaster, t] + ); const dragAndDrop = useCallback( async (event: ClipboardEvent<HTMLDivElement> | File[]) => { @@ -272,7 +296,7 @@ export const MediaBox: FC<{ return; } - const files = []; + const files: File[] = []; // @ts-ignore for (const item of clipboardItems) { if (item.kind === 'file') { @@ -283,11 +307,26 @@ export const MediaBox: FC<{ } } - for (const file of files.slice(0, 5)) { + const totalSize = files.reduce((acc, file) => acc + file.size, 0); + + if (totalSize > MAX_UPLOAD_SIZE) { + toaster.show( + t( + 'upload_size_limit_exceeded', + 'Upload size limit exceeded. Maximum 1 GB per upload session.' + ), + 'warning' + ); + return; + } + + setLoading(true); + + for (const file of files) { uppy.addFile(file); } }, - [] + [toaster, t] ); const maximize = useCallback( @@ -299,7 +338,10 @@ export const MediaBox: FC<{ children: ( <div className="w-full h-full p-[50px]"> {media.path.indexOf('mp4') > -1 ? ( - <VideoFrame autoplay={true} url={mediaDirectory.set(media.path)} /> + <VideoFrame + autoplay={true} + url={mediaDirectory.set(media.path)} + /> ) : ( <img width="100%" @@ -340,17 +382,24 @@ export const MediaBox: FC<{ const btn = useMemo(() => { return ( <button + disabled={loading} onClick={() => uploaderRef?.current?.click()} - className="cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]" + className="relative cursor-pointer bg-btnSimple changeColor flex gap-[8px] h-[44px] px-[18px] justify-center items-center rounded-[8px]" > - <PlusIcon size={14} /> - <div>{t('upload', 'Upload')}</div> + {loading ? ( + <div className="absolute left-[50%] top-[50%] -translate-y-[50%] -translate-x-[50%]"> + <div className="animate-spin h-[20px] w-[20px] border-4 border-white border-t-transparent rounded-full" /> + </div> + ) : ( + <PlusIcon size={14} /> + )} + <div className={loading && 'invisible'}>{t('upload', 'Upload')}</div> </button> ); - }, [t]); + }, [t, loading]); return ( - <DropFiles className="flex flex-col flex-1" onDrop={dragAndDrop}> + <DropFiles disabled={loading} className="flex flex-col flex-1" onDrop={dragAndDrop}> <div className="flex flex-col flex-1"> <div className={clsx( @@ -361,8 +410,8 @@ export const MediaBox: FC<{ {!isLoading && !!data?.results?.length && ( <div className="flex-1 text-[14px] font-[600] whitespace-pre-line"> {t( - 'select_or_upload_pictures_max_5', - 'Select or upload pictures (maximum 5 at a time).' + 'select_or_upload_pictures_max_1gb', + 'Select or upload pictures (maximum 1 GB per upload).' )} {'\n'} {t( @@ -423,8 +472,8 @@ export const MediaBox: FC<{ </div> <div className="whitespace-pre-line text-newTextColor/[0.6] text-center"> {t( - 'select_or_upload_pictures_max_5', - 'Select or upload pictures (maximum 5 at a time).' + 'select_or_upload_pictures_max_1gb', + 'Select or upload pictures (maximum 1 GB per upload).' )}{' '} {'\n'} {t( @@ -476,7 +525,7 @@ export const MediaBox: FC<{ onClick={addRemoveSelected(media)} > {!!selected.find((p: any) => p.id === media.id) ? ( - <div className="text-white flex justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]"> + <div className="text-white flex z-[101] justify-center items-center text-[14px] font-[500] w-[24px] h-[24px] rounded-full bg-[#612BD3] absolute -bottom-[10px] -end-[10px]"> {selected.findIndex((z: any) => z.id === media.id) + 1} </div> ) : ( diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index 540b9e10..fe7172e1 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -12,7 +12,7 @@ import Compressor from '@uppy/compressor'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useToaster } from '@gitroom/react/toaster/toaster'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; -import { uniq } from 'lodash'; +import { uniqBy } from 'lodash'; export class CompressionWrapper<M = any, B = any> extends Compressor<any, any> { override async prepareUpload(fileIDs: string[]) { @@ -35,50 +35,11 @@ export class CompressionWrapper<M = any, B = any> extends Compressor<any, any> { } } -export function MultipartFileUploader({ - onUploadSuccess, - allowedFileTypes, - uppRef, -}: { - // @ts-ignore - onUploadSuccess: (result: UploadResult) => void; - allowedFileTypes: string; - uppRef?: any; -}) { - const [loaded, setLoaded] = useState(false); - const [reload, setReload] = useState(false); - const onUploadSuccessExtended = useCallback( - (result: UploadResult<any, any>) => { - setReload(true); - onUploadSuccess(result); - }, - [onUploadSuccess] - ); - useEffect(() => { - if (reload) { - setTimeout(() => { - setReload(false); - }, 1); - } - }, [reload]); - useEffect(() => { - setLoaded(true); - }, []); - if (!loaded || reload) { - return null; - } - return ( - <MultipartFileUploaderAfter - uppRef={uppRef || {}} - onUploadSuccess={onUploadSuccessExtended} - allowedFileTypes={allowedFileTypes} - /> - ); -} - export function useUppyUploader(props: { // @ts-ignore onUploadSuccess: (result: UploadResult) => void; + onStart: () => void; + onEnd: () => void; allowedFileTypes: string; }) { const setLocked = useLaunchStore((state) => state.setLocked); @@ -88,6 +49,9 @@ export function useUppyUploader(props: { const { onUploadSuccess, allowedFileTypes } = props; const fetch = useFetch(); return useMemo(() => { + // Track file order to maintain original sequence after upload + let fileOrderIndex = 0; + const uppy2 = new Uppy({ autoProceed: true, restrictions: { @@ -221,49 +185,79 @@ export function useUppyUploader(props: { setLocked(true); uppy2.setFileMeta(file.id, { useCloudflare: storageProvider === 'cloudflare' ? 'true' : 'false', // Example of adding a custom field + addedOrder: fileOrderIndex++, // Track original order for sorting after upload // Add more fields as needed }); }); uppy2.on('error', (result) => { uppy2.clear(); setLocked(false); + props.onEnd(); + fileOrderIndex = 0; + }); + uppy2.on('upload-start', () => { + props.onStart(); }); uppy2.on('complete', async (result) => { + for (const file of [...result.successful]) { + uppy2.removeFile(file.id); + } + + props.onEnd(); + // Sort results by original add order to maintain file sequence + const sortedSuccessful = [...result.successful].sort((a, b) => { + const orderA = +((a.meta as any)?.addedOrder ?? 0); + const orderB = +((b.meta as any)?.addedOrder ?? 0); + return orderA - orderB; + }); + if (storageProvider === 'local') { setLocked(false); - onUploadSuccess(result.successful.map((p) => p.response.body)); + fileOrderIndex = 0; + onUploadSuccess(sortedSuccessful.map((p) => p.response.body)); return; } if (transloadit.length > 0) { // @ts-ignore const allRes = result.transloadit[0].results; - const toSave = uniq<string>( - (allRes[Object.keys(allRes)[0]] || []).flatMap((item: any) => - item.url.split('/').pop() - ) + const toSave = uniqBy<{ name: string; order: number }>( + (allRes[Object.keys(allRes)[0]] || []).flatMap((item: any) => ({ + name: item.url.split('/').pop(), + order: +item.user_meta.addedOrder, + })), + (item) => item.name ); - const loadAllMedia = await Promise.all( - toSave.map(async (name) => { - return ( - await fetch('/media/save-media', { - method: 'POST', - body: JSON.stringify({ - name, - }), - }) - ).json(); + const loadAllMedia = ( + await Promise.all( + toSave.map(async ({ name, order }) => ({ + file: await ( + await fetch('/media/save-media', { + method: 'POST', + body: JSON.stringify({ + name, + }), + }) + ).json(), + order, + })) + ) + ) + .sort((a, b) => { + return a.order - b.order; }) - ); + .map((p) => p.file); setLocked(false); + fileOrderIndex = 0; onUploadSuccess(loadAllMedia); return; } setLocked(false); - onUploadSuccess(result.successful.map((p) => p.response.body.saved)); + fileOrderIndex = 0; + onUploadSuccess(sortedSuccessful.map((p) => p.response.body.saved)); }); uppy2.on('upload-success', (file, response) => { // @ts-ignore @@ -279,53 +273,3 @@ export function useUppyUploader(props: { return uppy2; }, []); } -export function MultipartFileUploaderAfter({ - onUploadSuccess, - allowedFileTypes, - uppRef, -}: { - // @ts-ignore - onUploadSuccess: (result: UploadResult) => void; - allowedFileTypes: string; - uppRef: any; -}) { - const t = useT(); - const uppy = useUppyUploader({ - onUploadSuccess, - allowedFileTypes, - }); - const uppyInstance = useMemo(() => { - uppRef.current = uppy; - return uppy; - }, []); - return ( - <> - {/* <Dashboard uppy={uppy} /> */} - <div className="pointer-events-none bigWrap"> - <Dashboard - height={23} - width={200} - className="" - uppy={uppyInstance} - id={`media-uploader`} - showProgressDetails={true} - hideUploadButton={true} - hideRetryButton={true} - hidePauseResumeButton={true} - hideCancelButton={true} - hideProgressAfterFinish={true} - /> - </div> - <FileInput - uppy={uppyInstance} - locale={{ - strings: { - chooseFiles: t('upload', 'Upload'), - }, - // @ts-ignore - pluralize: (n: any) => n, - }} - /> - </> - ); -} diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 0c56ed0e..87ff52d2 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -56,6 +56,7 @@ import { suggestion } from '@gitroom/frontend/components/new-launch/mention.comp import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { AComponent } from '@gitroom/frontend/components/new-launch/a.component'; import { Placeholder } from '@tiptap/extensions'; +import { useToaster } from '@gitroom/react/toaster/toaster'; import { InformationComponent } from '@gitroom/frontend/components/launches/information.component'; import { LockIcon, @@ -67,6 +68,8 @@ import { } from '@gitroom/frontend/components/ui/icons'; import { DelayComponent } from '@gitroom/frontend/components/new-launch/delay.component'; +const MAX_UPLOAD_SIZE = 1024 * 1024 * 1024; // 1 GB + const InterceptBoldShortcut = Extension.create({ name: 'preventBoldWithUnderline', @@ -558,7 +561,9 @@ export const Editor: FC<{ const [id] = useState(makeId(10)); const [emojiPickerOpen, setEmojiPickerOpen] = useState(false); const t = useT(); + const toaster = useToaster(); const editorRef = useRef<undefined | { editor: any }>(); + const [loading, setLoading] = useState(false); const uppy = useUppyUploader({ onUploadSuccess: (result: any) => { @@ -566,15 +571,32 @@ export const Editor: FC<{ uppy.clear(); }, allowedFileTypes: 'image/*,video/mp4', + onStart: () => setLoading(true), + onEnd: () => setLoading(false), }); const onDrop = useCallback( (acceptedFiles: File[]) => { + const totalSize = acceptedFiles.reduce((acc, file) => acc + file.size, 0); + + if (totalSize > MAX_UPLOAD_SIZE) { + toaster.show( + t( + 'upload_size_limit_exceeded', + 'Upload size limit exceeded. Maximum 1 GB per upload session.' + ), + 'warning' + ); + return; + } + + setLoading(true); + for (const file of acceptedFiles) { uppy.addFile(file); } }, - [uppy] + [uppy, toaster, t] ); const paste = useCallback( @@ -588,21 +610,47 @@ export const Editor: FC<{ return; } + const files: File[] = []; // @ts-ignore for (const item of clipboardItems) { if (item.kind === 'file') { const file = item.getAsFile(); if (file) { - uppy.addFile(file); + files.push(file); } } } + + const totalSize = files.reduce((acc, file) => acc + file.size, 0); + + if (totalSize > MAX_UPLOAD_SIZE) { + toaster.show( + t( + 'upload_size_limit_exceeded', + 'Upload size limit exceeded. Maximum 1 GB per upload session.' + ), + 'warning' + ); + return; + } + + setLoading(true); + + for (const file of files) { + uppy.addFile(file); + } }, - [uppy, num, comments] + [uppy, num, comments, toaster, t] ); const { getRootProps, isDragActive } = useDropzone({ - onDrop, + onDrop: (files) => { + if (loading) { + toaster.show('Upload current in progress, please wait and then try again.', 'warning'); + return ; + } + onDrop(files); + }, noDrag: num > 0 && comments === 'no-media', }); From ba7fc7a8e8ed66db50bb057552d35dc05c4d2d7e Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 23 Jan 2026 16:20:28 +0700 Subject: [PATCH 168/340] feat: onboarding --- .../src/api/routes/integrations.controller.ts | 17 +- .../launches/add.provider.component.tsx | 49 ++-- .../launches/continue.integration.tsx | 7 +- .../launches/launches.component.tsx | 2 + .../onboarding/onboarding.modal.tsx | 245 ++++++++++++++++++ .../src/components/onboarding/onboarding.tsx | 57 ++-- 6 files changed, 312 insertions(+), 65 deletions(-) create mode 100644 apps/frontend/src/components/onboarding/onboarding.modal.tsx diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 7f3c31b1..40cf9c58 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -195,7 +195,8 @@ export class IntegrationsController { async getIntegrationUrl( @Param('integration') integration: string, @Query('refresh') refresh: string, - @Query('externalUrl') externalUrl: string + @Query('externalUrl') externalUrl: string, + @Query('onboarding') onboarding: string ) { if ( !this._integrationManager @@ -227,6 +228,10 @@ export class IntegrationsController { await ioRedis.set(`refresh:${state}`, refresh, 'EX', 300); } + if (onboarding === 'true') { + await ioRedis.set(`onboarding:${state}`, 'true', 'EX', 300); + } + await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 300); await ioRedis.set( `external:${state}`, @@ -409,6 +414,11 @@ export class IntegrationsController { await ioRedis.del(`refresh:${body.state}`); } + const onboarding = await ioRedis.get(`onboarding:${body.state}`); + if (onboarding) { + await ioRedis.del(`onboarding:${body.state}`); + } + const { error, accessToken, @@ -520,7 +530,10 @@ export class IntegrationsController { console.log(err); }); - return createUpdate; + return { + ...createUpdate, + onboarding: onboarding === 'true', + }; } @Post('/disable') diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 264658f3..589063c6 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -1,14 +1,14 @@ 'use client'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import React, { FC, useCallback, useEffect, useMemo } from 'react'; +import React, { FC, useCallback, useMemo } from 'react'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { Input } from '@gitroom/react/form/input'; import { FieldValues, FormProvider, useForm } from 'react-hook-form'; import { Button } from '@gitroom/react/form/button'; import { classValidatorResolver } from '@hookform/resolvers/class-validator'; import { ApiKeyDto } from '@gitroom/nestjs-libraries/dtos/integrations/api.key.dto'; -import { useRouter, useSearchParams } from 'next/navigation'; +import { useRouter } from 'next/navigation'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useToaster } from '@gitroom/react/toaster/toaster'; @@ -35,16 +35,9 @@ export const AddProviderButton: FC<{ update?: () => void; }> = (props) => { const { update } = props; - const query = useSearchParams(); const add = useAddProvider(update); const t = useT(); - useEffect(() => { - if (query.get('onboarding')) { - add(); - } - }, []); - return ( <button className="text-btnText bg-btnSimple h-[44px] pt-[12px] pb-[14px] ps-[16px] pe-[20px] justify-center items-center flex rounded-[8px] gap-[8px]" @@ -222,8 +215,9 @@ export const CustomVariables: FC<{ close?: () => void; identifier: string; gotoUrl(url: string): void; + onboarding?: boolean; }> = (props) => { - const { close, gotoUrl, identifier, variables } = props; + const { close, gotoUrl, identifier, variables, onboarding } = props; const modals = useModals(); const schema = useMemo(() => { return object({ @@ -263,10 +257,10 @@ export const CustomVariables: FC<{ gotoUrl( `/integrations/social/${identifier}?state=nostate&code=${Buffer.from( JSON.stringify(data) - ).toString('base64')}` + ).toString('base64')}${onboarding ? '&onboarding=true' : ''}` ); }, - [variables] + [variables, onboarding] ); const t = useT(); @@ -314,8 +308,9 @@ export const AddProviderComponent: FC<{ name: string; }>; update?: () => void; + onboarding?: boolean; }> = (props) => { - const { update, social, article } = props; + const { update, social, article, onboarding } = props; const { isGeneral } = useVariables(); const toaster = useToaster(); const router = useRouter(); @@ -335,12 +330,13 @@ export const AddProviderComponent: FC<{ }> ) => async () => { + const onboardingParam = onboarding ? 'onboarding=true' : ''; const openWeb3 = async () => { const { component: Web3Providers } = web3List.find( (item) => item.identifier === identifier )!; const { url } = await ( - await fetch(`/integrations/social/${identifier}`) + await fetch(`/integrations/social/${identifier}${onboarding ? '?onboarding=true' : ''}`) ).json(); modal.openModal({ title: t('web3_provider', 'Web3 provider'), @@ -351,7 +347,7 @@ export const AddProviderComponent: FC<{ children: ( <Web3Providers onComplete={(code, newState) => { - window.location.href = `/integrations/social/${identifier}?code=${code}&state=${newState}`; + window.location.href = `/integrations/social/${identifier}?code=${code}&state=${newState}${onboarding ? '&onboarding=true' : ''}`; }} nonce={url} /> @@ -360,11 +356,13 @@ export const AddProviderComponent: FC<{ return; }; const gotoIntegration = async (externalUrl?: string) => { + const params = [ + externalUrl ? `externalUrl=${externalUrl}` : '', + onboardingParam, + ].filter(Boolean).join('&'); const { url, err } = await ( await fetch( - `/integrations/social/${identifier}${ - externalUrl ? `?externalUrl=${externalUrl}` : `` - }` + `/integrations/social/${identifier}${params ? `?${params}` : ''}` ) ).json(); if (err) { @@ -378,10 +376,9 @@ export const AddProviderComponent: FC<{ return; } if (isExternal) { - modal.closeAll(); modal.openModal({ title: 'URL', - withCloseButton: false, + withCloseButton: true, classNames: { modal: 'bg-transparent text-textColor', }, @@ -390,10 +387,9 @@ export const AddProviderComponent: FC<{ return; } if (customFields) { - modal.closeAll(); modal.openModal({ title: t('add_provider_title', 'Add Provider'), - withCloseButton: false, + withCloseButton: true, classNames: { modal: 'bg-transparent text-textColor', }, @@ -402,6 +398,7 @@ export const AddProviderComponent: FC<{ identifier={identifier} gotoUrl={(url: string) => router.push(url)} variables={customFields} + onboarding={onboarding} /> ), }); @@ -409,14 +406,14 @@ export const AddProviderComponent: FC<{ } await gotoIntegration(); }, - [] + [onboarding] ); const showApiButton = useCallback( (identifier: string, name: string) => async () => { modal.openModal({ title: '', - withCloseButton: false, + withCloseButton: true, classNames: { modal: 'bg-transparent text-textColor', }, @@ -431,9 +428,9 @@ export const AddProviderComponent: FC<{ const t = useT(); return ( - <div className="w-full flex flex-col gap-[20px] rounded-[4px] relative"> + <div className="w-full flex flex-col gap-[20px] rounded-[4px] relative]"> <div className="flex flex-col"> - <div className="grid grid-cols-5 gap-[10px] justify-items-center justify-center"> + <div className={clsx("grid grid-cols-5 gap-[10px] justify-items-center justify-center", onboarding ? 'grid-cols-8' : 'grid-cols-5')}> {social.map((item) => ( <div key={item.identifier} diff --git a/apps/frontend/src/components/launches/continue.integration.tsx b/apps/frontend/src/components/launches/continue.integration.tsx index 0b3127d4..d3456c07 100644 --- a/apps/frontend/src/components/launches/continue.integration.tsx +++ b/apps/frontend/src/components/launches/continue.integration.tsx @@ -62,12 +62,13 @@ export const ContinueIntegration: FC<{ return; } - const { inBetweenSteps, id } = await data.json(); + const { inBetweenSteps, id, onboarding: resOnboarding } = await data.json(); + const onboarding = resOnboarding || searchParams.onboarding === 'true'; if (inBetweenSteps && !searchParams.refresh) { - push(`/launches?added=${provider}&continue=${id}`); + push(`/launches?added=${provider}&continue=${id}${onboarding ? '&onboarding=true' : ''}`); return; } - push(`/launches?added=${provider}&msg=Channel Updated`); + push(`/launches?added=${provider}&msg=Channel Updated${onboarding ? '&onboarding=true' : ''}`); })(); }, [provider, searchParams]); diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index b8940a1e..9731cd60 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -25,6 +25,7 @@ import { NewPost } from '@gitroom/frontend/components/launches/new.post'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useIntegrationList } from '@gitroom/frontend/components/launches/helpers/use.integration.list'; import useCookie from 'react-use-cookie'; +import { Onboarding } from '@gitroom/frontend/components/onboarding/onboarding'; export const SVGLine = () => { return ( @@ -490,6 +491,7 @@ export const LaunchesComponent = () => { // @ts-ignore return ( <DNDProvider> + <Onboarding /> <CalendarWeekProvider integrations={sortedIntegrations}> <div className={clsx( diff --git a/apps/frontend/src/components/onboarding/onboarding.modal.tsx b/apps/frontend/src/components/onboarding/onboarding.modal.tsx new file mode 100644 index 00000000..ca5e70cd --- /dev/null +++ b/apps/frontend/src/components/onboarding/onboarding.modal.tsx @@ -0,0 +1,245 @@ +'use client'; + +import React, { FC, useCallback, useMemo, useState } from 'react'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import useSWR from 'swr'; +import { orderBy } from 'lodash'; +import clsx from 'clsx'; +import Image from 'next/image'; +import { Button } from '@gitroom/react/form/button'; +import { AddProviderComponent } from '@gitroom/frontend/components/launches/add.provider.component'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +interface OnboardingModalProps { + onClose: () => void; +} + +export const OnboardingModal: FC<OnboardingModalProps> = ({ onClose }) => { + const [step, setStep] = useState(1); + const t = useT(); + + return ( + <div className="w-full min-h-full flex-1 p-[40px] flex relative"> + <div className="flex flex-1 bg-newBgColorInner rounded-[20px] flex-col"> + <div className="flex-1 flex p-[40px]"> + <div className="flex flex-col gap-[24px] flex-1"> + {/* Step indicators */} + <div className="flex items-center justify-center gap-[16px]"> + <div className="flex items-center gap-[8px]"> + <div + className={clsx( + 'w-[32px] h-[32px] rounded-full flex items-center justify-center text-[14px] font-semibold transition-colors', + step === 1 + ? 'bg-primary text-white' + : 'bg-customColor47 text-customColor18' + )} + > + 1 + </div> + <span + className={clsx( + 'text-[14px]', + step === 1 ? 'text-white font-medium' : 'text-customColor18' + )} + > + {t('connect_channels', 'Connect Channels')} + </span> + </div> + <div className="w-[40px] h-[2px] bg-customColor47" /> + <div className="flex items-center gap-[8px]"> + <div + className={clsx( + 'w-[32px] h-[32px] rounded-full flex items-center justify-center text-[14px] font-semibold transition-colors', + step === 2 + ? 'bg-primary text-white' + : 'bg-customColor47 text-customColor18' + )} + > + 2 + </div> + <span + className={clsx( + 'text-[14px]', + step === 2 ? 'text-white font-medium' : 'text-customColor18' + )} + > + {t('watch_tutorial', 'Watch Tutorial')} + </span> + </div> + </div> + + {/* Step content */} + {step === 1 && ( + <OnboardingStep1 + onNext={() => setStep(2)} + onSkip={() => setStep(2)} + /> + )} + {step === 2 && ( + <OnboardingStep2 onBack={() => setStep(1)} onFinish={onClose} /> + )} + </div> + </div> + </div> + </div> + ); +}; + +const OnboardingStep1: FC<{ onNext: () => void; onSkip: () => void }> = ({ + onNext, + onSkip, +}) => { + const fetch = useFetch(); + const t = useT(); + + const getIntegrations = useCallback(async () => { + return (await fetch('/integrations')).json(); + }, []); + + const load = useCallback(async (path: string) => { + const list = (await (await fetch(path)).json()).integrations; + return list; + }, []); + + const { data: integrations } = useSWR('/integrations/list', load, { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + revalidateOnMount: true, + refreshWhenHidden: false, + refreshWhenOffline: false, + fallbackData: [], + }); + + const sortedIntegrations = useMemo(() => { + return orderBy( + integrations, + ['type', 'disabled', 'identifier'], + ['desc', 'asc', 'asc'] + ); + }, [integrations]); + + const { data } = useSWR('get-all-integrations-onboarding', getIntegrations); + + return ( + <div className="flex flex-col gap-[24px]"> + <div className="flex gap-[4px] flex-col text-center"> + <div className="text-[24px] font-semibold"> + {t('connect_your_channels', 'Connect Your Channels')} + </div> + <div className="text-[14px] text-customColor18"> + {t( + 'connect_social_media_to_start', + 'Connect your social media accounts to start scheduling posts' + )} + </div> + </div> + + {/* Connected channels */} + {sortedIntegrations.length > 0 && ( + <div className="border border-customColor47 rounded-[8px] p-[16px]"> + <div className="text-[14px] font-medium mb-[12px]"> + {t('connected_channels', 'Connected Channels')} ( + {sortedIntegrations.length}) + </div> + <div className="flex flex-wrap gap-[12px]"> + {sortedIntegrations.map((integration: any) => ( + <div + key={integration.id} + className="flex items-center gap-[8px] bg-customColor47/30 rounded-[8px] px-[12px] py-[8px]" + > + <div className="relative w-[28px] h-[28px]"> + <Image + src={integration.picture} + className="rounded-full" + alt={integration.identifier} + width={28} + height={28} + /> + <Image + src={`/icons/platforms/${integration.identifier}.png`} + className="rounded-full absolute -bottom-[3px] -end-[3px] border border-fifth" + alt={integration.identifier} + width={14} + height={14} + /> + </div> + <span className="text-[13px]">{integration.name}</span> + </div> + ))} + </div> + </div> + )} + + {/* Available platforms - using AddProviderComponent */} + <div className="flex flex-col gap-[12px]"> + <div className="text-[14px] font-medium"> + {t('add_more_channels', 'Add More Channels')} + </div> + {data && ( + <AddProviderComponent + social={data.social || []} + article={data.article || []} + onboarding={true} + /> + )} + </div> + + {/* Action buttons */} + <div className="flex justify-end pt-[16px]"> + <Button onClick={onNext}> + {sortedIntegrations.length > 0 + ? t('continue', 'Continue') + : t('continue_without_channels', 'Continue without channels')} + </Button> + </div> + </div> + ); +}; + +const OnboardingStep2: FC<{ onBack: () => void; onFinish: () => void }> = ({ + onBack, + onFinish, +}) => { + const t = useT(); + + return ( + <div className="flex flex-col gap-[24px] flex-1"> + <div className="flex gap-[4px] flex-col text-center"> + <div className="text-[24px] font-semibold"> + {t('watch_tutorial_title', 'Learn How to Use Postiz')} + </div> + <div className="text-[14px] text-customColor18"> + {t( + 'watch_tutorial_description', + 'Watch this short video to learn how to get the most out of Postiz' + )} + </div> + </div> + + {/* YouTube Video Embed */} + <div className="relative flex-1 rounded-[12px] overflow-hidden"> + <div className="absolute left-0 top-0 w-full h-full flex justify-center"> + <iframe + className="h-full aspect-video" + src="https://www.youtube.com/embed/BdsCVvEYgHU?si=vvhaZJ8I5oXXvVJS?autoplay=1" + title="Postiz Tutorial" + allow="autoplay" + allowFullScreen + /> + </div> + </div> + + {/* Action buttons */} + <div className="flex justify-between pt-[16px]"> + <Button + className="bg-transparent border border-customColor47 hover:bg-customColor47/30" + onClick={onBack} + > + {t('back', 'Back')} + </Button> + <Button onClick={onFinish}>{t('get_started', 'Get Started')}</Button> + </div> + </div> + ); +}; diff --git a/apps/frontend/src/components/onboarding/onboarding.tsx b/apps/frontend/src/components/onboarding/onboarding.tsx index d5b5a21c..33f8e406 100644 --- a/apps/frontend/src/components/onboarding/onboarding.tsx +++ b/apps/frontend/src/components/onboarding/onboarding.tsx @@ -1,57 +1,46 @@ 'use client'; -import { FC, useCallback, useEffect, useRef, useState } from 'react'; +import { FC, useCallback, useEffect, useRef } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { Button } from '@gitroom/react/form/button'; -import { ConnectChannels } from '@gitroom/frontend/components/onboarding/connect.channels'; -import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { OnboardingModal } from '@gitroom/frontend/components/onboarding/onboarding.modal'; -const Welcome: FC = () => { - const { isGeneral } = useVariables(); - const [step, setStep] = useState(1); - const router = useRouter(); - const t = useT(); - - const goToLaunches = useCallback(() => { - router.push('/launches'); - }, []); - - return ( - <div className="relative"> - {step === 2 - (isGeneral ? 1 : 0) && ( - <div> - <ConnectChannels /> - <div className="flex justify-end gap-[8px]"> - <Button onClick={goToLaunches}>{t('close', 'Close')}</Button> - </div> - </div> - )} - </div> - ); -}; export const Onboarding: FC = () => { const query = useSearchParams(); const modal = useModals(); + const router = useRouter(); const modalOpen = useRef(false); const t = useT(); + const handleClose = useCallback(() => { + modal.closeAll(); + router.push('/launches'); + }, [modal, router]); + useEffect(() => { const onboarding = query.get('onboarding'); if (!onboarding) { - modalOpen.current = false; - modal.closeAll(); + if (modalOpen.current) { + modalOpen.current = false; + modal.closeAll(); + } + return; + } + if (modalOpen.current) { return; } modalOpen.current = true; modal.openModal({ - title: t('onboarding', 'Onboarding'), - withCloseButton: false, + // title: t('onboarding', 'Welcome to Postiz'), + withCloseButton: true, closeOnEscape: false, - size: '900px', - children: <Welcome />, + removeLayout: true, + fullScreen: true, + onClose: handleClose, + children: <OnboardingModal onClose={handleClose} />, }); - }, [query]); + }, [query, handleClose, t]); + return null; }; From 43ced2e7b65820fc5748545200eb77d0e12a218c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 23 Jan 2026 16:37:28 +0700 Subject: [PATCH 169/340] feat: onboarding --- .../launches/add.provider.component.tsx | 2 +- .../launches/launches.component.tsx | 2 +- .../onboarding/onboarding.modal.tsx | 99 ++++++++++++++++--- .../src/components/onboarding/onboarding.tsx | 1 + i18n.lock | 11 +++ .../translation/locales/ar/translation.json | 13 ++- .../translation/locales/bn/translation.json | 13 ++- .../translation/locales/de/translation.json | 13 ++- .../translation/locales/en/translation.json | 13 ++- .../translation/locales/es/translation.json | 13 ++- .../translation/locales/fr/translation.json | 13 ++- .../translation/locales/he/translation.json | 13 ++- .../translation/locales/it/translation.json | 13 ++- .../translation/locales/ja/translation.json | 13 ++- .../translation/locales/ko/translation.json | 13 ++- .../translation/locales/pt/translation.json | 13 ++- .../translation/locales/ru/translation.json | 13 ++- .../translation/locales/tr/translation.json | 13 ++- .../translation/locales/vi/translation.json | 13 ++- .../translation/locales/zh/translation.json | 13 ++- 20 files changed, 282 insertions(+), 28 deletions(-) diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 589063c6..6207ecf0 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -430,7 +430,7 @@ export const AddProviderComponent: FC<{ return ( <div className="w-full flex flex-col gap-[20px] rounded-[4px] relative]"> <div className="flex flex-col"> - <div className={clsx("grid grid-cols-5 gap-[10px] justify-items-center justify-center", onboarding ? 'grid-cols-8' : 'grid-cols-5')}> + <div className={clsx("grid grid-cols-5 gap-[10px] justify-items-center justify-center", onboarding ? 'grid-cols-9' : 'grid-cols-5')}> {social.map((item) => ( <div key={item.identifier} diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index 9731cd60..9d07acd3 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -457,7 +457,7 @@ export const LaunchesComponent = () => { return; } if (search.get('msg')) { - toast.show(search.get('msg')!, 'warning'); + toast.show(search.get('msg')!, 'success'); window?.opener?.postMessage( { msg: search.get('msg')!, diff --git a/apps/frontend/src/components/onboarding/onboarding.modal.tsx b/apps/frontend/src/components/onboarding/onboarding.modal.tsx index ca5e70cd..2905ab8b 100644 --- a/apps/frontend/src/components/onboarding/onboarding.modal.tsx +++ b/apps/frontend/src/components/onboarding/onboarding.modal.tsx @@ -6,9 +6,9 @@ import useSWR from 'swr'; import { orderBy } from 'lodash'; import clsx from 'clsx'; import Image from 'next/image'; -import { Button } from '@gitroom/react/form/button'; import { AddProviderComponent } from '@gitroom/frontend/components/launches/add.provider.component'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useModals } from '@gitroom/frontend/components/layout/new-modal'; interface OnboardingModalProps { onClose: () => void; @@ -16,11 +16,35 @@ interface OnboardingModalProps { export const OnboardingModal: FC<OnboardingModalProps> = ({ onClose }) => { const [step, setStep] = useState(1); + const modals = useModals(); const t = useT(); return ( <div className="w-full min-h-full flex-1 p-[40px] flex relative"> - <div className="flex flex-1 bg-newBgColorInner rounded-[20px] flex-col"> + <style> + {`#support-discord {display: none}`} + </style> + <div className="flex flex-1 bg-newBgColorInner rounded-[20px] flex-col relative"> + <button + className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" + type="button" + onClick={modals.closeAll} + > + <svg + viewBox="0 0 15 15" + fill="none" + xmlns="http://www.w3.org/2000/svg" + width="16" + height="16" + > + <path + d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" + fill="currentColor" + fillRule="evenodd" + clipRule="evenodd" + ></path> + </svg> + </button> <div className="flex-1 flex p-[40px]"> <div className="flex flex-col gap-[24px] flex-1"> {/* Step indicators */} @@ -174,7 +198,7 @@ const OnboardingStep1: FC<{ onNext: () => void; onSkip: () => void }> = ({ {/* Available platforms - using AddProviderComponent */} <div className="flex flex-col gap-[12px]"> <div className="text-[14px] font-medium"> - {t('add_more_channels', 'Add More Channels')} + {t('click_channel_to_add', 'Click a channel to add it')} </div> {data && ( <AddProviderComponent @@ -186,12 +210,30 @@ const OnboardingStep1: FC<{ onNext: () => void; onSkip: () => void }> = ({ </div> {/* Action buttons */} - <div className="flex justify-end pt-[16px]"> - <Button onClick={onNext}> + <div className="flex justify-end pt-[24px] mt-[8px]"> + <button + onClick={onNext} + className="group flex items-center gap-[12px] bg-gradient-to-r from-[#622aff] to-[#8b5cf6] hover:from-[#7c3aff] hover:to-[#9d6eff] text-white font-semibold px-[32px] py-[14px] rounded-[12px] text-[16px] transition-all shadow-lg shadow-purple-500/25 hover:shadow-purple-500/40" + > {sortedIntegrations.length > 0 ? t('continue', 'Continue') : t('continue_without_channels', 'Continue without channels')} - </Button> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + className="group-hover:translate-x-1 transition-transform" + > + <path d="M5 12h14" /> + <path d="m12 5 7 7-7 7" /> + </svg> + </button> </div> </div> ); @@ -231,14 +273,49 @@ const OnboardingStep2: FC<{ onBack: () => void; onFinish: () => void }> = ({ </div> {/* Action buttons */} - <div className="flex justify-between pt-[16px]"> - <Button - className="bg-transparent border border-customColor47 hover:bg-customColor47/30" + <div className="flex justify-between pt-[24px] mt-[8px]"> + <button onClick={onBack} + className="group flex items-center gap-[8px] bg-transparent border-2 border-customColor47 hover:border-white/50 hover:bg-white/5 text-white/80 hover:text-white font-medium px-[24px] py-[12px] rounded-[12px] text-[15px] transition-all" > + <svg + xmlns="http://www.w3.org/2000/svg" + width="18" + height="18" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + className="group-hover:-translate-x-1 transition-transform" + > + <path d="m12 19-7-7 7-7" /> + <path d="M19 12H5" /> + </svg> {t('back', 'Back')} - </Button> - <Button onClick={onFinish}>{t('get_started', 'Get Started')}</Button> + </button> + <button + onClick={onFinish} + className="group flex items-center gap-[12px] bg-gradient-to-r from-[#10b981] to-[#059669] hover:from-[#34d399] hover:to-[#10b981] text-white font-semibold px-[32px] py-[14px] rounded-[12px] text-[16px] transition-all shadow-lg shadow-emerald-500/25 hover:shadow-emerald-500/40" + > + {t('get_started', 'Get Started')} + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + strokeLinecap="round" + strokeLinejoin="round" + className="group-hover:scale-110 transition-transform" + > + <path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" /> + <polyline points="22 4 12 14.01 9 11.01" /> + </svg> + </button> </div> </div> ); diff --git a/apps/frontend/src/components/onboarding/onboarding.tsx b/apps/frontend/src/components/onboarding/onboarding.tsx index 33f8e406..fe6d39c3 100644 --- a/apps/frontend/src/components/onboarding/onboarding.tsx +++ b/apps/frontend/src/components/onboarding/onboarding.tsx @@ -36,6 +36,7 @@ export const Onboarding: FC = () => { withCloseButton: true, closeOnEscape: false, removeLayout: true, + askClose: true, fullScreen: true, onClose: handleClose, children: <OnboardingModal onClose={handleClose} />, diff --git a/i18n.lock b/i18n.lock index c82d5269..10662c0d 100644 --- a/i18n.lock +++ b/i18n.lock @@ -686,3 +686,14 @@ checksums: drop_files_here_to_upload: a7971302d02a19a461c519cffedfa5e5 insert_emoji: 6e2ab0e239c0ee87d385b1cd17185e00 write_something: bc9257cede62880c411001e61310ca43 + click_channel_to_add: 7dc67c45eba252d1fa931e5a101d568b + connect_your_channels: a8303310a9cc5f4e11510f8f55c0978d + connect_social_media_to_start: 8210aba8fa40ae5cdf23d78bd0ea53a4 + connected_channels: b64d400364bb3ccb4b1d4b547673f4c3 + continue: 3cfba90b4600131e82fc4260c568d044 + continue_without_channels: fa9c2e19c59e2eac86c1fcac35611337 + watch_tutorial: 7f202b82c805285109187f099d22e863 + watch_tutorial_title: c3c0d814f10a3325a7fd52a7fb44ae38 + watch_tutorial_description: 25dd65235d9603f6d65d62f953791722 + back: f541015a827e37cb3b1234e56bc2aa3c + get_started: 1d5f030c4ec9c869e647ae060518b948 diff --git a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json index 39b6794a..0becd8cf 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ar/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ar/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "حذف المنشور", "drop_files_here_to_upload": "أسقط ملفاتك هنا للتحميل", "insert_emoji": "إدراج رمز تعبيري", - "write_something": "اكتب شيئًا …" + "write_something": "اكتب شيئًا …", + "click_channel_to_add": "انقر على قناة لإضافتها", + "connect_your_channels": "قم بتوصيل قنواتك", + "connect_social_media_to_start": "قم بتوصيل حسابات التواصل الاجتماعي لبدء جدولة المنشورات", + "connected_channels": "القنوات المتصلة", + "continue": "متابعة", + "continue_without_channels": "متابعة بدون قنوات", + "watch_tutorial": "مشاهدة الدليل", + "watch_tutorial_title": "تعلم كيفية استخدام Postiz", + "watch_tutorial_description": "شاهد هذا الفيديو القصير لتتعلم كيف تستفيد من Postiz بأفضل شكل", + "back": "رجوع", + "get_started": "ابدأ" } diff --git a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json index 74554aa1..0fa46cdd 100644 --- a/libraries/react-shared-libraries/src/translation/locales/bn/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/bn/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "পোস্ট মুছুন", "drop_files_here_to_upload": "আপলোড করতে আপনার ফাইলগুলো এখানে ছেড়ে দিন", "insert_emoji": "ইমোজি যুক্ত করুন", - "write_something": "কিছু লিখুন …" + "write_something": "কিছু লিখুন …", + "click_channel_to_add": "যোগ করার জন্য একটি চ্যানেলে ক্লিক করুন", + "connect_your_channels": "আপনার চ্যানেলগুলি সংযোগ করুন", + "connect_social_media_to_start": "পোস্ট নির্ধারণ করতে শুরু করতে আপনার সামাজিক মাধ্যম অ্যাকাউন্ট সংযোগ করুন", + "connected_channels": "সংযুক্ত চ্যানেলসমূহ", + "continue": "চালিয়ে যান", + "continue_without_channels": "চ্যানেল ছাড়াই চালিয়ে যান", + "watch_tutorial": "টিউটোরিয়াল দেখুন", + "watch_tutorial_title": "Postiz ব্যবহারের উপায় শিখুন", + "watch_tutorial_description": "Postiz সর্বোচ্চভাবে কাজে লাগাতে এই সংক্ষিপ্ত ভিডিওটি দেখুন", + "back": "পেছনে যান", + "get_started": "শুরু করুন" } diff --git a/libraries/react-shared-libraries/src/translation/locales/de/translation.json b/libraries/react-shared-libraries/src/translation/locales/de/translation.json index fc8062c4..89f0ed73 100644 --- a/libraries/react-shared-libraries/src/translation/locales/de/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/de/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Beitrag löschen", "drop_files_here_to_upload": "Dateien hierher ziehen, um sie hochzuladen", "insert_emoji": "Emoji einfügen", - "write_something": "Schreibe etwas …" + "write_something": "Schreibe etwas …", + "click_channel_to_add": "Klicke auf einen Kanal, um ihn hinzuzufügen", + "connect_your_channels": "Verbinde deine Kanäle", + "connect_social_media_to_start": "Verbinde deine Social-Media-Konten, um mit der Planung von Beiträgen zu beginnen", + "connected_channels": "Verbundene Kanäle", + "continue": "Weiter", + "continue_without_channels": "Ohne Kanäle fortfahren", + "watch_tutorial": "Tutorial ansehen", + "watch_tutorial_title": "Lerne, wie du Postiz benutzt", + "watch_tutorial_description": "Sieh dir dieses kurze Video an, um das Beste aus Postiz herauszuholen", + "back": "Zurück", + "get_started": "Loslegen" } diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index 4537bb6c..faadd5ca 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -683,5 +683,16 @@ "delete_post_tooltip": "Delete Post", "drop_files_here_to_upload": "Drop your files here to upload", "insert_emoji": "Insert Emoji", - "write_something": "Write something …" + "write_something": "Write something …", + "click_channel_to_add": "Click a channel to add it", + "connect_your_channels": "Connect Your Channels", + "connect_social_media_to_start": "Connect your social media accounts to start scheduling posts", + "connected_channels": "Connected Channels", + "continue": "Continue", + "continue_without_channels": "Continue without channels", + "watch_tutorial": "Watch Tutorial", + "watch_tutorial_title": "Learn How to Use Postiz", + "watch_tutorial_description": "Watch this short video to learn how to get the most out of Postiz", + "back": "Back", + "get_started": "Get Started" } diff --git a/libraries/react-shared-libraries/src/translation/locales/es/translation.json b/libraries/react-shared-libraries/src/translation/locales/es/translation.json index db286261..5bb04282 100644 --- a/libraries/react-shared-libraries/src/translation/locales/es/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/es/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Eliminar publicación", "drop_files_here_to_upload": "Suelta tus archivos aquí para subirlos", "insert_emoji": "Insertar emoji", - "write_something": "Escribe algo…" + "write_something": "Escribe algo…", + "click_channel_to_add": "Haz clic en un canal para agregarlo", + "connect_your_channels": "Conecta tus canales", + "connect_social_media_to_start": "Conecta tus cuentas de redes sociales para empezar a programar publicaciones", + "connected_channels": "Canales conectados", + "continue": "Continuar", + "continue_without_channels": "Continuar sin canales", + "watch_tutorial": "Ver tutorial", + "watch_tutorial_title": "Aprende a usar Postiz", + "watch_tutorial_description": "Mira este breve video para aprender a sacar el máximo provecho de Postiz", + "back": "Atrás", + "get_started": "Comenzar" } diff --git a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json index 2792bd5b..b95d5a8e 100644 --- a/libraries/react-shared-libraries/src/translation/locales/fr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/fr/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Supprimer la publication", "drop_files_here_to_upload": "Déposez vos fichiers ici pour les télécharger", "insert_emoji": "Insérer un emoji", - "write_something": "Écrivez quelque chose…" + "write_something": "Écrivez quelque chose…", + "click_channel_to_add": "Cliquez sur un canal pour l'ajouter", + "connect_your_channels": "Connectez vos canaux", + "connect_social_media_to_start": "Connectez vos comptes de réseaux sociaux pour commencer à programmer des publications", + "connected_channels": "Canaux connectés", + "continue": "Continuer", + "continue_without_channels": "Continuer sans canaux", + "watch_tutorial": "Regarder le tutoriel", + "watch_tutorial_title": "Apprenez à utiliser Postiz", + "watch_tutorial_description": "Regardez cette courte vidéo pour apprendre à tirer le meilleur parti de Postiz", + "back": "Retour", + "get_started": "Commencer" } diff --git a/libraries/react-shared-libraries/src/translation/locales/he/translation.json b/libraries/react-shared-libraries/src/translation/locales/he/translation.json index 54f0d5de..85559234 100644 --- a/libraries/react-shared-libraries/src/translation/locales/he/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/he/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "מחק פוסט", "drop_files_here_to_upload": "גרור קבצים לכאן להעלאה", "insert_emoji": "הוסף אימוג'י", - "write_something": "כתוב משהו…" + "write_something": "כתוב משהו…", + "click_channel_to_add": "לחץ על ערוץ כדי להוסיף אותו", + "connect_your_channels": "חבר את הערוצים שלך", + "connect_social_media_to_start": "חבר את חשבונות המדיה החברתית שלך כדי להתחיל לקבוע פוסטים", + "connected_channels": "ערוצים מחוברים", + "continue": "המשך", + "continue_without_channels": "המשך ללא ערוצים", + "watch_tutorial": "צפה במדריך", + "watch_tutorial_title": "למד כיצד להשתמש ב-Postiz", + "watch_tutorial_description": "צפה בסרטון הקצר הזה ולמד כיצד להפיק את המרב מ-Postiz", + "back": "חזרה", + "get_started": "התחלה" } diff --git a/libraries/react-shared-libraries/src/translation/locales/it/translation.json b/libraries/react-shared-libraries/src/translation/locales/it/translation.json index b4e918df..09f09140 100644 --- a/libraries/react-shared-libraries/src/translation/locales/it/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/it/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Elimina post", "drop_files_here_to_upload": "Trascina qui i tuoi file per caricarli", "insert_emoji": "Inserisci emoji", - "write_something": "Scrivi qualcosa…" + "write_something": "Scrivi qualcosa…", + "click_channel_to_add": "Clicca su un canale per aggiungerlo", + "connect_your_channels": "Collega i tuoi canali", + "connect_social_media_to_start": "Collega i tuoi account social per iniziare a programmare i post", + "connected_channels": "Canali collegati", + "continue": "Continua", + "continue_without_channels": "Continua senza canali", + "watch_tutorial": "Guarda il tutorial", + "watch_tutorial_title": "Scopri come usare Postiz", + "watch_tutorial_description": "Guarda questo breve video per imparare a sfruttare al meglio Postiz", + "back": "Indietro", + "get_started": "Inizia" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json index 300929b1..5df138e8 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ja/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ja/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "投稿を削除", "drop_files_here_to_upload": "ここにファイルをドロップしてアップロード", "insert_emoji": "絵文字を挿入", - "write_something": "何かを書いてください…" + "write_something": "何かを書いてください…", + "click_channel_to_add": "追加するチャンネルをクリック", + "connect_your_channels": "チャンネルを接続する", + "connect_social_media_to_start": "ソーシャルメディアアカウントを接続して投稿の予約を始めましょう", + "connected_channels": "接続済みチャンネル", + "continue": "続行", + "continue_without_channels": "チャンネルなしで続行", + "watch_tutorial": "チュートリアルを見る", + "watch_tutorial_title": "Postizの使い方を学ぶ", + "watch_tutorial_description": "この短い動画を見て、Postizを最大限に活用する方法を学びましょう", + "back": "戻る", + "get_started": "開始" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json index 881cc319..4e6038c4 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ko/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ko/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "게시물 삭제", "drop_files_here_to_upload": "여기에 파일을 끌어다 놓아 업로드하세요", "insert_emoji": "이모지 삽입", - "write_something": "무엇인가를 작성하세요 …" + "write_something": "무엇인가를 작성하세요 …", + "click_channel_to_add": "채널을 추가하려면 클릭하세요", + "connect_your_channels": "채널 연결하기", + "connect_social_media_to_start": "게시물 예약을 시작하려면 소셜 미디어 계정을 연결하세요", + "connected_channels": "연결된 채널", + "continue": "계속하기", + "continue_without_channels": "채널 없이 계속하기", + "watch_tutorial": "튜토리얼 보기", + "watch_tutorial_title": "Postiz 사용법 배우기", + "watch_tutorial_description": "이 짧은 영상을 통해 Postiz를 최대한 활용하는 방법을 알아보세요", + "back": "뒤로가기", + "get_started": "시작하기" } diff --git a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json index 1b60edc7..af34db47 100644 --- a/libraries/react-shared-libraries/src/translation/locales/pt/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/pt/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Excluir postagem", "drop_files_here_to_upload": "Solte seus arquivos aqui para fazer upload", "insert_emoji": "Inserir emoji", - "write_something": "Escreva algo…" + "write_something": "Escreva algo…", + "click_channel_to_add": "Clique em um canal para adicionar", + "connect_your_channels": "Conecte seus canais", + "connect_social_media_to_start": "Conecte suas contas de redes sociais para começar a agendar posts", + "connected_channels": "Canais conectados", + "continue": "Continuar", + "continue_without_channels": "Continuar sem canais", + "watch_tutorial": "Assistir ao tutorial", + "watch_tutorial_title": "Aprenda a usar o Postiz", + "watch_tutorial_description": "Assista a este vídeo curto para aprender a tirar o máximo proveito do Postiz", + "back": "Voltar", + "get_started": "Começar" } diff --git a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json index 6b913a8e..a495f67e 100644 --- a/libraries/react-shared-libraries/src/translation/locales/ru/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/ru/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Удалить публикацию", "drop_files_here_to_upload": "Перетащите файлы сюда для загрузки", "insert_emoji": "Вставить эмодзи", - "write_something": "Напишите что-нибудь …" + "write_something": "Напишите что-нибудь …", + "click_channel_to_add": "Нажмите на канал, чтобы добавить его", + "connect_your_channels": "Подключите ваши каналы", + "connect_social_media_to_start": "Подключите свои аккаунты в социальных сетях, чтобы начать планировать публикации", + "connected_channels": "Подключенные каналы", + "continue": "Продолжить", + "continue_without_channels": "Продолжить без каналов", + "watch_tutorial": "Смотреть обучение", + "watch_tutorial_title": "Узнайте, как пользоваться Postiz", + "watch_tutorial_description": "Посмотрите это короткое видео, чтобы узнать, как получить максимум от Postiz", + "back": "Назад", + "get_started": "Начать" } diff --git a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json index 53ad9552..943007c4 100644 --- a/libraries/react-shared-libraries/src/translation/locales/tr/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/tr/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Gönderiyi Sil", "drop_files_here_to_upload": "Yüklemek için dosyalarınızı buraya bırakın", "insert_emoji": "Emoji Ekle", - "write_something": "Bir şeyler yazın …" + "write_something": "Bir şeyler yazın …", + "click_channel_to_add": "Eklemek için bir kanal seçin", + "connect_your_channels": "Kanallarınızı Bağlayın", + "connect_social_media_to_start": "Gönderi planlamaya başlamak için sosyal medya hesaplarınızı bağlayın", + "connected_channels": "Bağlı Kanallar", + "continue": "Devam Et", + "continue_without_channels": "Kanallar olmadan devam et", + "watch_tutorial": "Eğitimi İzle", + "watch_tutorial_title": "Postiz'i Nasıl Kullanacağınızı Öğrenin", + "watch_tutorial_description": "Postiz'den en iyi şekilde yararlanmayı öğrenmek için bu kısa videoyu izleyin", + "back": "Geri", + "get_started": "Başlayın" } diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json index 390e8fb5..2d6d355e 100644 --- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "Xóa bài đăng", "drop_files_here_to_upload": "Kéo thả tệp vào đây để tải lên", "insert_emoji": "Chèn biểu tượng cảm xúc", - "write_something": "Viết gì đó …" + "write_something": "Viết gì đó …", + "click_channel_to_add": "Nhấp vào kênh để thêm", + "connect_your_channels": "Kết nối các kênh của bạn", + "connect_social_media_to_start": "Kết nối tài khoản mạng xã hội của bạn để bắt đầu lên lịch đăng bài", + "connected_channels": "Các kênh đã kết nối", + "continue": "Tiếp tục", + "continue_without_channels": "Tiếp tục mà không có kênh", + "watch_tutorial": "Xem hướng dẫn", + "watch_tutorial_title": "Tìm hiểu cách sử dụng Postiz", + "watch_tutorial_description": "Xem video ngắn này để biết cách tận dụng tối đa Postiz", + "back": "Quay lại", + "get_started": "Bắt đầu" } diff --git a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json index 0431a116..c62f9c34 100644 --- a/libraries/react-shared-libraries/src/translation/locales/zh/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/zh/translation.json @@ -681,5 +681,16 @@ "delete_post_tooltip": "删除帖子", "drop_files_here_to_upload": "将文件拖到此处以上传", "insert_emoji": "插入表情", - "write_something": "写点什么…" + "write_something": "写点什么…", + "click_channel_to_add": "点击频道以添加", + "connect_your_channels": "连接你的频道", + "connect_social_media_to_start": "连接你的社交媒体账户以开始安排发布", + "connected_channels": "已连接频道", + "continue": "继续", + "continue_without_channels": "继续但不添加频道", + "watch_tutorial": "观看教程", + "watch_tutorial_title": "学习如何使用Postiz", + "watch_tutorial_description": "观看这个简短的视频,学习如何最大程度地利用Postiz", + "back": "返回", + "get_started": "开始使用" } From 608185fd9a9d5f066a0a7e1a7e8155c95d5f0863 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 23 Jan 2026 17:06:46 +0700 Subject: [PATCH 170/340] fix: onboarding --- .../components/onboarding/onboarding.modal.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/onboarding/onboarding.modal.tsx b/apps/frontend/src/components/onboarding/onboarding.modal.tsx index 2905ab8b..3a45c65e 100644 --- a/apps/frontend/src/components/onboarding/onboarding.modal.tsx +++ b/apps/frontend/src/components/onboarding/onboarding.modal.tsx @@ -54,8 +54,8 @@ export const OnboardingModal: FC<OnboardingModalProps> = ({ onClose }) => { className={clsx( 'w-[32px] h-[32px] rounded-full flex items-center justify-center text-[14px] font-semibold transition-colors', step === 1 - ? 'bg-primary text-white' - : 'bg-customColor47 text-customColor18' + ? 'bg-boxFocused text-textItemFocused' + : 'bg-newTableHeader' )} > 1 @@ -63,20 +63,20 @@ export const OnboardingModal: FC<OnboardingModalProps> = ({ onClose }) => { <span className={clsx( 'text-[14px]', - step === 1 ? 'text-white font-medium' : 'text-customColor18' + step === 1 ? 'font-medium' : 'text-textColor' )} > {t('connect_channels', 'Connect Channels')} </span> </div> - <div className="w-[40px] h-[2px] bg-customColor47" /> + <div className="w-[40px] h-[2px] bg-boxFocused" /> <div className="flex items-center gap-[8px]"> <div className={clsx( 'w-[32px] h-[32px] rounded-full flex items-center justify-center text-[14px] font-semibold transition-colors', step === 2 - ? 'bg-primary text-white' - : 'bg-customColor47 text-customColor18' + ? 'bg-boxFocused text-textItemFocused' + : 'bg-newTableHeader' )} > 2 @@ -84,7 +84,7 @@ export const OnboardingModal: FC<OnboardingModalProps> = ({ onClose }) => { <span className={clsx( 'text-[14px]', - step === 2 ? 'text-white font-medium' : 'text-customColor18' + step === 2 ? 'font-medium' : 'text-textColor' )} > {t('watch_tutorial', 'Watch Tutorial')} @@ -161,7 +161,7 @@ const OnboardingStep1: FC<{ onNext: () => void; onSkip: () => void }> = ({ {/* Connected channels */} {sortedIntegrations.length > 0 && ( - <div className="border border-customColor47 rounded-[8px] p-[16px]"> + <div className="bg-newTableHeader rounded-[8px] p-[16px]"> <div className="text-[14px] font-medium mb-[12px]"> {t('connected_channels', 'Connected Channels')} ( {sortedIntegrations.length}) @@ -276,7 +276,7 @@ const OnboardingStep2: FC<{ onBack: () => void; onFinish: () => void }> = ({ <div className="flex justify-between pt-[24px] mt-[8px]"> <button onClick={onBack} - className="group flex items-center gap-[8px] bg-transparent border-2 border-customColor47 hover:border-white/50 hover:bg-white/5 text-white/80 hover:text-white font-medium px-[24px] py-[12px] rounded-[12px] text-[15px] transition-all" + className="group flex items-center gap-[8px] bg-transparent border-2 border-boxFocused font-medium px-[24px] py-[12px] rounded-[12px] text-[15px] transition-all" > <svg xmlns="http://www.w3.org/2000/svg" From 8bb1fc826fffa57a5d6d61b4b36fc3da86470ecb Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 23 Jan 2026 18:42:35 +0700 Subject: [PATCH 171/340] upgrade upload images to v2 --- .../nestjs-libraries/src/integrations/social/x.provider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index fcfaa45b..0f79645d 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -319,7 +319,7 @@ export class XProvider extends SocialAbstract implements SocialProvider { return { id: await this.runInConcurrent( async () => - client.v1.uploadMedia( + client.v2.uploadMedia( m.path.indexOf('mp4') > -1 ? Buffer.from(await readOrFetch(m.path)) : await sharp(await readOrFetch(m.path), { @@ -331,7 +331,7 @@ export class XProvider extends SocialAbstract implements SocialProvider { .gif() .toBuffer(), { - mimeType: lookup(m.path) || '', + media_type: (lookup(m.path) || '') as any, } ), true From bf9ab0fd9561e97044feffc1aa3d36daa5a37e07 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 24 Jan 2026 01:01:30 +0700 Subject: [PATCH 172/340] youtube onboarding --- .../billing/first.billing.component.tsx | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index cc8c07e9..d945b5da 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -5,7 +5,6 @@ import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { loadStripe, Stripe } from '@stripe/stripe-js'; -import { useSearchParams } from 'next/navigation'; import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector'; import { LanguageComponent } from '@gitroom/frontend/components/layout/language.component'; import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/sentry.feedback.component'; @@ -24,6 +23,8 @@ import { import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useDubClickId } from '@gitroom/frontend/components/layout/dubAnalytics'; +import Image from 'next/image'; +import { useModals } from '@gitroom/frontend/components/layout/new-modal'; const ModeComponent = dynamic( () => import('@gitroom/frontend/components/layout/mode.component'), @@ -50,6 +51,7 @@ export const FirstBillingComponent = () => { const [tier, setTier] = useState('STANDARD'); const [period, setPeriod] = useState('MONTHLY'); const fetch = useFetch(); + const modals = useModals(); const t = useT(); useEffect(() => { @@ -69,6 +71,21 @@ export const FirstBillingComponent = () => { ).json(); }, [tier, period]); + const showYouTube = () => { + modals.openModal({ + title: 'Grow Fast With Postiz (Play the video)', + children: ( + <iframe + className="h-full aspect-video min-w-[800px]" + src="https://www.youtube.com/embed/BdsCVvEYgHU?si=vvhaZJ8I5oXXvVJS?autoplay=1" + title="Postiz Tutorial" + allow="autoplay" + allowFullScreen + /> + ), + }); + }; + const { data, isLoading } = useSWR( `/billing-${tier}-${period}`, loadCheckout, @@ -101,6 +118,21 @@ export const FirstBillingComponent = () => { )} </div> + <div className="flex" onClick={showYouTube}> + <div className="tablet:mb-[32px] cursor-pointer mt-[32px] flex gap-[10px] items-center underline hover:font-[700]"> + <div> + <Image + className="text-[12px]" + src="/icons/platforms/youtube.svg" + width={22.5} + height={16} + alt="YouTube" + /> + </div> + <div>See the power of Postiz (click here)</div> + </div> + </div> + {!!user?.allowTrial && ( <div className="flex mt-[32px] mb-[10px] gap-[15px] tablet:mt-[32px] tablet:mb-[32px] text-[16px] font-[500] mobile:flex-col"> <div className="flex gap-[8px]"> From 7e1e5f36e395fd036723a9fde6d221484be5fdaa Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 24 Jan 2026 15:00:42 +0700 Subject: [PATCH 173/340] feat: kick --- apps/frontend/public/icons/platforms/kick.png | Bin 0 -> 14410 bytes apps/frontend/public/no-picture.jpg | Bin 28350 -> 6993 bytes .../launches/launches.component.tsx | 2 +- .../new-launch/picks.socials.component.tsx | 40 +-- .../providers/kick/kick.channel.select.tsx | 63 ++++ .../providers/kick/kick.provider.tsx | 30 ++ .../providers/show.all.providers.tsx | 5 + .../components/new-launch/select.current.tsx | 8 + .../all.providers.settings.ts | 3 + .../dtos/posts/providers-settings/kick.dto.ts | 13 + .../src/integrations/integration.manager.ts | 2 + .../src/integrations/social/kick.provider.ts | 271 ++++++++++++++++++ .../translation/locales/en/translation.json | 5 +- 13 files changed, 423 insertions(+), 19 deletions(-) create mode 100644 apps/frontend/public/icons/platforms/kick.png create mode 100644 apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/kick.provider.ts diff --git a/apps/frontend/public/icons/platforms/kick.png b/apps/frontend/public/icons/platforms/kick.png new file mode 100644 index 0000000000000000000000000000000000000000..4bc490588e84c1b9991e0e0014ebc73d668bdc17 GIT binary patch literal 14410 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/public/no-picture.jpg b/apps/frontend/public/no-picture.jpg index 7a8a84327d080b877818d9fb2f528a5e4784c790..94c5b185d6eafcdc76a9dc9db54210a7afbf1211 100644 GIT binary patch literal 6993 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 literal 28350 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 diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx index 9d07acd3..01e6d2f0 100644 --- a/apps/frontend/src/components/launches/launches.component.tsx +++ b/apps/frontend/src/components/launches/launches.component.tsx @@ -287,7 +287,7 @@ export const MenuComponent: FC< </div> )} <ImageWithFallback - fallbackSrc={`/icons/platforms/${integration.identifier}.png`} + fallbackSrc={'/no-picture.jpg'} src={integration.picture || '/no-picture.jpg'} className="rounded-[8px] min-w-[36px] min-h-[36px]" alt={integration.identifier} diff --git a/apps/frontend/src/components/new-launch/picks.socials.component.tsx b/apps/frontend/src/components/new-launch/picks.socials.component.tsx index 25012393..8caa5ee0 100644 --- a/apps/frontend/src/components/new-launch/picks.socials.component.tsx +++ b/apps/frontend/src/components/new-launch/picks.socials.component.tsx @@ -27,13 +27,13 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ })) ); - return ( <div className={clsx('flex', locked && 'opacity-50 pointer-events-none')}> <div className="flex flex-1"> <div className="innerComponent flex-1 flex"> <div className="flex flex-wrap gap-[12px] flex-1"> - {integrations.filter((f) => { + {integrations + .filter((f) => { if (exising.integration) { return f.id === exising.integration; } @@ -64,20 +64,28 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ : 'border-[#622FF6]' )} > - <Image - src={integration.picture || '/no-picture.jpg'} - className={clsx( - 'rounded-full transition-all min-w-[42px] border-[1.5px] min-h-[42px]', - selectedIntegrations.findIndex( - (p) => p.integration.id === integration.id - ) === -1 - ? 'border-transparent' - : 'border-[#000]' - )} - alt={integration.identifier} - width={42} - height={42} - /> + <div + className="rounded-full min-w-[42px] min-h-[42px] bg-contain bg-center bg-no-repeat" + style={{ backgroundImage: `url(/no-picture.jpg)` }} + > + <Image + src={integration.picture || '/no-picture.jpg'} + className={clsx( + 'rounded-full transition-all min-w-[42px] border-[1.5px] min-h-[42px]', + selectedIntegrations.findIndex( + (p) => p.integration.id === integration.id + ) === -1 + ? 'border-transparent' + : 'border-[#000]' + )} + onError={(e) => { + e.currentTarget.style.display = 'none'; + }} + alt={integration.identifier} + width={42} + height={42} + /> + </div> {integration.identifier === 'youtube' ? ( <img src="/icons/platforms/youtube.svg" diff --git a/apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx b/apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx new file mode 100644 index 00000000..3bdadf04 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx @@ -0,0 +1,63 @@ +'use client'; + +import { FC, useEffect, useState } from 'react'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { Select } from '@gitroom/react/form/select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +export const KickChannelSelect: FC<{ + name: string; + onChange: (event: { + target: { + value: string; + name: string; + }; + }) => void; +}> = (props) => { + const { onChange, name } = props; + const t = useT(); + const customFunc = useCustomProviderFunction(); + const [channels, setChannels] = useState([]); + const { getValues } = useSettings(); + const [currentChannel, setCurrentChannel] = useState<string | undefined>(); + + const onChangeInner = (event: { + target: { + value: string; + name: string; + }; + }) => { + setCurrentChannel(event.target.value); + onChange(event); + }; + + useEffect(() => { + customFunc.get('channels').then((data) => setChannels(data)); + const settings = getValues()[props.name]; + if (settings) { + setCurrentChannel(settings); + } + }, []); + + if (!channels.length) { + return null; + } + + return ( + <Select + name={name} + label={t('kick_select_channel', 'Select Channel')} + onChange={onChangeInner} + value={currentChannel} + > + <option value="">{t('select_1', '--Select--')}</option> + {channels.map((channel: any) => ( + <option key={channel.id} value={channel.id}> + {channel.name} + </option> + ))} + </Select> + ); +}; + diff --git a/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx b/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx new file mode 100644 index 00000000..d603c5f9 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { FC } from 'react'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { KickChannelSelect } from '@gitroom/frontend/components/new-launch/providers/kick/kick.channel.select'; +import { KickDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/kick.dto'; + +const KickComponent: FC = () => { + const form = useSettings(); + return ( + <div> + <KickChannelSelect {...form.register('broadcasterUserId')} /> + </div> + ); +}; + +export default withProvider({ + postComment: PostComment.COMMENT, + minimumCharacters: [], + SettingsComponent: KickComponent, + CustomPreviewComponent: undefined, + dto: KickDto, + checkValidity: undefined, + maximumCharacters: 500, +}); + diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 521eb8ae..034519b0 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -15,6 +15,7 @@ import DribbbleProvider from '@gitroom/frontend/components/new-launch/providers/ import ThreadsProvider from '@gitroom/frontend/components/new-launch/providers/threads/threads.provider'; import DiscordProvider from '@gitroom/frontend/components/new-launch/providers/discord/discord.provider'; import SlackProvider from '@gitroom/frontend/components/new-launch/providers/slack/slack.provider'; +import KickProvider from '@gitroom/frontend/components/new-launch/providers/kick/kick.provider'; import MastodonProvider from '@gitroom/frontend/components/new-launch/providers/mastodon/mastodon.provider'; import BlueskyProvider from '@gitroom/frontend/components/new-launch/providers/bluesky/bluesky.provider'; import LemmyProvider from '@gitroom/frontend/components/new-launch/providers/lemmy/lemmy.provider'; @@ -103,6 +104,10 @@ export const Providers = [ identifier: 'slack', component: SlackProvider, }, + { + identifier: 'kick', + component: KickProvider, + }, { identifier: 'mastodon', component: MastodonProvider, diff --git a/apps/frontend/src/components/new-launch/select.current.tsx b/apps/frontend/src/components/new-launch/select.current.tsx index 07fda877..3d741782 100644 --- a/apps/frontend/src/components/new-launch/select.current.tsx +++ b/apps/frontend/src/components/new-launch/select.current.tsx @@ -99,6 +99,10 @@ export const SelectCurrent: FC = () => { > <IsGlobal id={integration.id} /> <div + {...{ + 'data-tooltip-id': 'tooltip', + 'data-tooltip-content': integration.name, + }} className={clsx( 'relative w-full h-full rounded-full flex justify-center items-center filter transition-all duration-500' )} @@ -109,6 +113,10 @@ export const SelectCurrent: FC = () => { alt={integration.identifier} width={26} height={26} + onError={(e) => { + e.currentTarget.src = '/no-picture.jpg'; + e.currentTarget.srcset = '/no-picture.jpg'; + }} /> {integration.identifier === 'youtube' ? ( <img diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 8ad6b8b8..6e294171 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -7,6 +7,7 @@ import { LemmySettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers import { DribbbleDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/dribbble.dto'; import { DiscordDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/discord.dto'; import { SlackDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/slack.dto'; +import { KickDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/kick.dto'; import { InstagramDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/instagram.dto'; import { LinkedinDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/linkedin.dto'; import { IsIn } from 'class-validator'; @@ -29,6 +30,7 @@ export type AllProvidersSettings = | ProviderExtension<'tiktok', TikTokDto> | ProviderExtension<'discord', DiscordDto> | ProviderExtension<'slack', SlackDto> + | ProviderExtension<'kick', KickDto> | ProviderExtension<'x', XDto> | ProviderExtension<'linkedin', LinkedinDto> | ProviderExtension<'linkedin-page', LinkedinDto> @@ -61,6 +63,7 @@ export const allProviders = (setEmpty?: any) => { { value: TikTokDto, name: 'tiktok' }, { value: DiscordDto, name: 'discord' }, { value: SlackDto, name: 'slack' }, + { value: KickDto, name: 'kick' }, { value: XDto, name: 'x' }, { value: LinkedinDto, name: 'linkedin' }, { value: LinkedinDto, name: 'linkedin-page' }, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts new file mode 100644 index 00000000..d5a4ab7a --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts @@ -0,0 +1,13 @@ +import { IsDefined, IsString, MinLength } from 'class-validator'; +import { JSONSchema } from 'class-validator-jsonschema'; + +export class KickDto { + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Broadcaster user ID to post to', + }) + broadcasterUserId: string; +} + diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 3ef95146..3dad09d7 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -29,6 +29,7 @@ import { VkProvider } from '@gitroom/nestjs-libraries/integrations/social/vk.pro import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social/wordpress.provider'; import { ListmonkProvider } from '@gitroom/nestjs-libraries/integrations/social/listmonk.provider'; import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; +import { KickProvider } from '@gitroom/nestjs-libraries/integrations/social/kick.provider'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ @@ -47,6 +48,7 @@ export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ new DribbbleProvider(), new DiscordProvider(), new SlackProvider(), + new KickProvider(), new MastodonProvider(), new BlueskyProvider(), new LemmyProvider(), diff --git a/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts b/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts new file mode 100644 index 00000000..f9b08b0c --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts @@ -0,0 +1,271 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { KickDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/kick.dto'; +import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; +import { createHash, randomBytes } from 'crypto'; + +export class KickProvider extends SocialAbstract implements SocialProvider { + override maxConcurrentJob = 3; + identifier = 'kick'; + name = 'Kick'; + isBetweenSteps = false; + editor = 'normal' as const; + scopes = ['chat:write', 'user:read', 'channel:read']; + dto = KickDto; + + maxLength() { + return 500; // Kick chat message max length + } + + private generatePKCE() { + const codeVerifier = randomBytes(64).toString('base64url'); + const challenge = Buffer.from( + createHash('sha256').update(codeVerifier).digest() + ) + .toString('base64') + .replace(/=*$/g, '') + .replace(/\+/g, '-') + .replace(/\//g, '_'); + + return { codeVerifier, codeChallenge: challenge }; + } + + async refreshToken(refreshToken: string): Promise<AuthTokenDetails> { + const response = await this.fetch('https://id.kick.com/oauth/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'refresh_token', + client_id: process.env.KICK_CLIENT_ID!, + client_secret: process.env.KICK_SECRET!, + refresh_token: refreshToken, + }), + }); + + const { access_token, refresh_token, expires_in } = await response.json(); + + // Get user info + const userInfo = await this.getUserInfo(access_token); + + return { + refreshToken: refresh_token, + expiresIn: expires_in, + accessToken: access_token, + id: userInfo.id, + name: userInfo.name, + picture: userInfo.picture || '', + username: userInfo.username, + }; + } + + async generateAuthUrl() { + const state = makeId(32); + const { codeVerifier, codeChallenge } = this.generatePKCE(); + + const redirectUri = `${process.env.FRONTEND_URL}/integrations/social/kick`; + + const url = + `https://id.kick.com/oauth/authorize` + + `?response_type=code` + + `&client_id=${process.env.KICK_CLIENT_ID}` + + `&redirect_uri=${encodeURIComponent(redirectUri)}` + + `&scope=${encodeURIComponent(this.scopes.join(' '))}` + + `&state=${state}` + + `&code_challenge=${codeChallenge}` + + `&code_challenge_method=S256`; + + return { + url, + codeVerifier, + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const redirectUri = `${process.env.FRONTEND_URL}/integrations/social/kick${ + params.refresh ? `?refresh=${params.refresh}` : '' + }`; + + const tokenResponse = await this.fetch('https://id.kick.com/oauth/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'authorization_code', + client_id: process.env.KICK_CLIENT_ID!, + client_secret: process.env.KICK_SECRET!, + redirect_uri: redirectUri, + code: params.code, + code_verifier: params.codeVerifier, + }), + }); + + const { access_token, refresh_token, expires_in, scope } = + await tokenResponse.json(); + + // Get user info + const userInfo = await this.getUserInfo(access_token); + + return { + id: userInfo.id, + name: userInfo.name, + accessToken: access_token, + refreshToken: refresh_token, + expiresIn: expires_in, + picture: userInfo.picture || '', + username: userInfo.username, + }; + } + + private async getUserInfo( + accessToken: string + ): Promise<{ id: string; name: string; username: string; picture?: string }> { + // Use token introspect to get basic info, then fetch user details + // Try to get full user info from the API + const userResponse = await fetch('https://api.kick.com/public/v1/users', { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + const userData = await userResponse.json(); + const user = userData.data?.[0] || userData.data; + return { + id: String(user.user_id || user.id), + name: user.name, + username: user.name, + picture: user.profile_picture || '', + }; + } + + @Tool({ + description: 'Get list of channels/broadcasters the user can post to', + dataSchema: [], + }) + async channels(accessToken: string, params: any, id: string) { + // For Kick, when using a user token, we need to know which broadcaster to post to + // The user posting as themselves would post to their own channel + // Try to get channels the user has access to + + try { + const response = await fetch('https://api.kick.com/public/v1/channels', { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + + if (response.ok) { + const data = await response.json(); + const channels = data.data || []; + return channels.map((channel: any) => ({ + id: String( + channel.broadcaster_user_id || channel.user_id || channel.id + ), + name: channel.slug || channel.username || channel.name, + })); + } + } catch (e) { + // Fall back to returning the authenticated user's info + } + + // If no channels found, return the user's own channel + // The id parameter is the user's own ID from authentication + return [ + { + id: id, + name: 'My Channel', + }, + ]; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const [firstPost] = postDetails; + const broadcasterUserId = firstPost.settings.broadcasterUserId; + + // Post chat message to Kick + // Note: Kick chat doesn't support media attachments directly in messages + const response = await this.fetch('https://api.kick.com/public/v1/chat', { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + type: 'user', + content: firstPost.message.substring(0, 500), // Ensure max length + broadcaster_user_id: parseInt(broadcasterUserId, 10), + }), + }); + + const data = await response.json(); + + return [ + { + id: firstPost.id, + postId: data.data?.message_id || data.message_id || makeId(10), + releaseURL: `https://kick.com/${integration.profile || 'channel'}`, + status: data.data?.is_sent || data.is_sent ? 'posted' : 'error', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const [commentPost] = postDetails; + const broadcasterUserId = commentPost.settings.broadcasterUserId; + + // Kick supports reply_to_message_id for replies + const response = await this.fetch('https://api.kick.com/public/v1/chat', { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + type: 'user', + content: commentPost.message.substring(0, 500), + broadcaster_user_id: parseInt(broadcasterUserId, 10), + reply_to_message_id: lastCommentId || postId, + }), + }); + + const data = await response.json(); + + return [ + { + id: commentPost.id, + postId: data.data?.message_id || data.message_id || makeId(10), + releaseURL: `https://kick.com/${integration.profile || 'channel'}`, + status: data.data?.is_sent || data.is_sent ? 'posted' : 'error', + }, + ]; + } +} diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json index faadd5ca..ad1642a4 100644 --- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json +++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json @@ -694,5 +694,6 @@ "watch_tutorial_title": "Learn How to Use Postiz", "watch_tutorial_description": "Watch this short video to learn how to get the most out of Postiz", "back": "Back", - "get_started": "Get Started" -} + "get_started": "Get Started", + "kick_select_channel": "Select Channel" +} \ No newline at end of file From e9f2660002385c6463472fe9e3ed5cdb61e1914b Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 24 Jan 2026 15:36:06 +0700 Subject: [PATCH 174/340] feat: remove channel selection --- .../providers/kick/kick.channel.select.tsx | 63 ------------------- .../providers/kick/kick.provider.tsx | 19 +----- .../dtos/posts/providers-settings/kick.dto.ts | 14 +---- .../src/integrations/social/kick.provider.ts | 48 +------------- 4 files changed, 6 insertions(+), 138 deletions(-) delete mode 100644 apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx diff --git a/apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx b/apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx deleted file mode 100644 index 3bdadf04..00000000 --- a/apps/frontend/src/components/new-launch/providers/kick/kick.channel.select.tsx +++ /dev/null @@ -1,63 +0,0 @@ -'use client'; - -import { FC, useEffect, useState } from 'react'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; -import { Select } from '@gitroom/react/form/select'; -import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; - -export const KickChannelSelect: FC<{ - name: string; - onChange: (event: { - target: { - value: string; - name: string; - }; - }) => void; -}> = (props) => { - const { onChange, name } = props; - const t = useT(); - const customFunc = useCustomProviderFunction(); - const [channels, setChannels] = useState([]); - const { getValues } = useSettings(); - const [currentChannel, setCurrentChannel] = useState<string | undefined>(); - - const onChangeInner = (event: { - target: { - value: string; - name: string; - }; - }) => { - setCurrentChannel(event.target.value); - onChange(event); - }; - - useEffect(() => { - customFunc.get('channels').then((data) => setChannels(data)); - const settings = getValues()[props.name]; - if (settings) { - setCurrentChannel(settings); - } - }, []); - - if (!channels.length) { - return null; - } - - return ( - <Select - name={name} - label={t('kick_select_channel', 'Select Channel')} - onChange={onChangeInner} - value={currentChannel} - > - <option value="">{t('select_1', '--Select--')}</option> - {channels.map((channel: any) => ( - <option key={channel.id} value={channel.id}> - {channel.name} - </option> - ))} - </Select> - ); -}; - diff --git a/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx b/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx index d603c5f9..a18a7ea7 100644 --- a/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/kick/kick.provider.tsx @@ -4,27 +4,14 @@ import { PostComment, withProvider, } from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; -import { FC } from 'react'; -import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; -import { KickChannelSelect } from '@gitroom/frontend/components/new-launch/providers/kick/kick.channel.select'; -import { KickDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/kick.dto'; - -const KickComponent: FC = () => { - const form = useSettings(); - return ( - <div> - <KickChannelSelect {...form.register('broadcasterUserId')} /> - </div> - ); -}; export default withProvider({ postComment: PostComment.COMMENT, + comments: 'no-media', minimumCharacters: [], - SettingsComponent: KickComponent, + SettingsComponent: undefined, CustomPreviewComponent: undefined, - dto: KickDto, + dto: undefined, checkValidity: undefined, maximumCharacters: 500, }); - diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts index d5a4ab7a..caf6959f 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/kick.dto.ts @@ -1,13 +1 @@ -import { IsDefined, IsString, MinLength } from 'class-validator'; -import { JSONSchema } from 'class-validator-jsonschema'; - -export class KickDto { - @MinLength(1) - @IsDefined() - @IsString() - @JSONSchema({ - description: 'Broadcaster user ID to post to', - }) - broadcasterUserId: string; -} - +export class KickDto {} diff --git a/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts b/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts index f9b08b0c..8c2e66ef 100644 --- a/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/kick.provider.ts @@ -9,7 +9,6 @@ import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.ab import dayjs from 'dayjs'; import { Integration } from '@prisma/client'; import { KickDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/kick.dto'; -import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; import { createHash, randomBytes } from 'crypto'; export class KickProvider extends SocialAbstract implements SocialProvider { @@ -154,47 +153,6 @@ export class KickProvider extends SocialAbstract implements SocialProvider { }; } - @Tool({ - description: 'Get list of channels/broadcasters the user can post to', - dataSchema: [], - }) - async channels(accessToken: string, params: any, id: string) { - // For Kick, when using a user token, we need to know which broadcaster to post to - // The user posting as themselves would post to their own channel - // Try to get channels the user has access to - - try { - const response = await fetch('https://api.kick.com/public/v1/channels', { - method: 'GET', - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }); - - if (response.ok) { - const data = await response.json(); - const channels = data.data || []; - return channels.map((channel: any) => ({ - id: String( - channel.broadcaster_user_id || channel.user_id || channel.id - ), - name: channel.slug || channel.username || channel.name, - })); - } - } catch (e) { - // Fall back to returning the authenticated user's info - } - - // If no channels found, return the user's own channel - // The id parameter is the user's own ID from authentication - return [ - { - id: id, - name: 'My Channel', - }, - ]; - } - async post( id: string, accessToken: string, @@ -202,7 +160,6 @@ export class KickProvider extends SocialAbstract implements SocialProvider { integration: Integration ): Promise<PostResponse[]> { const [firstPost] = postDetails; - const broadcasterUserId = firstPost.settings.broadcasterUserId; // Post chat message to Kick // Note: Kick chat doesn't support media attachments directly in messages @@ -215,7 +172,7 @@ export class KickProvider extends SocialAbstract implements SocialProvider { body: JSON.stringify({ type: 'user', content: firstPost.message.substring(0, 500), // Ensure max length - broadcaster_user_id: parseInt(broadcasterUserId, 10), + broadcaster_user_id: parseInt(id, 10), }), }); @@ -240,7 +197,6 @@ export class KickProvider extends SocialAbstract implements SocialProvider { integration: Integration ): Promise<PostResponse[]> { const [commentPost] = postDetails; - const broadcasterUserId = commentPost.settings.broadcasterUserId; // Kick supports reply_to_message_id for replies const response = await this.fetch('https://api.kick.com/public/v1/chat', { @@ -252,7 +208,7 @@ export class KickProvider extends SocialAbstract implements SocialProvider { body: JSON.stringify({ type: 'user', content: commentPost.message.substring(0, 500), - broadcaster_user_id: parseInt(broadcasterUserId, 10), + broadcaster_user_id: parseInt(id, 10), reply_to_message_id: lastCommentId || postId, }), }); From ec5210be5133076c7d45c32453bc97396f242243 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 24 Jan 2026 16:35:54 +0700 Subject: [PATCH 175/340] feat: add twitch --- .../public/icons/platforms/twitch.png | Bin 0 -> 14569 bytes .../providers/show.all.providers.tsx | 5 + .../providers/twitch/twitch.provider.tsx | 95 ++++++ .../all.providers.settings.ts | 3 + .../posts/providers-settings/twitch.dto.ts | 11 + .../src/integrations/integration.manager.ts | 2 + .../integrations/social/twitch.provider.ts | 291 ++++++++++++++++++ 7 files changed, 407 insertions(+) create mode 100644 apps/frontend/public/icons/platforms/twitch.png create mode 100644 apps/frontend/src/components/new-launch/providers/twitch/twitch.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/twitch.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/twitch.provider.ts diff --git a/apps/frontend/public/icons/platforms/twitch.png b/apps/frontend/public/icons/platforms/twitch.png new file mode 100644 index 0000000000000000000000000000000000000000..c33132506debf5bae4e8d09329f666e39dc85723 GIT binary patch literal 14569 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 034519b0..29a71721 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -16,6 +16,7 @@ import ThreadsProvider from '@gitroom/frontend/components/new-launch/providers/t import DiscordProvider from '@gitroom/frontend/components/new-launch/providers/discord/discord.provider'; import SlackProvider from '@gitroom/frontend/components/new-launch/providers/slack/slack.provider'; import KickProvider from '@gitroom/frontend/components/new-launch/providers/kick/kick.provider'; +import TwitchProvider from '@gitroom/frontend/components/new-launch/providers/twitch/twitch.provider'; import MastodonProvider from '@gitroom/frontend/components/new-launch/providers/mastodon/mastodon.provider'; import BlueskyProvider from '@gitroom/frontend/components/new-launch/providers/bluesky/bluesky.provider'; import LemmyProvider from '@gitroom/frontend/components/new-launch/providers/lemmy/lemmy.provider'; @@ -108,6 +109,10 @@ export const Providers = [ identifier: 'kick', component: KickProvider, }, + { + identifier: 'twitch', + component: TwitchProvider, + }, { identifier: 'mastodon', component: MastodonProvider, diff --git a/apps/frontend/src/components/new-launch/providers/twitch/twitch.provider.tsx b/apps/frontend/src/components/new-launch/providers/twitch/twitch.provider.tsx new file mode 100644 index 00000000..6ddccf88 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/twitch/twitch.provider.tsx @@ -0,0 +1,95 @@ +'use client'; + +import { FC } from 'react'; +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { TwitchDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/twitch.dto'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { Select } from '@gitroom/react/form/select'; +import { useWatch } from 'react-hook-form'; + +const messageTypes = [ + { + label: 'Chat Message', + value: 'message', + }, + { + label: 'Announcement', + value: 'announcement', + }, +]; + +const announcementColors = [ + { + label: 'Primary (Default)', + value: 'primary', + }, + { + label: 'Blue', + value: 'blue', + }, + { + label: 'Green', + value: 'green', + }, + { + label: 'Orange', + value: 'orange', + }, + { + label: 'Purple', + value: 'purple', + }, +]; + +const TwitchSettings: FC = () => { + const { register, control } = useSettings(); + const messageType = useWatch({ + control, + name: 'messageType', + }); + + return ( + <div className="flex flex-col"> + <Select + label="Message Type" + {...register('messageType', { + value: 'message', + })} + > + {messageTypes.map((t) => ( + <option key={t.value} value={t.value}> + {t.label} + </option> + ))} + </Select> + {messageType === 'announcement' && ( + <Select + label="Announcement Color" + {...register('announcementColor', { + value: 'primary', + })} + > + {announcementColors.map((c) => ( + <option key={c.value} value={c.value}> + {c.label} + </option> + ))} + </Select> + )} + </div> + ); +}; + +export default withProvider({ + postComment: PostComment.COMMENT, + comments: 'no-media', + minimumCharacters: [], + SettingsComponent: TwitchSettings, + CustomPreviewComponent: undefined, + dto: TwitchDto, + checkValidity: undefined, + maximumCharacters: 500, +}); diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 6e294171..a0c7f98c 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -8,6 +8,7 @@ import { DribbbleDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-sett import { DiscordDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/discord.dto'; import { SlackDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/slack.dto'; import { KickDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/kick.dto'; +import { TwitchDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/twitch.dto'; import { InstagramDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/instagram.dto'; import { LinkedinDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/linkedin.dto'; import { IsIn } from 'class-validator'; @@ -31,6 +32,7 @@ export type AllProvidersSettings = | ProviderExtension<'discord', DiscordDto> | ProviderExtension<'slack', SlackDto> | ProviderExtension<'kick', KickDto> + | ProviderExtension<'twitch', TwitchDto> | ProviderExtension<'x', XDto> | ProviderExtension<'linkedin', LinkedinDto> | ProviderExtension<'linkedin-page', LinkedinDto> @@ -64,6 +66,7 @@ export const allProviders = (setEmpty?: any) => { { value: DiscordDto, name: 'discord' }, { value: SlackDto, name: 'slack' }, { value: KickDto, name: 'kick' }, + { value: TwitchDto, name: 'twitch' }, { value: XDto, name: 'x' }, { value: LinkedinDto, name: 'linkedin' }, { value: LinkedinDto, name: 'linkedin-page' }, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/twitch.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/twitch.dto.ts new file mode 100644 index 00000000..23dfea12 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/twitch.dto.ts @@ -0,0 +1,11 @@ +import { IsIn, IsOptional, IsString } from 'class-validator'; + +export class TwitchDto { + @IsIn(['message', 'announcement']) + @IsOptional() + messageType?: 'message' | 'announcement'; + + @IsIn(['primary', 'blue', 'green', 'orange', 'purple']) + @IsOptional() + announcementColor?: 'primary' | 'blue' | 'green' | 'orange' | 'purple'; +} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 3dad09d7..4e1d2f4e 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -30,6 +30,7 @@ import { WordpressProvider } from '@gitroom/nestjs-libraries/integrations/social import { ListmonkProvider } from '@gitroom/nestjs-libraries/integrations/social/listmonk.provider'; import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.provider'; import { KickProvider } from '@gitroom/nestjs-libraries/integrations/social/kick.provider'; +import { TwitchProvider } from '@gitroom/nestjs-libraries/integrations/social/twitch.provider'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ @@ -49,6 +50,7 @@ export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ new DiscordProvider(), new SlackProvider(), new KickProvider(), + new TwitchProvider(), new MastodonProvider(), new BlueskyProvider(), new LemmyProvider(), diff --git a/libraries/nestjs-libraries/src/integrations/social/twitch.provider.ts b/libraries/nestjs-libraries/src/integrations/social/twitch.provider.ts new file mode 100644 index 00000000..c8848036 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/twitch.provider.ts @@ -0,0 +1,291 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { Integration } from '@prisma/client'; +import { TwitchDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/twitch.dto'; +import { timer } from '@gitroom/helpers/utils/timer'; + +export class TwitchProvider extends SocialAbstract implements SocialProvider { + override maxConcurrentJob = 1; + identifier = 'twitch'; + name = 'Twitch'; + isBetweenSteps = false; + editor = 'normal' as const; + scopes = ['user:write:chat', 'user:read:chat', 'moderator:manage:announcements']; + dto = TwitchDto; + + maxLength() { + return 500; // Twitch chat message max length + } + + async refreshToken(refreshToken: string): Promise<AuthTokenDetails> { + const response = await this.fetch('https://id.twitch.tv/oauth2/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'refresh_token', + client_id: process.env.TWITCH_CLIENT_ID!, + client_secret: process.env.TWITCH_CLIENT_SECRET!, + refresh_token: refreshToken, + }), + }); + + const { access_token, refresh_token, expires_in } = await response.json(); + + // Get user info + const userInfo = await this.getUserInfo(access_token); + + return { + refreshToken: refresh_token, + expiresIn: expires_in, + accessToken: access_token, + id: userInfo.id, + name: userInfo.name, + picture: userInfo.picture || '', + username: userInfo.username, + }; + } + + async generateAuthUrl() { + const state = makeId(32); + + const redirectUri = `${process.env.FRONTEND_URL}/integrations/social/twitch`; + + const url = + `https://id.twitch.tv/oauth2/authorize` + + `?response_type=code` + + `&client_id=${process.env.TWITCH_CLIENT_ID}` + + `&redirect_uri=${encodeURIComponent(redirectUri)}` + + `&scope=${encodeURIComponent(this.scopes.join(' '))}` + + `&state=${state}`; + + return { + url, + codeVerifier: makeId(10), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const redirectUri = `${process.env.FRONTEND_URL}/integrations/social/twitch${ + params.refresh ? `?refresh=${params.refresh}` : '' + }`; + + const tokenResponse = await this.fetch('https://id.twitch.tv/oauth2/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams({ + grant_type: 'authorization_code', + client_id: process.env.TWITCH_CLIENT_ID!, + client_secret: process.env.TWITCH_CLIENT_SECRET!, + redirect_uri: redirectUri, + code: params.code, + }), + }); + + const { access_token, refresh_token, expires_in } = + await tokenResponse.json(); + + // Get user info + const userInfo = await this.getUserInfo(access_token); + + return { + id: userInfo.id, + name: userInfo.name, + accessToken: access_token, + refreshToken: refresh_token, + expiresIn: expires_in, + picture: userInfo.picture || '', + username: userInfo.username, + }; + } + + private async getUserInfo( + accessToken: string + ): Promise<{ id: string; name: string; username: string; picture?: string }> { + const userResponse = await fetch('https://api.twitch.tv/helix/users', { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Client-Id': process.env.TWITCH_CLIENT_ID!, + }, + }); + + const userData = await userResponse.json(); + const user = userData.data?.[0]; + + return { + id: String(user.id), + name: user.display_name, + username: user.login, + picture: user.profile_image_url || '', + }; + } + + private async sendAnnouncement( + broadcasterId: string, + accessToken: string, + message: string, + color: string = 'primary' + ): Promise<{ success: boolean }> { + await fetch( + `https://api.twitch.tv/helix/chat/announcements?broadcaster_id=${broadcasterId}&moderator_id=${broadcasterId}`, + { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Client-Id': process.env.TWITCH_CLIENT_ID!, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + message: message.substring(0, 500), + color, + }), + } + ); + + // Announcements return 204 No Content on success + return { success: true }; + } + + private async sendChatMessage( + broadcasterId: string, + accessToken: string, + message: string, + replyToMessageId?: string + ): Promise<{ messageId: string; isSent: boolean }> { + const body: Record<string, string> = { + broadcaster_id: broadcasterId, + sender_id: broadcasterId, + message: message.substring(0, 500), + }; + + if (replyToMessageId) { + body.reply_parent_message_id = replyToMessageId; + } + + const response = await this.fetch( + 'https://api.twitch.tv/helix/chat/messages', + { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Client-Id': process.env.TWITCH_CLIENT_ID!, + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + } + ); + + const data = await response.json(); + + return { + messageId: data.data?.[0]?.message_id || makeId(10), + isSent: data.data?.[0]?.is_sent ?? false, + }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + await timer(2000); + const [firstPost] = postDetails; + const messageType = firstPost.settings?.messageType || 'message'; + const announcementColor = firstPost.settings?.announcementColor || 'primary'; + + if (messageType === 'announcement') { + const result = await this.sendAnnouncement( + id, + accessToken, + firstPost.message, + announcementColor + ); + + return [ + { + id: firstPost.id, + postId: makeId(10), // Announcements don't return a message ID + releaseURL: `https://twitch.tv/${integration.profile || integration.providerIdentifier}`, + status: result.success ? 'posted' : 'error', + }, + ]; + } + + // Regular chat message + const result = await this.sendChatMessage(id, accessToken, firstPost.message); + + return [ + { + id: firstPost.id, + postId: result.messageId, + releaseURL: `https://twitch.tv/${integration.profile || integration.providerIdentifier}`, + status: result.isSent ? 'posted' : 'error', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + await timer(2000); + const [commentPost] = postDetails; + const messageType = commentPost.settings?.messageType || 'message'; + const announcementColor = commentPost.settings?.announcementColor || 'primary'; + + if (messageType === 'announcement') { + const result = await this.sendAnnouncement( + id, + accessToken, + commentPost.message, + announcementColor + ); + + return [ + { + id: commentPost.id, + postId: makeId(10), + releaseURL: `https://twitch.tv/${integration.profile || integration.providerIdentifier}`, + status: result.success ? 'posted' : 'error', + }, + ]; + } + + // Regular chat message with reply + const result = await this.sendChatMessage( + id, + accessToken, + commentPost.message, + lastCommentId || postId + ); + + return [ + { + id: commentPost.id, + postId: result.messageId, + releaseURL: `https://twitch.tv/${integration.profile || integration.providerIdentifier}`, + status: result.isSent ? 'posted' : 'error', + }, + ]; + } +} From f39ab5dc80ba58b235b9f3f79b82f1a93fe7a533 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 24 Jan 2026 17:40:44 +0700 Subject: [PATCH 176/340] fix: share post not only with community --- libraries/nestjs-libraries/src/integrations/social/x.provider.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index 0f79645d..e96395ae 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -397,6 +397,7 @@ export class XProvider extends SocialAbstract implements SocialProvider { }), ...(firstPost?.settings?.community ? { + share_with_followers: true, community_id: firstPost?.settings?.community?.split('/').pop() || '', } From 1d9af510e51c655dc9fe6a7cb9c64cf337d01bb3 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 26 Jan 2026 11:51:37 +0700 Subject: [PATCH 177/340] fix: tiktok --- apps/frontend/src/components/new-launch/editor.tsx | 13 +++++++++---- .../src/integrations/social/tiktok.provider.ts | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index 87ff52d2..b7592150 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -571,7 +571,7 @@ export const Editor: FC<{ uppy.clear(); }, allowedFileTypes: 'image/*,video/mp4', - onStart: () => setLoading(true), + onStart: () => {}, onEnd: () => setLoading(false), }); @@ -634,7 +634,9 @@ export const Editor: FC<{ return; } - setLoading(true); + if (files.length > 0) { + setLoading(true); + } for (const file of files) { uppy.addFile(file); @@ -646,8 +648,11 @@ export const Editor: FC<{ const { getRootProps, isDragActive } = useDropzone({ onDrop: (files) => { if (loading) { - toaster.show('Upload current in progress, please wait and then try again.', 'warning'); - return ; + toaster.show( + 'Upload current in progress, please wait and then try again.', + 'warning' + ); + return; } onDrop(files); }, diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index d6b52960..96a0bbb1 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -52,7 +52,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { if (body.indexOf('scope_not_authorized') > -1) { return { - type: 'refresh-token' as const, + type: 'bad-body' as const, value: 'Missing required permissions, please re-authenticate with all scopes', }; @@ -60,7 +60,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { if (body.indexOf('scope_permission_missed') > -1) { return { - type: 'refresh-token' as const, + type: 'bad-body' as const, value: 'Additional permissions required, please re-authenticate', }; } From 7e92a767e4a319a12402d0ffc3b683703d6383cd Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 26 Jan 2026 12:28:50 +0700 Subject: [PATCH 178/340] remove focused channel --- apps/frontend/src/components/launches/menu/menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index e3d92045..8efd153a 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -216,7 +216,7 @@ export const Menu: FC<{ mutate={reloadCalendarView} integrations={integrations} selectedChannels={[integration.id]} - focusedChannel={integration.id} + // focusedChannel={integration.id} date={dayjs.utc(date).local()} /> ), From 8c4e69dd3de8598124137f89c58f7f7e2e61729f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 27 Jan 2026 17:48:59 +0700 Subject: [PATCH 179/340] feat: another safety to force termination of a workflow --- .../nestjs-libraries/src/database/prisma/posts/posts.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index c2b5a780..7d2deb10 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -570,6 +570,7 @@ export class PostsService { ?.workflow.start('postWorkflowV101', { workflowId: `post_${postId}`, taskQueue: 'main', + workflowIdConflictPolicy: 'TERMINATE_EXISTING', args: [ { taskQueue: taskQueue, From defd9e0a63ce1c2b06f3c76070713cb97e0dc2eb Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 27 Jan 2026 18:13:02 +0700 Subject: [PATCH 180/340] feat: scheduling edge-cases --- .../src/components/launches/calendar.tsx | 2 +- .../database/prisma/posts/posts.repository.ts | 3 ++- .../database/prisma/posts/posts.service.ts | 24 +++++++++++++++---- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index c2143821..3d08ddd7 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -525,7 +525,7 @@ export const CalendarColumn: FC<{ modal.openModal({ title: t('select_set', 'Select a Set'), closeOnClickOutside: true, - askClose: true, + askClose: false, closeOnEscape: true, withCloseButton: true, onClose: () => resolve('exit'), diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 5b735cdf..04490afc 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -330,13 +330,14 @@ export class PostsRepository { return update; } - async changeDate(orgId: string, id: string, date: string) { + async changeDate(orgId: string, id: string, date: string, isDraft: boolean) { return this._post.model.post.update({ where: { organizationId: orgId, id, }, data: { + state: isDraft ? 'DRAFT' : 'QUEUE', publishDate: dayjs(date).toDate(), }, }); diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 7d2deb10..5ed3feb4 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -541,7 +541,12 @@ export class PostsService { return this._postRepository.getPostByForWebhookId(id); } - async startWorkflow(taskQueue: string, postId: string, orgId: string) { + async startWorkflow( + taskQueue: string, + postId: string, + orgId: string, + state: State + ) { try { const workflows = this._temporalService.client .getRawClient() @@ -564,6 +569,10 @@ export class PostsService { } } catch (err) {} + if (state === 'DRAFT') { + return; + } + try { await this._temporalService.client .getRawClient() @@ -621,7 +630,8 @@ export class PostsService { this.startWorkflow( post.settings.__type.split('-')[0].toLowerCase(), posts[0].id, - orgId + orgId, + posts[0].state ).catch((err) => {}); Sentry.metrics.count('post_created', 1); @@ -644,13 +654,19 @@ export class PostsService { async changeDate(orgId: string, id: string, date: string) { const getPostById = await this._postRepository.getPostById(id, orgId); - const newDate = await this._postRepository.changeDate(orgId, id, date); + const newDate = await this._postRepository.changeDate( + orgId, + id, + date, + getPostById.state === 'DRAFT' + ); try { await this.startWorkflow( getPostById.integration.providerIdentifier.split('-')[0].toLowerCase(), getPostById.id, - orgId + orgId, + getPostById.state ); } catch (err) {} From 2813ed01f9c7460637e2c8b2a6f5d67d7ed064b2 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 27 Jan 2026 21:44:30 +0700 Subject: [PATCH 181/340] feat: protected reposting post when clicking update --- .../src/api/routes/posts.controller.ts | 5 +- .../src/components/launches/calendar.tsx | 68 ++++++++++++++++++- .../components/new-launch/manage.modal.tsx | 66 ++++++++++++++---- .../database/prisma/posts/posts.repository.ts | 27 ++++++-- .../database/prisma/posts/posts.service.ts | 45 +++++++----- .../src/dtos/posts/create.post.dto.ts | 4 +- 6 files changed, 177 insertions(+), 38 deletions(-) diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index ce03df00..69be508d 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -171,9 +171,10 @@ export class PostsController { changeDate( @GetOrgFromRequest() org: Organization, @Param('id') id: string, - @Body('date') date: string + @Body('date') date: string, + @Body('action') action: 'schedule' | 'update' = 'schedule' ) { - return this._postsService.changeDate(org.id, id, date); + return this._postsService.changeDate(org.id, id, date, action); } @Post('/separate-posts') diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 3d08ddd7..9dbd8e42 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -54,6 +54,7 @@ import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; +import { Button } from '@gitroom/react/form/button'; // Extend dayjs with necessary plugins extend(isSameOrAfter); @@ -418,6 +419,68 @@ export const CalendarColumn: FC<{ accept: 'post', drop: async (item: any) => { if (isBeforeNow) return; + + // Find the post to check its state + const post = posts.find((p) => p.id === item.id); + let action: 'schedule' | 'update' = 'schedule'; + + // Check if post is already published or queued in the past + if ( + post && + (post.state === 'PUBLISHED' || + (post.state === 'QUEUE' && dayjs().isAfter(dayjs.utc(post.publishDate)))) + ) { + const whatToDo = await new Promise<'schedule' | 'update' | 'cancel'>( + (resolve) => { + modal.openModal({ + title: t('what_do_you_want_to_do', 'What do you want to do?'), + children: ( + <div className="flex flex-col"> + <div className="text-[20px] mb-[20px]"> + {t( + 'post_already_published_drag', + 'This post was already published, what do you want to do?' + )} + </div> + <div className="flex w-full gap-[10px]"> + <div className="flex-1 flex"> + <Button + type="button" + className="flex-1" + onClick={() => { + modal.closeAll(); + resolve('update'); + }} + > + {t('just_update_post_details', 'Just update the post details')} + </Button> + </div> + <div className="flex-1 flex"> + <Button + type="button" + className="flex-1" + onClick={() => { + modal.closeAll(); + resolve('schedule'); + }} + > + {t('reschedule_post', 'Reschedule the post')} + </Button> + </div> + </div> + </div> + ), + onClose: () => resolve('cancel'), + }); + } + ); + + if (whatToDo === 'cancel') { + return; + } + action = whatToDo; + } + if (!item.interval) { changeDate(item.id, getDate); } @@ -425,10 +488,11 @@ export const CalendarColumn: FC<{ method: 'PUT', body: JSON.stringify({ date: getDate.utc().format('YYYY-MM-DDTHH:mm:ss'), + action, }), }); if (status !== 500) { - if (item.interval) { + if (item.interval || action === 'schedule') { reloadCalendarView(); return; } @@ -438,7 +502,7 @@ export const CalendarColumn: FC<{ collect: (monitor) => ({ canDrop: isBeforeNow ? false : !!monitor.canDrop() && !!monitor.isOver(), }), - })); + }), [posts]); const editPost = useCallback( ( diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index 8d1f9708..d7cf5626 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -42,6 +42,8 @@ import { } from '@gitroom/frontend/components/ui/icons'; import { useHasScroll } from '@gitroom/frontend/components/ui/is.scroll.hook'; import { useShortlinkPreference } from '@gitroom/frontend/components/settings/shortlink-preference.component'; +import dayjs from 'dayjs'; +import { Button } from '@gitroom/react/form/button'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -108,14 +110,9 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { return ( <div className="flex items-center gap-[10px]"> <div className="relative"> - <SettingsIcon - size={15} - className="text-white" - /> - </div> - <div> - Settings + <SettingsIcon size={15} className="text-white" /> </div> + <div>Settings</div> </div> ); } @@ -202,7 +199,51 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { }, [existingData, mutate, modal]); const schedule = useCallback( - (type: 'draft' | 'now' | 'schedule') => async () => { + (type: 'draft' | 'now' | 'schedule' | 'update') => async () => { + if ( + (type === 'now' || type === 'schedule') && + (existingData?.posts?.[0]?.state === 'PUBLISHED' || + (existingData?.posts?.[0]?.state === 'QUEUE' && + dayjs().isAfter(date.utc()))) + ) { + const whatToDo = await new Promise((resolve) => { + modal.openModal({ + title: 'What do you want to do?', + children: ( + <div className="flex flex-col"> + <div className="text-[20px] mb-[20px]"> + This post was already published, what do you want to do? + </div> + <div className="flex w-full gap-[10px]"> + <div className="flex-1 flex"> + <Button + type="button" + className="flex-1" + onClick={() => resolve('update')} + > + Just update the post details + </Button> + </div> + <div className="flex-1 flex"> + <Button + type="button" + className="flex-1" + onClick={() => resolve('republish')} + > + Republish the post + </Button> + </div> + </div> + </div> + ), + }); + }); + + if (whatToDo === 'update') { + type = 'update'; + } + } + setLoading(true); const checkAllValid = await ref.current.checkAllValid(); if (type !== 'draft') { @@ -476,10 +517,11 @@ export const ManageModal: FC<AddEditModalProps> = (props) => { 'text-[14px] text-textColor font-[500] relative' )} > - <div - className="absolute left-0 top-0 w-full h-full flex flex-col overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor" - > - <div id="social-settings" className="flex flex-col gap-[20px] bg-newBgColor" /> + <div className="absolute left-0 top-0 w-full h-full flex flex-col overflow-x-hidden overflow-y-auto scrollbar scrollbar-thumb-newBgColorInner scrollbar-track-newColColor"> + <div + id="social-settings" + className="flex flex-col gap-[20px] bg-newBgColor" + /> </div> </div> <style> diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 04490afc..3041f1d1 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -330,15 +330,29 @@ export class PostsRepository { return update; } - async changeDate(orgId: string, id: string, date: string, isDraft: boolean) { + async changeDate( + orgId: string, + id: string, + date: string, + isDraft: boolean, + action: 'schedule' | 'update' = 'schedule' + ) { return this._post.model.post.update({ where: { organizationId: orgId, id, }, data: { - state: isDraft ? 'DRAFT' : 'QUEUE', publishDate: dayjs(date).toDate(), + // schedule: set state to QUEUE (or DRAFT if it was a draft) + // update: don't change the state + ...(action === 'schedule' + ? { + state: isDraft ? 'DRAFT' : 'QUEUE', + releaseId: null, + releaseURL: null, + } + : {}), }, }); } @@ -366,7 +380,7 @@ export class PostsRepository { } async createOrUpdatePost( - state: 'draft' | 'schedule' | 'now', + state: 'draft' | 'schedule' | 'now' | 'update', orgId: string, date: string, body: PostBody, @@ -405,7 +419,12 @@ export class PostsRepository { group: uuid, intervalInDays: inter ? +inter : null, approvedSubmitForOrder: APPROVED_SUBMIT_FOR_ORDER.NO, - state: state === 'draft' ? ('DRAFT' as const) : ('QUEUE' as const), + ...(state === 'update' + ? {} + : { + state: + state === 'draft' ? ('DRAFT' as const) : ('QUEUE' as const), + }), image: JSON.stringify(value.image), settings: JSON.stringify(body.settings), organization: { diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 5ed3feb4..207e815b 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -627,12 +627,14 @@ export class PostsService { return [] as any[]; } - this.startWorkflow( - post.settings.__type.split('-')[0].toLowerCase(), - posts[0].id, - orgId, - posts[0].state - ).catch((err) => {}); + if (body.type !== 'update') { + this.startWorkflow( + post.settings.__type.split('-')[0].toLowerCase(), + posts[0].id, + orgId, + posts[0].state + ).catch((err) => {}); + } Sentry.metrics.count('post_created', 1); postList.push({ @@ -652,23 +654,34 @@ export class PostsService { return this._postRepository.changeState(id, state, err, body); } - async changeDate(orgId: string, id: string, date: string) { + async changeDate( + orgId: string, + id: string, + date: string, + action: 'schedule' | 'update' = 'schedule' + ) { const getPostById = await this._postRepository.getPostById(id, orgId); + + // schedule: Set status to QUEUE and change date (reschedule the post) + // update: Just change the date without changing the status const newDate = await this._postRepository.changeDate( orgId, id, date, - getPostById.state === 'DRAFT' + getPostById.state === 'DRAFT', + action ); - try { - await this.startWorkflow( - getPostById.integration.providerIdentifier.split('-')[0].toLowerCase(), - getPostById.id, - orgId, - getPostById.state - ); - } catch (err) {} + if (action === 'schedule') { + try { + await this.startWorkflow( + getPostById.integration.providerIdentifier.split('-')[0].toLowerCase(), + getPostById.id, + orgId, + getPostById.state === 'DRAFT' ? 'DRAFT' : 'QUEUE' + ); + } catch (err) {} + } return newDate; } diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts index 18842f4e..391fd56a 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts @@ -88,8 +88,8 @@ class Tags { export class CreatePostDto { @IsDefined() - @IsIn(['draft', 'schedule', 'now']) - type: 'draft' | 'schedule' | 'now'; + @IsIn(['draft', 'schedule', 'now', 'update']) + type: 'draft' | 'schedule' | 'now' | 'update'; @IsOptional() @IsString() From ec4b19c74116be25e83ca8b1a09b99e931b2dc4f Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 27 Jan 2026 22:17:25 +0700 Subject: [PATCH 182/340] feat: delete tags --- .../src/api/routes/posts.controller.ts | 8 ++ apps/frontend/src/app/global.scss | 11 +- .../components/launches/tags.component.tsx | 130 ++++++++++++++++-- .../database/prisma/posts/posts.repository.ts | 13 ++ .../database/prisma/posts/posts.service.ts | 4 + 5 files changed, 153 insertions(+), 13 deletions(-) diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index 69be508d..a3dcc1ad 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -81,6 +81,14 @@ export class PostsController { return this._postsService.editTag(id, org.id, body); } + @Delete('/tags/:id') + async deleteTag( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string + ) { + return this._postsService.deleteTag(id, org.id); + } + @Get('/') async getPosts( @GetOrgFromRequest() org: Organization, diff --git a/apps/frontend/src/app/global.scss b/apps/frontend/src/app/global.scss index 5a524d1f..f1398562 100644 --- a/apps/frontend/src/app/global.scss +++ b/apps/frontend/src/app/global.scss @@ -745,7 +745,9 @@ html[dir='rtl'] [dir='ltr'] { } .post-now { - box-shadow: -33px 57px 18px 0 rgba(0, 0, 0, 0.01), -21px 36px 17px 0 rgba(0, 0, 0, 0.06), -12px 20px 14px 0 rgba(0, 0, 0, 0.20), -5px 9px 11px 0 rgba(0, 0, 0, 0.34), -1px 2px 6px 0 rgba(0, 0, 0, 0.39); + box-shadow: -33px 57px 18px 0 rgba(0, 0, 0, 0.01), + -21px 36px 17px 0 rgba(0, 0, 0, 0.06), -12px 20px 14px 0 rgba(0, 0, 0, 0.2), + -5px 9px 11px 0 rgba(0, 0, 0, 0.34), -1px 2px 6px 0 rgba(0, 0, 0, 0.39); } .uppyChange .uppy-Dashboard-inner { @@ -780,4 +782,9 @@ html[dir='rtl'] [dir='ltr'] { .forceChange .changeColor { background: var(--new-btn-primary) !important; color: #fff !important; -} \ No newline at end of file +} + +.text-shadow-tags { + text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, + 1px 1px 0 black; +} diff --git a/apps/frontend/src/components/launches/tags.component.tsx b/apps/frontend/src/components/launches/tags.component.tsx index 1e0ff486..3d08f003 100644 --- a/apps/frontend/src/components/launches/tags.component.tsx +++ b/apps/frontend/src/components/launches/tags.component.tsx @@ -12,7 +12,12 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useClickOutside } from '@mantine/hooks'; import clsx from 'clsx'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; -import { TagIcon, DropdownArrowIcon, PlusIcon, CheckmarkIcon } from '@gitroom/frontend/components/ui/icons'; +import { + TagIcon, + DropdownArrowIcon, + PlusIcon, + CheckmarkIcon, +} from '@gitroom/frontend/components/ui/icons'; export const TagsComponent: FC<{ name: string; @@ -54,7 +59,9 @@ export const TagsComponentInner: FC<{ }) => void; }> = ({ initial, onChange, name, mutate, allTags: data }) => { const t = useT(); + const fetch = useFetch(); const [isOpen, setIsOpen] = useState(false); + const [allowClose, setAllowClose] = useState(true); const [tagValue, setTagValue] = useState<any[]>( (initial?.slice(0) || []).map((p: any) => { return data?.tags.find((a: any) => a.name === p.value) || p; @@ -63,7 +70,7 @@ export const TagsComponentInner: FC<{ const modals = useModals(); const ref = useClickOutside(() => { - if (!isOpen) { + if (!isOpen || !allowClose) { return; } setIsOpen(false); @@ -98,6 +105,58 @@ export const TagsComponentInner: FC<{ } }, []); + const deleteTag = useCallback( + async (tag: any, e: React.MouseEvent) => { + setAllowClose(false); + e.stopPropagation(); + const confirmed: boolean = await new Promise((resolve) => { + modals.openModal({ + title: t('delete_tag', 'Delete Tag'), + children: (close) => ( + <ConfirmDeleteModal + tagName={tag.name} + close={close} + resolve={resolve} + /> + ), + }); + }); + + if (!confirmed) { + setTimeout(() => { + setAllowClose(true); + }, 500); + return; + } + + await fetch(`/posts/tags/${tag.id}`, { + method: 'DELETE', + }); + + // Remove the tag from current selection if it was selected + const modify = tagValue.filter((a) => a.id !== tag.id); + if (modify.length !== tagValue.length) { + setTagValue(modify); + onChange({ + target: { + value: modify.map((p: any) => ({ + label: p.name, + value: p.name, + })), + name, + }, + }); + } + + await mutate(); + + setTimeout(() => { + setAllowClose(true); + }, 500); + }, + [tagValue, name, onChange, mutate, fetch, modals, t] + ); + return ( <div ref={ref} @@ -122,7 +181,7 @@ export const TagsComponentInner: FC<{ className="h-full flex justify-center items-center px-[8px] rounded-[4px]" style={{ backgroundColor: tagValue[0].color }} > - <span className="mix-blend-difference text-[#fff]"> + <span className="text-shadow-tags text-[#fff]"> {tagValue[0].name} </span> </div> @@ -158,20 +217,28 @@ export const TagsComponentInner: FC<{ }); }} key={p.name} - className="h-[40px] py-[8px] px-[20px] -mx-[12px] flex gap-[8px]" + className="min-h-[40px] py-[8px] px-[20px] -mx-[12px] flex gap-[8px] items-center group" > <Check onChange={() => {}} value={!!tagValue.find((a) => a.id === p.id)} /> - <div - className="h-full flex justify-center items-center px-[8px] rounded-[8px]" - style={{ backgroundColor: p.color }} - > - <span className="mix-blend-difference text-[#fff]"> + <div className="h-full flex items-center flex-1 break-all"> + <span + className="text-[#fff] px-[8px] rounded-[8px] text-shadow-tags" + style={{ backgroundColor: p.color }} + > {p.name} </span> </div> + {!tagValue.find((a) => a.id === p.id) && ( + <div + onClick={(e) => deleteTag(p, e)} + className="ms-auto transition-opacity cursor-pointer text-red-500 text-[14px] font-[600]" + > + × + </div> + )} </div> ))} <div @@ -181,7 +248,9 @@ export const TagsComponentInner: FC<{ <div> <PlusIcon /> </div> - <div className="text-[13px] font-[600]">{t('add_new_tag', 'Add New Tag')}</div> + <div className="text-[13px] font-[600]"> + {t('add_new_tag', 'Add New Tag')} + </div> </div> </div> )} @@ -197,7 +266,7 @@ const Check: FC<{ value: boolean; onChange: (value: boolean) => void }> = ({ <div onClick={() => onChange(!value)} className={clsx( - 'text-[10px] font-[500] text-center flex border border-btnSimple rounded-[6px] w-[20px] h-[20px] justify-center items-center', + 'text-[10px] font-[500] text-center flex border border-btnSimple rounded-[6px] min-w-[20px] min-h-[20px] w-[20px] h-[20px] justify-center items-center', value && 'bg-[#612BD3]' )} > @@ -403,6 +472,45 @@ export const TagsComponentA: FC<{ </> ); }; +const ConfirmDeleteModal: FC<{ + tagName: string; + close: () => void; + resolve: (value: boolean) => void; +}> = ({ tagName, close, resolve }) => { + const t = useT(); + + return ( + <div className="flex flex-col gap-[16px]"> + <p className="text-[14px]"> + {t( + 'confirm_delete_tag', + 'Are you sure you want to delete the tag "{{tagName}}"?', + { tagName } + )} + </p> + <div className="flex gap-[8px] justify-end"> + <Button + onClick={() => { + resolve(false); + close(); + }} + > + {t('cancel', 'Cancel')} + </Button> + <Button + onClick={() => { + resolve(true); + close(); + }} + className="bg-red-500 hover:bg-red-600" + > + {t('delete', 'Delete')} + </Button> + </div> + </div> + ); +}; + const ShowModal: FC<{ tag: string; color?: string; diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 3041f1d1..ebb33923 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -678,6 +678,7 @@ export class PostsRepository { return this._tags.model.tags.findMany({ where: { orgId, + deletedAt: null, }, }); } @@ -704,6 +705,18 @@ export class PostsRepository { }); } + deleteTag(id: string, orgId: string) { + return this._tags.model.tags.update({ + where: { + id, + orgId, + }, + data: { + deletedAt: new Date(), + }, + }); + } + createComment( orgId: string, userId: string, diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 207e815b..866b4897 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -841,6 +841,10 @@ export class PostsService { return this._postRepository.editTag(id, orgId, body); } + deleteTag(id: string, orgId: string) { + return this._postRepository.deleteTag(id, orgId); + } + createComment( orgId: string, userId: string, From 078d4739c8f9b66602718f1b3a59e71de49514c2 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 09:58:57 +0700 Subject: [PATCH 183/340] feat: fix instagram analytics --- .../src/integrations/social/instagram.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 9d509f2f..05bbb6ac 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -755,7 +755,7 @@ export class InstagramProvider // Fetch media insights from Instagram Graph API const { data } = await ( await this.fetch( - `https://${type}/v21.0/${postId}/insights?metric=impressions,reach,engagement,saved,likes,comments,shares&access_token=${accessToken}` + `https://${type}/v21.0/${postId}/insights?metric=impressions,reach,saved,likes,comments,shares&access_token=${accessToken}` ) ).json(); From b6928138d2cf13e967d132e28a7ed88857013d28 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 10:12:56 +0700 Subject: [PATCH 184/340] feat: fix instagram analytics --- .../src/integrations/social/instagram.provider.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 05bbb6ac..e0568663 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -755,7 +755,7 @@ export class InstagramProvider // Fetch media insights from Instagram Graph API const { data } = await ( await this.fetch( - `https://${type}/v21.0/${postId}/insights?metric=impressions,reach,saved,likes,comments,shares&access_token=${accessToken}` + `https://${type}/v21.0/${postId}/insights?metric=views,reach,saved,likes,comments,shares&access_token=${accessToken}` ) ).json(); @@ -772,8 +772,8 @@ export class InstagramProvider let label = ''; switch (metric.name) { - case 'impressions': - label = 'Impressions'; + case 'views': + label = 'Views'; break; case 'reach': label = 'Reach'; From ddbfd16030be370d9501f01bbad5d572b06737ba Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 10:50:32 +0700 Subject: [PATCH 185/340] feat: faster remove of socials from scheduling --- .../components/new-launch/select.current.tsx | 65 +++++++++++++++---- .../integrations/social/tiktok.provider.ts | 1 - 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/components/new-launch/select.current.tsx b/apps/frontend/src/components/new-launch/select.current.tsx index 3d741782..0792d9c4 100644 --- a/apps/frontend/src/components/new-launch/select.current.tsx +++ b/apps/frontend/src/components/new-launch/select.current.tsx @@ -1,12 +1,20 @@ 'use client'; -import { FC, RefObject, useEffect, useRef, useState } from 'react'; -import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; +import { FC, RefObject, useCallback, useEffect, useRef, useState } from 'react'; +import { + SelectedIntegrations, + useLaunchStore, +} from '@gitroom/frontend/components/new-launch/store'; import clsx from 'clsx'; import Image from 'next/image'; import { useShallow } from 'zustand/react/shallow'; import { GlobalIcon } from '@gitroom/frontend/components/ui/icons'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; +import { + useDecisionModal, + useModals, +} from '@gitroom/frontend/components/layout/new-modal'; export function useHasScroll(ref: RefObject<HTMLElement>): boolean { const [hasHorizontalScroll, setHasHorizontalScroll] = useState(false); @@ -43,20 +51,47 @@ export function useHasScroll(ref: RefObject<HTMLElement>): boolean { } export const SelectCurrent: FC = () => { - const { selectedIntegrations, current, setCurrent, locked, setHide } = - useLaunchStore( - useShallow((state) => ({ - selectedIntegrations: state.selectedIntegrations, - current: state.current, - setCurrent: state.setCurrent, - locked: state.locked, - setHide: state.setHide, - })) - ); + const modals = useDecisionModal(); + const { + selectedIntegrations, + current, + setCurrent, + locked, + setHide, + addOrRemoveSelectedIntegration, + } = useLaunchStore( + useShallow((state) => ({ + selectedIntegrations: state.selectedIntegrations, + addOrRemoveSelectedIntegration: state.addOrRemoveSelectedIntegration, + current: state.current, + setCurrent: state.setCurrent, + locked: state.locked, + setHide: state.setHide, + })) + ); const contentRef = useRef<HTMLDivElement>(null); const hasScroll = useHasScroll(contentRef); + const removeSocial = useCallback( + (sIntegration: Integrations) => async (e: any) => { + e.stopPropagation(); + e.preventDefault(); + const open = await modals.open({ + title: 'Remove Social Account', + description: + 'Are you sure you want to remove this social from scheduling?', + }); + + if (!open) { + return; + } + + addOrRemoveSelectedIntegration(sIntegration, {}); + }, + [] + ); + return ( <> <div className="select-none left-0 absolute w-full z-[100] px-[20px]"> @@ -97,6 +132,12 @@ export const SelectCurrent: FC = () => { : 'border-transparent' )} > + <div + onClick={removeSocial(integration)} + className="absolute justify-center items-center flex w-[8px] h-[8px] -top-[1px] -start-[3px] bg-red-500 rounded-full text-white text-[8px]" + > + X + </div> <IsGlobal id={integration.id} /> <div {...{ diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 96a0bbb1..2ad966a8 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -312,7 +312,6 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { }) ).json(); - console.log(this.scopes, scope); this.checkScopes(this.scopes, scope); const { From aa17498a5217b5bc06d4fa86251cac999c498c2c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 10:53:39 +0700 Subject: [PATCH 186/340] feat: easy close statistics --- apps/frontend/src/components/launches/calendar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 9dbd8e42..d73bd54f 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -661,7 +661,7 @@ export const CalendarColumn: FC<{ title: t('statistics', 'Statistics'), closeOnClickOutside: true, closeOnEscape: true, - withCloseButton: false, + withCloseButton: true, classNames: { modal: 'w-[100%] max-w-[1400px]', }, From c3cdee5c210e97cd4fcad2da8935ba83f28be0f5 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 14:53:45 +0700 Subject: [PATCH 187/340] feat: streak --- .../src/activities/email.activity.ts | 5 ++++ .../src/activities/post.activity.ts | 25 +++++++++++++++-- apps/orchestrator/src/workflows/index.ts | 1 + .../src/workflows/streak.workflow.ts | 28 +++++++++++++++++++ .../organizations/organization.repository.ts | 19 +++++++++++++ .../organizations/organization.service.ts | 4 +++ .../src/database/prisma/schema.prisma | 8 ++++-- 7 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 apps/orchestrator/src/workflows/streak.workflow.ts diff --git a/apps/orchestrator/src/activities/email.activity.ts b/apps/orchestrator/src/activities/email.activity.ts index c591c8d8..8ecfc236 100644 --- a/apps/orchestrator/src/activities/email.activity.ts +++ b/apps/orchestrator/src/activities/email.activity.ts @@ -25,4 +25,9 @@ export class EmailActivity { async getUserOrgs(id: string) { return this._organizationService.getTeam(id); } + + @ActivityMethod() + async setStreak(organizationId: string, type: 'start' | 'end') { + return this._organizationService.setStreak(organizationId, type); + } } diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index 71200476..fcb9997e 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -83,7 +83,11 @@ export class PostActivity { @ActivityMethod() async getPostsList(orgId: string, postId: string) { - const getPosts = await this._postService.getPostsRecursively(postId, true, orgId); + const getPosts = await this._postService.getPostsRecursively( + postId, + true, + orgId + ); if (!getPosts || getPosts.length === 0 || getPosts[0].parentPostId) { return []; } @@ -155,7 +159,7 @@ export class PostActivity { posts ); - return getIntegration.post( + const postNow = await getIntegration.post( integration.internalId, integration.token, await Promise.all( @@ -179,6 +183,23 @@ export class PostActivity { ), integration ); + + await this._temporalService.client + .getRawClient() + .workflow.start('streakWorkflow', { + args: [{ organizationId: integration.organizationId }], + workflowId: `streak_${integration.organizationId}`, + taskQueue: 'main', + workflowIdConflictPolicy: 'TERMINATE_EXISTING', + typedSearchAttributes: new TypedSearchAttributes([ + { + key: organizationId, + value: integration.organizationId, + }, + ]), + }); + + return postNow; } @ActivityMethod() diff --git a/apps/orchestrator/src/workflows/index.ts b/apps/orchestrator/src/workflows/index.ts index 72f10d68..76b0d16f 100644 --- a/apps/orchestrator/src/workflows/index.ts +++ b/apps/orchestrator/src/workflows/index.ts @@ -4,3 +4,4 @@ export * from './digest.email.workflow'; export * from './missing.post.workflow'; export * from './send.email.workflow'; export * from './refresh.token.workflow'; +export * from './streak.workflow'; diff --git a/apps/orchestrator/src/workflows/streak.workflow.ts b/apps/orchestrator/src/workflows/streak.workflow.ts new file mode 100644 index 00000000..02fc7fc8 --- /dev/null +++ b/apps/orchestrator/src/workflows/streak.workflow.ts @@ -0,0 +1,28 @@ +import { proxyActivities, sleep } from '@temporalio/workflow'; +import { EmailActivity } from '@gitroom/orchestrator/activities/email.activity'; + +const { sendEmailAsync, getUserOrgs, setStreak } = proxyActivities<EmailActivity>({ + startToCloseTimeout: '10 minute', + taskQueue: 'main', + cancellationType: 'ABANDON', +}); + +export async function streakWorkflow({ + organizationId, +}: { + organizationId: string; +}) { + await setStreak(organizationId, 'start'); + await sleep(79200000); + const userOrgs = await getUserOrgs(organizationId); + for (const user of userOrgs.users) { + await sendEmailAsync( + user.user.email, + 'Streak Reminder', + '<p>You are about to lose your streak in two hours! schedule a post now to keep it!</p>', + 'bottom' + ); + } + await sleep(7200000); + await setStreak(organizationId, 'end'); +} diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index 57bae74e..32c81d5a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -260,6 +260,25 @@ export class OrganizationRepository { }); } + async setStreak(organizationId: string, type: 'start' | 'end') { + try { + await this._organization.model.organization.update({ + where: { + id: organizationId, + ...(type === 'start' + ? { + streakSince: null, + } + : {}), + }, + data: { + ...(type === 'end' ? { streakSince: null } : {}), + ...(type === 'start' ? { streakSince: new Date() } : {}), + }, + }); + } catch (err) {} + } + async getTeam(orgId: string) { return this._organization.model.organization.findUnique({ where: { diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts index c9fca065..e914e534 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts @@ -65,6 +65,10 @@ export class OrganizationService { return this._organizationRepository.getTeam(orgId); } + async setStreak(organizationId: string, type: 'start' | 'end') { + return this._organizationRepository.setStreak(organizationId, type); + } + getOrgByCustomerId(customerId: string) { return this._organizationRepository.getOrgByCustomerId(customerId); } diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 32310bb9..5cda9afe 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -14,6 +14,7 @@ model Organization { description String? apiKey String? paymentId String? + streakSince DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt allowTrial Boolean @default(false) @@ -30,8 +31,8 @@ model Organization { buyerOrganization MessagesGroup[] notifications Notifications[] plugs Plugs[] - post Post[] @relation("organization") - submittedPost Post[] @relation("submittedForOrg") + post Post[] @relation("organization") + submittedPost Post[] @relation("submittedForOrg") sets Sets[] signatures Signatures[] subscription Subscription? @@ -40,6 +41,9 @@ model Organization { usedCodes UsedCodes[] users UserOrganization[] webhooks Webhooks[] + + @@index([streakSince]) + @@index([paymentId]) } model Tags { From 881c37ed6f6c9d078afaff043b80cf5b1e8ddd33 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 16:00:48 +0700 Subject: [PATCH 188/340] feat: streak --- .../src/api/routes/users.controller.ts | 1 + .../components/layout/streak.component.tsx | 54 +++++++++++++++++++ .../src/components/layout/user.context.tsx | 1 + .../new-layout/layout.component.tsx | 3 ++ .../email-notifications.component.tsx | 27 ++++++++++ .../src/workflows/streak.workflow.ts | 3 ++ .../organizations/organization.repository.ts | 1 + .../src/database/prisma/schema.prisma | 1 + .../database/prisma/users/users.repository.ts | 2 + .../src/dtos/users/email-notifications.dto.ts | 3 ++ 10 files changed, 96 insertions(+) create mode 100644 apps/frontend/src/components/layout/streak.component.tsx diff --git a/apps/backend/src/api/routes/users.controller.ts b/apps/backend/src/api/routes/users.controller.ts index bdbcb46c..7f195a60 100644 --- a/apps/backend/src/api/routes/users.controller.ts +++ b/apps/backend/src/api/routes/users.controller.ts @@ -69,6 +69,7 @@ export class UsersController { impersonate: !!impersonate, isTrailing: !process.env.STRIPE_PUBLISHABLE_KEY ? false : organization?.isTrailing, allowTrial: organization?.allowTrial, + streakSince: organization?.streakSince || null, // @ts-ignore publicApi: organization?.users[0]?.role === 'SUPERADMIN' || organization?.users[0]?.role === 'ADMIN' ? organization?.apiKey : '', }; diff --git a/apps/frontend/src/components/layout/streak.component.tsx b/apps/frontend/src/components/layout/streak.component.tsx new file mode 100644 index 00000000..ec35b970 --- /dev/null +++ b/apps/frontend/src/components/layout/streak.component.tsx @@ -0,0 +1,54 @@ +'use client'; + +import { FC, useMemo } from 'react'; +import { useUser } from '@gitroom/frontend/components/layout/user.context'; + +export const StreakComponent: FC = () => { + const user = useUser(); + + const streakDays = useMemo(() => { + if (!user?.streakSince) return 0; + const streakStart = new Date(user.streakSince); + const now = new Date(); + const diffTime = now.getTime() - streakStart.getTime(); + const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24)); + if (diffDays + 1 <= 0) { + return 1; + } + + return diffDays + 1; + }, [user?.streakSince]); + + const tooltipContent = useMemo(() => { + if (streakDays === 1) { + return 'You started your streak today! Keep posting daily to maintain it.'; + } + return `You're on a ${streakDays} day posting streak! Keep it going!`; + }, [streakDays]); + + if (!user?.streakSince || streakDays <= 0) { + return null; + } + + return ( + <div + className="flex items-center gap-[6px] text-orange-500 hover:text-orange-400 cursor-default" + data-tooltip-id="tooltip" + data-tooltip-content={tooltipContent} + > + <svg + width="24" + height="24" + viewBox="0 0 22 27" + fill="none" + xmlns="http://www.w3.org/2000/svg" + > + <path + d="M17.9862 17.1673C17.7269 18.6157 17.0301 19.9498 15.9896 20.9902C14.949 22.0305 13.6147 22.7271 12.1663 22.986C12.1113 22.9949 12.0557 22.9995 12 22.9998C11.7492 22.9997 11.5075 22.9054 11.323 22.7355C11.1384 22.5656 11.0245 22.3326 11.0037 22.0826C10.9829 21.8326 11.0569 21.584 11.2108 21.3859C11.3648 21.1879 11.5876 21.055 11.835 21.0135C13.9062 20.6648 15.6637 18.9073 16.015 16.8323C16.0594 16.5707 16.2059 16.3375 16.4223 16.184C16.6387 16.0304 16.9072 15.9691 17.1688 16.0135C17.4303 16.058 17.6635 16.2045 17.8171 16.4209C17.9706 16.6372 18.0319 16.9057 17.9875 17.1673H17.9862ZM22 15.9998C22 18.9172 20.8411 21.7151 18.7782 23.778C16.7153 25.8409 13.9174 26.9998 11 26.9998C8.08262 26.9998 5.28473 25.8409 3.22183 23.778C1.15893 21.7151 0 18.9172 0 15.9998C0 12.5098 1.375 8.94105 4.0825 5.39355C4.1682 5.28122 4.27674 5.18833 4.40095 5.12099C4.52516 5.05365 4.66223 5.01341 4.80313 5.00289C4.94403 4.99238 5.08556 5.01185 5.21838 5.06001C5.35121 5.10817 5.47233 5.18393 5.57375 5.2823L8.58875 8.20855L11.3388 0.657298C11.3937 0.50669 11.484 0.371499 11.6022 0.263121C11.7203 0.154744 11.8628 0.0763568 12.0175 0.0345691C12.1723 -0.00721869 12.3349 -0.0111825 12.4915 0.023012C12.6481 0.0572064 12.7942 0.128557 12.9175 0.231048C15.6512 2.4998 22 8.56855 22 15.9998ZM20 15.9998C20 10.2385 15.5262 5.2598 12.7237 2.70855L9.94 10.3423C9.88287 10.4991 9.78741 10.6391 9.66232 10.7495C9.53723 10.86 9.38648 10.9374 9.22383 10.9747C9.06117 11.0119 8.89177 11.0079 8.73107 10.963C8.57036 10.918 8.42346 10.8336 8.30375 10.7173L5.0075 7.5198C3.01125 10.401 2 13.2498 2 15.9998C2 18.3867 2.94821 20.6759 4.63604 22.3638C6.32387 24.0516 8.61305 24.9998 11 24.9998C13.3869 24.9998 15.6761 24.0516 17.364 22.3638C19.0518 20.6759 20 18.3867 20 15.9998Z" + fill="currentColor" + /> + </svg> + <span className="text-[14px] font-semibold">{streakDays}</span> + </div> + ); +}; diff --git a/apps/frontend/src/components/layout/user.context.tsx b/apps/frontend/src/components/layout/user.context.tsx index 9e5ec48d..c6490c5f 100644 --- a/apps/frontend/src/components/layout/user.context.tsx +++ b/apps/frontend/src/components/layout/user.context.tsx @@ -18,6 +18,7 @@ export const UserContext = createContext< impersonate: boolean; allowTrial: boolean; isTrailing: boolean; + streakSince: string | null; }) >(undefined); export const ContextWrapper: FC<{ diff --git a/apps/frontend/src/components/new-layout/layout.component.tsx b/apps/frontend/src/components/new-layout/layout.component.tsx index 2d0d070e..26b44e8f 100644 --- a/apps/frontend/src/components/new-layout/layout.component.tsx +++ b/apps/frontend/src/components/new-layout/layout.component.tsx @@ -36,6 +36,7 @@ import { LanguageComponent } from '@gitroom/frontend/components/layout/language. import { ChromeExtensionComponent } from '@gitroom/frontend/components/layout/chrome.extension.component'; import NotificationComponent from '@gitroom/frontend/components/notifications/notification.component'; import { OrganizationSelector } from '@gitroom/frontend/components/layout/organization.selector'; +import { StreakComponent } from '@gitroom/frontend/components/layout/streak.component'; import { PreConditionComponent } from '@gitroom/frontend/components/layout/pre-condition.component'; import { AttachToFeedbackIcon } from '@gitroom/frontend/components/new-layout/sentry.feedback.component'; import { FirstBillingComponent } from '@gitroom/frontend/components/billing/first.billing.component'; @@ -115,6 +116,8 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => { <Title /> </div> <div className="flex gap-[20px] text-textItemBlur"> + <StreakComponent /> + <div className="w-[1px] h-[20px] bg-blockSeparator" /> <OrganizationSelector /> <div className="hover:text-newTextColor"> <ModeComponent /> diff --git a/apps/frontend/src/components/settings/email-notifications.component.tsx b/apps/frontend/src/components/settings/email-notifications.component.tsx index e780ae4a..e91f11c6 100644 --- a/apps/frontend/src/components/settings/email-notifications.component.tsx +++ b/apps/frontend/src/components/settings/email-notifications.component.tsx @@ -10,6 +10,7 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client'; interface EmailNotifications { sendSuccessEmails: boolean; sendFailureEmails: boolean; + sendStreakEmails: boolean; } export const useEmailNotifications = () => { @@ -38,6 +39,7 @@ const EmailNotificationsComponent = () => { const [localSettings, setLocalSettings] = useState<EmailNotifications>({ sendSuccessEmails: true, sendFailureEmails: true, + sendStreakEmails: true, }); // Keep a ref to always have the latest state @@ -87,6 +89,13 @@ const EmailNotificationsComponent = () => { [updateSetting] ); + const handleStreakEmailsChange = useCallback( + (value: 'on' | 'off') => { + updateSetting('sendStreakEmails', value === 'on'); + }, + [updateSetting] + ); + if (isLoading) { return ( <div className="my-[16px] mt-[16px] bg-sixth border-fifth border rounded-[4px] p-[24px]"> @@ -138,6 +147,24 @@ const EmailNotificationsComponent = () => { fill={true} /> </div> + <div className="flex items-center justify-between"> + <div className="flex flex-col"> + <div className="text-[14px]"> + {t('streak_emails', 'Streak Reminder Emails')} + </div> + <div className="text-[12px] text-customColor18"> + {t( + 'streak_emails_description', + 'Receive email reminders when your posting streak is about to end' + )} + </div> + </div> + <Slider + value={localSettings.sendStreakEmails ? 'on' : 'off'} + onChange={handleStreakEmailsChange} + fill={true} + /> + </div> </div> ); }; diff --git a/apps/orchestrator/src/workflows/streak.workflow.ts b/apps/orchestrator/src/workflows/streak.workflow.ts index 02fc7fc8..0ee49ed0 100644 --- a/apps/orchestrator/src/workflows/streak.workflow.ts +++ b/apps/orchestrator/src/workflows/streak.workflow.ts @@ -16,6 +16,9 @@ export async function streakWorkflow({ await sleep(79200000); const userOrgs = await getUserOrgs(organizationId); for (const user of userOrgs.users) { + if (!user.user.sendStreakEmails) { + continue; + } await sendEmailAsync( user.user.email, 'Streak Reminder', diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index 32c81d5a..e6386e5e 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -294,6 +294,7 @@ export class OrganizationRepository { id: true, sendSuccessEmails: true, sendFailureEmails: true, + sendStreakEmails: true, }, }, }, diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index 5cda9afe..b5ca2e7c 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -108,6 +108,7 @@ model User { organizations UserOrganization[] sendSuccessEmails Boolean @default(true) sendFailureEmails Boolean @default(true) + sendStreakEmails Boolean @default(true) @@unique([email, providerName]) @@index([lastReadNotifications]) diff --git a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts index 96b35c78..38a03c76 100644 --- a/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/users/users.repository.ts @@ -157,6 +157,7 @@ export class UsersRepository { select: { sendSuccessEmails: true, sendFailureEmails: true, + sendStreakEmails: true, }, }); } @@ -169,6 +170,7 @@ export class UsersRepository { data: { sendSuccessEmails: body.sendSuccessEmails, sendFailureEmails: body.sendFailureEmails, + sendStreakEmails: body.sendStreakEmails, }, }); } diff --git a/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts b/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts index d9d62d08..e596fc7b 100644 --- a/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/users/email-notifications.dto.ts @@ -6,5 +6,8 @@ export class EmailNotificationsDto { @IsBoolean() sendFailureEmails: boolean; + + @IsBoolean() + sendStreakEmails: boolean; } From e551d151d622c08762df762f6153becb028c8b1a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 16:24:02 +0700 Subject: [PATCH 189/340] fix: border image --- .../new-launch/picks.socials.component.tsx | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/frontend/src/components/new-launch/picks.socials.component.tsx b/apps/frontend/src/components/new-launch/picks.socials.component.tsx index 8caa5ee0..3f8afea0 100644 --- a/apps/frontend/src/components/new-launch/picks.socials.component.tsx +++ b/apps/frontend/src/components/new-launch/picks.socials.component.tsx @@ -7,6 +7,7 @@ import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useShallow } from 'zustand/react/shallow'; import { useExistingData } from '@gitroom/frontend/components/launches/helpers/use.existing.data'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import ImageWithFallback from '@gitroom/react/helpers/image.with.fallback'; export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ toolTip, @@ -64,28 +65,24 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ : 'border-[#622FF6]' )} > - <div - className="rounded-full min-w-[42px] min-h-[42px] bg-contain bg-center bg-no-repeat" - style={{ backgroundImage: `url(/no-picture.jpg)` }} - > - <Image - src={integration.picture || '/no-picture.jpg'} - className={clsx( - 'rounded-full transition-all min-w-[42px] border-[1.5px] min-h-[42px]', - selectedIntegrations.findIndex( - (p) => p.integration.id === integration.id - ) === -1 - ? 'border-transparent' - : 'border-[#000]' - )} - onError={(e) => { - e.currentTarget.style.display = 'none'; - }} - alt={integration.identifier} - width={42} - height={42} - /> - </div> + <ImageWithFallback + fallbackSrc="/no-picture.jpg" + src={integration.picture || '/no-picture.jpg'} + className={clsx( + 'rounded-full transition-all min-w-[42px] border-[1.5px] min-h-[42px]', + selectedIntegrations.findIndex( + (p) => p.integration.id === integration.id + ) === -1 + ? 'border-transparent' + : 'border-[#000]' + )} + onError={(e) => { + e.currentTarget.style.display = 'none'; + }} + alt={integration.identifier} + width={42} + height={42} + /> {integration.identifier === 'youtube' ? ( <img src="/icons/platforms/youtube.svg" From ee012fb0214df3b379b20d7c5896877774803faf Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 28 Jan 2026 18:44:35 +0700 Subject: [PATCH 190/340] fix: border image --- .../src/components/new-launch/picks.socials.component.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/frontend/src/components/new-launch/picks.socials.component.tsx b/apps/frontend/src/components/new-launch/picks.socials.component.tsx index 3f8afea0..5fd54b2d 100644 --- a/apps/frontend/src/components/new-launch/picks.socials.component.tsx +++ b/apps/frontend/src/components/new-launch/picks.socials.component.tsx @@ -76,9 +76,6 @@ export const PicksSocialsComponent: FC<{ toolTip?: boolean }> = ({ ? 'border-transparent' : 'border-[#000]' )} - onError={(e) => { - e.currentTarget.style.display = 'none'; - }} alt={integration.identifier} width={42} height={42} From 5c50e962ae8fb0a1a786f7159649dce70319c02c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 30 Jan 2026 11:06:50 +0700 Subject: [PATCH 191/340] feat: tiktok analytics --- CLAUDE.md | 61 +++++ .../platform-analytics/platform.analytics.tsx | 4 +- .../platform-analytics/render.analytics.tsx | 18 +- .../integrations/social/tiktok.provider.ts | 216 ++++++++++++++++++ 4 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..f13cb399 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,61 @@ +This project is Postiz, a tool to schedule social media and chat posts to 28+ channels. +You can add posts to the calendar, they will be added into a workflow and posted at the right time. +You can find things like: +- Schedule posts +- Calendar view +- Analytics +- Team management +- Media library + +This project is a monorepo with a root only package.json of dependencies. +Made with PNPM. +We have 3 important folders + +- apps/backend - this is where the API code is (NESTJS) +- apps/orchestrator - this is temporal, it's for background jobs (NESTJS) it contains all the workflows and activities +- apps/frontend - this is the code of the frontend (Vite ReactJS) +- /libraries contains a lot of services shared between backend and orchestrator and frontend components. + +We are using only pnpm, don't use any other dependency manager. +Never install frontend components from npmjs, focus on writing native components. + +The project uses tailwind 3, before writing any component look at: +- /apps/frontend/src/app/colors.scss +- /apps/frontend/src/app/global.scss +- /apps/frontend/tailwind.config.js + +All the --color-custom* are deprecated, don't use them. + +And check other components in the system before to get the right design. + +When working on the backend we need to pass the 3 layers: +Controller >> Service >> Repository (no shortcuts) +In some cases we will have +Controller >> Mananger >> Service >> Repository. + +Most of the server logic should be inside of libs/server. +The backend repository is mostly used to write controller, and import files from libs.server. + +For the frontend follow this: +- Many of the UI components lives in /apps/frontend/src/components/ui +- Routing is in /apps/frontend/src/app +- Components are in /apps/frontend/src/components +- always use SWR to fetch stuff, and use "useFetch" hook from /libraries/helpers/src/utils/custom.fetch.tsx + +When using SWR, each one have to be in a seperate hook and must comply with react-hooks/rules-of-hooks, never put eslint-disable-next-line on it. + +It means that this is valid: +const useCommunity = () => { + return useSWR.... +} + +This is not valid: +const useCommunity = () => { + return { + communities: () => useSWR<CommunitiesListResponse>("communities", getCommunities), + providers: () => useSWR<ProvidersListResponse>("providers", getProviders), + }; +} + +- Linting of the project can run only from the root. +- Use only pnpm. \ No newline at end of file diff --git a/apps/frontend/src/components/platform-analytics/platform.analytics.tsx b/apps/frontend/src/components/platform-analytics/platform.analytics.tsx index 0d2c865e..21b866ee 100644 --- a/apps/frontend/src/components/platform-analytics/platform.analytics.tsx +++ b/apps/frontend/src/components/platform-analytics/platform.analytics.tsx @@ -22,7 +22,7 @@ const allowedIntegrations = [ 'instagram', 'instagram-standalone', 'linkedin-page', - // 'tiktok', + 'tiktok', 'youtube', 'gmb', 'pinterest', @@ -86,6 +86,7 @@ export const PlatformAnalytics = () => { 'threads', 'gmb', 'x', + 'tiktok', ].indexOf(currentIntegration.identifier) !== -1 ) { arr.push({ @@ -104,6 +105,7 @@ export const PlatformAnalytics = () => { 'threads', 'gmb', 'x', + 'tiktok', ].indexOf(currentIntegration.identifier) !== -1 ) { arr.push({ diff --git a/apps/frontend/src/components/platform-analytics/render.analytics.tsx b/apps/frontend/src/components/platform-analytics/render.analytics.tsx index 55358f27..e56a6a4d 100644 --- a/apps/frontend/src/components/platform-analytics/render.analytics.tsx +++ b/apps/frontend/src/components/platform-analytics/render.analytics.tsx @@ -91,12 +91,20 @@ export const RenderAnalytics: FC<{ <div className="flex items-center gap-[14px]"> <div className="text-[20px]">{p.label}</div> </div> - <div className="flex-1"> - <div className="h-[156px] relative"> - <ChartSocial {...p} key={`p-${index}`} /> + {p.data.length > 1 ? ( + <> + <div className="flex-1"> + <div className="h-[156px] relative"> + <ChartSocial {...p} key={`p-${index}`} /> + </div> + </div> + <div className="text-[50px] leading-[60px]">{total[index]}</div> + </> + ) : ( + <div className="flex-1 flex flex-col items-center justify-center min-h-[216px]"> + <div className="text-[64px] leading-[72px] font-medium">{total[index]}</div> </div> - </div> - <div className="text-[50px] leading-[60px]">{total[index]}</div> + )} </div> </div> ))} diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 2ad966a8..6f8f74c3 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -1,4 +1,5 @@ import { + AnalyticsData, AuthTokenDetails, PostDetails, PostResponse, @@ -23,10 +24,12 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { isBetweenSteps = false; convertToJPEG = true; scopes = [ + 'video.list', 'user.info.basic', 'video.publish', 'video.upload', 'user.info.profile', + 'user.info.stats', ]; override maxConcurrentJob = 1; // TikTok has strict video upload limits dto = TikTokDto; @@ -538,4 +541,217 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { }, ]; } + + async analytics( + id: string, + accessToken: string, + date: number + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + + try { + // Get user stats (follower_count, following_count, likes_count, video_count) + const userStatsResponse = await this.fetch( + 'https://open.tiktokapis.com/v2/user/info/?fields=follower_count,following_count,likes_count,video_count', + { + method: 'GET', + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + + const userStatsData = await userStatsResponse.json(); + const userStats = userStatsData?.data?.user; + + const result: AnalyticsData[] = []; + + if (userStats) { + if (userStats.follower_count !== undefined) { + result.push({ + label: 'Followers', + percentageChange: 0, + data: [{ total: String(userStats.follower_count), date: today }], + }); + } + + if (userStats.following_count !== undefined) { + result.push({ + label: 'Following', + percentageChange: 0, + data: [{ total: String(userStats.following_count), date: today }], + }); + } + + if (userStats.likes_count !== undefined) { + result.push({ + label: 'Total Likes', + percentageChange: 0, + data: [{ total: String(userStats.likes_count), date: today }], + }); + } + + if (userStats.video_count !== undefined) { + result.push({ + label: 'Videos', + percentageChange: 0, + data: [{ total: String(userStats.video_count), date: today }], + }); + } + } + + // Get recent videos and aggregate their stats + const videoListResponse = await this.fetch( + 'https://open.tiktokapis.com/v2/video/list/?fields=id', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ max_count: 20 }), + } + ); + + const videoListData = await videoListResponse.json(); + const videos = videoListData?.data?.videos; + + if (videos && videos.length > 0) { + const videoIds = videos.map((v: { id: string }) => v.id); + + // Query video details to get engagement metrics + const videoQueryResponse = await this.fetch( + 'https://open.tiktokapis.com/v2/video/query/?fields=id,like_count,comment_count,share_count,view_count', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + filters: { video_ids: videoIds }, + }), + } + ); + + const videoQueryData = await videoQueryResponse.json(); + const videoDetails = videoQueryData?.data?.videos; + + if (videoDetails && videoDetails.length > 0) { + let totalViews = 0; + let totalLikes = 0; + let totalComments = 0; + let totalShares = 0; + + for (const video of videoDetails) { + totalViews += video.view_count || 0; + totalLikes += video.like_count || 0; + totalComments += video.comment_count || 0; + totalShares += video.share_count || 0; + } + + result.push({ + label: 'Recent Views', + percentageChange: 0, + data: [{ total: String(totalViews), date: today }], + }); + + result.push({ + label: 'Recent Likes', + percentageChange: 0, + data: [{ total: String(totalLikes), date: today }], + }); + + result.push({ + label: 'Recent Comments', + percentageChange: 0, + data: [{ total: String(totalComments), date: today }], + }); + + result.push({ + label: 'Recent Shares', + percentageChange: 0, + data: [{ total: String(totalShares), date: today }], + }); + } + } + + return result; + } catch (err) { + console.error('Error fetching TikTok analytics:', err); + return []; + } + } + + async postAnalytics( + integrationId: string, + accessToken: string, + postId: string, + fromDate: number + ): Promise<AnalyticsData[]> { + const today = dayjs().format('YYYY-MM-DD'); + + try { + // Query video details using the video ID + const response = await this.fetch( + 'https://open.tiktokapis.com/v2/video/query/?fields=id,like_count,comment_count,share_count,view_count', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + filters: { video_ids: [postId] }, + }), + } + ); + + const data = await response.json(); + const video = data?.data?.videos?.[0]; + + if (!video) { + return []; + } + + const result: AnalyticsData[] = []; + + if (video.view_count !== undefined) { + result.push({ + label: 'Views', + percentageChange: 0, + data: [{ total: String(video.view_count), date: today }], + }); + } + + if (video.like_count !== undefined) { + result.push({ + label: 'Likes', + percentageChange: 0, + data: [{ total: String(video.like_count), date: today }], + }); + } + + if (video.comment_count !== undefined) { + result.push({ + label: 'Comments', + percentageChange: 0, + data: [{ total: String(video.comment_count), date: today }], + }); + } + + if (video.share_count !== undefined) { + result.push({ + label: 'Shares', + percentageChange: 0, + data: [{ total: String(video.share_count), date: today }], + }); + } + + return result; + } catch (err) { + console.error('Error fetching TikTok post analytics:', err); + return []; + } + } } From 17bde5858c584e92cf8973c440f28f3479d1c0f7 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 30 Jan 2026 12:23:28 +0700 Subject: [PATCH 192/340] feat: tiktok analytics --- .../src/components/analytics/chart-social.tsx | 77 +++++- .../src/components/launches/statistics.tsx | 45 +++- .../platform-analytics/render.analytics.tsx | 227 ++++++++++++++---- .../integrations/social/tiktok.provider.ts | 29 ++- .../src/integrations/social/x.provider.ts | 2 +- 5 files changed, 311 insertions(+), 69 deletions(-) diff --git a/apps/frontend/src/components/analytics/chart-social.tsx b/apps/frontend/src/components/analytics/chart-social.tsx index 80173255..a8c81733 100644 --- a/apps/frontend/src/components/analytics/chart-social.tsx +++ b/apps/frontend/src/components/analytics/chart-social.tsx @@ -5,6 +5,7 @@ import DrawChart from 'chart.js/auto'; import { TotalList } from '@gitroom/frontend/components/analytics/stars.and.forks.interface'; import { chunk } from 'lodash'; import useCookie from 'react-use-cookie'; + function mergeDataPoints(data: TotalList[], numPoints: number): TotalList[] { const res = chunk(data, Math.ceil(data.length / numPoints)); return res.map((row) => { @@ -14,32 +15,63 @@ function mergeDataPoints(data: TotalList[], numPoints: number): TotalList[] { }; }); } + export const ChartSocial: FC<{ data: TotalList[]; + color?: 'purple' | 'green' | 'blue'; }> = (props) => { - const { data } = props; + const { data, color = 'purple' } = props; const [mode] = useCookie('mode', 'dark'); const list = useMemo(() => { return mergeDataPoints(data, 7); }, [data]); const ref = useRef<any>(null); const chart = useRef<null | DrawChart>(null); + + const colorSchemes = { + purple: { + start: 'rgba(97, 43, 211, 0.8)', + end: 'rgba(97, 43, 211, 0.1)', + border: 'rgb(97, 43, 211)', + }, + green: { + start: 'rgba(50, 213, 131, 0.8)', + end: 'rgba(50, 213, 131, 0.1)', + border: 'rgb(50, 213, 131)', + }, + blue: { + start: 'rgba(29, 155, 240, 0.8)', + end: 'rgba(29, 155, 240, 0.1)', + border: 'rgb(29, 155, 240)', + }, + }; + + const colors = colorSchemes[color]; + useEffect(() => { - const gradient = ref.current - .getContext('2d') - .createLinearGradient(0, 0, 0, ref.current.height); - gradient.addColorStop(0, 'rgb(90,46,203)'); // Start color with some transparency - gradient.addColorStop(1, 'rgb(65, 38, 136, 1)'); + const ctx = ref.current.getContext('2d'); + const gradient = ctx.createLinearGradient(0, 0, 0, ref.current.height); + gradient.addColorStop(0, colors.start); + gradient.addColorStop(1, colors.end); + chart.current = new DrawChart(ref.current!, { type: 'line', options: { maintainAspectRatio: false, responsive: true, + animation: { + duration: 750, + easing: 'easeOutQuart', + }, + interaction: { + mode: 'index', + intersect: false, + }, layout: { padding: { left: 0, right: 0, - top: 0, + top: 4, bottom: 0, }, }, @@ -60,19 +92,43 @@ export const ChartSocial: FC<{ legend: { display: false, }, + tooltip: { + enabled: true, + backgroundColor: mode === 'dark' ? '#1e1d1d' : '#fff', + titleColor: mode === 'dark' ? '#fff' : '#000', + bodyColor: mode === 'dark' ? '#9c9c9c' : '#777', + borderColor: mode === 'dark' ? '#2b2b2b' : '#e7e9eb', + borderWidth: 1, + padding: 10, + cornerRadius: 8, + displayColors: false, + titleFont: { + size: 12, + weight: 'normal', + }, + bodyFont: { + size: 14, + weight: 'bold', + }, + }, }, }, data: { labels: list.map((row) => row.date), datasets: [ { - borderColor: mode === 'dark' ? '#fff' : '#000', - // @ts-ignore + borderColor: colors.border, + borderWidth: 2, label: 'Total', backgroundColor: gradient, fill: true, - // @ts-ignore data: list.map((row) => row.total), + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6, + pointHoverBackgroundColor: colors.border, + pointHoverBorderColor: mode === 'dark' ? '#1e1d1d' : '#fff', + pointHoverBorderWidth: 2, }, ], }, @@ -81,5 +137,6 @@ export const ChartSocial: FC<{ chart?.current?.destroy(); }; }, []); + return <canvas className="w-full h-full" ref={ref} />; }; diff --git a/apps/frontend/src/components/launches/statistics.tsx b/apps/frontend/src/components/launches/statistics.tsx index 9054ec52..ff136f87 100644 --- a/apps/frontend/src/components/launches/statistics.tsx +++ b/apps/frontend/src/components/launches/statistics.tsx @@ -102,22 +102,41 @@ export const StatisticsModal: FC<{ </Select> </div> </div> - <div className="grid grid-cols-3 gap-[20px]"> - {analyticsData.map((p: AnalyticsData, index: number) => ( - <div key={`analytics-${index}`} className="flex"> - <div className="flex-1 bg-newTableHeader rounded-[8px] py-[10px] px-[16px] gap-[10px] flex flex-col"> - <div className="flex items-center gap-[14px]"> - <div className="text-[20px]">{p.label}</div> - </div> - <div className="flex-1"> - <div className="h-[156px] relative"> - <ChartSocial data={p.data} key={`chart-${index}`} /> + <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-[16px]"> + {analyticsData.map((p: AnalyticsData, index: number) => { + const colorVariants = ['purple', 'green', 'blue'] as const; + const color = colorVariants[index % colorVariants.length]; + return ( + <div key={`analytics-${index}`} className="group"> + <div className="flex flex-col h-full bg-newTableHeader border border-newTableBorder rounded-[12px] overflow-hidden transition-all duration-200 hover:border-[#612bd3]/50"> + <div className="flex items-center justify-between px-[16px] pt-[14px] pb-[8px]"> + <div className="flex items-center gap-[10px]"> + <div + className={`w-[8px] h-[8px] rounded-full ${ + color === 'purple' ? 'bg-[#612bd3]' : '' + } ${color === 'green' ? 'bg-[#32d583]' : ''} ${ + color === 'blue' ? 'bg-[#1d9bf0]' : '' + }`} + /> + <span className="text-[15px] font-medium text-newTableText"> + {p.label} + </span> + </div> + </div> + <div className="flex-1 px-[12px] py-[8px]"> + <div className="h-[120px] relative"> + <ChartSocial data={p.data} color={color} key={`chart-${index}`} /> + </div> + </div> + <div className="px-[16px] pb-[14px]"> + <div className="text-[36px] leading-[42px] font-semibold tracking-tight"> + {totals[index]} + </div> </div> </div> - <div className="text-[50px] leading-[60px]">{totals[index]}</div> </div> - </div> - ))} + ); + })} </div> </div> )} diff --git a/apps/frontend/src/components/platform-analytics/render.analytics.tsx b/apps/frontend/src/components/platform-analytics/render.analytics.tsx index e56a6a4d..c1cf6ca5 100644 --- a/apps/frontend/src/components/platform-analytics/render.analytics.tsx +++ b/apps/frontend/src/components/platform-analytics/render.analytics.tsx @@ -5,6 +5,168 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { ChartSocial } from '@gitroom/frontend/components/analytics/chart-social'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +interface AnalyticsDataItem { + label: string; + data: Array<{ total: number; date: string }>; + average?: boolean; + percentageChange?: number; +} + +const TrendIndicator: FC<{ value: number; average?: boolean }> = ({ + value, + average, +}) => { + if (value === 0) return null; + + const isPositive = value > 0; + const displayValue = Math.abs(value).toFixed(1); + + return ( + <div + className={`flex items-center gap-[4px] text-[13px] font-medium ${ + isPositive ? 'text-[#32d583]' : 'text-[#f97066]' + }`} + > + <svg + width="12" + height="12" + viewBox="0 0 12 12" + fill="none" + className={isPositive ? '' : 'rotate-180'} + > + <path + d="M6 2.5L10 7.5H2L6 2.5Z" + fill="currentColor" + /> + </svg> + <span> + {displayValue} + {average ? 'pp' : '%'} + </span> + </div> + ); +}; + +const AnalyticsCard: FC<{ + item: AnalyticsDataItem; + total: string | number; + index: number; +}> = ({ item, total, index }) => { + const colorVariants = ['purple', 'green', 'blue'] as const; + const color = colorVariants[index % colorVariants.length]; + + const hasMultipleDataPoints = item.data.length > 1; + + return ( + <div className="group relative"> + <div + className={` + flex flex-col h-full + bg-newTableHeader + border border-newTableBorder + rounded-[12px] + overflow-hidden + transition-all duration-200 + hover:border-[#612bd3]/50 + `} + > + {/* Header */} + <div className="flex items-center justify-between px-[16px] pt-[14px] pb-[8px]"> + <div className="flex items-center gap-[10px]"> + <div + className={` + w-[8px] h-[8px] rounded-full + ${color === 'purple' ? 'bg-[#612bd3]' : ''} + ${color === 'green' ? 'bg-[#32d583]' : ''} + ${color === 'blue' ? 'bg-[#1d9bf0]' : ''} + `} + /> + <span className="text-[15px] font-medium text-newTableText"> + {item.label} + </span> + </div> + {item.percentageChange !== undefined && ( + <TrendIndicator value={item.percentageChange} average={item.average} /> + )} + </div> + + {/* Content */} + {hasMultipleDataPoints ? ( + <> + {/* Chart */} + <div className="flex-1 px-[12px] py-[8px]"> + <div className="h-[120px] relative"> + <ChartSocial data={item.data} color={color} key={`chart-${index}`} /> + </div> + </div> + + {/* Value */} + <div className="px-[16px] pb-[14px]"> + <div className="text-[36px] leading-[42px] font-semibold tracking-tight"> + {total} + </div> + </div> + </> + ) : ( + /* Single value display */ + <div className="flex-1 flex flex-col items-center justify-center py-[32px] px-[16px]"> + <div className="text-[48px] leading-[56px] font-semibold tracking-tight"> + {total} + </div> + </div> + )} + </div> + </div> + ); +}; + +const EmptyState: FC<{ onRefresh: () => void }> = ({ onRefresh }) => { + const t = useT(); + + return ( + <div className="col-span-full flex flex-col items-center justify-center py-[48px] px-[24px] bg-newTableHeader border border-newTableBorder rounded-[12px]"> + <div className="w-[48px] h-[48px] mb-[16px] rounded-full bg-[#612bd3]/10 flex items-center justify-center"> + <svg + width="24" + height="24" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + className="text-[#612bd3]" + > + <path d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> + <path d="M12 8v4l2 2" /> + </svg> + </div> + <p className="text-[15px] text-newTableText text-center mb-[12px]"> + {t( + 'this_channel_needs_to_be_refreshed', + 'This channel needs to be refreshed to display analytics' + )} + </p> + <button + onClick={onRefresh} + className="inline-flex items-center gap-[6px] px-[16px] py-[8px] text-[14px] font-medium text-white bg-[#612bd3] hover:bg-[#5023b8] rounded-[8px] transition-colors" + > + <svg + width="16" + height="16" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="2" + > + <path d="M23 4v6h-6M1 20v-6h6" /> + <path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15" /> + </svg> + {t('refresh_channel', 'Refresh Channel')} + </button> + </div> + ); +}; + export const RenderAnalytics: FC<{ integration: Integration; date: number; @@ -12,6 +174,7 @@ export const RenderAnalytics: FC<{ const { integration, date } = props; const [loading, setLoading] = useState(true); const fetch = useFetch(); + const load = useCallback(async () => { setLoading(true); const load = ( @@ -20,6 +183,7 @@ export const RenderAnalytics: FC<{ setLoading(false); return load; }, [integration, date]); + const { data } = useSWR(`/analytics-${integration?.id}-${date}`, load, { refreshInterval: 0, refreshWhenHidden: false, @@ -29,16 +193,17 @@ export const RenderAnalytics: FC<{ refreshWhenOffline: false, revalidateOnMount: true, }); + const refreshChannel = useCallback( ( - integration: Integration & { + integrationData: Integration & { identifier: string; } ) => async () => { const { url } = await ( await fetch( - `/integrations/social/${integration.identifier}?refresh=${integration.internalId}`, + `/integrations/social/${integrationData.identifier}?refresh=${integrationData.internalId}`, { method: 'GET', } @@ -51,62 +216,38 @@ export const RenderAnalytics: FC<{ const t = useT(); - const total = useMemo(() => { - return data?.map((p: any) => { + const totals = useMemo(() => { + return data?.map((p: AnalyticsDataItem) => { const value = - (p?.data.reduce((acc: number, curr: any) => acc + curr.total, 0) || 0) / + (p?.data.reduce((acc: number, curr: { total: number }) => acc + curr.total, 0) || 0) / (p.average ? p.data.length : 1); if (p.average) { return value.toFixed(2) + '%'; } - return value; + return new Intl.NumberFormat().format(Math.round(value)); }); }, [data]); + if (loading) { return ( - <> + <div className="flex items-center justify-center py-[48px]"> <LoadingComponent /> - </> + </div> ); } + return ( - <div className="grid grid-cols-3 gap-[20px]"> + <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-[16px]"> {data?.length === 0 && ( - <div> - {t( - 'this_channel_needs_to_be_refreshed', - 'This channel needs to be refreshed,' - )} - <div - className="underline hover:font-bold cursor-pointer" - onClick={refreshChannel(integration as any)} - > - {t('click_here_to_refresh', 'click here to refresh')} - </div> - </div> + <EmptyState onRefresh={refreshChannel(integration as any)} /> )} - {data?.map((p: any, index: number) => ( - <div key={`pl-${index}`} className="flex"> - <div className="flex-1 bg-newTableHeader rounded-[8px] py-[10px] px-[16px] gap-[10px] flex flex-col"> - <div className="flex items-center gap-[14px]"> - <div className="text-[20px]">{p.label}</div> - </div> - {p.data.length > 1 ? ( - <> - <div className="flex-1"> - <div className="h-[156px] relative"> - <ChartSocial {...p} key={`p-${index}`} /> - </div> - </div> - <div className="text-[50px] leading-[60px]">{total[index]}</div> - </> - ) : ( - <div className="flex-1 flex flex-col items-center justify-center min-h-[216px]"> - <div className="text-[64px] leading-[72px] font-medium">{total[index]}</div> - </div> - )} - </div> - </div> + {data?.map((item: AnalyticsDataItem, index: number) => ( + <AnalyticsCard + key={`analytics-${index}`} + item={item} + total={totals[index]} + index={index} + /> ))} </div> ); diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 6f8f74c3..032ee1db 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -651,7 +651,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { } result.push({ - label: 'Recent Views', + label: 'Views', percentageChange: 0, data: [{ total: String(totalViews), date: today }], }); @@ -691,6 +691,29 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { ): Promise<AnalyticsData[]> { const today = dayjs().format('YYYY-MM-DD'); + const post = await ( + await this.fetch( + 'https://open.tiktokapis.com/v2/post/publish/status/fetch/', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + publish_id: postId, + }), + }, + '', + 0, + true + ) + ).json(); + + if (!post?.data?.publicaly_available_post_id?.[0]) { + return []; + } + try { // Query video details using the video ID const response = await this.fetch( @@ -702,7 +725,9 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ - filters: { video_ids: [postId] }, + filters: { + video_ids: post?.data?.publicaly_available_post_id.map(String), + }, }), } ); diff --git a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts index e96395ae..6d2151df 100644 --- a/libraries/nestjs-libraries/src/integrations/social/x.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/x.provider.ts @@ -511,7 +511,7 @@ export class XProvider extends SocialAbstract implements SocialProvider { } const until = dayjs().endOf('day'); - const since = dayjs().subtract(date, 'day'); + const since = dayjs().subtract(date > 100 ? 100 : date, 'day'); const [accessTokenSplit, accessSecretSplit] = accessToken.split(':'); const client = new TwitterApi({ From 79092282713887496a70d19ce90543be1e79ee3a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 30 Jan 2026 12:50:06 +0700 Subject: [PATCH 193/340] feat: better validation --- .../src/database/prisma/posts/posts.service.ts | 1 + libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 866b4897..a002876a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -180,6 +180,7 @@ export class PostsService { } return { + type: replaceDraft ? 'schedule' : body.type, ...post, settings: { ...(post.settings || ({} as any)), diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts index 391fd56a..2e5ce3ea 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts @@ -49,6 +49,8 @@ export class PostContent { } export class Post { + type?: string; + @IsDefined() @Type(() => Integration) @ValidateNested() @@ -65,6 +67,7 @@ export class Post { @IsString() group: string; + @ValidateIf((o) => o.type !== 'draft') @ValidateNested() @Type(() => EmptySettings, { keepDiscriminatorProperty: true, @@ -112,7 +115,6 @@ export class CreatePostDto { @ValidateNested({ each: true }) tags: Tags[]; - @ValidateIf((o) => o.type !== 'draft') @IsDefined() @Type(() => Post) @IsArray() From ccce5d81222e6324d19c05876e70acfaa9fd204c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 30 Jan 2026 16:22:04 +0700 Subject: [PATCH 194/340] feat: list view --- .../src/api/routes/posts.controller.ts | 9 + .../components/launches/calendar.context.tsx | 127 +++++-- .../src/components/launches/calendar.tsx | 345 +++++++++++------- .../src/components/launches/filters.tsx | 307 ++++++++++++---- .../database/prisma/posts/posts.repository.ts | 79 ++++ .../database/prisma/posts/posts.service.ts | 5 + .../src/dtos/posts/get.posts.list.dto.ts | 27 ++ 7 files changed, 670 insertions(+), 229 deletions(-) create mode 100644 libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index a3dcc1ad..2f4e0858 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -13,6 +13,7 @@ import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/po import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; import { Organization, User } from '@prisma/client'; import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; +import { GetPostsListDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.list.dto'; import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability'; import { ApiTags } from '@nestjs/swagger'; import { GeneratorDto } from '@gitroom/nestjs-libraries/dtos/generator/generator.dto'; @@ -114,6 +115,14 @@ export class PostsController { return { date: await this._postsService.findFreeDateTime(org.id, id) }; } + @Get('/list') + async getPostsList( + @GetOrgFromRequest() org: Organization, + @Query() query: GetPostsListDto + ) { + return this._postsService.getPostsList(org.id, query); + } + @Get('/old') oldPosts( @GetOrgFromRequest() org: Organization, diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx index 651634f5..1eac57a7 100644 --- a/apps/frontend/src/components/launches/calendar.context.tsx +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -55,7 +55,7 @@ export const CalendarContext = createContext({ setFilters: (filters: { startDate: string; endDate: string; - display: 'week' | 'month' | 'day'; + display: 'week' | 'month' | 'day' | 'list'; customer: string | null; }) => { /** empty **/ @@ -63,6 +63,20 @@ export const CalendarContext = createContext({ changeDate: (id: string, date: dayjs.Dayjs) => { /** empty **/ }, + // List view specific + listPosts: [] as Array< + Post & { + integration: Integration; + tags: { + tag: Tags; + }[]; + } + >, + listPage: 0, + listTotalPages: 0, + setListPage: (page: number) => { + /** empty **/ + }, }); export interface Integrations { @@ -126,6 +140,9 @@ export const CalendarWeekProvider: FC<{ const [displaySaved, setDisplaySaved] = useCookie('calendar-display', 'week'); const display = searchParams.get('display') || displaySaved; + // List view state + const [listPage, setListPage] = useState(0); + // Initialize with current date range based on URL params or defaults const initStartDate = searchParams.get('startDate'); const initEndDate = searchParams.get('endDate'); @@ -152,6 +169,7 @@ export const CalendarWeekProvider: FC<{ }).toString(); }, [filters]); + // Calendar view data fetcher const loadData = useCallback(async () => { const modifiedParams = new URLSearchParams({ display: filters.display, @@ -164,12 +182,51 @@ export const CalendarWeekProvider: FC<{ return data; }, [filters, params]); - const swr = useSWR(`/posts-${params}`, loadData, { - refreshInterval: 3600000, - refreshWhenOffline: false, - refreshWhenHidden: false, - revalidateOnFocus: false, - }); + // List view data fetcher + const listParams = useMemo(() => { + return new URLSearchParams({ + page: listPage.toString(), + limit: '100', + customer: filters?.customer?.toString() || '', + }).toString(); + }, [listPage, filters.customer]); + + const loadListData = useCallback(async () => { + const response = await fetch(`/posts/list?${listParams}`); + return response.json(); + }, [listParams]); + + // SWR for calendar view + const { + data: calendarData, + isLoading: calendarIsLoading, + mutate: mutateCalendar, + } = useSWR( + filters.display !== 'list' ? `/posts-${params}` : null, + loadData, + { + refreshInterval: 3600000, + refreshWhenOffline: false, + refreshWhenHidden: false, + revalidateOnFocus: false, + } + ); + + // SWR for list view + const { + data: listData, + isLoading: listIsLoading, + mutate: mutateList, + } = useSWR( + filters.display === 'list' ? `/posts-list-${listParams}` : null, + loadListData, + { + refreshInterval: 3600000, + refreshWhenOffline: false, + refreshWhenHidden: false, + revalidateOnFocus: false, + } + ); const defaultSign = useCallback(async () => { return await (await fetch('/signatures/default')).json(); @@ -197,31 +254,39 @@ export const CalendarWeekProvider: FC<{ }); const setFiltersWrapper = useCallback( - (filters: { + (newFilters: { startDate: string; endDate: string; - display: 'week' | 'month' | 'day'; + display: 'week' | 'month' | 'day' | 'list'; customer: string | null; }) => { - setDisplaySaved(filters.display); - setFilters(filters); + setDisplaySaved(newFilters.display); + setFilters(newFilters); setInternalData([]); + + // Reset page when switching to list view + if (newFilters.display === 'list') { + setListPage(0); + } + const path = [ - `startDate=${filters.startDate}`, - `endDate=${filters.endDate}`, - `display=${filters.display}`, - filters.customer ? `customer=${filters.customer}` : ``, + `startDate=${newFilters.startDate}`, + `endDate=${newFilters.endDate}`, + `display=${newFilters.display}`, + newFilters.customer ? `customer=${newFilters.customer}` : ``, ].filter((f) => f); window.history.replaceState(null, '', `/launches?${path.join('&')}`); }, - [filters, swr.mutate] + [] ); - const { isLoading } = swr; - const { posts, comments } = swr?.data || { - posts: [], - comments: [], - }; + const posts = useMemo(() => calendarData?.posts || [], [calendarData?.posts]); + const comments = useMemo(() => calendarData?.comments || [], [calendarData?.comments]); + + // List view data + const listPosts = useMemo(() => listData?.posts || [], [listData?.posts]); + const listTotal = listData?.total || 0; + const listTotalPages = Math.ceil(listTotal / 100); const changeDate = useCallback( (id: string, date: dayjs.Dayjs) => { @@ -246,20 +311,34 @@ export const CalendarWeekProvider: FC<{ } }, [posts]); + // Combined reload function that handles both calendar and list views + const reloadCalendarView = useCallback(() => { + mutateCalendar(); + mutateList(); + }, [mutateCalendar, mutateList]); + + // Determine loading state based on current view + const loading = filters.display === 'list' ? listIsLoading : calendarIsLoading; + return ( <CalendarContext.Provider value={{ trendings, - reloadCalendarView: swr.mutate, + reloadCalendarView, ...filters, - posts: isLoading ? [] : internalData, - loading: swr.isLoading, + posts: calendarIsLoading ? [] : internalData, + loading, integrations, setFilters: setFiltersWrapper, changeDate, comments, sets: sets || [], signature: sign, + // List view specific + listPosts, + listPage, + listTotalPages, + setListPage, }} > {children} diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index d73bd54f..88c27f38 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -83,21 +83,140 @@ const convertTimeFormatBasedOnLocality = (time: number) => { } }; -export const days = [ - 'Monday', - 'Tuesday', - 'Wednesday', - 'Thursday', - 'Friday', - 'Saturday', - 'Sunday', -]; export const hours = Array.from( { length: 24, }, (_, i) => i ); + +// Shared hook for post actions (edit, delete, statistics) +const usePostActions = (onMutate?: () => void) => { + const t = useT(); + const fetch = useFetch(); + const modal = useModals(); + const toaster = useToaster(); + const { integrations, reloadCalendarView } = useCalendar(); + + const mutate = useCallback(() => { + reloadCalendarView(); + onMutate?.(); + }, [reloadCalendarView, onMutate]); + + const editPost = useCallback( + (loadPost: any, isDuplicate?: boolean) => async () => { + const post = { + ...loadPost, + publishDate: loadPost.actualDate || loadPost.publishDate, + }; + + const data = await (await fetch(`/posts/group/${post.group}`)).json(); + const date = !isDuplicate + ? null + : (await (await fetch('/posts/find-slot')).json()).date; + const publishDate = dayjs + .utc(date || data.posts[0].publishDate) + .local(); + const ExistingData = !isDuplicate + ? ExistingDataContextProvider + : Fragment; + modal.openModal({ + id: 'add-edit-modal', + closeOnClickOutside: false, + removeLayout: true, + closeOnEscape: false, + withCloseButton: false, + askClose: true, + fullScreen: true, + classNames: { + modal: 'w-[100%] max-w-[1400px] text-textColor', + }, + children: ( + <ExistingData value={data}> + <AddEditModal + {...(isDuplicate + ? { + onlyValues: data.posts.map( + ({ image, settings, content }: any) => ({ + image, + settings, + content, + }) + ), + } + : {})} + allIntegrations={integrations.map((p) => ({ ...p }))} + reopenModal={editPost(post)} + mutate={mutate} + integrations={ + isDuplicate + ? integrations + : integrations + .slice(0) + .filter((f) => f.id === data.integration) + .map((p) => ({ + ...p, + picture: data.integrationPicture, + })) + } + date={publishDate} + /> + </ExistingData> + ), + size: '80%', + title: ``, + }); + }, + [integrations, fetch, modal, mutate] + ); + + const deletePost = useCallback( + (post: any) => async () => { + if ( + !(await deleteDialog( + t( + 'are_you_sure_you_want_to_delete_post', + 'Are you sure you want to delete post?' + ) + )) + ) { + return; + } + + await fetch(`/posts/${post.group}`, { + method: 'DELETE', + }); + + toaster.show( + t('post_deleted_successfully', 'Post deleted successfully'), + 'success' + ); + + mutate(); + }, + [toaster, t, fetch, mutate] + ); + + const openStatistics = useCallback( + (id: string) => () => { + modal.openModal({ + title: t('statistics', 'Statistics'), + closeOnClickOutside: true, + closeOnEscape: true, + withCloseButton: true, + classNames: { + modal: 'w-[100%] max-w-[1400px]', + }, + children: <StatisticsModal postId={id} />, + size: '80%', + }); + }, + [modal, t] + ); + + return { editPost, deletePost, openStatistics }; +}; + export const DayView = () => { const calendar = useCalendar(); const { integrations, posts, startDate } = calendar; @@ -328,11 +447,84 @@ export const MonthView = () => { </div> ); }; +export const ListView = () => { + const t = useT(); + const { integrations, loading, listPosts } = useCalendar(); + + // Use shared post actions hook + const { editPost, deletePost, openStatistics } = usePostActions(); + + // Group posts by date + const groupedPosts = useMemo(() => { + const groups: { [key: string]: any[] } = {}; + listPosts.forEach((post) => { + const dateKey = newDayjs(post.publishDate).local().format('YYYY-MM-DD'); + if (!groups[dateKey]) { + groups[dateKey] = []; + } + groups[dateKey].push(post); + }); + return Object.entries(groups).sort(([a], [b]) => a.localeCompare(b)); + }, [listPosts]); + + if (loading) { + return ( + <div className="flex flex-col flex-1 items-center justify-center"> + <div className="text-textColor">{t('loading', 'Loading...')}</div> + </div> + ); + } + + if (listPosts.length === 0) { + return ( + <div className="flex flex-col flex-1 items-center justify-center"> + <div className="text-textColor text-[16px]"> + {t('no_upcoming_posts', 'No upcoming posts scheduled')} + </div> + </div> + ); + } + + return ( + <div className="flex flex-col gap-[10px] flex-1 relative"> + <div className="absolute start-0 top-0 w-full h-full flex flex-col overflow-auto scrollbar scrollbar-thumb-fifth scrollbar-track-newBgColor"> + {groupedPosts.map(([dateKey, datePosts]) => ( + <Fragment key={dateKey}> + <div className="text-center text-[14px] min-h-[21px] text-textColor font-[500] mt-[10px]"> + {newDayjs(dateKey).format(isUSCitizen() ? 'dddd, MMMM D, YYYY' : 'dddd, D MMMM YYYY')} + </div> + <div className="flex flex-col gap-[10px] mb-[20px] px-[10px]"> + {datePosts.map((post) => ( + <CalendarItem + key={post.id} + display="day" + isBeforeNow={false} + date={newDayjs(post.publishDate)} + state={post.state} + statistics={openStatistics(post.id)} + editPost={editPost(post, false)} + duplicatePost={editPost(post, true)} + post={post} + integrations={integrations} + deletePost={deletePost(post)} + showTime={true} + /> + ))} + </div> + </Fragment> + ))} + </div> + </div> + ); +}; + export const Calendar = () => { const { display } = useCalendar(); return ( <> - {display === 'day' ? ( + {display === 'list' ? ( + <ListView /> + ) : display === 'day' ? ( <DayView /> ) : display === 'week' ? ( <WeekView /> @@ -361,9 +553,11 @@ export const CalendarColumn: FC<{ signature, loading, } = useCalendar(); - const toaster = useToaster(); const modal = useModals(); const fetch = useFetch(); + + // Use shared post actions hook + const { editPost, deletePost, openStatistics } = usePostActions(); const postList = useMemo(() => { return posts.filter((post) => { const pList = dayjs.utc(post.publishDate).local(); @@ -504,84 +698,6 @@ export const CalendarColumn: FC<{ }), }), [posts]); - const editPost = useCallback( - ( - loadPost: Post & { - integration: Integration; - }, - isDuplicate?: boolean - ) => - async () => { - const post = { - ...loadPost, - // @ts-ignore - publishDate: loadPost.actualDate || loadPost.publishDate, - }; - - const data = await (await fetch(`/posts/group/${post.group}`)).json(); - const date = !isDuplicate - ? null - : (await (await fetch('/posts/find-slot')).json()).date; - const publishDate = dayjs - .utc(date || data.posts[0].publishDate) - .local(); - const ExistingData = !isDuplicate - ? ExistingDataContextProvider - : Fragment; - modal.openModal({ - id: 'add-edit-modal', - closeOnClickOutside: false, - removeLayout: true, - closeOnEscape: false, - withCloseButton: false, - askClose: true, - fullScreen: true, - classNames: { - modal: 'w-[100%] max-w-[1400px] text-textColor', - }, - children: ( - <ExistingData value={data}> - <AddEditModal - {...(isDuplicate - ? { - onlyValues: data.posts.map( - ({ image, settings, content }: any) => { - return { - image, - settings, - content, - }; - } - ), - } - : {})} - allIntegrations={integrations.map((p) => ({ - ...p, - }))} - reopenModal={editPost(post)} - mutate={reloadCalendarView} - integrations={ - isDuplicate - ? integrations - : integrations - .slice(0) - .filter((f) => f.id === data.integration) - .map((p) => ({ - ...p, - picture: data.integrationPicture, - })) - } - date={publishDate} - /> - </ExistingData> - ), - size: '80%', - title: ``, - }); - }, - [integrations] - ); - const addModal = useCallback(async () => { const set: any = !sets.length ? undefined @@ -655,50 +771,6 @@ export const CalendarColumn: FC<{ size: '80%', }); }, [integrations, getDate, sets, signature]); - const openStatistics = useCallback( - (id: string) => () => { - modal.openModal({ - title: t('statistics', 'Statistics'), - closeOnClickOutside: true, - closeOnEscape: true, - withCloseButton: true, - classNames: { - modal: 'w-[100%] max-w-[1400px]', - }, - children: <StatisticsModal postId={id} />, - size: '80%', - // title: `Adding posts for ${getDate.format('DD/MM/YYYY HH:mm')}`, - }); - }, - [] - ); - - const deletePost = useCallback( - (post: Post) => async () => { - if ( - !(await deleteDialog( - t( - 'are_you_sure_you_want_to_delete_post', - 'Are you sure you want to delete post?' - ) - )) - ) { - return; - } - - await fetch(`/posts/${post.group}`, { - method: 'DELETE', - }); - - toaster.show( - t('post_deleted_successfully', 'Post deleted successfully'), - 'success' - ); - - reloadCalendarView(); - }, - [toaster, t] - ); const addProvider = useAddProvider(); return ( @@ -860,6 +932,7 @@ const CalendarItem: FC<{ integrations: Integrations[]; state: State; display: 'day' | 'week' | 'month'; + showTime?: boolean; post: Post & { integration: Integration; tags: { @@ -878,6 +951,7 @@ const CalendarItem: FC<{ state, display, deletePost, + showTime, } = props; const { disableXAnalytics } = useVariables(); const preview = useCallback(() => { @@ -992,6 +1066,11 @@ const CalendarItem: FC<{ </div> </div> </div> + {showTime && ( + <div className="text-textColor/50 text-[12px] whitespace-nowrap flex items-center"> + {newDayjs(post.publishDate).local().format(isUSCitizen() ? 'hh:mm A' : 'HH:mm')} + </div> + )} </div> </div> ); diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index 43332f12..57635288 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -11,7 +11,7 @@ import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; // Helper function to get start and end dates based on display type function getDateRange( - display: 'day' | 'week' | 'month', + display: 'day' | 'week' | 'month' | 'list', referenceDate?: string ) { const date = referenceDate ? newDayjs(referenceDate) : newDayjs(); @@ -32,6 +32,11 @@ function getDateRange( startDate: date.startOf('month').format('YYYY-MM-DD'), endDate: date.endOf('month').format('YYYY-MM-DD'), }; + case 'list': + return { + startDate: date.format('YYYY-MM-DD'), + endDate: date.format('YYYY-MM-DD'), + }; } } @@ -136,6 +141,34 @@ export const Filters = () => { }); }, [calendar]); + const setList = useCallback(() => { + if (calendar.display === 'list') { + return; + } + + const range = getDateRange('list'); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, + display: 'list', + customer: calendar.customer, + }); + }, [calendar]); + + const setCalendarView = useCallback(() => { + if (calendar.display !== 'list') { + return; + } + + const range = getDateRange('week'); + calendar.setFilters({ + startDate: range.startDate, + endDate: range.endDate, + display: 'week', + customer: calendar.customer, + }); + }, [calendar]); + const setCustomer = useCallback( (customer: string) => { if (calendar.customer === customer) { @@ -224,99 +257,229 @@ export const Filters = () => { [setDay, setWeek, setMonth] ); + const isListView = calendar.display === 'list'; + + const previousPage = useCallback(() => { + if (calendar.listPage > 0) { + calendar.setListPage(calendar.listPage - 1); + } + }, [calendar]); + + const nextPage = useCallback(() => { + if (calendar.listPage < calendar.listTotalPages - 1) { + calendar.setListPage(calendar.listPage + 1); + } + }, [calendar]); + return ( <div className="text-textColor flex flex-col md:flex-row gap-[8px] items-center select-none"> - <div className="flex flex-grow flex-row items-center gap-[10px]"> - <div className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden"> - <div - onClick={previous} - className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused" - > - <svg - xmlns="http://www.w3.org/2000/svg" - width="8" - height="12" - viewBox="0 0 8 12" - fill="none" - > - <path - d="M6.5 11L1.5 6L6.5 1" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> - </div> - <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> - <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]"> - {getDisplayText()} - </div> - </div> - <div - onClick={next} - className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused" - > - <svg - xmlns="http://www.w3.org/2000/svg" - width="8" - height="12" - viewBox="0 0 8 12" - fill="none" - > - <path - d="M1.5 11L6.5 6L1.5 1" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> - </svg> - </div> - </div> - <div className="flex-1 text-[14px] font-[500]"> - <div className="text-center flex h-[42px]"> + {!isListView && ( + <div className="flex flex-grow flex-row items-center gap-[10px]"> + <div className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden"> <div - onClick={setToday} - className="hover:text-textItemFocused hover:bg-boxFocused py-[3px] px-[9px] flex justify-center items-center rounded-[8px] transition-all cursor-pointer text-[14px] bg-newBgColorInner border border-newTableBorder" + onClick={previous} + className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused" > - {t('today', 'Today')} + <svg + xmlns="http://www.w3.org/2000/svg" + width="8" + height="12" + viewBox="0 0 8 12" + fill="none" + > + <path + d="M6.5 11L1.5 6L6.5 1" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> + <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]"> + {getDisplayText()} + </div> + </div> + <div + onClick={next} + className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused" + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="8" + height="12" + viewBox="0 0 8 12" + fill="none" + > + <path + d="M1.5 11L6.5 6L1.5 1" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + <div className="flex-1 text-[14px] font-[500]"> + <div className="text-center flex h-[42px]"> + <div + onClick={setToday} + className="hover:text-textItemFocused hover:bg-boxFocused py-[3px] px-[9px] flex justify-center items-center rounded-[8px] transition-all cursor-pointer text-[14px] bg-newBgColorInner border border-newTableBorder" + > + {t('today', 'Today')} + </div> </div> </div> </div> - </div> + )} + {isListView && ( + <div className="flex flex-grow flex-row items-center gap-[10px]"> + <div className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden"> + <div + onClick={previousPage} + className={clsx( + 'text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center', + calendar.listPage > 0 + ? 'cursor-pointer hover:text-textItemFocused hover:bg-boxFocused' + : 'opacity-50 cursor-not-allowed' + )} + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="8" + height="12" + viewBox="0 0 8 12" + fill="none" + > + <path + d="M6.5 11L1.5 6L6.5 1" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> + <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]"> + {t('page', 'Page')} {calendar.listPage + 1} {t('of', 'of')} {Math.max(1, calendar.listTotalPages)} + </div> + </div> + <div + onClick={nextPage} + className={clsx( + 'text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center', + calendar.listPage < calendar.listTotalPages - 1 + ? 'cursor-pointer hover:text-textItemFocused hover:bg-boxFocused' + : 'opacity-50 cursor-not-allowed' + )} + > + <svg + xmlns="http://www.w3.org/2000/svg" + width="8" + height="12" + viewBox="0 0 8 12" + fill="none" + > + <path + d="M1.5 11L6.5 6L1.5 1" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + </div> + <div className="flex-1" /> + </div> + )} <SelectCustomer customer={calendar.customer as string} onChange={(customer: string) => setCustomer(customer)} integrations={calendar.integrations} /> + {!isListView && ( + <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]"> + <div + className={clsx( + 'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]', + calendar.display === 'day' && 'text-textItemFocused bg-boxFocused' + )} + onClick={setDay} + > + {t('day', 'Day')} + </div> + <div + className={clsx( + 'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]', + calendar.display === 'week' && 'text-textItemFocused bg-boxFocused' + )} + onClick={setWeek} + > + {t('week', 'Week')} + </div> + <div + className={clsx( + 'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]', + calendar.display === 'month' && 'text-textItemFocused bg-boxFocused' + )} + onClick={setMonth} + > + {t('month', 'Month')} + </div> + </div> + )} <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]"> <div + onClick={setCalendarView} className={clsx( - 'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]', - calendar.display === 'day' && 'text-textItemFocused bg-boxFocused' + 'pt-[6px] pb-[5px] cursor-pointer flex justify-center items-center w-[34px] text-center rounded-[6px]', + !isListView && 'text-textItemFocused bg-boxFocused' )} - onClick={setDay} > - {t('day', 'Day')} + {/*calendar*/} + <svg + xmlns="http://www.w3.org/2000/svg" + width="17" + height="19" + viewBox="0 0 17 19" + fill="none" + > + <path + d="M15.75 7.41667H0.75M11.5833 0.75V4.08333M4.91667 0.75V4.08333M4.75 17.4167H11.75C13.1501 17.4167 13.8502 17.4167 14.385 17.1442C14.8554 16.9045 15.2378 16.522 15.4775 16.0516C15.75 15.5169 15.75 14.8168 15.75 13.4167V6.41667C15.75 5.01654 15.75 4.31647 15.4775 3.78169C15.2378 3.31129 14.8554 2.92883 14.385 2.68915C13.8502 2.41667 13.1501 2.41667 11.75 2.41667H4.75C3.34987 2.41667 2.6498 2.41667 2.11502 2.68915C1.64462 2.92883 1.26217 3.31129 1.02248 3.78169C0.75 4.31647 0.75 5.01654 0.75 6.41667V13.4167C0.75 14.8168 0.75 15.5169 1.02248 16.0516C1.26217 16.522 1.64462 16.9045 2.11502 17.1442C2.6498 17.4167 3.34987 17.4167 4.75 17.4167Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> </div> <div + onClick={setList} className={clsx( - 'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]', - calendar.display === 'week' && 'text-textItemFocused bg-boxFocused' + 'pt-[6px] pb-[5px] flex justify-center items-center cursor-pointer w-[34px] text-center rounded-[6px]', + isListView && 'text-textItemFocused bg-boxFocused' )} - onClick={setWeek} > - {t('week', 'Week')} - </div> - <div - className={clsx( - 'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]', - calendar.display === 'month' && 'text-textItemFocused bg-boxFocused' - )} - onClick={setMonth} - > - {t('month', 'Month')} + {/*list*/} + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M17.5 10L7.5 10M17.5 5.00002L7.5 5.00002M17.5 15L7.5 15M4.16667 10C4.16667 10.4603 3.79357 10.8334 3.33333 10.8334C2.8731 10.8334 2.5 10.4603 2.5 10C2.5 9.53978 2.8731 9.16669 3.33333 9.16669C3.79357 9.16669 4.16667 9.53978 4.16667 10ZM4.16667 5.00002C4.16667 5.46026 3.79357 5.83335 3.33333 5.83335C2.8731 5.83335 2.5 5.46026 2.5 5.00002C2.5 4.53978 2.8731 4.16669 3.33333 4.16669C3.79357 4.16669 4.16667 4.53978 4.16667 5.00002ZM4.16667 15C4.16667 15.4603 3.79357 15.8334 3.33333 15.8334C2.8731 15.8334 2.5 15.4603 2.5 15C2.5 14.5398 2.8731 14.1667 3.33333 14.1667C3.79357 14.1667 4.16667 14.5398 4.16667 15Z" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> </div> </div> </div> diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index ebb33923..4d7ab250 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common'; import { Post as PostBody } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto'; import { APPROVED_SUBMIT_FOR_ORDER, Post, State } from '@prisma/client'; import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; +import { GetPostsListDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.list.dto'; import dayjs from 'dayjs'; import isoWeek from 'dayjs/plugin/isoWeek'; import weekOfYear from 'dayjs/plugin/weekOfYear'; @@ -211,6 +212,84 @@ export class PostsRepository { }, [] as any[]); } + async getPostsList(orgId: string, query: GetPostsListDto) { + const page = query.page || 0; + const limit = query.limit || 20; + const skip = page * limit; + + const where = { + AND: [ + { + OR: [ + { + organizationId: orgId, + }, + { + submittedForOrganizationId: orgId, + }, + ], + }, + { + publishDate: { + gte: dayjs.utc().toDate(), + }, + }, + ], + deletedAt: null as Date | null, + parentPostId: null as string | null, + intervalInDays: null as number | null, + ...(query.customer + ? { + integration: { + customerId: query.customer, + }, + } + : {}), + }; + + const [posts, total] = await Promise.all([ + this._post.model.post.findMany({ + where, + skip, + take: limit, + orderBy: { + publishDate: 'asc', + }, + select: { + id: true, + content: true, + publishDate: true, + releaseURL: true, + releaseId: true, + state: true, + group: true, + tags: { + select: { + tag: true, + }, + }, + integration: { + select: { + id: true, + providerIdentifier: true, + name: true, + picture: true, + }, + }, + }, + }), + this._post.model.post.count({ where }), + ]); + + return { + posts, + total, + page, + limit, + hasMore: skip + posts.length < total, + }; + } + async deletePost(orgId: string, group: string) { await this._post.model.post.updateMany({ where: { diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index a002876a..5a084c7e 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -9,6 +9,7 @@ import dayjs from 'dayjs'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { Integration, Post, Media, From, State } from '@prisma/client'; import { GetPostsDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.dto'; +import { GetPostsListDto } from '@gitroom/nestjs-libraries/dtos/posts/get.posts.list.dto'; import { shuffle } from 'lodash'; import { CreateGeneratedPostsDto } from '@gitroom/nestjs-libraries/dtos/generator/create.generated.posts.dto'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; @@ -239,6 +240,10 @@ export class PostsService { return this._postRepository.getPosts(orgId, query); } + async getPostsList(orgId: string, query: GetPostsListDto) { + return this._postRepository.getPostsList(orgId, query); + } + async updateMedia(id: string, imagesList: any[], convertToJPEG = false) { try { let imageUpdateNeeded = false; diff --git a/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts new file mode 100644 index 00000000..89ef94dd --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts @@ -0,0 +1,27 @@ +import { + IsOptional, + IsString, + IsNumber, + Min, + Max, +} from 'class-validator'; +import { Transform } from 'class-transformer'; + +export class GetPostsListDto { + @IsOptional() + @IsNumber() + @Min(0) + @Transform(({ value }) => parseInt(value, 10)) + page?: number = 0; + + @IsOptional() + @IsNumber() + @Min(1) + @Max(100) + @Transform(({ value }) => parseInt(value, 10)) + limit?: number = 20; + + @IsOptional() + @IsString() + customer?: string; +} From 4ed1ffe206ad419e3157dffca15b52e2cf6d6112 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus <egelhaus@ennogelhaus.de> Date: Fri, 30 Jan 2026 11:14:56 +0100 Subject: [PATCH 195/340] Update Node.js version in build workflow --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e866231..2e985a34 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: - node-version: ['20.17.0'] + node-version: ['22.12.0'] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: From 42529ac43841ceaebc3a681b5aab59fa19a77420 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 30 Jan 2026 18:39:45 +0700 Subject: [PATCH 196/340] feat: filters --- apps/frontend/src/components/launches/filters.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx index 57635288..c6b534c8 100644 --- a/apps/frontend/src/components/launches/filters.tsx +++ b/apps/frontend/src/components/launches/filters.tsx @@ -296,7 +296,7 @@ export const Filters = () => { /> </svg> </div> - <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> + <div className="min-w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]"> {getDisplayText()} </div> @@ -362,7 +362,7 @@ export const Filters = () => { /> </svg> </div> - <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> + <div className="min-w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center"> <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]"> {t('page', 'Page')} {calendar.listPage + 1} {t('of', 'of')} {Math.max(1, calendar.listTotalPages)} </div> From e1c51effad1926c4d297e7c72f7cf54fb5635a94 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 1 Feb 2026 09:04:20 +0700 Subject: [PATCH 197/340] feat: counter in global mode --- .../launches/information.component.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/apps/frontend/src/components/launches/information.component.tsx b/apps/frontend/src/components/launches/information.component.tsx index b65acfa7..3c441b69 100644 --- a/apps/frontend/src/components/launches/information.component.tsx +++ b/apps/frontend/src/components/launches/information.component.tsx @@ -110,6 +110,31 @@ export const InformationComponent: FC<{ return true; }, [totalAllowedChars, totalChars, isInternal, isPicture, chars]); + const globalDisplayLimit = useMemo(() => { + if (!isGlobal || !selectedIntegrations.length) { + return null; + } + + // Get all limits from non-internal integrations, sorted ascending + const limits = selectedIntegrations + .map((p, index) => ({ + limit: chars?.[p.integration.id] || 0, + isInternal: isInternal[index], + })) + .filter((item) => !item.isInternal && item.limit > 0) + .map((item) => item.limit) + .sort((a, b) => a - b); + + if (!limits.length) { + return null; + } + + // Find the smallest limit that hasn't been exceeded yet + // If all are exceeded, show the smallest one + const validLimit = limits.find((limit) => totalChars <= limit); + return validLimit ?? limits[0]; + }, [isGlobal, selectedIntegrations, chars, isInternal, totalChars]); + return ( <div className={clsx( @@ -124,6 +149,11 @@ export const InformationComponent: FC<{ {totalChars}/{totalAllowedChars} </div> )} + {isGlobal && globalDisplayLimit !== null && ( + <div className={clsx("text-[10px] font-[600] flex justify-center items-center", !isValid && 'text-white')}> + {totalChars}/{globalDisplayLimit} + </div> + )} {((isGlobal && selectedIntegrations.length) || !isValid) && ( <svg className={clsx('group-hover:rotate-180', !isValid && 'text-white')} From 5f830f11b10301b0a82d2cae0c0843d48cf6d24d Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 1 Feb 2026 11:50:59 +0700 Subject: [PATCH 198/340] feat: large json --- apps/backend/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/main.ts b/apps/backend/src/main.ts index 3f1c1ae5..b5a0ceba 100644 --- a/apps/backend/src/main.ts +++ b/apps/backend/src/main.ts @@ -51,7 +51,7 @@ async function start() { }) ); - app.use('/copilot/*', (req: any, res: any, next: any) => { + app.use(['/copilot/*', '/posts'], (req: any, res: any, next: any) => { json({ limit: '50mb' })(req, res, next); }); From fe457573ecaee9c87745f5f449eb548d74e04687 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 1 Feb 2026 17:01:37 +0700 Subject: [PATCH 199/340] feat: independent provider adding --- apps/backend/src/api/api.module.ts | 2 + .../src/api/routes/integrations.controller.ts | 200 +--------- .../routes/no.auth.integrations.controller.ts | 281 +++++++++++++ .../auth/permissions/permissions.guard.ts | 4 +- .../auth/providers/google.provider.ts | 2 +- .../(site)/integrations/social/layout.tsx | 18 - .../integrations/social/[provider]/page.tsx | 6 +- .../app/(app)/integrations/social/layout.tsx | 13 + .../launches/add.provider.component.tsx | 225 +++++++---- .../launches/continue.integration.tsx | 370 ++++++++++++++++-- .../src/components/launches/new.post.tsx | 2 +- .../facebook/facebook.continue.tsx | 147 ++----- .../continue-provider/gmb/gmb.continue.tsx | 212 ++++------ .../instagram/instagram.continue.tsx | 162 +++----- .../linkedin/linkedin.continue.tsx | 137 ++----- .../with-continue-provider.tsx | 151 +++++++ .../youtube/youtube.continue.tsx | 217 ++++------ .../onboarding/onboarding.modal.tsx | 1 + apps/frontend/src/middleware.ts | 8 + .../integrations/integration.repository.ts | 21 +- .../integrations/integration.service.ts | 1 + .../src/integrations/social.abstract.ts | 4 +- 22 files changed, 1271 insertions(+), 913 deletions(-) create mode 100644 apps/backend/src/api/routes/no.auth.integrations.controller.ts delete mode 100644 apps/frontend/src/app/(app)/(site)/integrations/social/layout.tsx rename apps/frontend/src/app/(app)/{(site) => }/integrations/social/[provider]/page.tsx (74%) create mode 100644 apps/frontend/src/app/(app)/integrations/social/layout.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/continue-provider/with-continue-provider.tsx diff --git a/apps/backend/src/api/api.module.ts b/apps/backend/src/api/api.module.ts index 291479c7..80b9a1ec 100644 --- a/apps/backend/src/api/api.module.ts +++ b/apps/backend/src/api/api.module.ts @@ -31,6 +31,7 @@ import { AutopostController } from '@gitroom/backend/api/routes/autopost.control import { SetsController } from '@gitroom/backend/api/routes/sets.controller'; import { ThirdPartyController } from '@gitroom/backend/api/routes/third-party.controller'; import { MonitorController } from '@gitroom/backend/api/routes/monitor.controller'; +import { NoAuthIntegrationsController } from '@gitroom/backend/api/routes/no.auth.integrations.controller'; const authenticatedController = [ UsersController, @@ -56,6 +57,7 @@ const authenticatedController = [ AuthController, PublicController, MonitorController, + NoAuthIntegrationsController, ...authenticatedController, ], providers: [ diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 40cf9c58..0a3ab089 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -3,15 +3,12 @@ import { Controller, Delete, Get, - HttpException, Param, Post, Put, Query, - UseFilters, } from '@nestjs/common'; import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; -import { ConnectIntegrationDto } from '@gitroom/nestjs-libraries/dtos/integrations/connect.integration.dto'; import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; @@ -21,16 +18,11 @@ import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permis import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; import { ApiTags } from '@nestjs/swagger'; import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; -import { NotEnoughScopesFilter } from '@gitroom/nestjs-libraries/integrations/integration.missing.scopes'; import { PostsService } from '@gitroom/nestjs-libraries/database/prisma/posts/posts.service'; import { IntegrationTimeDto } from '@gitroom/nestjs-libraries/dtos/integrations/integration.time.dto'; -import { AuthService } from '@gitroom/helpers/auth/auth.service'; -import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; import { PlugDto } from '@gitroom/nestjs-libraries/dtos/plugs/plug.dto'; -import { - NotEnoughScopes, - RefreshToken, -} from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract'; + import { timer } from '@gitroom/helpers/utils/timer'; import { TelegramProvider } from '@gitroom/nestjs-libraries/integrations/social/telegram.provider'; import { @@ -196,7 +188,8 @@ export class IntegrationsController { @Param('integration') integration: string, @Query('refresh') refresh: string, @Query('externalUrl') externalUrl: string, - @Query('onboarding') onboarding: string + @Query('onboarding') onboarding: string, + @GetOrgFromRequest() org: Organization ) { if ( !this._integrationManager @@ -225,19 +218,20 @@ export class IntegrationsController { await integrationProvider.generateAuthUrl(getExternalUrl); if (refresh) { - await ioRedis.set(`refresh:${state}`, refresh, 'EX', 300); + await ioRedis.set(`refresh:${state}`, refresh, 'EX', 3600); } if (onboarding === 'true') { - await ioRedis.set(`onboarding:${state}`, 'true', 'EX', 300); + await ioRedis.set(`onboarding:${state}`, 'true', 'EX', 3600); } - await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 300); + await ioRedis.set(`organization:${state}`, org.id, 'EX', 3600); + await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 3600); await ioRedis.set( `external:${state}`, JSON.stringify(getExternalUrl), 'EX', - 300 + 3600 ); return { url }; @@ -371,171 +365,6 @@ export class IntegrationsController { throw new Error('Function not found'); } - @Post('/social/:integration/connect') - @CheckPolicies([AuthorizationActions.Create, Sections.CHANNEL]) - @UseFilters(new NotEnoughScopesFilter()) - async connectSocialMedia( - @GetOrgFromRequest() org: Organization, - @Param('integration') integration: string, - @Body() body: ConnectIntegrationDto - ) { - if ( - !this._integrationManager - .getAllowedSocialsIntegrations() - .includes(integration) - ) { - throw new Error('Integration not allowed'); - } - - const integrationProvider = - this._integrationManager.getSocialIntegration(integration); - - const getCodeVerifier = integrationProvider.customFields - ? 'none' - : await ioRedis.get(`login:${body.state}`); - if (!getCodeVerifier) { - throw new Error('Invalid state'); - } - - if (!integrationProvider.customFields) { - await ioRedis.del(`login:${body.state}`); - } - - const details = integrationProvider.externalUrl - ? await ioRedis.get(`external:${body.state}`) - : undefined; - - if (details) { - await ioRedis.del(`external:${body.state}`); - } - - const refresh = await ioRedis.get(`refresh:${body.state}`); - if (refresh) { - await ioRedis.del(`refresh:${body.state}`); - } - - const onboarding = await ioRedis.get(`onboarding:${body.state}`); - if (onboarding) { - await ioRedis.del(`onboarding:${body.state}`); - } - - const { - error, - accessToken, - expiresIn, - refreshToken, - id, - name, - picture, - username, - additionalSettings, - // eslint-disable-next-line no-async-promise-executor - } = await new Promise<AuthTokenDetails>(async (res) => { - const auth = await integrationProvider.authenticate( - { - code: body.code, - codeVerifier: getCodeVerifier, - refresh: body.refresh, - }, - details ? JSON.parse(details) : undefined - ); - - if (typeof auth === 'string') { - return res({ - error: auth, - accessToken: '', - id: '', - name: '', - picture: '', - username: '', - additionalSettings: [], - }); - } - - if (refresh && integrationProvider.reConnect) { - const newAuth = await integrationProvider.reConnect( - auth.id, - refresh, - auth.accessToken - ); - return res({ ...newAuth, refreshToken: body.refresh }); - } - - return res(auth); - }); - - if (error) { - throw new NotEnoughScopes(error); - } - - if (!id) { - throw new NotEnoughScopes('Invalid API key'); - } - - if (refresh && String(id) !== String(refresh)) { - throw new NotEnoughScopes( - 'Please refresh the channel that needs to be refreshed' - ); - } - - let validName = name; - if (!validName) { - if (username) { - validName = username.split('.')[0] ?? username; - } else { - validName = `Channel_${String(id).slice(0, 8)}`; - } - } - - if ( - process.env.STRIPE_PUBLISHABLE_KEY && - org.isTrailing && - (await this._integrationService.checkPreviousConnections( - org.id, - String(id) - )) - ) { - throw new HttpException('', 412); - } - - const createUpdate = - await this._integrationService.createOrUpdateIntegration( - additionalSettings, - !!integrationProvider.oneTimeToken, - org.id, - validName.trim(), - picture, - 'social', - String(id), - integration, - accessToken, - refreshToken, - expiresIn, - username, - refresh ? false : integrationProvider.isBetweenSteps, - body.refresh, - +body.timezone, - details - ? AuthService.fixedEncryption(details) - : integrationProvider.customFields - ? AuthService.fixedEncryption( - Buffer.from(body.code, 'base64').toString() - ) - : undefined - ); - - this._refreshIntegrationService - .startRefreshWorkflow(org.id, createUpdate.id, integrationProvider) - .catch((err) => { - console.log(err); - }); - - return { - ...createUpdate, - onboarding: onboarding === 'true', - }; - } - @Post('/disable') disableChannel( @GetOrgFromRequest() org: Organization, @@ -544,15 +373,6 @@ export class IntegrationsController { return this._integrationService.disableChannel(org.id, id); } - @Post('/provider/:id/connect') - async saveProviderPage( - @Param('id') id: string, - @Body() body: any, - @GetOrgFromRequest() org: Organization - ) { - return this._integrationService.saveProviderPage(org.id, id, body); - } - @Post('/enable') enableChannel( @GetOrgFromRequest() org: Organization, @@ -577,7 +397,7 @@ export class IntegrationsController { ); if (isTherePosts.length) { for (const post of isTherePosts) { - await this._postService.deletePost(org.id, post.group); + this._postService.deletePost(org.id, post.group).catch((err) => {}); } } diff --git a/apps/backend/src/api/routes/no.auth.integrations.controller.ts b/apps/backend/src/api/routes/no.auth.integrations.controller.ts new file mode 100644 index 00000000..673e77e7 --- /dev/null +++ b/apps/backend/src/api/routes/no.auth.integrations.controller.ts @@ -0,0 +1,281 @@ +import { + Body, + Controller, + HttpException, + Param, + Post, + UseFilters, +} from '@nestjs/common'; +import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; +import { ConnectIntegrationDto } from '@gitroom/nestjs-libraries/dtos/integrations/connect.integration.dto'; +import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; +import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability'; +import { ApiTags } from '@nestjs/swagger'; +import { NotEnoughScopesFilter } from '@gitroom/nestjs-libraries/integrations/integration.missing.scopes'; +import { AuthService } from '@gitroom/helpers/auth/auth.service'; +import { AuthTokenDetails } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { NotEnoughScopes } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { + AuthorizationActions, + Sections, +} from '@gitroom/backend/services/auth/permissions/permission.exception.class'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; +import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; + +@ApiTags('Integrations') +@Controller('/integrations') +export class NoAuthIntegrationsController { + constructor( + private _integrationManager: IntegrationManager, + private _integrationService: IntegrationService, + private _refreshIntegrationService: RefreshIntegrationService, + private _organizationService: OrganizationService + ) {} + + @Post('/social-connect/:integration') + @CheckPolicies([AuthorizationActions.Create, Sections.CHANNEL]) + @UseFilters(new NotEnoughScopesFilter()) + async connectSocialMedia( + @Param('integration') integration: string, + @Body() body: ConnectIntegrationDto + ) { + if ( + !this._integrationManager + .getAllowedSocialsIntegrations() + .includes(integration) + ) { + throw new Error('Integration not allowed'); + } + + const integrationProvider = + this._integrationManager.getSocialIntegration(integration); + + const getCodeVerifier = integrationProvider.customFields + ? 'none' + : await ioRedis.get(`login:${body.state}`); + if (!getCodeVerifier) { + throw new Error('Invalid state'); + } + + const organization = await ioRedis.get(`organization:${body.state}`); + if (!organization) { + throw new Error('Organization not found'); + } + + const org = await this._organizationService.getOrgById(organization); + + if (!integrationProvider.customFields) { + await ioRedis.del(`login:${body.state}`); + } + + const details = integrationProvider.externalUrl + ? await ioRedis.get(`external:${body.state}`) + : undefined; + + if (details) { + await ioRedis.del(`external:${body.state}`); + } + + const refresh = await ioRedis.get(`refresh:${body.state}`); + if (refresh) { + await ioRedis.del(`refresh:${body.state}`); + } + + const onboarding = await ioRedis.get(`onboarding:${body.state}`); + if (onboarding) { + await ioRedis.del(`onboarding:${body.state}`); + } + + const { + error, + accessToken, + expiresIn, + refreshToken, + id, + name, + picture, + username, + additionalSettings, + // eslint-disable-next-line no-async-promise-executor + } = await new Promise<AuthTokenDetails>(async (res) => { + try { + const auth = await integrationProvider.authenticate( + { + code: body.code, + codeVerifier: getCodeVerifier, + refresh: body.refresh, + }, + details ? JSON.parse(details) : undefined + ); + + if (typeof auth === 'string') { + return res({ + error: auth, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + additionalSettings: [], + }); + } + + if (refresh && integrationProvider.reConnect) { + console.log('reconnect'); + try { + const newAuth = await integrationProvider.reConnect( + auth.id, + refresh, + auth.accessToken + ); + return res({ ...newAuth, refreshToken: body.refresh }); + } catch (err: any) { + return res({ + error: err.message, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + additionalSettings: [], + }); + } + } + + return res(auth); + } catch (err) { + if (err instanceof NotEnoughScopes) { + return res({ + error: err.message, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + additionalSettings: [], + }); + } + + return res({ + error: 'Authentication failed', + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + additionalSettings: [], + }); + } + }); + + if (error) { + throw new NotEnoughScopes(error); + } + + if (!id) { + throw new NotEnoughScopes('Invalid API key'); + } + + if (refresh && String(id) !== String(refresh)) { + throw new NotEnoughScopes( + 'Please refresh the channel that needs to be refreshed' + ); + } + + let validName = name; + if (!validName) { + if (username) { + validName = username.split('.')[0] ?? username; + } else { + validName = `Channel_${String(id).slice(0, 8)}`; + } + } + + if ( + process.env.STRIPE_PUBLISHABLE_KEY && + org.isTrailing && + (await this._integrationService.checkPreviousConnections( + org.id, + String(id) + )) + ) { + throw new HttpException('', 412); + } + + const createUpdate = + await this._integrationService.createOrUpdateIntegration( + additionalSettings, + !!integrationProvider.oneTimeToken, + org.id, + validName.trim(), + picture, + 'social', + String(id), + integration, + accessToken, + refreshToken, + expiresIn, + username, + refresh ? false : integrationProvider.isBetweenSteps, + body.refresh, + +body.timezone, + details + ? AuthService.fixedEncryption(details) + : integrationProvider.customFields + ? AuthService.fixedEncryption( + Buffer.from(body.code, 'base64').toString() + ) + : undefined + ); + + this._refreshIntegrationService + .startRefreshWorkflow(org.id, createUpdate.id, integrationProvider) + .catch((err) => { + console.log(err); + }); + + // Fetch pages if this is a two-step provider and not a refresh + let pages: any[] = []; + if (integrationProvider.isBetweenSteps && !refresh) { + try { + // Check which method the provider uses (pages or companies) + const fetchMethod = + 'pages' in integrationProvider + ? 'pages' + : 'companies' in integrationProvider + ? 'companies' + : null; + + if (fetchMethod) { + // @ts-ignore - dynamic method call + pages = await integrationProvider[fetchMethod](accessToken); + } + } catch (err) { + console.log('Failed to fetch pages:', err); + } + } + + return { + ...createUpdate, + onboarding: onboarding === 'true', + pages, + }; + } + + @Post('/provider/:id/connect') + async saveProviderPage(@Param('id') id: string, @Body() body: any) { + if (!body.state) { + throw new Error('Invalid state'); + } + + const organization = await ioRedis.get(`organization:${body.state}`); + if (!organization) { + throw new Error('Organization not found'); + } + + const org = await this._organizationService.getOrgById(organization); + + return this._integrationService.saveProviderPage(org.id, id, body); + } +} diff --git a/apps/backend/src/services/auth/permissions/permissions.guard.ts b/apps/backend/src/services/auth/permissions/permissions.guard.ts index f1ab478c..98666258 100644 --- a/apps/backend/src/services/auth/permissions/permissions.guard.ts +++ b/apps/backend/src/services/auth/permissions/permissions.guard.ts @@ -23,7 +23,9 @@ export class PoliciesGuard implements CanActivate { const request: Request = context.switchToHttp().getRequest(); if ( request.path.indexOf('/auth') > -1 || - request.path.indexOf('/stripe') > -1 + request.path.indexOf('/auth') > -1 || + request.path.indexOf('/integrations/social-connect') > -1 || + request.path.indexOf('/integrations/provider') > -1 ) { return true; } diff --git a/apps/backend/src/services/auth/providers/google.provider.ts b/apps/backend/src/services/auth/providers/google.provider.ts index 993eda7e..67487d78 100644 --- a/apps/backend/src/services/auth/providers/google.provider.ts +++ b/apps/backend/src/services/auth/providers/google.provider.ts @@ -33,7 +33,7 @@ const clientAndYoutube = () => { export class GoogleProvider implements ProvidersInterface { generateLink() { - const state = makeId(7); + const state = 'login'; const { client } = clientAndYoutube(); return client.generateAuthUrl({ access_type: 'online', diff --git a/apps/frontend/src/app/(app)/(site)/integrations/social/layout.tsx b/apps/frontend/src/app/(app)/(site)/integrations/social/layout.tsx deleted file mode 100644 index 124e0fec..00000000 --- a/apps/frontend/src/app/(app)/(site)/integrations/social/layout.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { ReactNode } from 'react'; -import { getT } from '@gitroom/react/translation/get.translation.service.backend'; -export default async function IntegrationLayout({ - children, -}: { - children: ReactNode; -}) { - const t = await getT(); - - return ( - <div className="bg-newBgColorInner p-[20px] flex flex-col transition-all flex-1"> - <div className="text-6xl text-center mt-[50px]"> - {t('adding_channel_redirecting_you', 'Adding channel, Redirecting You')} - {children} - </div> - </div> - ); -} diff --git a/apps/frontend/src/app/(app)/(site)/integrations/social/[provider]/page.tsx b/apps/frontend/src/app/(app)/integrations/social/[provider]/page.tsx similarity index 74% rename from apps/frontend/src/app/(app)/(site)/integrations/social/[provider]/page.tsx rename to apps/frontend/src/app/(app)/integrations/social/[provider]/page.tsx index 473a0cf1..57d207bf 100644 --- a/apps/frontend/src/app/(app)/(site)/integrations/social/[provider]/page.tsx +++ b/apps/frontend/src/app/(app)/integrations/social/[provider]/page.tsx @@ -1,5 +1,8 @@ import { ContinueIntegration } from '@gitroom/frontend/components/launches/continue.integration'; +import { cookies } from 'next/headers'; + export const dynamic = 'force-dynamic'; + export default async function Page({ params: { provider }, searchParams, @@ -9,5 +12,6 @@ export default async function Page({ }; searchParams: any; }) { - return <ContinueIntegration searchParams={searchParams} provider={provider} />; + const get = cookies().get('auth'); + return <ContinueIntegration searchParams={searchParams} provider={provider} logged={!!get?.name} />; } diff --git a/apps/frontend/src/app/(app)/integrations/social/layout.tsx b/apps/frontend/src/app/(app)/integrations/social/layout.tsx new file mode 100644 index 00000000..af980b6c --- /dev/null +++ b/apps/frontend/src/app/(app)/integrations/social/layout.tsx @@ -0,0 +1,13 @@ +import { ReactNode } from 'react'; + +export default async function IntegrationLayout({ + children, +}: { + children: ReactNode; +}) { + return ( + <div className="bg-[#0B0A0A] flex flex-1 min-h-screen w-screen"> + {children} + </div> + ); +} diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 6207ecf0..77cc2abf 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -16,10 +16,11 @@ import { object, string } from 'yup'; import { yupResolver } from '@hookform/resolvers/yup'; import { web3List } from '@gitroom/frontend/components/launches/web3/web3.list'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component'; import clsx from 'clsx'; +import copy from 'copy-to-clipboard'; const resolver = classValidatorResolver(ApiKeyDto); -export const useAddProvider = (update?: () => void) => { + +export const useAddProvider = (update?: () => void, invite?: boolean) => { const modal = useModals(); const fetch = useFetch(); return useCallback(async () => { @@ -27,7 +28,9 @@ export const useAddProvider = (update?: () => void) => { modal.openModal({ title: 'Add Channel', withCloseButton: true, - children: <AddProviderComponent update={update} {...data} />, + children: ( + <AddProviderComponent invite={!!invite} update={update} {...data} /> + ), }); }, []); }; @@ -36,34 +39,69 @@ export const AddProviderButton: FC<{ }> = (props) => { const { update } = props; const add = useAddProvider(update); + const invite = useAddProvider(update, true); const t = useT(); return ( - <button - className="text-btnText bg-btnSimple h-[44px] pt-[12px] pb-[14px] ps-[16px] pe-[20px] justify-center items-center flex rounded-[8px] gap-[8px]" - onClick={add} - > - <div> + <div className="flex group-[.sidebar]:block gap-[8px]"> + <button + className="flex-1 group-[.sidebar]:w-[100%] group-[.sidebar]:flex-none text-btnText bg-btnSimple h-[44px] pt-[12px] pb-[14px] ps-[16px] pe-[20px] justify-center items-center flex rounded-[8px] gap-[8px]" + onClick={add} + > + <div> + <svg + xmlns="http://www.w3.org/2000/svg" + width="20" + height="20" + viewBox="0 0 20 20" + fill="none" + > + <path + d="M1.66675 10.0417C3.35907 10.2299 4.93698 10.9884 6.14101 12.1924C7.34504 13.3964 8.10353 14.9743 8.29175 16.6667M1.66675 13.4167C2.46749 13.58 3.20253 13.9751 3.7804 14.553C4.35827 15.1309 4.75344 15.8659 4.91675 16.6667M1.66675 16.6667H1.67508M11.6667 17.5H14.3334C15.7335 17.5 16.4336 17.5 16.9684 17.2275C17.4388 16.9878 17.8212 16.6054 18.0609 16.135C18.3334 15.6002 18.3334 14.9001 18.3334 13.5V6.5C18.3334 5.09987 18.3334 4.3998 18.0609 3.86502C17.8212 3.39462 17.4388 3.01217 16.9684 2.77248C16.4336 2.5 15.7335 2.5 14.3334 2.5H5.66675C4.26662 2.5 3.56655 2.5 3.03177 2.77248C2.56137 3.01217 2.17892 3.39462 1.93923 3.86502C1.66675 4.3998 1.66675 5.09987 1.66675 6.5V6.66667" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" + /> + </svg> + </div> + <div className="text-start text-[14px] group-[.sidebar]:hidden"> + {t('add_channel', 'Add Channel')} + </div> + </button> + <button + onClick={invite} + data-tooltip-id="tooltip" + data-tooltip-content={t( + 'invite_link', + 'Send Invite Link to a customer' + )} + className="group-[.sidebar]:hidden min-h-[44px] min-w-[44px] bg-btnSimple justify-center items-center flex rounded-[8px] cursor-pointer" + > <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" - viewBox="0 0 20 20" + viewBox="0 0 16 16" fill="none" > - <path - d="M1.66675 10.0417C3.35907 10.2299 4.93698 10.9884 6.14101 12.1924C7.34504 13.3964 8.10353 14.9743 8.29175 16.6667M1.66675 13.4167C2.46749 13.58 3.20253 13.9751 3.7804 14.553C4.35827 15.1309 4.75344 15.8659 4.91675 16.6667M1.66675 16.6667H1.67508M11.6667 17.5H14.3334C15.7335 17.5 16.4336 17.5 16.9684 17.2275C17.4388 16.9878 17.8212 16.6054 18.0609 16.135C18.3334 15.6002 18.3334 14.9001 18.3334 13.5V6.5C18.3334 5.09987 18.3334 4.3998 18.0609 3.86502C17.8212 3.39462 17.4388 3.01217 16.9684 2.77248C16.4336 2.5 15.7335 2.5 14.3334 2.5H5.66675C4.26662 2.5 3.56655 2.5 3.03177 2.77248C2.56137 3.01217 2.17892 3.39462 1.93923 3.86502C1.66675 4.3998 1.66675 5.09987 1.66675 6.5V6.66667" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - /> + <g clip-path="url(#clip0_2452_193804)"> + <path + d="M6.6668 8.66599C6.9531 9.04875 7.31837 9.36545 7.73783 9.59462C8.1573 9.82379 8.62114 9.96007 9.0979 9.99422C9.57466 10.0284 10.0532 9.95957 10.501 9.79251C10.9489 9.62546 11.3555 9.36404 11.6935 9.02599L13.6935 7.02599C14.3007 6.39732 14.6366 5.55531 14.629 4.68132C14.6215 3.80733 14.2709 2.97129 13.6529 2.35326C13.0348 1.73524 12.1988 1.38467 11.3248 1.37708C10.4508 1.36948 9.60881 1.70547 8.98013 2.31266L7.83347 3.45266M9.33347 7.33266C9.04716 6.94991 8.68189 6.6332 8.26243 6.40403C7.84297 6.17486 7.37913 6.03858 6.90237 6.00444C6.4256 5.97029 5.94708 6.03908 5.49924 6.20614C5.0514 6.3732 4.64472 6.63461 4.3068 6.97266L2.3068 8.97266C1.69961 9.60133 1.36363 10.4433 1.37122 11.3173C1.37881 12.1913 1.72938 13.0274 2.3474 13.6454C2.96543 14.2634 3.80147 14.614 4.67546 14.6216C5.54945 14.6292 6.39146 14.2932 7.02013 13.686L8.16013 12.546" + stroke="currentColor" + strokeWidth="1.2" + strokeLinecap="round" + strokeLinejoin="round" + ></path> + </g> + <defs> + <clipPath id="clip0_2452_193804"> + <rect width="16" height="16" fill="textColor"></rect> + </clipPath> + </defs> </svg> - </div> - <div className="text-start text-[16px] group-[.sidebar]:hidden"> - {t('add_channel', 'Add Channel')} - </div> - </button> + </button> + </div> ); }; export const ApiModal: FC<{ @@ -307,6 +345,7 @@ export const AddProviderComponent: FC<{ identifier: string; name: string; }>; + invite: boolean; update?: () => void; onboarding?: boolean; }> = (props) => { @@ -318,6 +357,7 @@ export const AddProviderComponent: FC<{ const modal = useModals(); const getSocialLink = useCallback( ( + invite: boolean, identifier: string, isExternal: boolean, isWeb3: boolean, @@ -336,7 +376,11 @@ export const AddProviderComponent: FC<{ (item) => item.identifier === identifier )!; const { url } = await ( - await fetch(`/integrations/social/${identifier}${onboarding ? '?onboarding=true' : ''}`) + await fetch( + `/integrations/social/${identifier}${ + onboarding ? '?onboarding=true' : '' + }` + ) ).json(); modal.openModal({ title: t('web3_provider', 'Web3 provider'), @@ -347,7 +391,9 @@ export const AddProviderComponent: FC<{ children: ( <Web3Providers onComplete={(code, newState) => { - window.location.href = `/integrations/social/${identifier}?code=${code}&state=${newState}${onboarding ? '&onboarding=true' : ''}`; + window.location.href = `/integrations/social/${identifier}?code=${code}&state=${newState}${ + onboarding ? '&onboarding=true' : '' + }`; }} nonce={url} /> @@ -359,16 +405,35 @@ export const AddProviderComponent: FC<{ const params = [ externalUrl ? `externalUrl=${externalUrl}` : '', onboardingParam, - ].filter(Boolean).join('&'); + ] + .filter(Boolean) + .join('&'); const { url, err } = await ( await fetch( `/integrations/social/${identifier}${params ? `?${params}` : ''}` ) ).json(); if (err) { - toaster.show(t('could_not_connect_to_platform', 'Could not connect to the platform'), 'warning'); + toaster.show( + t( + 'could_not_connect_to_platform', + 'Could not connect to the platform' + ), + 'warning' + ); return; } + + if (invite) { + toaster.show( + 'Invite link copied to clipboard, link will be available for 1 hour', + 'success' + ); + modal.closeAll(); + copy(url); + return; + } + window.location.href = url; }; if (isWeb3) { @@ -430,56 +495,74 @@ export const AddProviderComponent: FC<{ return ( <div className="w-full flex flex-col gap-[20px] rounded-[4px] relative]"> <div className="flex flex-col"> - <div className={clsx("grid grid-cols-5 gap-[10px] justify-items-center justify-center", onboarding ? 'grid-cols-9' : 'grid-cols-5')}> - {social.map((item) => ( - <div - key={item.identifier} - onClick={getSocialLink( - item.identifier, - item.isExternal, - item.isWeb3, - item.customFields - )} - {...(!!item.toolTip - ? { - 'data-tooltip-id': 'tooltip', - 'data-tooltip-content': item.toolTip, - } - : {})} - className={ - 'w-full h-[100px] text-[14px] p-[10px] rounded-[8px] bg-newTableHeader text-textColor relative justify-center items-center flex flex-col gap-[10px] cursor-pointer' + <div + className={clsx( + 'grid grid-cols-5 gap-[10px] justify-items-center justify-center', + onboarding ? 'grid-cols-9' : 'grid-cols-5' + )} + > + {social + .filter((item) => { + if (!props.invite) { + return true; } - > - <div> - {item.identifier === 'youtube' ? ( - <img src={`/icons/platforms/youtube.svg`} /> - ) : ( - <img - className={clsx("w-[32px] h-[32px]", item.identifier !== 'google_my_business' && 'rounded-full')} - src={`/icons/platforms/${item.identifier}.png`} - /> + + return !item.isExternal && !item.isWeb3 && !item.customFields; + }) + .map((item) => ( + <div + key={item.identifier} + onClick={getSocialLink( + props.invite, + item.identifier, + item.isExternal, + item.isWeb3, + item.customFields )} - </div> - <div className="whitespace-pre-wrap text-center"> - {item.name} - {!!item.toolTip && ( - <svg - width="15" - height="15" - viewBox="0 0 26 26" - fill="none" - xmlns="http://www.w3.org/2000/svg" - className="absolute top-[10px] end-[10px]" - > - <path - d="M13 0C10.4288 0 7.91543 0.762437 5.77759 2.1909C3.63975 3.61935 1.97351 5.64968 0.989572 8.02512C0.0056327 10.4006 -0.251811 13.0144 0.249797 15.5362C0.751405 18.0579 1.98953 20.3743 3.80762 22.1924C5.6257 24.0105 7.94208 25.2486 10.4638 25.7502C12.9856 26.2518 15.5995 25.9944 17.9749 25.0104C20.3503 24.0265 22.3807 22.3603 23.8091 20.2224C25.2376 18.0846 26 15.5712 26 13C25.9964 9.5533 24.6256 6.24882 22.1884 3.81163C19.7512 1.37445 16.4467 0.00363977 13 0ZM13 21C12.7033 21 12.4133 20.912 12.1667 20.7472C11.92 20.5824 11.7277 20.3481 11.6142 20.074C11.5007 19.7999 11.471 19.4983 11.5288 19.2074C11.5867 18.9164 11.7296 18.6491 11.9393 18.4393C12.1491 18.2296 12.4164 18.0867 12.7074 18.0288C12.9983 17.9709 13.2999 18.0007 13.574 18.1142C13.8481 18.2277 14.0824 18.42 14.2472 18.6666C14.412 18.9133 14.5 19.2033 14.5 19.5C14.5 19.8978 14.342 20.2794 14.0607 20.5607C13.7794 20.842 13.3978 21 13 21ZM14 14.91V15C14 15.2652 13.8946 15.5196 13.7071 15.7071C13.5196 15.8946 13.2652 16 13 16C12.7348 16 12.4804 15.8946 12.2929 15.7071C12.1054 15.5196 12 15.2652 12 15V14C12 13.7348 12.1054 13.4804 12.2929 13.2929C12.4804 13.1054 12.7348 13 13 13C14.6538 13 16 11.875 16 10.5C16 9.125 14.6538 8 13 8C11.3463 8 10 9.125 10 10.5V11C10 11.2652 9.89465 11.5196 9.70711 11.7071C9.51958 11.8946 9.26522 12 9.00001 12C8.73479 12 8.48044 11.8946 8.2929 11.7071C8.10536 11.5196 8.00001 11.2652 8.00001 11V10.5C8.00001 8.01875 10.2425 6 13 6C15.7575 6 18 8.01875 18 10.5C18 12.6725 16.28 14.4913 14 14.91Z" - fill="currentColor" + {...(!!item.toolTip + ? { + 'data-tooltip-id': 'tooltip', + 'data-tooltip-content': item.toolTip, + } + : {})} + className={ + 'w-full h-[100px] text-[14px] p-[10px] rounded-[8px] bg-newTableHeader text-textColor relative justify-center items-center flex flex-col gap-[10px] cursor-pointer' + } + > + <div> + {item.identifier === 'youtube' ? ( + <img src={`/icons/platforms/youtube.svg`} /> + ) : ( + <img + className={clsx( + 'w-[32px] h-[32px]', + item.identifier !== 'google_my_business' && + 'rounded-full' + )} + src={`/icons/platforms/${item.identifier}.png`} /> - </svg> - )} + )} + </div> + <div className="whitespace-pre-wrap text-center"> + {item.name} + {!!item.toolTip && ( + <svg + width="15" + height="15" + viewBox="0 0 26 26" + fill="none" + xmlns="http://www.w3.org/2000/svg" + className="absolute top-[10px] end-[10px]" + > + <path + d="M13 0C10.4288 0 7.91543 0.762437 5.77759 2.1909C3.63975 3.61935 1.97351 5.64968 0.989572 8.02512C0.0056327 10.4006 -0.251811 13.0144 0.249797 15.5362C0.751405 18.0579 1.98953 20.3743 3.80762 22.1924C5.6257 24.0105 7.94208 25.2486 10.4638 25.7502C12.9856 26.2518 15.5995 25.9944 17.9749 25.0104C20.3503 24.0265 22.3807 22.3603 23.8091 20.2224C25.2376 18.0846 26 15.5712 26 13C25.9964 9.5533 24.6256 6.24882 22.1884 3.81163C19.7512 1.37445 16.4467 0.00363977 13 0ZM13 21C12.7033 21 12.4133 20.912 12.1667 20.7472C11.92 20.5824 11.7277 20.3481 11.6142 20.074C11.5007 19.7999 11.471 19.4983 11.5288 19.2074C11.5867 18.9164 11.7296 18.6491 11.9393 18.4393C12.1491 18.2296 12.4164 18.0867 12.7074 18.0288C12.9983 17.9709 13.2999 18.0007 13.574 18.1142C13.8481 18.2277 14.0824 18.42 14.2472 18.6666C14.412 18.9133 14.5 19.2033 14.5 19.5C14.5 19.8978 14.342 20.2794 14.0607 20.5607C13.7794 20.842 13.3978 21 13 21ZM14 14.91V15C14 15.2652 13.8946 15.5196 13.7071 15.7071C13.5196 15.8946 13.2652 16 13 16C12.7348 16 12.4804 15.8946 12.2929 15.7071C12.1054 15.5196 12 15.2652 12 15V14C12 13.7348 12.1054 13.4804 12.2929 13.2929C12.4804 13.1054 12.7348 13 13 13C14.6538 13 16 11.875 16 10.5C16 9.125 14.6538 8 13 8C11.3463 8 10 9.125 10 10.5V11C10 11.2652 9.89465 11.5196 9.70711 11.7071C9.51958 11.8946 9.26522 12 9.00001 12C8.73479 12 8.48044 11.8946 8.2929 11.7071C8.10536 11.5196 8.00001 11.2652 8.00001 11V10.5C8.00001 8.01875 10.2425 6 13 6C15.7575 6 18 8.01875 18 10.5C18 12.6725 16.28 14.4913 14 14.91Z" + fill="currentColor" + /> + </svg> + )} + </div> </div> - </div> - ))} + ))} </div> </div> {!isGeneral && ( diff --git a/apps/frontend/src/components/launches/continue.integration.tsx b/apps/frontend/src/components/launches/continue.integration.tsx index d3456c07..7e05c027 100644 --- a/apps/frontend/src/components/launches/continue.integration.tsx +++ b/apps/frontend/src/components/launches/continue.integration.tsx @@ -1,56 +1,121 @@ 'use client'; -import { FC, useEffect, useState } from 'react'; +import { FC, useCallback, useEffect, useMemo, useState } from 'react'; import { HttpStatusCode } from 'axios'; import { useRouter } from 'next/navigation'; import { Redirect } from '@gitroom/frontend/components/layout/redirect'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import dayjs from 'dayjs'; +import { continueProviderList } from '@gitroom/frontend/components/new-launch/providers/continue-provider/list'; +import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration'; +import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; + +interface TwoStepState { + integrationId: string; + onboarding: boolean; + pages: any[]; + returnURL?: string; +} + +interface SuccessState { + message: string; +} export const ContinueIntegration: FC<{ provider: string; searchParams: any; + logged: boolean; }> = (props) => { - const { provider, searchParams } = props; + const { provider, searchParams, logged } = props; const { push } = useRouter(); const t = useT(); const fetch = useFetch(); const [error, setError] = useState(false); + const [errorMessage, setErrorMessage] = useState<string | null>(null); + const [twoStepState, setTwoStepState] = useState<TwoStepState | null>(null); + const [successState, setSuccessState] = useState<SuccessState | null>(null); + const [isSaving, setIsSaving] = useState(false); + + // Helper to handle navigation - redirects if logged or returnURL exists, otherwise shows inline + const navigateOrShow = useCallback( + ( + path: string, + returnURL: string | undefined, + successMessage: string + ) => { + if (returnURL) { + // If returnURL exists, always redirect to it with the path params + const params = path.includes('?') ? path.split('?')[1] : ''; + push(params ? `${returnURL}?${params}` : returnURL); + } else if (logged) { + // If logged in without returnURL, use normal navigation + push(path); + } else { + // If not logged in without returnURL, show success inline + setSuccessState({ message: successMessage }); + } + }, + [logged, push] + ); + const modifiedParams = useMemo(() => { + if (provider === 'x') { + return { + state: searchParams.oauth_token || '', + code: searchParams.oauth_verifier || '', + refresh: searchParams.refresh || '', + }; + } + + if (provider === 'vk') { + return { + ...searchParams, + state: searchParams.state || '', + code: searchParams.code + '&&&&' + searchParams.device_id, + }; + } + + return searchParams; + }, []); useEffect(() => { (async () => { const timezone = String(dayjs.tz().utcOffset()); - const modifiedParams = { ...searchParams }; - if (provider === 'x') { - Object.assign(modifiedParams, { - state: searchParams.oauth_token || '', - code: searchParams.oauth_verifier || '', - refresh: searchParams.refresh || '', - }); - } - if (provider === 'vk') { - Object.assign(modifiedParams, { - ...searchParams, - state: searchParams.state || '', - code: searchParams.code + '&&&&' + searchParams.device_id, - }); - } - - const data = await fetch(`/integrations/social/${provider}/connect`, { + // Try public endpoint first (handles both public and fallback scenarios) + let data = await fetch(`/integrations/social-connect/${provider}`, { method: 'POST', body: JSON.stringify({ ...modifiedParams, timezone }), }); + // If public endpoint fails with specific errors, try authenticated endpoint + if (data.status === HttpStatusCode.BadRequest) { + const errorData = await data.json().catch(() => ({})); + // "Invalid connection type" means this wasn't started as a public flow + if ( + errorData.message?.includes('Invalid connection type') || + errorData.message?.includes('Invalid or expired state') + ) { + data = await fetch(`/integrations/social-connect/${provider}`, { + method: 'POST', + body: JSON.stringify({ ...modifiedParams, timezone }), + }); + } + } + if (data.status === HttpStatusCode.PreconditionFailed) { - push(`/launches?precondition=true`); - return ; + const { returnURL } = await data.json().catch(() => ({})); + navigateOrShow( + `/launches?precondition=true`, + returnURL, + 'Precondition failed' + ); + return; } if (data.status === HttpStatusCode.NotAcceptable) { - const { msg } = await data.json(); - push(`/launches?msg=${msg}`); + const { msg, returnURL } = await data.json(); + navigateOrShow(`/launches?msg=${msg}`, returnURL, msg); return; } @@ -58,28 +123,259 @@ export const ContinueIntegration: FC<{ data.status !== HttpStatusCode.Ok && data.status !== HttpStatusCode.Created ) { + const errorData = await data.json().catch(() => ({})); + setErrorMessage(errorData.message || errorData.msg || 'Could not add provider'); setError(true); return; } - const { inBetweenSteps, id, onboarding: resOnboarding } = await data.json(); + const { + inBetweenSteps, + id, + onboarding: resOnboarding, + pages, + returnURL, + } = await data.json(); const onboarding = resOnboarding || searchParams.onboarding === 'true'; + + // If it's a two-step provider, show the selection UI inline if (inBetweenSteps && !searchParams.refresh) { - push(`/launches?added=${provider}&continue=${id}${onboarding ? '&onboarding=true' : ''}`); + setTwoStepState({ + integrationId: id, + onboarding, + pages: pages || [], + returnURL, + }); return; } - push(`/launches?added=${provider}&msg=Channel Updated${onboarding ? '&onboarding=true' : ''}`); - })(); - }, [provider, searchParams]); - return error ? ( - <> - <div className="mt-[50px] text-[50px]"> - {t('could_not_add_provider', 'Could not add provider.')} - <br /> - {t('you_are_being_redirected_back', 'You are being redirected back')} + navigateOrShow( + `/launches?added=${provider}&msg=Channel Updated${ + onboarding ? '&onboarding=true' : '' + }`, + returnURL, + 'Channel Updated' + ); + })(); + }, []); + + const onSave = useCallback( + async (data: any) => { + if (!twoStepState) return; + + setIsSaving(true); + + try { + // Use public or authenticated endpoint based on the flow + const endpoint = `/integrations/provider/${twoStepState.integrationId}/connect`; + + const response = await fetch(endpoint, { + method: 'POST', + body: JSON.stringify({ ...modifiedParams, ...data }), + }); + + if ( + response.status !== HttpStatusCode.Ok && + response.status !== HttpStatusCode.Created + ) { + const errorData = await response.json().catch(() => ({})); + setErrorMessage( + errorData.message || 'Failed to save channel configuration' + ); + setError(true); + return; + } + + navigateOrShow( + `/launches?added=${provider}&msg=Channel Added${ + twoStepState.onboarding ? '&onboarding=true' : '' + }`, + twoStepState.returnURL, + 'Channel Added' + ); + } finally { + setIsSaving(false); + } + }, + [twoStepState, fetch, modifiedParams, provider, navigateOrShow] + ); + + const Provider = useMemo(() => { + return ( + continueProviderList[provider as keyof typeof continueProviderList] || + null + ); + }, [provider]); + + const providerDisplayName = useMemo(() => { + const names: Record<string, string> = { + facebook: 'Facebook', + instagram: 'Instagram', + 'linkedin-page': 'LinkedIn', + youtube: 'YouTube', + gmb: 'Google Business', + }; + return names[provider] || provider; + }, [provider]); + + // Success state for non-logged users without returnURL + if (successState) { + return ( + <div className="flex flex-1 items-center justify-center text-white relative overflow-hidden"> + {/* Background gradient decoration */} + <div className="absolute inset-0 opacity-30"> + <div className="absolute top-[20%] left-[10%] w-[300px] h-[300px] bg-[#612BD3] rounded-full blur-[120px]" /> + <div className="absolute bottom-[20%] right-[10%] w-[250px] h-[250px] bg-[#FC69FF] rounded-full blur-[120px]" /> + </div> + + <div className="relative z-10 text-center"> + <div className="w-[80px] h-[80px] mx-auto mb-[24px] rounded-full bg-green-500/20 flex items-center justify-center"> + <svg + className="w-[40px] h-[40px] text-green-500" + fill="currentColor" + viewBox="0 0 20 20" + > + <path + fillRule="evenodd" + d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="text-[28px] font-semibold mb-[12px]"> + {t('channel_connected', 'Channel Connected!')} + </div> + <div className="text-[16px] text-gray-400 max-w-[400px]"> + {successState.message || + t( + 'channel_connected_description', + `Your ${providerDisplayName} channel has been successfully connected. You can close this window now.` + )} + </div> + </div> </div> - <Redirect url="/launches" delay={3000} /> - </> - ) : null; + ); + } + + // Show the two-step selection UI + if (twoStepState && Provider) { + return ( + <div className="flex flex-1 items-center justify-center text-white relative overflow-hidden"> + {/* Background gradient decoration */} + <div className="absolute inset-0 opacity-30"> + <div className="absolute top-[20%] left-[10%] w-[300px] h-[300px] bg-[#612BD3] rounded-full blur-[120px]" /> + <div className="absolute bottom-[20%] right-[10%] w-[250px] h-[250px] bg-[#FC69FF] rounded-full blur-[120px]" /> + </div> + + {/* Content */} + <div className="relative z-10 w-full max-w-[550px] mx-auto px-[20px]"> + <div className="bg-[#1A1919] rounded-[16px] p-[32px] flex flex-col gap-[24px]"> + <div className="flex flex-col gap-[8px] text-center"> + <h1 className="text-[24px] font-semibold"> + {t('configure_your_channel', 'Configure Your Channel')} + </h1> + <p className="text-[14px] text-gray-400"> + {t( + 'select_the_page_or_account', + `Select the ${providerDisplayName} page or account you want to connect.` + )} + </p> + </div> + + <IntegrationContext.Provider + value={{ + date: newDayjs(), + value: [], + allIntegrations: [], + integration: { + editor: 'normal', + additionalSettings: '', + display: '', + time: [{ time: 0 }], + id: twoStepState.integrationId, + type: '', + name: '', + picture: '', + inBetweenSteps: true, + changeNickName: false, + changeProfilePicture: false, + identifier: provider, + }, + }} + > + <Provider + onSave={onSave} + existingId={[]} + initialData={twoStepState.pages} + isSaving={isSaving} + /> + </IntegrationContext.Provider> + </div> + </div> + </div> + ); + } + + if (error) { + return ( + <div className="flex flex-1 items-center justify-center text-white relative overflow-hidden"> + {/* Background gradient decoration */} + <div className="absolute inset-0 opacity-30"> + <div className="absolute top-[20%] left-[10%] w-[300px] h-[300px] bg-[#612BD3] rounded-full blur-[120px]" /> + <div className="absolute bottom-[20%] right-[10%] w-[250px] h-[250px] bg-[#FC69FF] rounded-full blur-[120px]" /> + </div> + + <div className="relative z-10 text-center"> + <div className="w-[80px] h-[80px] mx-auto mb-[24px] rounded-full bg-red-500/20 flex items-center justify-center"> + <svg + className="w-[40px] h-[40px] text-red-500" + fill="currentColor" + viewBox="0 0 20 20" + > + <path + fillRule="evenodd" + d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" + clipRule="evenodd" + /> + </svg> + </div> + <div className="text-[28px] font-semibold mb-[12px]"> + {t('could_not_add_provider', 'Could not add provider')} + </div> + <div className="text-[16px] text-gray-400 max-w-[400px]"> + {errorMessage || + t( + 'you_are_being_redirected_back', + 'An error occurred. Please try again.' + )} + </div> + {logged && <Redirect url="/launches" delay={3000} />} + </div> + </div> + ); + } + + // Loading state + return ( + <div className="flex flex-1 items-center justify-center text-white relative overflow-hidden"> + {/* Background gradient decoration */} + <div className="absolute inset-0 opacity-30"> + <div className="absolute top-[20%] left-[10%] w-[300px] h-[300px] bg-[#612BD3] rounded-full blur-[120px]" /> + <div className="absolute bottom-[20%] right-[10%] w-[250px] h-[250px] bg-[#FC69FF] rounded-full blur-[120px]" /> + </div> + + <div className="relative z-10 text-center"> + <div className="text-[28px] font-semibold mb-[12px]"> + {t('adding_channel', 'Adding Channel')} + </div> + <div className="text-[16px] text-gray-400"> + {t('please_wait', 'Please wait while we connect your account...')} + </div> + {/* Loading spinner */} + <div className="mt-[32px] flex justify-center"> + <div className="w-[48px] h-[48px] border-[3px] border-[#612BD3] border-t-transparent rounded-full animate-spin" /> + </div> + </div> + </div> + ); }; diff --git a/apps/frontend/src/components/launches/new.post.tsx b/apps/frontend/src/components/launches/new.post.tsx index d011290c..359822ed 100644 --- a/apps/frontend/src/components/launches/new.post.tsx +++ b/apps/frontend/src/components/launches/new.post.tsx @@ -95,7 +95,7 @@ export const NewPost = () => { strokeLinejoin="round" /> </svg> - <div className="flex-1 text-start text-[16px] group-[.sidebar]:hidden"> + <div className="flex-1 text-start text-[14px] group-[.sidebar]:hidden"> {t('create_new_post', 'Create Post')} </div> </button> diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx index 59391cb4..bd988d2e 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/facebook/facebook.continue.tsx @@ -1,112 +1,47 @@ 'use client'; -import { FC, useCallback, useMemo, useState } from 'react'; -import useSWR from 'swr'; -import clsx from 'clsx'; -import { Button } from '@gitroom/react/form/button'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { withContinueProvider } from '../with-continue-provider'; -export const FacebookContinue: FC<{ - onSave: (data: any) => Promise<void>; - existingId: string[]; -}> = (props) => { - const { onSave, existingId } = props; - const call = useCustomProviderFunction(); - const [page, setSelectedPage] = useState<null | string>(null); - const loadPages = useCallback(async () => { - try { - const pages = await call.get('pages'); - return pages; - } catch (e) { - // Handle error silently - } - }, []); - const setPage = useCallback( - (id: string) => () => { - setSelectedPage(id); +interface FacebookItem { + id: string; + username: string; + name: string; + picture: { + data: { + url: string; + }; + }; +} + +export const FacebookContinue = withContinueProvider<FacebookItem, string>({ + endpoint: 'pages', + swrKey: 'load-facebook-pages', + titleKey: 'select_page', + titleDefault: 'Select Page:', + emptyStateMessages: [ + { + key: 'we_couldn_t_find_any_business_connected_to_the_selected_pages', + text: "We couldn't find any business connected to the selected pages.", }, - [] - ); - const { data, isLoading } = useSWR('load-pages', loadPages, { - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, - revalidateIfStale: false, - revalidateOnMount: true, - revalidateOnReconnect: false, - refreshInterval: 0, - }); - const t = useT(); - - const saveFacebook = useCallback(async () => { - await onSave({ page }); - }, [onSave, page]); - const filteredData = useMemo(() => { - return ( - data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] - ); - }, [data]); - if (!isLoading && !data?.length) { - return ( - <div className="text-center flex justify-center items-center text-[18px] leading-[50px] h-[300px]"> - {t( - 'we_couldn_t_find_any_business_connected_to_the_selected_pages', - "We couldn't find any business connected to the selected pages." - )} - <br /> - {t( - 'we_recommend_you_to_connect_all_the_pages_and_all_the_businesses', - 'We recommend you to connect all the pages and all the businesses.' - )} - <br /> - {t( - 'please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again', - 'Please close this dialog, delete your integration and add a new channel\n again.' - )} - </div> - ); - } - return ( - <div className="flex flex-col gap-[20px]"> - <div>{t('select_page', 'Select Page:')}</div> - <div className="grid grid-cols-3 justify-items-center select-none cursor-pointer"> - {filteredData?.map( - (p: { - id: string; - username: string; - name: string; - picture: { - data: { - url: string; - }; - }; - }) => ( - <div - key={p.id} - className={clsx( - 'flex flex-col w-full text-center gap-[10px] border border-input p-[10px] hover:bg-seventh', - page === p.id && 'bg-seventh' - )} - onClick={setPage(p.id)} - > - <div> - <img - className="w-full" - src={p.picture.data.url} - alt="profile" - /> - </div> - <div>{p.name}</div> - </div> - ) - )} - </div> + { + key: 'we_recommend_you_to_connect_all_the_pages_and_all_the_businesses', + text: 'We recommend you to connect all the pages and all the businesses.', + }, + { + key: 'please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again', + text: 'Please close this dialog, delete your integration and add a new channel again.', + }, + ], + getItemId: (item) => item.id, + getSelectionValue: (item) => item.id, + transformSaveData: (selection) => ({ page: selection }), + isSelected: (item, selection) => selection === item.id, + renderItem: (item) => ( + <> <div> - <Button disabled={!page} onClick={saveFacebook}> - {t('save', 'Save')} - </Button> + <img className="w-full" src={item.picture.data.url} alt="profile" /> </div> - </div> - ); -}; + <div>{item.name}</div> + </> + ), +}); diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx index 87e2cafb..d4adf6f3 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/gmb/gmb.continue.tsx @@ -1,149 +1,81 @@ 'use client'; -import { FC, useCallback, useMemo, useState } from 'react'; -import useSWR from 'swr'; -import clsx from 'clsx'; -import { Button } from '@gitroom/react/form/button'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { withContinueProvider } from '../with-continue-provider'; -export const GmbContinue: FC<{ - onSave: (data: any) => Promise<void>; - existingId: string[]; -}> = (props) => { - const { onSave, existingId } = props; - const call = useCustomProviderFunction(); - const [location, setSelectedLocation] = useState<null | { - id: string; - accountName: string; - locationName: string; - }>(null); - const t = useT(); +interface GmbItem { + id: string; + name: string; + accountName: string; + locationName: string; + picture?: { + data: { + url: string; + }; + }; +} - const loadPages = useCallback(async () => { - try { - const pages = await call.get('pages'); - return pages; - } catch (e) { - // Handle error silently - } - }, []); +interface GmbSelection { + id: string; + accountName: string; + locationName: string; +} - const setLocation = useCallback( - (param: { id: string; accountName: string; locationName: string }) => () => { - setSelectedLocation(param); +export const GmbContinue = withContinueProvider<GmbItem, GmbSelection>({ + endpoint: 'pages', + swrKey: 'load-gmb-locations', + titleKey: 'select_location', + titleDefault: 'Select Business Location:', + emptyStateMessages: [ + { + key: 'gmb_no_locations_found', + text: "We couldn't find any business locations connected to your account.", }, - [] - ); - - const { data, isLoading } = useSWR('load-gmb-locations', loadPages, { - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, - revalidateIfStale: false, - revalidateOnMount: true, - revalidateOnReconnect: false, - refreshInterval: 0, - }); - - const saveGmb = useCallback(async () => { - await onSave(location); - }, [onSave, location]); - - const filteredData = useMemo(() => { - return ( - data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] - ); - }, [data, existingId]); - - if (!isLoading && !data?.length) { - return ( - <div className="text-center flex flex-col justify-center items-center text-[18px] leading-[26px] h-[300px]"> - {t( - 'gmb_no_locations_found', - "We couldn't find any business locations connected to your account." - )} - <br /> - <br /> - {t( - 'gmb_ensure_business_verified', - 'Please ensure your business is verified on Google My Business.' - )} - <br /> - <br /> - {t( - 'gmb_try_again', - 'Please close this dialog, delete the integration and try again.' - )} - </div> - ); - } - - return ( - <div className="flex flex-col gap-[20px]"> - <div>{t('select_location', 'Select Business Location:')}</div> - <div className="grid grid-cols-3 justify-items-center select-none cursor-pointer gap-[10px]"> - {filteredData?.map( - (p: { - id: string; - name: string; - accountName: string; - locationName: string; - picture: { - data: { - url: string; - }; - }; - }) => ( - <div - key={p.id} - className={clsx( - 'flex flex-col w-full text-center gap-[10px] border border-input p-[10px] hover:bg-seventh rounded-[8px]', - location?.id === p.id && 'bg-seventh border-primary' - )} - onClick={setLocation({ - id: p.id, - accountName: p.accountName, - locationName: p.locationName, - })} + { + key: 'gmb_ensure_business_verified', + text: 'Please ensure your business is verified on Google My Business.', + }, + { + key: 'gmb_try_again', + text: 'Please close this dialog, delete the integration and try again.', + }, + ], + getItemId: (item) => item.id, + getSelectionValue: (item) => ({ + id: item.id, + accountName: item.accountName, + locationName: item.locationName, + }), + transformSaveData: (selection) => selection, + isSelected: (item, selection) => selection?.id === item.id, + renderItem: (item) => ( + <> + <div className="flex justify-center"> + {item.picture?.data?.url ? ( + <img + className="w-[80px] h-[80px] object-cover rounded-[8px]" + src={item.picture.data.url} + alt={item.name} + /> + ) : ( + <div className="w-[80px] h-[80px] bg-input rounded-[8px] flex items-center justify-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="40" + height="40" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" > - <div className="flex justify-center"> - {p.picture?.data?.url ? ( - <img - className="w-[80px] h-[80px] object-cover rounded-[8px]" - src={p.picture.data.url} - alt={p.name} - /> - ) : ( - <div className="w-[80px] h-[80px] bg-input rounded-[8px] flex items-center justify-center"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="40" - height="40" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - > - <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" /> - <circle cx="12" cy="10" r="3" /> - </svg> - </div> - )} - </div> - <div className="text-sm font-medium">{p.name}</div> - </div> - ) + <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" /> + <circle cx="12" cy="10" r="3" /> + </svg> + </div> )} </div> - <div> - <Button disabled={!location} onClick={saveGmb}> - {t('save', 'Save')} - </Button> - </div> - </div> - ); -}; - + <div className="text-sm font-medium">{item.name}</div> + </> + ), +}); diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx index 9b7abd7f..4dc7a8c1 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/instagram/instagram.continue.tsx @@ -1,116 +1,60 @@ 'use client'; -import { FC, useCallback, useMemo, useState } from 'react'; -import useSWR from 'swr'; -import clsx from 'clsx'; -import { Button } from '@gitroom/react/form/button'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { withContinueProvider } from '../with-continue-provider'; -export const InstagramContinue: FC<{ - onSave: (data: any) => Promise<void>; - existingId: string[]; -}> = (props) => { - const { onSave, existingId } = props; - const call = useCustomProviderFunction(); - const [page, setSelectedPage] = useState<null | { - id: string; - pageId: string; - }>(null); - const loadPages = useCallback(async () => { - try { - const pages = await call.get('pages'); - return pages; - } catch (e) { - // Handle error silently - } - }, []); - const t = useT(); +interface InstagramItem { + id: string; + pageId: string; + username: string; + name: string; + picture: { + data: { + url: string; + }; + }; +} - const setPage = useCallback( - (param: { id: string; pageId: string }) => () => { - setSelectedPage(param); +interface InstagramSelection { + id: string; + pageId: string; +} + +export const InstagramContinue = withContinueProvider< + InstagramItem, + InstagramSelection +>({ + endpoint: 'pages', + swrKey: 'load-instagram-pages', + titleKey: 'select_instagram_account', + titleDefault: 'Select Instagram Account:', + emptyStateMessages: [ + { + key: 'we_couldn_t_find_any_business_connected_to_the_selected_pages', + text: "We couldn't find any business connected to the selected pages.", }, - [] - ); - const { data, isLoading } = useSWR('load-pages', loadPages, { - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, - revalidateIfStale: false, - revalidateOnMount: true, - revalidateOnReconnect: false, - refreshInterval: 0, - }); - const saveInstagram = useCallback(async () => { - await onSave(page); - }, [onSave, page]); - const filteredData = useMemo(() => { - return ( - data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] - ); - }, [data]); - if (!isLoading && !data?.length) { - return ( - <div className="text-center flex justify-center items-center text-[18px] leading-[50px] h-[300px]"> - {t( - 'we_couldn_t_find_any_business_connected_to_the_selected_pages', - "We couldn't find any business connected to the selected pages." - )} - <br /> - {t( - 'we_recommend_you_to_connect_all_the_pages_and_all_the_businesses', - 'We recommend you to connect all the pages and all the businesses.' - )} - <br /> - {t( - 'please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again', - 'Please close this dialog, delete your integration and add a new channel\n again.' - )} - </div> - ); - } - return ( - <div className="flex flex-col gap-[20px]"> - <div>{t('select_instagram_account', 'Select Instagram Account:')}</div> - <div className="grid grid-cols-3 justify-items-center select-none cursor-pointer"> - {filteredData?.map( - (p: { - id: string; - pageId: string; - username: string; - name: string; - picture: { - data: { - url: string; - }; - }; - }) => ( - <div - key={p.id} - className={clsx( - 'flex flex-col w-full text-center gap-[10px] border border-input p-[10px] hover:bg-seventh', - page?.id === p.id && 'bg-seventh' - )} - onClick={setPage(p)} - > - <div> - <img - className="w-full max-w-[156px]" - src={p.picture.data.url} - alt="profile" - /> - </div> - <div>{p.name}</div> - </div> - ) - )} - </div> + { + key: 'we_recommend_you_to_connect_all_the_pages_and_all_the_businesses', + text: 'We recommend you to connect all the pages and all the businesses.', + }, + { + key: 'please_close_this_dialog_delete_your_integration_and_add_a_new_channel_again', + text: 'Please close this dialog, delete your integration and add a new channel again.', + }, + ], + getItemId: (item) => item.id, + getSelectionValue: (item) => ({ id: item.id, pageId: item.pageId }), + transformSaveData: (selection) => selection, + isSelected: (item, selection) => selection?.id === item.id, + renderItem: (item) => ( + <> <div> - <Button disabled={!page} onClick={saveInstagram}> - {t('save', 'Save')} - </Button> + <img + className="w-full max-w-[156px]" + src={item.picture.data.url} + alt="profile" + /> </div> - </div> - ); -}; + <div>{item.name}</div> + </> + ), +}); diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx index c9da01a2..3bc55822 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/linkedin/linkedin.continue.tsx @@ -1,103 +1,48 @@ 'use client'; -import { FC, useCallback, useMemo, useState } from 'react'; -import useSWR from 'swr'; -import clsx from 'clsx'; -import { Button } from '@gitroom/react/form/button'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { withContinueProvider } from '../with-continue-provider'; -export const LinkedinContinue: FC<{ - onSave: (data: any) => Promise<void>; - existingId: string[]; -}> = (props) => { - const { onSave, existingId } = props; - const t = useT(); +interface LinkedinItem { + id: string; + pageId: string; + username: string; + name: string; + picture: string; +} - const call = useCustomProviderFunction(); - const [page, setSelectedPage] = useState<null | { - id: string; - pageId: string; - }>(null); - const loadPages = useCallback(async () => { - try { - const pages = await call.get('companies'); - return pages; - } catch (e) { - // Handle error silently - } - }, []); - const setPage = useCallback( - (param: { id: string; pageId: string }) => () => { - setSelectedPage(param); +interface LinkedinSelection { + id: string; + pageId: string; +} + +export const LinkedinContinue = withContinueProvider< + LinkedinItem, + LinkedinSelection +>({ + endpoint: 'companies', + swrKey: 'load-linkedin-pages', + titleKey: 'select_linkedin_page', + titleDefault: 'Select Linkedin Page:', + emptyStateMessages: [ + { + key: 'we_couldn_t_find_any_business_connected_to_your_linkedin_page', + text: "We couldn't find any business connected to your LinkedIn Page.", }, - [] - ); - const { data, isLoading } = useSWR('load-pages', loadPages, { - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, - revalidateIfStale: false, - revalidateOnMount: true, - revalidateOnReconnect: false, - refreshInterval: 0, - }); - const saveLinkedin = useCallback(async () => { - await onSave({ page: page?.id }); - }, [onSave, page]); - const filteredData = useMemo(() => { - return ( - data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] - ); - }, [data]); - if (!isLoading && !data?.length) { - return ( - <div className="text-center flex justify-center items-center text-[18px] leading-[50px] h-[300px]"> - {t( - 'we_couldn_t_find_any_business_connected_to_your_linkedin_page', - "We couldn't find any business connected to your LinkedIn Page." - )} - <br /> - {t( - 'please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again', - 'Please close this dialog, create a new page, and add a new channel again.' - )} - </div> - ); - } - return ( - <div className="flex flex-col gap-[20px]"> - <div>{t('select_linkedin_page', 'Select Linkedin Page:')}</div> - <div className="grid grid-cols-3 justify-items-center select-none cursor-pointer"> - {filteredData?.map( - (p: { - id: string; - pageId: string; - username: string; - name: string; - picture: string; - }) => ( - <div - key={p.id} - className={clsx( - 'flex flex-col w-full text-center gap-[10px] border border-input p-[10px] hover:bg-seventh', - page?.id === p.id && 'bg-seventh' - )} - onClick={setPage(p)} - > - <div> - <img className="w-full" src={p.picture} alt="profile" /> - </div> - <div>{p.name}</div> - </div> - ) - )} - </div> + { + key: 'please_close_this_dialog_create_a_new_page_and_add_a_new_channel_again', + text: 'Please close this dialog, create a new page, and add a new channel again.', + }, + ], + getItemId: (item) => item.id, + getSelectionValue: (item) => ({ id: item.id, pageId: item.pageId }), + transformSaveData: (selection) => ({ page: selection.id }), + isSelected: (item, selection) => selection?.id === item.id, + renderItem: (item) => ( + <> <div> - <Button disabled={!page} onClick={saveLinkedin}> - {t('save', 'Save')} - </Button> + <img className="w-full" src={item.picture} alt="profile" /> </div> - </div> - ); -}; + <div>{item.name}</div> + </> + ), +}); diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/with-continue-provider.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/with-continue-provider.tsx new file mode 100644 index 00000000..50491d64 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/with-continue-provider.tsx @@ -0,0 +1,151 @@ +'use client'; + +import { FC, ReactNode, useCallback, useMemo, useState } from 'react'; +import useSWR from 'swr'; +import clsx from 'clsx'; +import { Button } from '@gitroom/react/form/button'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; + +const SWR_OPTIONS = { + refreshWhenHidden: false, + refreshWhenOffline: false, + revalidateOnFocus: false, + revalidateIfStale: false, + revalidateOnMount: true, + revalidateOnReconnect: false, + refreshInterval: 0, +}; + +export interface ContinueProviderProps { + onSave: (data: any) => Promise<void>; + existingId: string[]; + initialData?: any[]; + isSaving?: boolean; +} + +export interface EmptyStateMessage { + key: string; + text: string; +} + +export interface ContinueProviderConfig<TItem, TSelection> { + endpoint: string; + swrKey: string; + titleKey: string; + titleDefault: string; + emptyStateMessages: EmptyStateMessage[]; + getSelectionValue: (item: TItem) => TSelection; + transformSaveData: (selection: TSelection) => any; + renderItem: (item: TItem, isSelected: boolean) => ReactNode; + isSelected: (item: TItem, selection: TSelection | null) => boolean; + getItemId: (item: TItem) => string; +} + +export function withContinueProvider<TItem, TSelection>( + config: ContinueProviderConfig<TItem, TSelection> +): FC<ContinueProviderProps> { + const { + endpoint, + swrKey, + titleKey, + titleDefault, + emptyStateMessages, + getSelectionValue, + transformSaveData, + renderItem, + isSelected, + getItemId, + } = config; + + return function ContinueProviderComponent(props: ContinueProviderProps) { + const { onSave, existingId, initialData, isSaving } = props; + const call = useCustomProviderFunction(); + const t = useT(); + const [selection, setSelection] = useState<TSelection | null>(null); + + const loadData = useCallback(async () => { + // Skip fetch if initial data was provided + if (initialData) { + return initialData; + } + try { + return await call.get(endpoint); + } catch (e) { + // Handle error silently + } + }, [initialData]); + + const { data, isLoading } = useSWR( + initialData ? null : swrKey, + loadData, + SWR_OPTIONS + ); + + const resolvedData = initialData || data; + + const handleSelect = useCallback( + (item: TItem) => () => { + setSelection(getSelectionValue(item)); + }, + [] + ); + + const handleSave = useCallback(async () => { + if (selection) { + await onSave(transformSaveData(selection)); + } + }, [onSave, selection]); + + const filteredData = useMemo(() => { + return ( + (resolvedData as TItem[])?.filter( + (item) => !existingId.includes(getItemId(item)) + ) || [] + ); + }, [resolvedData, existingId]); + + if (!isLoading && !resolvedData?.length) { + return ( + <div className="text-center flex flex-col justify-center items-center text-[18px] leading-[26px] h-[300px]"> + {emptyStateMessages.map((msg, index) => ( + <span key={msg.key}> + {t(msg.key, msg.text)} + {index < emptyStateMessages.length - 1 && ( + <> + <br /> + <br /> + </> + )} + </span> + ))} + </div> + ); + } + + return ( + <div className="flex flex-col gap-[20px]"> + <div>{t(titleKey, titleDefault)}</div> + <div className="grid grid-cols-3 justify-items-center select-none cursor-pointer gap-[10px]"> + {filteredData.map((item) => ( + <div + key={getItemId(item)} + className={clsx( + 'flex flex-col w-full text-center gap-[10px] border border-input p-[10px] hover:bg-seventh rounded-[8px]', + isSelected(item, selection) && 'bg-seventh border-primary' + )} + onClick={handleSelect(item)} + > + {renderItem(item, isSelected(item, selection))} + </div> + ))} + </div> + <div> + <Button disabled={!selection || isSaving} loading={isSaving} onClick={handleSave}> + {t('save', 'Save')} + </Button> + </div> + </div> + ); + }; +} diff --git a/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx b/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx index ace60189..a631d409 100644 --- a/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx +++ b/apps/frontend/src/components/new-launch/providers/continue-provider/youtube/youtube.continue.tsx @@ -1,149 +1,86 @@ 'use client'; -import { FC, useCallback, useMemo, useState } from 'react'; -import useSWR from 'swr'; -import clsx from 'clsx'; -import { Button } from '@gitroom/react/form/button'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { withContinueProvider } from '../with-continue-provider'; -export const YoutubeContinue: FC<{ - onSave: (data: any) => Promise<void>; - existingId: string[]; -}> = (props) => { - const { onSave, existingId } = props; - const call = useCustomProviderFunction(); - const [channel, setSelectedChannel] = useState<null | { id: string }>(null); - const t = useT(); +interface YoutubeItem { + id: string; + name: string; + username?: string; + subscriberCount?: string; + picture?: { + data: { + url: string; + }; + }; +} - const loadChannels = useCallback(async () => { - try { - const channels = await call.get('pages'); - return channels; - } catch (e) { - // Handle error silently - } - }, []); +interface YoutubeSelection { + id: string; +} - const setChannel = useCallback( - (param: { id: string }) => () => { - setSelectedChannel(param); +export const YoutubeContinue = withContinueProvider< + YoutubeItem, + YoutubeSelection +>({ + endpoint: 'pages', + swrKey: 'load-youtube-channels', + titleKey: 'select_channel', + titleDefault: 'Select YouTube Channel:', + emptyStateMessages: [ + { + key: 'youtube_no_channels_found', + text: "We couldn't find any YouTube channels connected to your account.", }, - [] - ); - - const { data, isLoading } = useSWR('load-youtube-channels', loadChannels, { - refreshWhenHidden: false, - refreshWhenOffline: false, - revalidateOnFocus: false, - revalidateIfStale: false, - revalidateOnMount: true, - revalidateOnReconnect: false, - refreshInterval: 0, - }); - - const saveYoutube = useCallback(async () => { - await onSave(channel); - }, [onSave, channel]); - - const filteredData = useMemo(() => { - return ( - data?.filter((p: { id: string }) => !existingId.includes(p.id)) || [] - ); - }, [data, existingId]); - - if (!isLoading && !data?.length) { - return ( - <div className="text-center flex flex-col justify-center items-center text-[18px] leading-[26px] h-[300px]"> - {t( - 'youtube_no_channels_found', - "We couldn't find any YouTube channels connected to your account." - )} - <br /> - <br /> - {t( - 'youtube_ensure_channel_exists', - 'Please ensure you have a YouTube channel created.' - )} - <br /> - <br /> - {t( - 'youtube_try_again', - 'Please close this dialog, delete the integration and try again.' - )} - </div> - ); - } - - return ( - <div className="flex flex-col gap-[20px]"> - <div>{t('select_channel', 'Select YouTube Channel:')}</div> - <div className="grid grid-cols-3 justify-items-center select-none cursor-pointer gap-[10px]"> - {filteredData?.map( - (p: { - id: string; - name: string; - username: string; - subscriberCount: string; - picture: { - data: { - url: string; - }; - }; - }) => ( - <div - key={p.id} - className={clsx( - 'flex flex-col w-full text-center gap-[10px] border border-input p-[10px] hover:bg-seventh rounded-[8px]', - channel?.id === p.id && 'bg-seventh border-primary' - )} - onClick={setChannel({ id: p.id })} + { + key: 'youtube_ensure_channel_exists', + text: 'Please ensure you have a YouTube channel created.', + }, + { + key: 'youtube_try_again', + text: 'Please close this dialog, delete the integration and try again.', + }, + ], + getItemId: (item) => item.id, + getSelectionValue: (item) => ({ id: item.id }), + transformSaveData: (selection) => selection, + isSelected: (item, selection) => selection?.id === item.id, + renderItem: (item) => ( + <> + <div className="flex justify-center"> + {item.picture?.data?.url ? ( + <img + className="w-[80px] h-[80px] object-cover rounded-full" + src={item.picture.data.url} + alt={item.name} + /> + ) : ( + <div className="w-[80px] h-[80px] bg-input rounded-full flex items-center justify-center"> + <svg + xmlns="http://www.w3.org/2000/svg" + width="40" + height="40" + viewBox="0 0 24 24" + fill="none" + stroke="currentColor" + strokeWidth="1.5" + strokeLinecap="round" + strokeLinejoin="round" > - <div className="flex justify-center"> - {p.picture?.data?.url ? ( - <img - className="w-[80px] h-[80px] object-cover rounded-full" - src={p.picture.data.url} - alt={p.name} - /> - ) : ( - <div className="w-[80px] h-[80px] bg-input rounded-full flex items-center justify-center"> - <svg - xmlns="http://www.w3.org/2000/svg" - width="40" - height="40" - viewBox="0 0 24 24" - fill="none" - stroke="currentColor" - strokeWidth="1.5" - strokeLinecap="round" - strokeLinejoin="round" - > - <path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z" /> - <polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02" /> - </svg> - </div> - )} - </div> - <div className="text-sm font-medium">{p.name}</div> - {p.username && ( - <div className="text-xs text-gray-500">{p.username}</div> - )} - {p.subscriberCount && ( - <div className="text-xs text-gray-400"> - {parseInt(p.subscriberCount).toLocaleString()} subscribers - </div> - )} - </div> - ) + <path d="M22.54 6.42a2.78 2.78 0 0 0-1.94-2C18.88 4 12 4 12 4s-6.88 0-8.6.46a2.78 2.78 0 0 0-1.94 2A29 29 0 0 0 1 11.75a29 29 0 0 0 .46 5.33A2.78 2.78 0 0 0 3.4 19c1.72.46 8.6.46 8.6.46s6.88 0 8.6-.46a2.78 2.78 0 0 0 1.94-2 29 29 0 0 0 .46-5.25 29 29 0 0 0-.46-5.33z" /> + <polygon points="9.75 15.02 15.5 11.75 9.75 8.48 9.75 15.02" /> + </svg> + </div> )} </div> - <div> - <Button disabled={!channel} onClick={saveYoutube}> - {t('save', 'Save')} - </Button> - </div> - </div> - ); -}; - + <div className="text-sm font-medium">{item.name}</div> + {item.username && ( + <div className="text-xs text-gray-500">{item.username}</div> + )} + {item.subscriberCount && ( + <div className="text-xs text-gray-400"> + {parseInt(item.subscriberCount).toLocaleString()} subscribers + </div> + )} + </> + ), +}); diff --git a/apps/frontend/src/components/onboarding/onboarding.modal.tsx b/apps/frontend/src/components/onboarding/onboarding.modal.tsx index 3a45c65e..69c0d05e 100644 --- a/apps/frontend/src/components/onboarding/onboarding.modal.tsx +++ b/apps/frontend/src/components/onboarding/onboarding.modal.tsx @@ -202,6 +202,7 @@ const OnboardingStep1: FC<{ onNext: () => void; onSkip: () => void }> = ({ </div> {data && ( <AddProviderComponent + invite={false} social={data.social || []} article={data.article || []} onboarding={true} diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts index 9e65ff84..b78535b9 100644 --- a/apps/frontend/src/middleware.ts +++ b/apps/frontend/src/middleware.ts @@ -42,6 +42,14 @@ export async function middleware(request: NextRequest) { ) { return topResponse; } + + if ( + nextUrl.pathname.startsWith('/integrations/social/') && + nextUrl.href.indexOf('state=login') === -1 + ) { + return topResponse; + } + // If the URL is logout, delete the cookie and redirect to login if (nextUrl.href.indexOf('/auth/logout') > -1) { const response = NextResponse.redirect( diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts index bb81ae8c..549b06b5 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -151,12 +151,31 @@ export class IntegrationRepository { params.picture = await this.storage.uploadSimple(params.picture); } + const existing = await this._integration.model.integration.findUnique({ + where: { + organizationId_internalId: { + organizationId: params.organizationId!, + internalId: params.internalId, + }, + }, + }); + + if (existing) { + await this._integration.model.integration.delete({ + where: { + id, + }, + }); + } + return this._integration.model.integration.update({ where: { - id, + ...(existing ? { id: existing.id } : { id }), }, data: { ...params, + disabled: false, + deletedAt: null, }, }); } diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts index 0a145eb0..de6dd1af 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts @@ -316,6 +316,7 @@ export class IntegrationService { await this._integrationRepository.updateIntegration(id, { picture: getIntegrationInformation.picture, internalId: String(getIntegrationInformation.id), + organizationId: org, name: getIntegrationInformation.name, inBetweenSteps: false, token: getIntegrationInformation.access_token, diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 4d96e605..10795896 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -27,7 +27,9 @@ export class BadBody extends ApplicationFailure { } export class NotEnoughScopes { - constructor(public message = 'Not enough scopes') {} + constructor( + public message = 'Not enough scopes, when choosing a provider, please add all the scopes' + ) {} } function safeStringify(obj: any) { From 9b712bb634a3ae435303fbbbc4cb50878a677e2d Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 1 Feb 2026 19:41:07 +0700 Subject: [PATCH 200/340] feat: independent provider adding --- .../frontend/src/components/launches/add.provider.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 77cc2abf..a84c581c 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -74,7 +74,7 @@ export const AddProviderButton: FC<{ data-tooltip-id="tooltip" data-tooltip-content={t( 'invite_link', - 'Send Invite Link to a customer' + 'Send Invite Link to a customer to add channel' )} className="group-[.sidebar]:hidden min-h-[44px] min-w-[44px] bg-btnSimple justify-center items-center flex rounded-[8px] cursor-pointer" > From 1a8f6ff6ca444e79d6035ca515cd96451b4fd2fe Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 1 Feb 2026 22:29:41 +0700 Subject: [PATCH 201/340] fix: new error for facebook --- .../integrations/social/facebook.provider.ts | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index 6a062af6..ccad72e7 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -126,6 +126,13 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { }; } + if (body.indexOf('1404112') > -1) { + return { + type: 'bad-body' as const, + value: 'For security reasons, your account has limited access to the site for a few days', + }; + } + if (body.indexOf('Name parameter too long') > -1) { return { type: 'bad-body' as const, @@ -504,10 +511,9 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { case 'post_clicks_by_type': // This returns an object with click types if (typeof value === 'object') { - const totalClicks = Object.values(value as Record<string, number>).reduce( - (sum: number, v: number) => sum + v, - 0 - ); + const totalClicks = Object.values( + value as Record<string, number> + ).reduce((sum: number, v: number) => sum + v, 0); label = 'Clicks by Type'; total = String(totalClicks); } @@ -515,10 +521,9 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { case 'post_reactions_by_type_total': // This returns an object with reaction types if (typeof value === 'object') { - const totalReactions = Object.values(value as Record<string, number>).reduce( - (sum: number, v: number) => sum + v, - 0 - ); + const totalReactions = Object.values( + value as Record<string, number> + ).reduce((sum: number, v: number) => sum + v, 0); label = 'Reactions'; total = String(totalReactions); } From a936da485101a195c3e4a8a1ebf8fd3c1943a69a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 2 Feb 2026 11:12:04 +0700 Subject: [PATCH 202/340] feat: fix state for bluesky --- .../launches/add.provider.component.tsx | 48 ++++--------------- .../integrations/social/bluesky.provider.ts | 2 +- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index a84c581c..cfaad232 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -256,6 +256,7 @@ export const CustomVariables: FC<{ onboarding?: boolean; }> = (props) => { const { close, gotoUrl, identifier, variables, onboarding } = props; + const fetch = useFetch(); const modals = useModals(); const schema = useMemo(() => { return object({ @@ -291,9 +292,16 @@ export const CustomVariables: FC<{ }); const submit = useCallback( async (data: FieldValues) => { + const { url } = await ( + await fetch( + `/integrations/social/${identifier}${ + onboarding ? '?onboarding=true' : '' + }` + ) + ).json(); modals.closeAll(); gotoUrl( - `/integrations/social/${identifier}?state=nostate&code=${Buffer.from( + `/integrations/social/${identifier}?state=${url}&code=${Buffer.from( JSON.stringify(data) ).toString('base64')}${onboarding ? '&onboarding=true' : ''}` ); @@ -474,22 +482,6 @@ export const AddProviderComponent: FC<{ [onboarding] ); - const showApiButton = useCallback( - (identifier: string, name: string) => async () => { - modal.openModal({ - title: '', - withCloseButton: true, - classNames: { - modal: 'bg-transparent text-textColor', - }, - children: ( - <ApiModal update={update} name={name} identifier={identifier} /> - ), - }); - }, - [] - ); - const t = useT(); return ( @@ -565,28 +557,6 @@ export const AddProviderComponent: FC<{ ))} </div> </div> - {!isGeneral && ( - <div className="flex flex-col"> - <h2 className="pb-[10px]">{t('articles', 'Articles')}</h2> - <div className="grid grid-cols-3 gap-[10px]"> - {article.map((item) => ( - <div - key={item.identifier} - onClick={showApiButton(item.identifier, item.name)} - className="w-[120px] h-[100px] bg-input text-textColor justify-center items-center flex flex-col gap-[10px] cursor-pointer" - > - <div> - <img - className="w-[32px] h-[32px] rounded-full" - src={`/icons/platforms/${item.identifier}.png`} - /> - </div> - <div>{item.name}</div> - </div> - ))} - </div> - </div> - )} </div> ); }; diff --git a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts index 1f28c38d..936aae68 100644 --- a/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/bluesky.provider.ts @@ -196,7 +196,7 @@ export class BlueskyProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; From 68705fca691d0991822f75559c299427253f58e8 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 2 Feb 2026 11:27:28 +0700 Subject: [PATCH 203/340] feat: fix providers --- .../launches/add.provider.component.tsx | 85 ---- .../onboarding/connect.channels.tsx | 380 ------------------ .../integrations/social/dev.to.provider.ts | 2 +- .../integrations/social/hashnode.provider.ts | 2 +- .../src/integrations/social/lemmy.provider.ts | 2 +- .../integrations/social/listmonk.provider.ts | 2 +- .../integrations/social/medium.provider.ts | 2 +- .../src/integrations/social/nostr.provider.ts | 2 +- .../integrations/social/wordpress.provider.ts | 2 +- 9 files changed, 7 insertions(+), 472 deletions(-) delete mode 100644 apps/frontend/src/components/onboarding/connect.channels.tsx diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index cfaad232..20aef111 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -104,92 +104,7 @@ export const AddProviderButton: FC<{ </div> ); }; -export const ApiModal: FC<{ - identifier: string; - name: string; - update?: () => void; - close?: () => void; -}> = (props) => { - const { update, name, close: closePopup } = props; - const fetch = useFetch(); - const router = useRouter(); - const modal = useModals(); - const methods = useForm({ - mode: 'onChange', - resolver, - }); - const close = useCallback(() => { - if (closePopup) { - return closePopup(); - } - modal.closeAll(); - }, []); - const submit = useCallback(async (data: FieldValues) => { - const add = await fetch( - `/integrations/article/${props.identifier}/connect`, - { - method: 'POST', - body: JSON.stringify({ - api: data.api, - }), - } - ); - if (add.ok) { - if (closePopup) { - closePopup(); - } else { - modal.closeAll(); - } - router.refresh(); - if (update) update(); - return; - } - methods.setError('api', { - message: t('invalid_api_key', 'Invalid API key'), - }); - }, []); - const t = useT(); - - return ( - <div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative"> - <TopTitle title={`Add API key for ${name}`} /> - <button - onClick={close} - className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" - type="button" - > - <svg - viewBox="0 0 15 15" - fill="none" - xmlns="http://www.w3.org/2000/svg" - width="16" - height="16" - > - <path - d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" - fill="currentColor" - fillRule="evenodd" - clipRule="evenodd" - ></path> - </svg> - </button> - <FormProvider {...methods}> - <form - className="gap-[8px] flex flex-col" - onSubmit={methods.handleSubmit(submit)} - > - <div className="pt-[10px]"> - <Input label="API Key" name="api" /> - </div> - <div> - <Button type="submit">{t('add_platform', 'Add platform')}</Button> - </div> - </form> - </FormProvider> - </div> - ); -}; export const UrlModal: FC<{ gotoUrl(url: string): void; }> = (props) => { diff --git a/apps/frontend/src/components/onboarding/connect.channels.tsx b/apps/frontend/src/components/onboarding/connect.channels.tsx deleted file mode 100644 index 96ed0ea1..00000000 --- a/apps/frontend/src/components/onboarding/connect.channels.tsx +++ /dev/null @@ -1,380 +0,0 @@ -'use client'; - -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; -import useSWR from 'swr'; -import { orderBy } from 'lodash'; -import { useUser } from '@gitroom/frontend/components/layout/user.context'; -import clsx from 'clsx'; -import Image from 'next/image'; -import { Menu } from '@gitroom/frontend/components/launches/menu/menu'; -import { - ApiModal, - CustomVariables, -} from '@gitroom/frontend/components/launches/add.provider.component'; -import { useRouter } from 'next/navigation'; -import { useVariables } from '@gitroom/react/helpers/variable.context'; -import { useToaster } from '@gitroom/react/toaster/toaster'; -import { Integration } from '@prisma/client'; -import { web3List } from '@gitroom/frontend/components/launches/web3/web3.list'; -import { timer } from '@gitroom/helpers/utils/timer'; -import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; -import { useT } from '@gitroom/react/translation/get.transation.service.client'; -import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component'; -export const ConnectChannels: FC = () => { - const fetch = useFetch(); - const { isGeneral } = useVariables(); - const router = useRouter(); - const [identifier, setIdentifier] = useState<any>(undefined); - const [popup, setPopups] = useState<undefined | string[]>(undefined); - const toaster = useToaster(); - const [showCustom, setShowCustom] = useState<any>(undefined); - const getIntegrations = useCallback(async () => { - return (await fetch('/integrations')).json(); - }, []); - const [reload, setReload] = useState(false); - - const openWeb3 = async (id: string) => { - const { component: Web3Providers } = web3List.find( - (item) => item.identifier === id - )!; - const { url } = await (await fetch(`/integrations/social/${id}`)).json(); - setShowCustom( - <Web3Providers - onComplete={async (code, newState) => { - if ( - await deleteDialog( - 'Connection found, should we continue?', - 'Continue' - ) - ) { - window.open( - `/integrations/social/${id}?code=${code}&state=${newState}`, - 'Social Connect', - 'width=700,height=700' - ); - return; - } - setShowCustom(undefined); - }} - nonce={url} - /> - ); - return; - }; - const refreshChannel = useCallback( - ( - integration: Integration & { - identifier: string; - } - ) => - async () => { - const { url } = await ( - await fetch( - `/integrations/social/${integration.identifier}?refresh=${integration.internalId}`, - { - method: 'GET', - } - ) - ).json(); - window.location.href = url; - }, - [] - ); - const addMessage = useCallback( - ( - event: MessageEvent<{ - msg: string; - success: boolean; - }> - ) => { - if (!event.data.msg) { - return; - } - toaster.show(event.data.msg, event.data.success ? 'success' : 'warning'); - setShowCustom(undefined); - }, - [] - ); - useEffect(() => { - window.addEventListener('message', addMessage); - return () => { - window.removeEventListener('message', addMessage); - }; - }); - const getSocialLink = useCallback( - ( - identifier: string, - isExternal: boolean, - isWeb3: boolean, - customFields?: Array<{ - key: string; - label: string; - validation: string; - defaultValue?: string; - type: 'text' | 'password'; - }> - ) => - async () => { - const gotoIntegration = async (externalUrl?: string) => { - const { url, err } = await ( - await fetch( - `/integrations/social/${identifier}${ - externalUrl ? `?externalUrl=${externalUrl}` : `` - }` - ) - ).json(); - if (err) { - toaster.show('Could not connect to the platform', 'warning'); - return; - } - setShowCustom(undefined); - window.open(url, 'Social Connect', 'width=700,height=700'); - }; - - if (isWeb3) { - openWeb3(identifier); - return; - } - if (customFields) { - setShowCustom( - <CustomVariables - identifier={identifier} - gotoUrl={(url: string) => - window.open(url, 'Social Connect', 'width=700,height=700') - } - variables={customFields} - close={() => setShowCustom(undefined)} - /> - ); - return; - } - await gotoIntegration(); - }, - [] - ); - const load = useCallback(async (path: string) => { - const list = (await (await fetch(path)).json()).integrations; - setPopups(list.map((p: any) => p.id)); - return list; - }, []); - - const { data: integrations, mutate } = useSWR('/integrations/list', load, { - revalidateOnFocus: false, - revalidateOnReconnect: false, - revalidateIfStale: false, - revalidateOnMount: true, - refreshWhenHidden: false, - refreshWhenOffline: false, - fallbackData: [], - }); - - const user = useUser(); - const totalNonDisabledChannels = useMemo(() => { - return ( - integrations?.filter((integration: any) => !integration.disabled) - ?.length || 0 - ); - }, [integrations]); - - const sortedIntegrations = useMemo(() => { - return orderBy( - integrations, - ['type', 'disabled', 'identifier'], - ['desc', 'asc', 'asc'] - ); - }, [integrations]); - useEffect(() => { - if (sortedIntegrations.length === 0 || !popup) { - return; - } - const betweenSteps = sortedIntegrations.find((p) => p.inBetweenSteps); - if (betweenSteps && popup.indexOf(betweenSteps.id) === -1) { - const url = new URL(window.location.href); - url.searchParams.append('added', betweenSteps.identifier); - url.searchParams.append('continue', betweenSteps.id); - router.push(url.toString()); - setPopups([...popup, betweenSteps.id]); - } - }, [sortedIntegrations, popup]); - const update = useCallback(async (shouldReload: boolean) => { - if (shouldReload) { - setReload(true); - } - await mutate(); - if (shouldReload) { - setReload(false); - } - }, []); - - const t = useT(); - - const continueIntegration = useCallback( - (integration: any) => async () => { - const url = new URL(window.location.href); - url.searchParams.append('added', integration.identifier); - url.searchParams.append('continue', integration.id); - router.push(url.toString()); - }, - [] - ); - const finishUpdate = useCallback(() => { - setIdentifier(undefined); - update(true); - }, []); - const { data } = useSWR('get-all-integrations', getIntegrations); - return ( - <> - {!!showCustom && ( - <div className="absolute w-full h-full top-0 start-0 z-[400]"> - <div className="absolute w-full h-full bg-primary/80 start-0 top-0 z-[200] p-[50px] flex justify-center"> - <div className="w-[400px]"> - <ModalWrapperComponent - title="" - customClose={() => setShowCustom(undefined)} - > - {showCustom} - </ModalWrapperComponent> - </div> - </div> - </div> - )} - {!!identifier && ( - <div className="absolute w-full h-full bg-primary/80 start-0 top-0 z-[200] p-[30px] flex items-center justify-center"> - <div className="w-[400px]"> - <ApiModal - close={() => setIdentifier(undefined)} - update={finishUpdate} - identifier={identifier.identifier} - name={identifier.name} - /> - </div> - </div> - )} - <div className="flex flex-col gap-[24px]"> - <div className="flex gap-[4px] flex-col"> - <div className="text-[20px]"> - {t('connect_channels', 'Connect Channels')} - </div> - <div className="text-[14px] text-customColor18"> - {t( - 'connect_your_social_media_and_publishing_websites_channels_to_schedule_posts_later', - 'Connect your social media and publishing websites channels to\n schedule posts later' - )} - </div> - </div> - {sortedIntegrations.length !== 0 && ( - <div className="border border-customColor47 rounded-[4px] p-[16px]"> - <div className="gap-[16px] flex flex-col"> - {sortedIntegrations.length === 0 && ( - <div className="text-[12px]"> - {t('no_channels', 'No channels')} - </div> - )} - {sortedIntegrations.map((integration) => ( - <div - key={integration.id} - className="flex gap-[8px] items-center" - > - <div - className={clsx( - 'relative w-[34px] h-[34px] rounded-full flex justify-center items-center bg-fifth', - integration.disabled && 'opacity-50' - )} - > - {integration.inBetweenSteps && ( - <div - className="absolute start-0 top-0 w-[39px] h-[46px] cursor-pointer" - onClick={continueIntegration(integration)} - > - <div className="bg-red-500 w-[15px] h-[15px] rounded-full -start-[5px] -top-[5px] absolute z-[200] text-[10px] flex justify-center items-center"> - ! - </div> - <div className="bg-primary/60 w-[39px] h-[46px] start-0 top-0 absolute rounded-full z-[199]" /> - </div> - )} - <Image - src={integration.picture} - className="rounded-full" - alt={integration.identifier} - width={32} - height={32} - /> - <Image - src={`/icons/platforms/${integration.identifier}.png`} - className="rounded-full absolute z-10 -bottom-[5px] -end-[5px] border border-fifth" - alt={integration.identifier} - width={20} - height={20} - /> - </div> - <div - {...(integration.disabled && - totalNonDisabledChannels === user?.totalChannels - ? { - 'data-tooltip-id': 'tooltip', - 'data-tooltip-content': - 'This channel is disabled, please upgrade your plan to enable it.', - } - : {})} - className={clsx( - 'flex-1', - integration.disabled && 'opacity-50' - )} - > - {integration.name} - </div> - <Menu - canChangeProfilePicture={integration.changeProfile} - canChangeNickName={integration.changeNickName} - mutate={mutate} - onChange={update} - id={integration.id} - refreshChannel={refreshChannel} - canEnable={ - user?.totalChannels! > totalNonDisabledChannels && - integration.disabled - } - canDisable={!integration.disabled} - /> - </div> - ))} - </div> - </div> - )} - - <div className="flex mb-[16px]"> - <div className="flex-1 flex flex-col gap-[10px]"> - <div className="grid grid-cols-3 gap-[16px]"> - {data?.social.map((social: any) => ( - <div - key={social.identifier} - onClick={getSocialLink( - social.identifier, - social.isExternal, - social.isWeb3, - social.customFields - )} - className="h-[96px] bg-input flex flex-col justify-center items-center gap-[10px] cursor-pointer" - > - <div> - <Image - alt={social.identifier} - src={`/icons/platforms/${social.identifier}.png`} - className="rounded-full w-[32px] h-[32px]" - width={32} - height={32} - /> - </div> - <div className="text-textColor text-[10px] tracking-[1.2px] uppercase"> - {social.name} - </div> - </div> - ))} - </div> - </div> - </div> - </div> - </> - ); -}; diff --git a/libraries/nestjs-libraries/src/integrations/social/dev.to.provider.ts b/libraries/nestjs-libraries/src/integrations/social/dev.to.provider.ts index 9f59d671..f0aee14a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/dev.to.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/dev.to.provider.ts @@ -26,7 +26,7 @@ export class DevToProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/hashnode.provider.ts b/libraries/nestjs-libraries/src/integrations/social/hashnode.provider.ts index 41c0c80f..7eeadef0 100644 --- a/libraries/nestjs-libraries/src/integrations/social/hashnode.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/hashnode.provider.ts @@ -28,7 +28,7 @@ export class HashnodeProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts b/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts index 465a0d86..53d230d9 100644 --- a/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/lemmy.provider.ts @@ -63,7 +63,7 @@ export class LemmyProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/listmonk.provider.ts b/libraries/nestjs-libraries/src/integrations/social/listmonk.provider.ts index 937bb5b5..371126c2 100644 --- a/libraries/nestjs-libraries/src/integrations/social/listmonk.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/listmonk.provider.ts @@ -65,7 +65,7 @@ export class ListmonkProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/medium.provider.ts b/libraries/nestjs-libraries/src/integrations/social/medium.provider.ts index e5b8752b..5aab52d6 100644 --- a/libraries/nestjs-libraries/src/integrations/social/medium.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/medium.provider.ts @@ -26,7 +26,7 @@ export class MediumProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts index 04d90475..c89f21dd 100644 --- a/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/nostr.provider.ts @@ -65,7 +65,7 @@ export class NostrProvider extends SocialAbstract implements SocialProvider { async generateAuthUrl() { const state = makeId(17); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; diff --git a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts index 88e9dab9..84d19919 100644 --- a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts @@ -32,7 +32,7 @@ export class WordpressProvider async generateAuthUrl() { const state = makeId(6); return { - url: '', + url: state, codeVerifier: makeId(10), state, }; From 47414acf29d3deb856eb547577e1baf8497d901a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 2 Feb 2026 13:13:54 +0700 Subject: [PATCH 204/340] feat: moltbook --- .../src/api/routes/integrations.controller.ts | 29 +++ .../public/icons/platforms/moltbook.png | Bin 0 -> 4207 bytes .../launches/add.provider.component.tsx | 3 +- .../web3/providers/moltbook.provider.tsx | 174 ++++++++++++++++ .../components/launches/web3/web3.list.tsx | 5 + .../providers/moltbook/moltbook.provider.tsx | 35 ++++ .../providers/show.all.providers.tsx | 5 + .../src/dtos/posts/create.post.dto.ts | 1 - .../all.providers.settings.ts | 3 + .../posts/providers-settings/moltbook.dto.ts | 8 + .../src/integrations/integration.manager.ts | 2 + .../integrations/social/moltbook.provider.ts | 195 ++++++++++++++++++ 12 files changed, 458 insertions(+), 2 deletions(-) create mode 100644 apps/frontend/public/icons/platforms/moltbook.png create mode 100644 apps/frontend/src/components/launches/web3/providers/moltbook.provider.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/moltbook/moltbook.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/moltbook.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/moltbook.provider.ts diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 0a3ab089..3c681245 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -25,6 +25,7 @@ import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abst import { timer } from '@gitroom/helpers/utils/timer'; import { TelegramProvider } from '@gitroom/nestjs-libraries/integrations/social/telegram.provider'; +import { MoltbookProvider } from '@gitroom/nestjs-libraries/integrations/social/moltbook.provider'; import { AuthorizationActions, Sections, @@ -439,4 +440,32 @@ export class IntegrationsController { async getUpdates(@Query() query: { word: string; id?: number }) { return new TelegramProvider().getBotId(query); } + + @Post('/moltbook/register') + async moltbookRegister( + @Body() body: { name: string; description: string } + ) { + try { + const provider = new MoltbookProvider(); + const result = await provider.registerAgent(body.name, body.description); + return { + apiKey: result.api_key, + claimUrl: result.claim_url, + verificationCode: result.verification_code, + }; + } catch (err: any) { + return { error: err.message || 'Registration failed' }; + } + } + + @Get('/moltbook/status') + async moltbookStatus(@Query('apiKey') apiKey: string) { + try { + const provider = new MoltbookProvider(); + const result = await provider.checkAgentStatus(apiKey); + return { claimed: result?.status === 'claimed' }; + } catch (err) { + return { claimed: false }; + } + } } diff --git a/apps/frontend/public/icons/platforms/moltbook.png b/apps/frontend/public/icons/platforms/moltbook.png new file mode 100644 index 0000000000000000000000000000000000000000..644b7d1e05f3c4304977501740b950a92e1e4c96 GIT binary patch literal 4207 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 FV1lGx4ekH{ literal 0 HcmV?d00001 diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index 20aef111..db11ae2f 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -18,6 +18,7 @@ import { web3List } from '@gitroom/frontend/components/launches/web3/web3.list'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import clsx from 'clsx'; import copy from 'copy-to-clipboard'; +import { capitalize } from 'lodash'; const resolver = classValidatorResolver(ApiKeyDto); export const useAddProvider = (update?: () => void, invite?: boolean) => { @@ -306,7 +307,7 @@ export const AddProviderComponent: FC<{ ) ).json(); modal.openModal({ - title: t('web3_provider', 'Web3 provider'), + title: `Add ${capitalize(identifier)}`, withCloseButton: true, classNames: { modal: 'bg-transparent text-textColor', diff --git a/apps/frontend/src/components/launches/web3/providers/moltbook.provider.tsx b/apps/frontend/src/components/launches/web3/providers/moltbook.provider.tsx new file mode 100644 index 00000000..479b5e23 --- /dev/null +++ b/apps/frontend/src/components/launches/web3/providers/moltbook.provider.tsx @@ -0,0 +1,174 @@ +'use client'; + +import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; +import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { timer } from '@gitroom/helpers/utils/timer'; +import { Input } from '@gitroom/react/form/input'; +import { Button } from '@gitroom/react/form/button'; +import copy from 'copy-to-clipboard'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +export const MoltbookProvider: FC<Web3ProviderInterface> = (props) => { + const { onComplete, nonce } = props; + const fetch = useFetch(); + const stop = useRef(false); + const [step, setStep] = useState<'init' | 'registering' | 'waiting' | 'error'>('init'); + const [agentName, setAgentName] = useState(''); + const [agentDescription, setAgentDescription] = useState(''); + const [claimUrl, setClaimUrl] = useState(''); + const [apiKey, setApiKey] = useState(''); + const [error, setError] = useState(''); + const toaster = useToaster(); + const t = useT(); + + const register = async () => { + if (!agentName.trim()) { + toaster.show('Please enter an agent name', 'warning'); + return; + } + + setStep('registering'); + setError(''); + + try { + const response = await fetch('/integrations/moltbook/register', { + method: 'POST', + body: JSON.stringify({ + name: agentName.trim(), + description: agentDescription.trim() || 'Postiz social media scheduler', + }), + }); + + const data = await response.json(); + + if (data.error) { + setError(data.error); + setStep('error'); + return; + } + + setApiKey(data.apiKey); + setClaimUrl(data.claimUrl); + setStep('waiting'); + + pollForClaim(data.apiKey); + } catch (err) { + setError('Failed to register agent'); + setStep('error'); + } + }; + + const pollForClaim = async (key: string) => { + stop.current = false; + + while (!stop.current) { + try { + const response = await fetch(`/integrations/moltbook/status?apiKey=${encodeURIComponent(key)}`); + const data = await response.json(); + + if (data.claimed) { + onComplete(key, nonce); + return; + } + } catch (err) { + // Continue polling + } + + await timer(3000); + } + }; + + const copyClaimUrl = useCallback(() => { + copy(claimUrl); + toaster.show('Claim URL copied to clipboard', 'success'); + }, [claimUrl, toaster]); + + useEffect(() => { + return () => { + stop.current = true; + }; + }, []); + + return ( + <div className="justify-center items-center flex flex-col pt-[16px]"> + {step === 'init' && ( + <> + <div className="text-center mb-[16px]"> + {t('moltbook_register_description', 'Register your Moltbook agent to connect:')} + </div> + <div className="w-full space-y-[12px]"> + <Input + label={t('agent_name', 'Agent Name')} + value={agentName} + name="agentName" + disableForm={true} + onChange={(e) => setAgentName(e.target.value)} + placeholder="MyPostizAgent" + /> + <Input + label={t('description_optional', 'Description (optional)')} + value={agentDescription} + name="agentDescription" + disableForm={true} + onChange={(e) => setAgentDescription(e.target.value)} + placeholder="Social media scheduler" + /> + <Button className="w-full" onClick={register}> + {t('register_agent', 'Register Agent')} + </Button> + </div> + </> + )} + + {step === 'registering' && ( + <div className="text-center"> + {t('registering_agent', 'Registering agent...')} + </div> + )} + + {step === 'waiting' && ( + <div className="w-full text-center"> + <div className="mb-[16px]"> + {t('moltbook_claim_instructions', 'Please visit the claim URL to verify your agent:')} + </div> + <div className="flex gap-[8px]"> + <div className="flex-1"> + <Input + label="" + value={claimUrl} + name="claimUrl" + disableForm={true} + readOnly + /> + </div> + <Button onClick={copyClaimUrl}>{t('copy', 'Copy')}</Button> + </div> + <div className="mt-[16px] text-sm opacity-70"> + {t('waiting_for_claim', 'Waiting for you to claim your agent...')} + </div> + <div className="mt-[8px]"> + <a + href={claimUrl} + target="_blank" + rel="noopener noreferrer" + className="text-blue-500 hover:underline" + > + {t('open_claim_page', 'Open claim page')} + </a> + </div> + </div> + )} + + {step === 'error' && ( + <div className="w-full text-center"> + <div className="text-red-500 mb-[16px]">{error}</div> + <Button onClick={() => setStep('init')}> + {t('try_again', 'Try Again')} + </Button> + </div> + )} + </div> + ); +}; diff --git a/apps/frontend/src/components/launches/web3/web3.list.tsx b/apps/frontend/src/components/launches/web3/web3.list.tsx index 278da2dc..5e68f261 100644 --- a/apps/frontend/src/components/launches/web3/web3.list.tsx +++ b/apps/frontend/src/components/launches/web3/web3.list.tsx @@ -2,6 +2,7 @@ import { FC } from 'react'; import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; import { WrapcasterProvider } from '@gitroom/frontend/components/launches/web3/providers/wrapcaster.provider'; import { TelegramProvider } from '@gitroom/frontend/components/launches/web3/providers/telegram.provider'; +import { MoltbookProvider } from '@gitroom/frontend/components/launches/web3/providers/moltbook.provider'; export const web3List: { identifier: string; component: FC<Web3ProviderInterface>; @@ -14,4 +15,8 @@ export const web3List: { identifier: 'wrapcast', component: WrapcasterProvider, }, + { + identifier: 'moltbook', + component: MoltbookProvider, + }, ]; diff --git a/apps/frontend/src/components/new-launch/providers/moltbook/moltbook.provider.tsx b/apps/frontend/src/components/new-launch/providers/moltbook/moltbook.provider.tsx new file mode 100644 index 00000000..a6e2fb9b --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/moltbook/moltbook.provider.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { FC } from 'react'; +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { MoltbookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/moltbook.dto'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { Input } from '@gitroom/react/form/input'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +const MoltbookSettings: FC = () => { + const form = useSettings(); + const t = useT(); + + return ( + <div> + <Input + label={t('submolt', 'Submolt')} + placeholder="general" + {...form.register('submolt')} + /> + </div> + ); +}; + +export default withProvider({ + postComment: PostComment.COMMENT, + minimumCharacters: [], + SettingsComponent: MoltbookSettings, + CustomPreviewComponent: undefined, + dto: MoltbookDto, + maximumCharacters: 300, +}); diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 29a71721..09fbc0fa 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -35,6 +35,7 @@ import { PostComment } from '@gitroom/frontend/components/new-launch/providers/h import WordpressProvider from '@gitroom/frontend/components/new-launch/providers/wordpress/wordpress.provider'; import ListmonkProvider from '@gitroom/frontend/components/new-launch/providers/listmonk/listmonk.provider'; import GmbProvider from '@gitroom/frontend/components/new-launch/providers/gmb/gmb.provider'; +import MoltbookProvider from '@gitroom/frontend/components/new-launch/providers/moltbook/moltbook.provider'; export const Providers = [ { @@ -153,6 +154,10 @@ export const Providers = [ identifier: 'gmb', component: GmbProvider, }, + { + identifier: 'moltbook', + component: MoltbookProvider, + } ]; export const ShowAllProviders = forwardRef((props, ref) => { const { date, current, global, selectedIntegrations, allIntegrations } = diff --git a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts index 2e5ce3ea..21001e4c 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts @@ -8,7 +8,6 @@ import { IsNumber, IsOptional, IsString, - MinLength, Validate, ValidateIf, ValidateNested, diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index a0c7f98c..2bce9a69 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -20,6 +20,7 @@ import { ListmonkDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-sett import { GmbSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/gmb.settings.dto'; import { FarcasterDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/farcaster.dto'; import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/facebook.dto'; +import { MoltbookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/moltbook.dto'; export type ProviderExtension<T extends string, M> = { __type: T } & M; export type AllProvidersSettings = @@ -51,6 +52,7 @@ export type AllProvidersSettings = | ProviderExtension<'bluesky', None> | ProviderExtension<'telegram', None> | ProviderExtension<'nostr', None> + | ProviderExtension<'moltbook', MoltbookDto> | ProviderExtension<'vk', None>; type None = NonNullable<unknown>; @@ -86,6 +88,7 @@ export const allProviders = (setEmpty?: any) => { { value: setEmpty, name: 'telegram' }, { value: setEmpty, name: 'nostr' }, { value: setEmpty, name: 'vk' }, + { value: MoltbookDto, name: 'moltbook' }, ].filter((f) => f.value); }; diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/moltbook.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/moltbook.dto.ts new file mode 100644 index 00000000..2d697797 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/moltbook.dto.ts @@ -0,0 +1,8 @@ +import { IsDefined, IsString, MinLength } from 'class-validator'; + +export class MoltbookDto { + @MinLength(1) + @IsDefined() + @IsString() + submolt: string; +} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 4e1d2f4e..0bb78338 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -32,6 +32,7 @@ import { GmbProvider } from '@gitroom/nestjs-libraries/integrations/social/gmb.p import { KickProvider } from '@gitroom/nestjs-libraries/integrations/social/kick.provider'; import { TwitchProvider } from '@gitroom/nestjs-libraries/integrations/social/twitch.provider'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { MoltbookProvider } from '@gitroom/nestjs-libraries/integrations/social/moltbook.provider'; export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ new XProvider(), @@ -63,6 +64,7 @@ export const socialIntegrationList: Array<SocialAbstract & SocialProvider> = [ new HashnodeProvider(), new WordpressProvider(), new ListmonkProvider(), + new MoltbookProvider(), // new MastodonCustomProvider(), ]; diff --git a/libraries/nestjs-libraries/src/integrations/social/moltbook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/moltbook.provider.ts new file mode 100644 index 00000000..50d44445 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/moltbook.provider.ts @@ -0,0 +1,195 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import axios from 'axios'; + +const MOLTBOOK_API_BASE = 'https://www.moltbook.com/api/v1'; + +export class MoltbookProvider extends SocialAbstract implements SocialProvider { + override maxConcurrentJob = 100; // Moltbook: 100 requests/minute + identifier = 'moltbook'; + name = 'Moltbook'; + isBetweenSteps = false; + scopes = [] as string[]; + isWeb3 = true; + editor = 'normal' as const; + + maxLength() { + return 300; + } + + async refreshToken(refreshToken: string): Promise<AuthTokenDetails> { + return { + refreshToken: '', + expiresIn: 0, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + }; + } + + async generateAuthUrl() { + const state = makeId(6); + return { + url: state, + codeVerifier: makeId(10), + state, + }; + } + + async registerAgent(name: string, description: string) { + const response = await axios.post( + `${MOLTBOOK_API_BASE}/agents/register`, + { name, description }, + { headers: { 'Content-Type': 'application/json' } } + ); + + if (!response.data.success) { + throw new Error(response.data.error || 'Registration failed'); + } + + return response.data.agent; + } + + async checkAgentStatus(apiKey: string) { + const response = await axios.get(`${MOLTBOOK_API_BASE}/agents/status`, { + headers: { Authorization: `Bearer ${apiKey}` }, + }); + + return response.data; + } + + async getAgentProfile(apiKey: string) { + const response = await axios.get(`${MOLTBOOK_API_BASE}/agents/me`, { + headers: { Authorization: `Bearer ${apiKey}` }, + }); + + if (!response.data.success) { + throw new Error(response.data.error || 'Failed to get profile'); + } + + return response.data.agent; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const apiKey = params.code; + + const profile = await this.getAgentProfile(apiKey); + + return { + id: profile.name || profile.id, + name: profile.display_name || profile.name, + accessToken: apiKey, + refreshToken: '', + expiresIn: dayjs().add(200, 'year').unix() - dayjs().unix(), + picture: '', + username: profile.name, + }; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const results: PostResponse[] = []; + + for (const post of postDetails) { + const postData: { + submolt: string; + title: string; + content?: string; + url?: string; + } = { + submolt: post.settings?.submolt || 'general', + title: post.message.slice(0, 100), + content: post.message, + }; + + const response = await axios.post( + `${MOLTBOOK_API_BASE}/posts`, + postData, + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + } + ); + + if (!response.data.success) { + throw new Error(response.data.error || 'Failed to create post'); + } + + const postId = response.data.post.id; + results.push({ + id: post.id, + postId: String(postId), + releaseURL: `https://www.moltbook.com/post/${postId}`, + status: 'completed', + }); + } + + return results; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise<PostResponse[]> { + const results: PostResponse[] = []; + + for (const post of postDetails) { + const commentData: { content: string; parent_id?: string } = { + content: post.message, + }; + + if (lastCommentId) { + commentData.parent_id = lastCommentId; + } + + const response = await axios.post( + `${MOLTBOOK_API_BASE}/posts/${postId}/comments`, + commentData, + { + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + } + ); + + if (!response.data.success) { + throw new Error(response.data.error || 'Failed to create comment'); + } + + const commentId = response.data.comment.id; + results.push({ + id: post.id, + postId: String(commentId), + releaseURL: `https://www.moltbook.com/post/${postId}`, + status: 'completed', + }); + } + + return results; + } +} From 437a3b51435fa6c710a8985a927907ed40090e33 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 2 Feb 2026 22:15:08 +0700 Subject: [PATCH 205/340] feat: tracking --- .../backend/src/api/routes/auth.controller.ts | 2 +- .../backend/src/services/auth/auth.service.ts | 31 +++++++++++++++++-- apps/frontend/src/app/(app)/layout.tsx | 9 ++++++ .../frontend/src/components/auth/register.tsx | 11 ++++--- .../billing/first.billing.component.tsx | 6 ++++ .../src/dtos/auth/create.org.user.dto.ts | 2 ++ .../src/dtos/auth/login.user.dto.ts | 2 ++ .../src/dtos/billing/billing.subscribe.dto.ts | 3 ++ 8 files changed, 59 insertions(+), 7 deletions(-) diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts index fb41f672..a154c18e 100644 --- a/apps/backend/src/api/routes/auth.controller.ts +++ b/apps/backend/src/api/routes/auth.controller.ts @@ -103,7 +103,7 @@ export class AuthController { } } - Sentry.metrics.count("new_user", 1); + Sentry.metrics.count('new_user', 1); response.header('onboarding', 'true'); response.status(200).json({ register: true, diff --git a/apps/backend/src/services/auth/auth.service.ts b/apps/backend/src/services/auth/auth.service.ts index c01c2ac5..2a420a4d 100644 --- a/apps/backend/src/services/auth/auth.service.ts +++ b/apps/backend/src/services/auth/auth.service.ts @@ -162,16 +162,43 @@ export class AuthService { password: '', provider, providerId: providerUser.id, + datafast_visitor_id: body.datafast_visitor_id, }, ip, userAgent ); + this._track(providerUser.email, body.datafast_visitor_id).catch( + (err) => {} + ); + await NewsletterService.register(providerUser.email); return create.users[0].user; } + private async _track(email: string, datafast_visitor_id: string) { + if (email && datafast_visitor_id && process.env.DATAFAST_API_KEY) { + try { + await fetch('https://datafa.st/api/v1/goals', { + method: 'POST', + headers: { + Authorization: `Bearer ${process.env.DATAFAST_API_KEY}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + datafast_visitor_id: datafast_visitor_id, + name: 'newsletter_signup', + metadata: { + name: 'Elon Musk', + email: 'musk@x.com', + }, + }), + }); + } catch (err) {} + } + } + async forgot(email: string) { const user = await this._userService.getUserByEmail(email); if (!user || user.providerName !== Provider.LOCAL) { @@ -224,7 +251,7 @@ export class AuthService { async resendActivationEmail(email: string) { const user = await this._userService.getUserByEmail(email); - + if (!user) { throw new Error('User not found'); } @@ -234,7 +261,7 @@ export class AuthService { } const jwt = await this.jwt(user); - + await this._emailService.sendEmail( user.email, 'Activate your account', diff --git a/apps/frontend/src/app/(app)/layout.tsx b/apps/frontend/src/app/(app)/layout.tsx index 4a60fc05..a6a62c02 100644 --- a/apps/frontend/src/app/(app)/layout.tsx +++ b/apps/frontend/src/app/(app)/layout.tsx @@ -18,6 +18,7 @@ import { FacebookComponent } from '@gitroom/frontend/components/layout/facebook. import { headers } from 'next/headers'; import { headerName } from '@gitroom/react/translation/i18n.config'; import { HtmlComponent } from '@gitroom/frontend/components/layout/html.component'; +import Script from 'next/script'; // import dynamicLoad from 'next/dynamic'; // const SetTimezone = dynamicLoad( // () => import('@gitroom/frontend/components/layout/set.timezone'), @@ -41,6 +42,14 @@ export default async function AppLayout({ children }: { children: ReactNode }) { <html> <head> <link rel="icon" href="/favicon.ico" sizes="any" /> + {!!process.env.DATAFAST_WEBSITE_ID && ( + <Script + data-website-id={process.env.DATAFAST_WEBSITE_ID} + data-domain="postiz.com" + src="https://datafa.st/js/script.js" + strategy="afterInteractive" + /> + )} </head> <body className={clsx(jakartaSans.className, 'dark text-primary !bg-primary')} diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 592c354e..3557bcb4 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -22,6 +22,7 @@ import { FarcasterProvider } from '@gitroom/frontend/components/auth/providers/f import dynamic from 'next/dynamic'; import { WalletUiProvider } from '@gitroom/frontend/components/auth/providers/placeholder/wallet.ui.provider'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import useCookie from 'react-use-cookie'; const WalletProvider = dynamic( () => import('@gitroom/frontend/components/auth/providers/wallet.provider'), { @@ -94,6 +95,7 @@ export function RegisterAfter({ const router = useRouter(); const fireEvents = useFireEvents(); const track = useTrack(); + const [datafast_visitor_id] = useCookie('datafast_visitor_id'); const isAfterProvider = useMemo(() => { return !!token && !!provider; }, [token, provider]); @@ -114,6 +116,7 @@ export function RegisterAfter({ method: 'POST', body: JSON.stringify({ ...data, + datafast_visitor_id, }), }) .then(async (response) => { @@ -151,7 +154,9 @@ export function RegisterAfter({ {t('sign_up', 'Sign Up')} </h1> </div> - <div className="text-[14px] mt-[32px] mb-[12px]">{t('continue_with', 'Continue With')}</div> + <div className="text-[14px] mt-[32px] mb-[12px]"> + {t('continue_with', 'Continue With')} + </div> <div className="flex flex-col"> {!isAfterProvider && (!isGeneral ? ( @@ -173,9 +178,7 @@ export function RegisterAfter({ <div className={`absolute z-[1] justify-center items-center w-full start-0 -top-[4px] flex`} > - <div className="px-[16px]"> - {t('or', 'or')} - </div> + <div className="px-[16px]">{t('or', 'or')}</div> </div> </div> )} diff --git a/apps/frontend/src/components/billing/first.billing.component.tsx b/apps/frontend/src/components/billing/first.billing.component.tsx index d945b5da..01049e9c 100644 --- a/apps/frontend/src/components/billing/first.billing.component.tsx +++ b/apps/frontend/src/components/billing/first.billing.component.tsx @@ -25,6 +25,7 @@ import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useDubClickId } from '@gitroom/frontend/components/layout/dubAnalytics'; import Image from 'next/image'; import { useModals } from '@gitroom/frontend/components/layout/new-modal'; +import useCookie from 'react-use-cookie'; const ModeComponent = dynamic( () => import('@gitroom/frontend/components/layout/mode.component'), @@ -53,6 +54,8 @@ export const FirstBillingComponent = () => { const fetch = useFetch(); const modals = useModals(); const t = useT(); + const [datafast_visitor_id] = useCookie('datafast_visitor_id', ''); + const [datafast_session_id] = useCookie('datafast_session_id', ''); useEffect(() => { setStripe(loadStripe(stripeClient)); @@ -65,6 +68,9 @@ export const FirstBillingComponent = () => { body: JSON.stringify({ billing: tier, period: period, + ...(datafast_visitor_id && datafast_session_id + ? { datafast_visitor_id, datafast_session_id } + : {}), ...(dub ? { dub } : {}), }), }) diff --git a/libraries/nestjs-libraries/src/dtos/auth/create.org.user.dto.ts b/libraries/nestjs-libraries/src/dtos/auth/create.org.user.dto.ts index 6421806b..42fad6f0 100644 --- a/libraries/nestjs-libraries/src/dtos/auth/create.org.user.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/auth/create.org.user.dto.ts @@ -35,4 +35,6 @@ export class CreateOrgUserDto { @MinLength(3) @MaxLength(128) company: string; + + datafast_visitor_id: string; } diff --git a/libraries/nestjs-libraries/src/dtos/auth/login.user.dto.ts b/libraries/nestjs-libraries/src/dtos/auth/login.user.dto.ts index f3ea9ded..c7bb8b9c 100644 --- a/libraries/nestjs-libraries/src/dtos/auth/login.user.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/auth/login.user.dto.ts @@ -26,4 +26,6 @@ export class LoginUserDto { @IsEmail() @IsDefined() email: string; + + datafast_visitor_id: string; } diff --git a/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts b/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts index 0d4a3a91..f1247f06 100644 --- a/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/billing/billing.subscribe.dto.ts @@ -10,4 +10,7 @@ export class BillingSubscribeDto { utm: string; dub: string; + + datafast_session_id: string; + datafast_visitor_id: string; } From 656f29eb34d6a4b703403d17e935e9438d0ff6ce Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 2 Feb 2026 22:34:21 +0700 Subject: [PATCH 206/340] feat: activate --- apps/backend/src/api/routes/auth.controller.ts | 3 ++- apps/backend/src/services/auth/auth.service.ts | 16 ++++++++++------ .../src/components/auth/after.activate.tsx | 3 +++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/apps/backend/src/api/routes/auth.controller.ts b/apps/backend/src/api/routes/auth.controller.ts index a154c18e..4f87d07d 100644 --- a/apps/backend/src/api/routes/auth.controller.ts +++ b/apps/backend/src/api/routes/auth.controller.ts @@ -207,9 +207,10 @@ export class AuthController { @Post('/activate') async activate( @Body('code') code: string, + @Body('datafast_visitor_id') datafast_visitor_id: string, @Res({ passthrough: false }) response: Response ) { - const activate = await this._authService.activate(code); + const activate = await this._authService.activate(code, datafast_visitor_id); if (!activate) { return response.status(200).json({ can: false }); } diff --git a/apps/backend/src/services/auth/auth.service.ts b/apps/backend/src/services/auth/auth.service.ts index 2a420a4d..3b639fb6 100644 --- a/apps/backend/src/services/auth/auth.service.ts +++ b/apps/backend/src/services/auth/auth.service.ts @@ -168,7 +168,7 @@ export class AuthService { userAgent ); - this._track(providerUser.email, body.datafast_visitor_id).catch( + this._track('register', providerUser.email, body.datafast_visitor_id).catch( (err) => {} ); @@ -177,7 +177,11 @@ export class AuthService { return create.users[0].user; } - private async _track(email: string, datafast_visitor_id: string) { + private async _track( + name: string, + email: string, + datafast_visitor_id: string + ) { if (email && datafast_visitor_id && process.env.DATAFAST_API_KEY) { try { await fetch('https://datafa.st/api/v1/goals', { @@ -188,10 +192,9 @@ export class AuthService { }, body: JSON.stringify({ datafast_visitor_id: datafast_visitor_id, - name: 'newsletter_signup', + name: name, metadata: { - name: 'Elon Musk', - email: 'musk@x.com', + email, }, }), }); @@ -229,7 +232,7 @@ export class AuthService { return this._userService.updatePassword(user.id, body.password); } - async activate(code: string) { + async activate(code: string, tracking: string) { const user = AuthChecker.verifyJWT(code) as { id: string; activated: boolean; @@ -242,6 +245,7 @@ export class AuthService { } await this._userService.activateUser(user.id); user.activated = true; + this._track('register', user.email, tracking).catch((err) => {}); await NewsletterService.register(user.email); return this.jwt(user as any); } diff --git a/apps/frontend/src/components/auth/after.activate.tsx b/apps/frontend/src/components/auth/after.activate.tsx index 9b59ce3d..ef2dee1f 100644 --- a/apps/frontend/src/components/auth/after.activate.tsx +++ b/apps/frontend/src/components/auth/after.activate.tsx @@ -6,12 +6,14 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { useParams } from 'next/navigation'; import Link from 'next/link'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import useCookie from 'react-use-cookie'; export const AfterActivate = () => { const fetch = useFetch(); const params = useParams(); const [showLoader, setShowLoader] = useState(true); const run = useRef(false); const t = useT(); + const [datafast_visitor_id] = useCookie('datafast_visitor_id'); useEffect(() => { if (!run.current) { @@ -26,6 +28,7 @@ export const AfterActivate = () => { method: 'POST', body: JSON.stringify({ code: params.code, + datafast_visitor_id, }), headers: { 'Content-Type': 'application/json', From bb9e7311741e317b5e87a644b6d5e48a209bcd87 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Mon, 2 Feb 2026 23:01:33 +0700 Subject: [PATCH 207/340] feat: metadata --- libraries/nestjs-libraries/src/services/stripe.service.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index 4a9bbb88..96aaa8ec 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -450,6 +450,14 @@ export class StripeService { ud, }, }, + ...(body.datafast_session_id && body.datafast_visitor_id + ? { + metadata: { + datafast_visitor_id: body.datafast_visitor_id, + datafast_session_id: body.datafast_session_id, + }, + } + : {}), allow_promotion_codes: body.period === 'MONTHLY', line_items: [ { From f5ef80546edbf6a1882b3e0f6ffefbb71297b814 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 3 Feb 2026 21:12:28 +0700 Subject: [PATCH 208/340] feat: enterprise --- .../src/api/routes/enterprise.controller.ts | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 apps/backend/src/api/routes/enterprise.controller.ts diff --git a/apps/backend/src/api/routes/enterprise.controller.ts b/apps/backend/src/api/routes/enterprise.controller.ts new file mode 100644 index 00000000..06abf7d6 --- /dev/null +++ b/apps/backend/src/api/routes/enterprise.controller.ts @@ -0,0 +1,78 @@ +import { Body, Controller, Param, Post, Res } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { AuthService } from '@gitroom/helpers/auth/auth.service'; +import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; +import { IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; + +@ApiTags('Enterprise') +@Controller('/enterprise') +export class EnterpriseController { + constructor( + private _integrationManager: IntegrationManager, + private _organizationService: OrganizationService + ) {} + + @Post('/test') + async test(@Body('params') params: string) { + try { + const load = AuthService.verifyJWT(params) as { + apiKey: string; + }; + + const org = await this._organizationService.getOrgByApiKey(load.apiKey); + return { + connection: !!org, + }; + } catch (err) { + return { connection: false }; + } + } + + @Post('/url') + async redirectParams(@Body('params') params: string) { + try { + const load = AuthService.verifyJWT(params) as { + redirectUrl: string; + apiKey: string; + refreshId?: string; + provider: string; + }; + + if (!load || !load.redirectUrl || !load.apiKey || !load.provider) { + return; + } + + const org = await this._organizationService.getOrgByApiKey(load.apiKey); + + if (!org) { + throw new Error('Organization not found'); + } + + if ( + !this._integrationManager + .getAllowedSocialsIntegrations() + .includes(load.provider) + ) { + throw new Error('Integration not allowed'); + } + + const integrationProvider = this._integrationManager.getSocialIntegration( + load.provider + ); + + const { codeVerifier, state, url } = + await integrationProvider.generateAuthUrl(); + + if (load.refreshId) { + await ioRedis.set(`refresh:${state}`, load.refreshId, 'EX', 3600); + } + + await ioRedis.set(`redirect:${state}`, load.redirectUrl, 'EX', 3600); + await ioRedis.set(`organization:${state}`, org.id, 'EX', 3600); + await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 3600); + + return url; + } catch (err) {} + } +} From 638debdaa445851f694fa86ff1a51a31801b65ae Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Tue, 3 Feb 2026 21:34:23 +0700 Subject: [PATCH 209/340] feat: enterprise --- apps/backend/src/api/api.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/backend/src/api/api.module.ts b/apps/backend/src/api/api.module.ts index 80b9a1ec..2abae59c 100644 --- a/apps/backend/src/api/api.module.ts +++ b/apps/backend/src/api/api.module.ts @@ -32,6 +32,7 @@ import { SetsController } from '@gitroom/backend/api/routes/sets.controller'; import { ThirdPartyController } from '@gitroom/backend/api/routes/third-party.controller'; import { MonitorController } from '@gitroom/backend/api/routes/monitor.controller'; import { NoAuthIntegrationsController } from '@gitroom/backend/api/routes/no.auth.integrations.controller'; +import { EnterpriseController } from '@gitroom/backend/api/routes/enterprise.controller'; const authenticatedController = [ UsersController, @@ -57,6 +58,7 @@ const authenticatedController = [ AuthController, PublicController, MonitorController, + EnterpriseController, NoAuthIntegrationsController, ...authenticatedController, ], From 6d173ab7cdf0e1a76fd01178b97dbf184a10c4a6 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 4 Feb 2026 12:32:00 +0700 Subject: [PATCH 210/340] feat: enterprise --- .../src/api/routes/enterprise.controller.ts | 16 ++++---- .../organizations/organization.repository.ts | 38 +++++++++++++++++++ .../organizations/organization.service.ts | 4 ++ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/apps/backend/src/api/routes/enterprise.controller.ts b/apps/backend/src/api/routes/enterprise.controller.ts index 06abf7d6..a594f9fe 100644 --- a/apps/backend/src/api/routes/enterprise.controller.ts +++ b/apps/backend/src/api/routes/enterprise.controller.ts @@ -13,17 +13,17 @@ export class EnterpriseController { private _organizationService: OrganizationService ) {} - @Post('/test') - async test(@Body('params') params: string) { + @Post('/create-user') + async createUser(@Body('params') params: string) { try { - const load = AuthService.verifyJWT(params) as { - apiKey: string; + const { id, name, saasName, email } = AuthService.verifyJWT(params) as { + id: string; + name: string; + email: string; + saasName: string; }; - const org = await this._organizationService.getOrgByApiKey(load.apiKey); - return { - connection: !!org, - }; + return this._organizationService.createMaxUser(id, name, saasName, email); } catch (err) { return { connection: false }; } diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index e6386e5e..6cd72bae 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -13,6 +13,44 @@ export class OrganizationRepository { private _user: PrismaRepository<'user'> ) {} + createMaxUser(id: string, name: string, saasName: string, email: string) { + return this._organization.model.organization.create({ + select: { + id: true, + apiKey: true, + }, + data: { + name: name ? `${name}###${id}` : `Unnamed User###${id}`, + isTrailing: false, + subscription: { + create: { + totalChannels: 1000000, + subscriptionTier: 'ULTIMATE', + isLifetime: true, + period: 'YEARLY', + }, + }, + users: { + create: { + role: Role.SUPERADMIN, + user: { + create: { + activated: true, + email: email + ? email.split('@').join(`+${saasName}@`) + : `${saasName}+` + makeId(10) + '@postiz.com', + name: name ? `${name}###${id}` : `Unnamed User###${id}`, + providerName: 'LOCAL', + password: AuthService.hashPassword(makeId(500)), + timezone: 0, + }, + }, + }, + }, + }, + }); + } + getOrgByApiKey(api: string) { return this._organization.model.organization.findFirst({ where: { diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts index e914e534..54ff6807 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.service.ts @@ -32,6 +32,10 @@ export class OrganizationService { return this._organizationRepository.getCount(); } + async createMaxUser(id: string, name: string, saasName: string, email: string) { + return this._organizationRepository.createMaxUser(id, name, saasName, email); + } + addUserToOrg( userId: string, id: string, From 979b81d6d0d732c889ce344acf23effdc2f3b602 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 4 Feb 2026 12:32:44 +0700 Subject: [PATCH 211/340] feat: enterprise --- .../backend/src/api/routes/enterprise.controller.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/api/routes/enterprise.controller.ts b/apps/backend/src/api/routes/enterprise.controller.ts index a594f9fe..ac3780af 100644 --- a/apps/backend/src/api/routes/enterprise.controller.ts +++ b/apps/backend/src/api/routes/enterprise.controller.ts @@ -23,9 +23,18 @@ export class EnterpriseController { saasName: string; }; - return this._organizationService.createMaxUser(id, name, saasName, email); + try { + return await this._organizationService.createMaxUser( + id, + name, + saasName, + email + ); + } catch (err) { + return { create: false }; + } } catch (err) { - return { connection: false }; + return { success: false }; } } From e7999ea1512f51c4378085b77e563a3b3b22fcd5 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 4 Feb 2026 13:01:10 +0700 Subject: [PATCH 212/340] feat: apiKey --- .../src/database/prisma/organizations/organization.repository.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts index 6cd72bae..29533892 100644 --- a/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/organizations/organization.repository.ts @@ -21,6 +21,7 @@ export class OrganizationRepository { }, data: { name: name ? `${name}###${id}` : `Unnamed User###${id}`, + apiKey: AuthService.fixedEncryption(makeId(20)), isTrailing: false, subscription: { create: { From 70be520c0bfebab5a66155ea3e92524d4c3fb6a1 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 4 Feb 2026 15:14:01 +0700 Subject: [PATCH 213/340] feat: public list integrations --- apps/backend/src/api/routes/integrations.controller.ts | 4 ---- .../src/api/routes/no.auth.integrations.controller.ts | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/backend/src/api/routes/integrations.controller.ts b/apps/backend/src/api/routes/integrations.controller.ts index 3c681245..7463136a 100644 --- a/apps/backend/src/api/routes/integrations.controller.ts +++ b/apps/backend/src/api/routes/integrations.controller.ts @@ -42,10 +42,6 @@ export class IntegrationsController { private _postService: PostsService, private _refreshIntegrationService: RefreshIntegrationService ) {} - @Get('/') - getIntegrations() { - return this._integrationManager.getAllIntegrations(); - } @Get('/:identifier/internal-plugs') getInternalPlugs(@Param('identifier') identifier: string) { diff --git a/apps/backend/src/api/routes/no.auth.integrations.controller.ts b/apps/backend/src/api/routes/no.auth.integrations.controller.ts index 673e77e7..9b51c81e 100644 --- a/apps/backend/src/api/routes/no.auth.integrations.controller.ts +++ b/apps/backend/src/api/routes/no.auth.integrations.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, + Get, HttpException, Param, Post, @@ -33,6 +34,11 @@ export class NoAuthIntegrationsController { private _organizationService: OrganizationService ) {} + @Get('/') + getIntegrations() { + return this._integrationManager.getAllIntegrations(); + } + @Post('/social-connect/:integration') @CheckPolicies([AuthorizationActions.Create, Sections.CHANNEL]) @UseFilters(new NotEnoughScopesFilter()) From 34243312ad4f9a2cd6593aa072c6fbdf0821905a Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 4 Feb 2026 15:43:22 +0700 Subject: [PATCH 214/340] feat: generate url --- .../src/api/routes/enterprise.controller.ts | 2 ++ .../routes/no.auth.integrations.controller.ts | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/apps/backend/src/api/routes/enterprise.controller.ts b/apps/backend/src/api/routes/enterprise.controller.ts index ac3780af..6be0fc7c 100644 --- a/apps/backend/src/api/routes/enterprise.controller.ts +++ b/apps/backend/src/api/routes/enterprise.controller.ts @@ -46,6 +46,7 @@ export class EnterpriseController { apiKey: string; refreshId?: string; provider: string; + webhookUrl: string; }; if (!load || !load.redirectUrl || !load.apiKey || !load.provider) { @@ -77,6 +78,7 @@ export class EnterpriseController { await ioRedis.set(`refresh:${state}`, load.refreshId, 'EX', 3600); } + await ioRedis.set(`webhookUrl:${state}`, load.webhookUrl, 'EX', 3600); await ioRedis.set(`redirect:${state}`, load.redirectUrl, 'EX', 3600); await ioRedis.set(`organization:${state}`, org.id, 'EX', 3600); await ioRedis.set(`login:${state}`, codeVerifier, 'EX', 3600); diff --git a/apps/backend/src/api/routes/no.auth.integrations.controller.ts b/apps/backend/src/api/routes/no.auth.integrations.controller.ts index 9b51c81e..832505ba 100644 --- a/apps/backend/src/api/routes/no.auth.integrations.controller.ts +++ b/apps/backend/src/api/routes/no.auth.integrations.controller.ts @@ -262,10 +262,33 @@ export class NoAuthIntegrationsController { } } + const webhookUrl = await ioRedis.get(`webhookUrl:${body.state}`); + if (webhookUrl) { + try { + await fetch(webhookUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + params: AuthService.signJWT({ + apiKey: org.apiKey, + }), + }), + }); + } catch (err) {} + + await ioRedis.del(`webhookUrl:${body.state}`); + } + + const returnURL = await ioRedis.get(`redirect:${body.state}`); + if (returnURL) { + await ioRedis.del(`redirect:${body.state}`); + } + return { ...createUpdate, onboarding: onboarding === 'true', pages, + ...(returnURL ? { returnURL } : {}), }; } From c970bfab95a86d668adeb802124b213eb3451dc5 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Wed, 4 Feb 2026 20:36:46 +0700 Subject: [PATCH 215/340] feat: hostinger --- .github/sponsors/hostinger.png | Bin 0 -> 70777 bytes README.md | 6 ++++++ 2 files changed, 6 insertions(+) create mode 100644 .github/sponsors/hostinger.png diff --git a/.github/sponsors/hostinger.png b/.github/sponsors/hostinger.png new file mode 100644 index 0000000000000000000000000000000000000000..cf82ac5e11a39892e5c7af606afd59e8d27085bb GIT binary patch literal 70777 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�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 F{0}BJ=6V1C literal 0 HcmV?d00001 diff --git a/README.md b/README.md index de079adf..cf45e2a3 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,12 @@ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | ![Image 3](https://github.com/user-attachments/assets/d51786ee-ddd8-4ef8-8138-5192e9cfe7c3) | ![Image 4](https://github.com/user-attachments/assets/91f83c89-22f6-43d6-b7aa-d2d3378289fb) | +### Our Sponsors + +| Sponsor | Logo | Description | +|---------|:-----------------------------------------------------------------------:|-----------------| +| [Hostinger](https://www.hostinger.com/vps-hosting?ref=dokploy) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="200"/> | Hostinger is on a mission to make online success possible for anyone – from developers to aspiring bloggers and business owners | + # Intro - Schedule all your social media posts (many AI features) From 0928f762848a3a3b63c938d98dd985440873e4e3 Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:38:02 +0700 Subject: [PATCH 216/340] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cf45e2a3..9b9fbf2f 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ | Sponsor | Logo | Description | |---------|:-----------------------------------------------------------------------:|-----------------| -| [Hostinger](https://www.hostinger.com/vps-hosting?ref=dokploy) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="200"/> | Hostinger is on a mission to make online success possible for anyone – from developers to aspiring bloggers and business owners | +| [Hostinger](https://www.hostinger.com/vps-hosting?ref=dokploy) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="500"/> | Hostinger is on a mission to make online success\npossible for anyone – from developers to aspiring bloggers and business owners | # Intro From 5a193fd19e1fb3a40f39a2a480c561870a384708 Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:38:31 +0700 Subject: [PATCH 217/340] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b9fbf2f..b120615d 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ | Sponsor | Logo | Description | |---------|:-----------------------------------------------------------------------:|-----------------| -| [Hostinger](https://www.hostinger.com/vps-hosting?ref=dokploy) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="500"/> | Hostinger is on a mission to make online success\npossible for anyone – from developers to aspiring bloggers and business owners | +| [Hostinger](https://www.hostinger.com/?ref=postiz) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="500"/> | Hostinger is on a mission to make online success\npossible for anyone – from developers to aspiring bloggers and business owners | # Intro From 58bc35148c842ead565b58a7f3185fbb469e9fde Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:40:12 +0700 Subject: [PATCH 218/340] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b120615d..4c846bdd 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ | Sponsor | Logo | Description | |---------|:-----------------------------------------------------------------------:|-----------------| -| [Hostinger](https://www.hostinger.com/?ref=postiz) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="500"/> | Hostinger is on a mission to make online success\npossible for anyone – from developers to aspiring bloggers and business owners | +| [Hostinger](https://www.hostinger.com/?ref=postiz) | <img src=".github/sponsors/hostinger.png" alt="Hostinger" width="500"/> | Hostinger is on a mission to make online success possible for anyone – from developers to aspiring bloggers and business owners | # Intro From 650d92bb562c0ac32cc373c369e808fa50e1368c Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 5 Feb 2026 11:41:42 +0700 Subject: [PATCH 219/340] feat: allow mov --- apps/frontend/src/components/media/new.uploader.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index fe7172e1..84313613 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -56,7 +56,7 @@ export function useUppyUploader(props: { autoProceed: true, restrictions: { // maxNumberOfFiles: 5, - allowedFileTypes: allowedFileTypes.split(','), + // allowedFileTypes: allowedFileTypes.split(','), maxFileSize: 1000000000, // Default 1GB, but we'll override with custom validation }, }); @@ -82,7 +82,10 @@ export function useUppyUploader(props: { ]; } if (type === 'video/*') { - return ['video/mp4', 'video/mpeg']; + return ['video/mp4', 'video/mpeg', 'video/quicktime']; + } + if (type === 'video/mp4' && transloadit && transloadit.length > 0) { + return ['video/mp4', 'video/mpeg', 'video/quicktime']; } return [type]; }); From eccc767701b2a8e0e018d7c38b656859755bdeea Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Thu, 5 Feb 2026 14:15:35 +0700 Subject: [PATCH 220/340] feat: fix transloadit --- apps/frontend/src/components/media/new.uploader.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/frontend/src/components/media/new.uploader.tsx b/apps/frontend/src/components/media/new.uploader.tsx index 84313613..7f2b4bde 100644 --- a/apps/frontend/src/components/media/new.uploader.tsx +++ b/apps/frontend/src/components/media/new.uploader.tsx @@ -202,6 +202,7 @@ export function useUppyUploader(props: { props.onStart(); }); uppy2.on('complete', async (result) => { + console.log(result); for (const file of [...result.successful]) { uppy2.removeFile(file.id); } @@ -225,10 +226,13 @@ export function useUppyUploader(props: { // @ts-ignore const allRes = result.transloadit[0].results; const toSave = uniqBy<{ name: string; order: number }>( - (allRes[Object.keys(allRes)[0]] || []).flatMap((item: any) => ({ - name: item.url.split('/').pop(), - order: +item.user_meta.addedOrder, - })), + // @ts-ignore + Object.values(allRes).flatMap((p: any[]) => { + return p.flatMap((item) => ({ + name: item.url.split('/').pop(), + order: +item.user_meta.addedOrder, + })); + }), (item) => item.name ); From 572de8a2d09fb3ed304bf3fb16c89a7e0540b00b Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Thu, 5 Feb 2026 20:48:29 +0700 Subject: [PATCH 221/340] Revise Postiz-as-a-service to Enterprise offering Updated the Postiz-as-a-service section to reflect the enterprise offering and enhanced the description for clarity. --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 4c846bdd..c4637f7f 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,19 @@ <br /> +## New - Postiz-as-a-service - Enterprise (Cloud) + +Integrate powerful social media scheduling capabilities into your SaaS. <br />Multi-tenant architecture designed for SaaS companies who want to offer social media management to their users. +- **Skip App Approvals** - Use Postiz apps directly without going through lengthy social platform approval processes. Get the full power of Postiz instantly. +- **Multi-Tenant Architecture** - each of your customers gets their own isolated environment with separate accounts, channels, and team management. +- **Headless API** - Full REST API access to build your own frontend experience. Complete control over the user interface and branding. +- **Full OAuth Support** - Connect all major social platforms including Facebook, Instagram, Twitter, LinkedIn, TikTok, and more. + + +[Check it here](https://postiz.com/enterprise) + +<br /><br /> + ## 🔌 See the leading Postiz features <p align="center"> From c481add80b449df450df4d5ed41f4da92acdafb1 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 6 Feb 2026 09:40:55 +0700 Subject: [PATCH 222/340] feat: switch org --- .../src/components/layout/organization.selector.tsx | 1 + apps/frontend/tailwind.config.js | 7 +++++++ .../src/integrations/social/linkedin.provider.ts | 1 - 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/layout/organization.selector.tsx b/apps/frontend/src/components/layout/organization.selector.tsx index 5cb8cea3..a3e37ce6 100644 --- a/apps/frontend/src/components/layout/organization.selector.tsx +++ b/apps/frontend/src/components/layout/organization.selector.tsx @@ -51,6 +51,7 @@ export const OrganizationSelector: FC<{ asOpenSelect?: boolean }> = ({ {!asOpenSelect && ( <div className="flex items-center"> <svg + className={user?.tier.current === 'FREE' ? 'animate-bounce drop-shadow-glow': ''} width="24" height="24" viewBox="0 0 26 26" diff --git a/apps/frontend/tailwind.config.js b/apps/frontend/tailwind.config.js index cb2ccce8..1aa72aaf 100644 --- a/apps/frontend/tailwind.config.js +++ b/apps/frontend/tailwind.config.js @@ -144,6 +144,13 @@ module.exports = { menu: 'var(--menu-shadow)', previewShadow: 'var(--preview-box-shadow)', }, + dropShadow: { + glow: [ + '0 0 6px rgba(250,204,21,0.6)', + '0 0 12px rgba(250,204,21,0.5)', + '0 0 24px rgba(250,204,21,0.4)', + ], + }, // that is actual animation keyframes: (theme) => ({ fadeOut: { diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index b07b361c..a0be3438 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -15,7 +15,6 @@ import { LinkedinDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-sett import imageToPDF from 'image-to-pdf'; import { Readable } from 'stream'; import { Rules } from '@gitroom/nestjs-libraries/chat/rules.description.decorator'; -import { string } from 'yup'; @Rules( 'LinkedIn can have maximum one attachment when selecting video, when choosing a carousel on LinkedIn minimum amount of attachment must be two, and only pictures, if uploading a video, LinkedIn can have only one attachment' From 52ec0914001c5d1554b4983849ca9f80a7d50de7 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 6 Feb 2026 15:21:51 +0700 Subject: [PATCH 223/340] feat: wordpress --- .../new-launch/providers/wordpress/wordpress.provider.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx b/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx index 47fb98a7..fe3e993d 100644 --- a/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/wordpress/wordpress.provider.tsx @@ -9,6 +9,7 @@ import { Input } from '@gitroom/react/form/input'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import { WordpressPostType } from '@gitroom/frontend/components/new-launch/providers/wordpress/wordpress.post.type'; import { MediaComponent } from '@gitroom/frontend/components/media/media.component'; +import { WordpressDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/wordpress.dto'; const WordpressSettings: FC = () => { const form = useSettings(); @@ -29,7 +30,7 @@ export default withProvider({ minimumCharacters: [], SettingsComponent: WordpressSettings, CustomPreviewComponent: undefined, // WordpressPreview, - dto: undefined, + dto: WordpressDto, checkValidity: undefined, maximumCharacters: 100000, }); From ee8b26f3833d269acaba690441561ba5717d0a20 Mon Sep 17 00:00:00 2001 From: Emi Rexhepi <em.rexhepi@gmail.com> Date: Fri, 6 Feb 2026 15:30:02 +0100 Subject: [PATCH 224/340] fix(api): preserve file extension in upload-from-url endpoint The upload-from-url endpoint passed an empty originalname to uploadFile(), causing extname('') to return '' and files to be saved without an extension. Derive the extension from the response Content-Type header (or URL fallback) so files are stored with the correct extension on disk. Fixes gitroomhq/postiz-app#1147 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --- .../routes/v1/public.integrations.controller.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 2655f699..eb71201c 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -29,7 +29,7 @@ import { VideoFunctionDto } from '@gitroom/nestjs-libraries/dtos/videos/video.fu import { UploadDto } from '@gitroom/nestjs-libraries/dtos/media/upload.dto'; import axios from 'axios'; import { Readable } from 'stream'; -import { lookup } from 'mime-types'; +import { lookup, extension } from 'mime-types'; import * as Sentry from '@sentry/nestjs'; @ApiTags('Public API') @@ -73,17 +73,21 @@ export class PublicIntegrationsController { }); const buffer = Buffer.from(response.data); + const responseMime = response.headers?.['content-type']?.split(';')[0]?.trim(); + const urlMime = lookup(body?.url?.split?.('?')?.[0]); + const mimetype = (urlMime || responseMime || 'image/jpeg') as string; + const ext = extension(mimetype) || 'jpg'; const getFile = await this.storage.uploadFile({ buffer, - mimetype: lookup(body?.url?.split?.('?')?.[0]) || 'image/jpeg', + mimetype, size: buffer.length, path: '', fieldname: '', destination: '', stream: new Readable(), filename: '', - originalname: '', + originalname: `upload.${ext}`, encoding: '', }); From 1c3cb008c67e29bc3888d774a1e831d265de3476 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Fri, 6 Feb 2026 23:49:11 +0700 Subject: [PATCH 225/340] feat: retry resource --- .../social/linkedin.page.provider.ts | 18 ++++++++-------- .../integrations/social/linkedin.provider.ts | 21 ++++++++++++------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts index f60944d3..a8aed73a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.page.provider.ts @@ -143,7 +143,7 @@ export class LinkedinPageProvider headers: { Authorization: `Bearer ${accessToken}`, 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202601', }, } ) @@ -308,7 +308,7 @@ export class LinkedinPageProvider { headers: { Authorization: `Bearer ${accessToken}`, - 'Linkedin-Version': '202511', + 'Linkedin-Version': '202601', 'X-Restli-Protocol-Version': '2.0.0', }, } @@ -323,7 +323,7 @@ export class LinkedinPageProvider { headers: { Authorization: `Bearer ${accessToken}`, - 'Linkedin-Version': '202511', + 'Linkedin-Version': '202601', 'X-Restli-Protocol-Version': '2.0.0', }, } @@ -338,7 +338,7 @@ export class LinkedinPageProvider { headers: { Authorization: `Bearer ${accessToken}`, - 'Linkedin-Version': '202511', + 'Linkedin-Version': '202601', 'X-Restli-Protocol-Version': '2.0.0', }, } @@ -436,7 +436,7 @@ export class LinkedinPageProvider await this.fetch(shareStatsUrl, { headers: { Authorization: `Bearer ${accessToken}`, - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', 'X-Restli-Protocol-Version': '2.0.0', }, }) @@ -452,7 +452,7 @@ export class LinkedinPageProvider await this.fetch(socialActionsUrl, { headers: { Authorization: `Bearer ${accessToken}`, - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', 'X-Restli-Protocol-Version': '2.0.0', }, }) @@ -575,7 +575,7 @@ export class LinkedinPageProvider headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${integration.token}`, }, } @@ -604,7 +604,7 @@ export class LinkedinPageProvider headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202504', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${integration.token}`, }, }); @@ -653,7 +653,7 @@ export class LinkedinPageProvider headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202501', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${integration.token}`, }, } diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index a0be3438..bbab352c 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -53,6 +53,13 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { }; } + if (body.indexOf('resource is forbidden') > -1) { + return { + type: 'retry', + value: 'Resource is forbidden', + }; + } + return undefined; } @@ -204,7 +211,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'Content-Type': 'application/json', 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${token}`, }, } @@ -249,7 +256,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'Content-Type': 'application/json', 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ @@ -282,7 +289,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { method: 'PUT', headers: { 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${accessToken}`, ...(isVideo ? { 'Content-Type': 'application/octet-stream' } @@ -314,7 +321,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { }), headers: { 'X-Restli-Protocol-Version': '2.0.0', - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, }, @@ -573,7 +580,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { const response = await this.fetch(`https://api.linkedin.com/rest/posts`, { method: 'POST', headers: { - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, @@ -785,7 +792,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${integration.token}`, }, }); @@ -801,7 +808,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { headers: { 'X-Restli-Protocol-Version': '2.0.0', 'Content-Type': 'application/json', - 'LinkedIn-Version': '202511', + 'LinkedIn-Version': '202601', Authorization: `Bearer ${token}`, }, } From f72d63bd430260e6c883b474b8f4676c68656727 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sat, 7 Feb 2026 00:16:42 +0700 Subject: [PATCH 226/340] feat: update token to all orgs --- .../src/database/prisma/integrations/integration.repository.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts index 549b06b5..e6fae04e 100644 --- a/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/integrations/integration.repository.ts @@ -298,7 +298,6 @@ export class IntegrationRepository { id: { not: upsert.id, }, - organizationId: org, rootInternalId: rootId, }, data: { From e3c3854840461994187d59633b3f82f4e5790239 Mon Sep 17 00:00:00 2001 From: Nevo David <me@nevos.io> Date: Sun, 8 Feb 2026 20:53:28 +0700 Subject: [PATCH 227/340] feat: platforms with a chrome extension --- .env.example | 3 + .../routes/no.auth.integrations.controller.ts | 85 +++++ apps/extension/manifest.dev.json | 37 +- apps/extension/manifest.json | 44 ++- apps/extension/nodemon.chrome.json | 16 - apps/extension/nodemon.firefox.json | 16 - apps/extension/package.json | 12 +- apps/extension/public/contentStyle.css | 0 apps/extension/public/dev-icon-128.png | Bin 5730 -> 0 bytes apps/extension/public/dev-icon-32.png | Bin 1049 -> 0 bytes apps/extension/src/assets/img/logo.svg | 7 - apps/extension/src/assets/styles/tailwind.css | 13 - apps/extension/src/background.ts | 209 +++++++++++ apps/extension/src/global.d.ts | 11 - apps/extension/src/locales/en/messages.json | 10 - apps/extension/src/pages/background/index.ts | 37 -- .../content/elements/action.component.tsx | 115 ------ apps/extension/src/pages/content/index.tsx | 11 - .../src/pages/content/main.content.tsx | 191 ---------- apps/extension/src/pages/content/style.css | 27 -- apps/extension/src/pages/options/Options.css | 8 - apps/extension/src/pages/options/Options.tsx | 6 - apps/extension/src/pages/options/index.css | 0 apps/extension/src/pages/options/index.html | 12 - apps/extension/src/pages/options/index.tsx | 13 - apps/extension/src/pages/panel/Panel.css | 7 - apps/extension/src/pages/panel/Panel.tsx | 10 - apps/extension/src/pages/panel/index.css | 0 apps/extension/src/pages/panel/index.html | 12 - apps/extension/src/pages/panel/index.tsx | 14 - apps/extension/src/pages/popup/Popup.tsx | 77 ---- apps/extension/src/pages/popup/index.css | 16 - apps/extension/src/pages/popup/index.html | 12 - apps/extension/src/pages/popup/index.tsx | 14 - .../providers/cookie-provider.interface.ts | 19 + .../src/providers/list/linkedin.provider.ts | 12 - .../src/providers/list/skool.provider.ts | 12 + .../src/providers/list/x.provider.ts | 24 -- .../src/providers/provider.interface.ts | 8 - apps/extension/src/providers/provider.list.ts | 8 - .../src/providers/provider.registry.ts | 18 + apps/extension/src/types/messages.ts | 85 +++++ apps/extension/src/utils/load.cookie.ts | 13 - apps/extension/src/utils/load.storage.ts | 6 - apps/extension/src/utils/request.util.ts | 33 -- apps/extension/src/utils/save.storage.ts | 7 - apps/extension/src/vite-env.d.ts | 1 - apps/extension/tsconfig.json | 3 +- apps/extension/vite.config.base.ts | 19 +- apps/extension/vite.config.chrome.ts | 4 +- apps/extension/vite.config.firefox.ts | 31 -- apps/extension/vite.config.ts | 23 ++ .../frontend/public/icons/platforms/skool.png | Bin 0 -> 1805 bytes apps/frontend/src/app/(app)/layout.tsx | 1 + apps/frontend/src/app/(extension)/layout.tsx | 1 + apps/frontend/src/chrome.d.ts | 15 + .../launches/add.provider.component.tsx | 219 +++++++++++- .../components/launches/calendar.context.tsx | 2 +- .../launches/continue.integration.tsx | 27 ++ .../src/components/launches/menu/menu.tsx | 20 +- .../src/components/new-launch/editor.tsx | 24 +- .../providers/show.all.providers.tsx | 5 + .../providers/skool/skool.group.select.tsx | 57 +++ .../providers/skool/skool.label.select.tsx | 65 ++++ .../providers/skool/skool.provider.tsx | 37 ++ .../src/components/new-launch/store.ts | 6 +- .../src/utils/strip.html.validation.ts | 10 + .../all.providers.settings.ts | 5 +- .../posts/providers-settings/skool.dto.ts | 28 ++ .../src/integrations/integration.manager.ts | 4 + .../src/integrations/social/skool.provider.ts | 333 ++++++++++++++++++ .../social/social.integrations.interface.ts | 4 +- .../src/helpers/variable.context.tsx | 2 + 73 files changed, 1358 insertions(+), 878 deletions(-) mode change 100755 => 100644 apps/extension/manifest.dev.json delete mode 100644 apps/extension/nodemon.chrome.json delete mode 100644 apps/extension/nodemon.firefox.json delete mode 100644 apps/extension/public/contentStyle.css delete mode 100644 apps/extension/public/dev-icon-128.png delete mode 100755 apps/extension/public/dev-icon-32.png delete mode 100644 apps/extension/src/assets/img/logo.svg delete mode 100644 apps/extension/src/assets/styles/tailwind.css create mode 100644 apps/extension/src/background.ts delete mode 100644 apps/extension/src/global.d.ts delete mode 100644 apps/extension/src/locales/en/messages.json delete mode 100644 apps/extension/src/pages/background/index.ts delete mode 100644 apps/extension/src/pages/content/elements/action.component.tsx delete mode 100644 apps/extension/src/pages/content/index.tsx delete mode 100644 apps/extension/src/pages/content/main.content.tsx delete mode 100644 apps/extension/src/pages/content/style.css delete mode 100644 apps/extension/src/pages/options/Options.css delete mode 100644 apps/extension/src/pages/options/Options.tsx delete mode 100644 apps/extension/src/pages/options/index.css delete mode 100644 apps/extension/src/pages/options/index.html delete mode 100644 apps/extension/src/pages/options/index.tsx delete mode 100644 apps/extension/src/pages/panel/Panel.css delete mode 100644 apps/extension/src/pages/panel/Panel.tsx delete mode 100644 apps/extension/src/pages/panel/index.css delete mode 100644 apps/extension/src/pages/panel/index.html delete mode 100644 apps/extension/src/pages/panel/index.tsx delete mode 100644 apps/extension/src/pages/popup/Popup.tsx delete mode 100644 apps/extension/src/pages/popup/index.css delete mode 100644 apps/extension/src/pages/popup/index.html delete mode 100644 apps/extension/src/pages/popup/index.tsx create mode 100644 apps/extension/src/providers/cookie-provider.interface.ts delete mode 100644 apps/extension/src/providers/list/linkedin.provider.ts create mode 100644 apps/extension/src/providers/list/skool.provider.ts delete mode 100644 apps/extension/src/providers/list/x.provider.ts delete mode 100644 apps/extension/src/providers/provider.interface.ts delete mode 100644 apps/extension/src/providers/provider.list.ts create mode 100644 apps/extension/src/providers/provider.registry.ts create mode 100644 apps/extension/src/types/messages.ts delete mode 100644 apps/extension/src/utils/load.cookie.ts delete mode 100644 apps/extension/src/utils/load.storage.ts delete mode 100644 apps/extension/src/utils/request.util.ts delete mode 100644 apps/extension/src/utils/save.storage.ts delete mode 100644 apps/extension/src/vite-env.d.ts delete mode 100644 apps/extension/vite.config.firefox.ts create mode 100644 apps/extension/vite.config.ts create mode 100644 apps/frontend/public/icons/platforms/skool.png create mode 100644 apps/frontend/src/chrome.d.ts create mode 100644 apps/frontend/src/components/new-launch/providers/skool/skool.group.select.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/skool/skool.label.select.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/skool.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/skool.provider.ts diff --git a/.env.example b/.env.example index 61d2a020..e6fc8396 100644 --- a/.env.example +++ b/.env.example @@ -76,6 +76,9 @@ MASTODON_URL="https://mastodon.social" MASTODON_CLIENT_ID="" MASTODON_CLIENT_SECRET="" +# Chrome Extension Settings (for cookie-based platform integrations like Skool) +EXTENSION_ID="" + # Misc Settings OPENAI_API_KEY="" NEXT_PUBLIC_DISCORD_SUPPORT="" diff --git a/apps/backend/src/api/routes/no.auth.integrations.controller.ts b/apps/backend/src/api/routes/no.auth.integrations.controller.ts index 832505ba..cce5450b 100644 --- a/apps/backend/src/api/routes/no.auth.integrations.controller.ts +++ b/apps/backend/src/api/routes/no.auth.integrations.controller.ts @@ -232,6 +232,10 @@ export class NoAuthIntegrationsController { ? AuthService.fixedEncryption( Buffer.from(body.code, 'base64').toString() ) + : integrationProvider.isChromeExtension + ? AuthService.signJWT( + JSON.parse(Buffer.from(body.code, 'base64').toString()) + ) : undefined ); @@ -284,11 +288,21 @@ export class NoAuthIntegrationsController { await ioRedis.del(`redirect:${body.state}`); } + const extensionToken = integrationProvider.isChromeExtension + ? AuthService.signJWT({ + integrationId: createUpdate.id, + organizationId: org.id, + internalId: String(id), + provider: integration, + }) + : undefined; + return { ...createUpdate, onboarding: onboarding === 'true', pages, ...(returnURL ? { returnURL } : {}), + ...(extensionToken ? { extensionToken } : {}), }; } @@ -307,4 +321,75 @@ export class NoAuthIntegrationsController { return this._integrationService.saveProviderPage(org.id, id, body); } + + @Post('/extension-refresh') + async extensionRefreshCookies( + @Body() body: { jwt: string; cookies: string } + ) { + let payload: any; + try { + payload = AuthService.verifyJWT(body.jwt); + } catch { + throw new HttpException('Invalid token', 401); + } + + const { integrationId, organizationId, internalId, provider } = payload; + if (!integrationId || !organizationId || !internalId || !provider) { + throw new HttpException('Invalid token payload', 400); + } + + const integration = await this._integrationService.getIntegrationById( + organizationId, + integrationId + ); + if (!integration || integration.internalId !== internalId) { + throw new HttpException('Integration not found', 404); + } + + const integrationProvider = + this._integrationManager.getSocialIntegration(provider); + if (!integrationProvider?.isChromeExtension) { + throw new HttpException('Not a Chrome extension integration', 400); + } + + const authResult = await integrationProvider.authenticate({ + code: body.cookies, + codeVerifier: '', + }); + + if (typeof authResult === 'string') { + throw new HttpException(authResult, 400); + } + + if (String(authResult.id) !== String(integration.internalId)) { + await this._integrationService.refreshNeeded( + organizationId, + integrationId + ); + return { success: false, reason: 'account_mismatch' }; + } + + await this._integrationService.createOrUpdateIntegration( + undefined, + false, + organizationId, + integration.name, + undefined, + 'social', + integration.internalId, + integration.providerIdentifier, + authResult.accessToken, + '', + authResult.expiresIn, + undefined, + false, + undefined, + undefined, + AuthService.signJWT( + JSON.parse(Buffer.from(body.cookies, 'base64').toString()) + ) + ); + + return { success: true }; + } } diff --git a/apps/extension/manifest.dev.json b/apps/extension/manifest.dev.json old mode 100755 new mode 100644 index 1d1d2f16..465c8f53 --- a/apps/extension/manifest.dev.json +++ b/apps/extension/manifest.dev.json @@ -1,15 +1,30 @@ { - "action": { - "default_icon": "public/dev-icon-32.png", - "default_popup": "src/pages/popup/index.html" - }, + "manifest_version": 3, + "name": "Postiz", + "version": "2.0.0", + "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqtH6qclAsfFf6qbUKfPmhBbfycGrt13+0h6ti/olniCGnjQjhkVVTnURfLFz+v+842Ee+pAS5HBEXo57dQ9xUtwFGXnavVR+myjN+Un9NIfFyYmYEBvLrinclsMJBwWMM8JkhxKuaOagxp1hqGgNAO4C0bzE3YN/SPoTjNpGU8TGm/ENZ/TDUneZyyVM5HEEmOTZEmjmy9FJaxbzGmZ2rixNO45pkjXMFp8+/XrFSNiCqNZt6LQNIqL5SfVIRUKGBjE3OG/gtahVToBdlXi5yzP1uYE0Qs4grJ/T1rUUzTXFAQa7heWA9mskf0xAMEtTSED4N9bZ4sF8cf5J+SGGlwIDAQAB", + "description": "Postiz browser extension for social media scheduling", "icons": { - "128": "public/dev-icon-128.png" + "32": "icon-32.png", + "128": "icon-128.png" }, - "web_accessible_resources": [ - { - "resources": ["contentStyle.css", "dev-icon-128.png", "dev-icon-32.png"], - "matches": [] - } - ] + "permissions": [ + "cookies", + "alarms", + "storage" + ], + "host_permissions": [ + "*://*.skool.com/*" + ], + "background": { + "service_worker": "background.js", + "type": "module" + }, + "externally_connectable": { + "matches": [ + "http://localhost/*", + "https://localhost/*", + "https://*.postiz.com/*" + ] + } } diff --git a/apps/extension/manifest.json b/apps/extension/manifest.json index 519fb342..f8c1c348 100755 --- a/apps/extension/manifest.json +++ b/apps/extension/manifest.json @@ -1,31 +1,29 @@ { "manifest_version": 3, "name": "Postiz", - "description": "Your ultimate social media scheduling tool", - "options_ui": { - "page": "src/pages/options/index.html" - }, - "action": { - "default_popup": "src/pages/popup/index.html", - "default_icon": { - "32": "icon-32.png" - } - }, + "version": "2.0.0", + "description": "Postiz browser extension for social media scheduling", "icons": { + "32": "icon-32.png", "128": "icon-128.png" }, - "permissions": ["activeTab", "cookies", "tabs"], - "content_scripts": [ - { - "matches": ["http://*/*", "https://*/*", "<all_urls>"], - "js": ["src/pages/content/index.tsx"], - "css": ["contentStyle.css"] - } + "permissions": [ + "cookies", + "alarms", + "storage" ], - "web_accessible_resources": [ - { - "resources": ["contentStyle.css", "icon-128.png", "icon-32.png"], - "matches": [] - } - ] + "host_permissions": [ + "*://*.skool.com/*" + ], + "background": { + "service_worker": "background.js", + "type": "module" + }, + "externally_connectable": { + "matches": [ + "http://localhost/*", + "https://localhost/*", + "https://*.postiz.com/*" + ] + } } diff --git a/apps/extension/nodemon.chrome.json b/apps/extension/nodemon.chrome.json deleted file mode 100644 index 95f85a87..00000000 --- a/apps/extension/nodemon.chrome.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "env": { - "__DEV__": "true" - }, - "watch": [ - "src", - "utils", - "vite.config.base.ts", - "vite.config.chrome.ts", - "manifest.json", - "manifest.dev.json" - ], - "ext": "tsx,css,html,ts,json", - "ignore": ["src/**/*.spec.ts"], - "exec": "vite build --config vite.config.chrome.ts --mode development" -} diff --git a/apps/extension/nodemon.firefox.json b/apps/extension/nodemon.firefox.json deleted file mode 100644 index 996650fe..00000000 --- a/apps/extension/nodemon.firefox.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "env": { - "__DEV__": "true" - }, - "watch": [ - "src", - "utils", - "vite.config.base.ts", - "vite.config.firefox.ts", - "manifest.json", - "manifest.dev.json" - ], - "ext": "tsx,css,html,ts,json", - "ignore": ["src/**/*.spec.ts"], - "exec": "vite build --config vite.config.firefox.ts --mode development" -} diff --git a/apps/extension/package.json b/apps/extension/package.json index a3feacd9..1e175dae 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -1,14 +1,10 @@ { "name": "postiz-extension", - "version": "1.0.3", - "description": "A simple chrome & firefox extension template with Vite, React, TypeScript and Tailwind CSS.", + "version": "2.0.0", + "description": "Postiz browser extension for cookie-based platform authentication", "scripts": { - "build": "rm -rf dist && vite build --config vite.config.chrome.ts && zip -r extension.zip dist", - "build:chrome": "vite build --config vite.config.chrome.ts", - "build:firefox": "vite build --config vite.config.firefox.ts", - "dev": "rm -rf dist && dotenv -e ../../.env -- vite build --config vite.config.chrome.ts --mode development --watch", - "dev:chrome": "nodemon --config nodemon.chrome.json", - "dev:firefox": "nodemon --config nodemon.firefox.json" + "build": "rm -rf dist && vite build && cp manifest.json dist/manifest.json && cd dist && zip -r ../extension.zip .", + "dev": "rm -rf dist && HOT_RELOAD_EXTENSION_VITE_PORT=8081 NODE_ENV=development dotenv -e ../../.env -- vite build --config vite.config.chrome.ts --mode development --watch" }, "type": "module" } diff --git a/apps/extension/public/contentStyle.css b/apps/extension/public/contentStyle.css deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/extension/public/dev-icon-128.png b/apps/extension/public/dev-icon-128.png deleted file mode 100644 index 69c0e48328e98e78461be74abb941943cfa27ea7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5730 zcmb_g<yREi*B*uuhEh5Oh7dtOy1PT_4jls$N=t+EP(uj}jZy+bDkY78bazWgGjd0| z1`zmh|Acp~_ru=j%RXoAwf8yCe$ErCr=vzfNKXg=07x{{m7xzS{67QZJ@_73>yd|r zz+K(c3jiRZ_|I?vpK@pc0BTANWd(!xIs3VeNLyo{fzV4=GqdSH=p~O#UY}52<p32i zEpB-YpEWNO#s5<2vpZywT{JcxNonw6IU1xMXTWc60!5}#7@veOlFt)b3pCb}Aimii zy_=X8T21*ibDa@*x}ov!!Cv1g)Ld8f+PEda%m|ZLdK!xU6?lhUd0y3utr~z!Q+!~P z#$8OUr=nm-wBfziFc(m;IjTqTW<gA~!2G*eq_0N>SUR(872yH{kpSC70-XM*6u=J> z93Wb+|5HGZ`V!oV7Yt&(%ZxeXEoLYDi28@YMiK5a<x`9xr3W&y2n!PnTNheGB7gbA z@a4<FdYtUgN5@t~6fhKlUBX*$YL!UZf=($8kV%OBzhIkar*3%)yzm2E5*A_*u$)z% zH8#c|UcE8KE;Yl6bXYR1f^3k!;|z?7=93B9BUew-69F4i6tg1+Q^YPk6n3LV-{vVv zy_oqhUJyHl{a39gt^-2qz&Nnyps413Fq$VM<yWa#Xb{QD3UyWd`QVnmX+dOwhR=}= zd2G0Qo3H$$yfQ=PkBTHi40@hmunN6zS=(GgBjY`d-A(uS1{rRSyQ9^@K||&>8l3F( zcwFs~#Z)%-!_;99+2OhVgdjx}4F@tC5-Fq-^_fEI&jLTUCVnMT76~#fVw^qhAuX&l zc{YeT(bC?jArfu0)gMF;?RagU`U@8b$c1JsziZF+JQr>flhu~MO7Q)>a?_#<bWkoO z{jED|+{h)(1%?=^&o3sFYYVKkre9qo9f#vPDBC-zrRUY%mg$hrIYx^tDb%RgD=rNS z7!Lk3NK@k~bvIGVl^#bK|G3WvZC|cjZqV!D@^rv-1KZ$!fBWQUA@p1<=)y|24|gZ~ zs>)Y{Z}sYsmh}~CY59ocJSq7(+<@iLDe2g6;WfF-mrNC*Q5D9)D>d1IrCGze$rDuW zagQ4rrJRe|;u+DtxpY6CTMed;?2szDjEFpy^hQnuU8HL1W@6Xng5ZJNYwPkCsydXv zR7IMfEarJ!hc*AZ`Q1XbUP|2eE~>c;H|TbhDyQNOsHjfozq`9D?meaO*4|G*NJz=C zwZ6NhU8)ZGJb%J1TL{0eST|U->3&=7>GHXtW$TS9{f4yDmjb7o^#pg2p52+_(zk9g z^x9x~pA|eDQ+3N$)v>#85p--Zjdu|x*0D#ja+k{d&v9rNgS5I?J_~Z_9G%FI19A*T zF!TK7N^0?o2xpKz`=_e?fKh`%l(e<?{O`<cPwMvAZ7lg~Z$WZ&*EC^TMBw|HJC&o2 zVeVglQGT+Wc`|hl-Gx(5lOM(lQ#EJvlApoLcT64D`fT=DK=cbgFs&%<CF-=2S7|wP zF`s!yASpgwgXHo(C@Nmk>(?|TKYzGcO>?*sY0jv)4B&NU&h^f8v%LaD%98<+Lw1w( zQzAaxgD4?!-%P*|^SEI6T7^6fHC=GrS0-kw*>_EjHzD1=uFN@wxbhGfiZDP@g`74F z23Mru$aw1qZHqIi?+n7c9B(+3cL+DCQyKg6%=LGpwLb_6>Wzh@$MD9I$&*>fN$dCX zba->!T-L1Lr}bS-aNYRnKR#eV`UsY2f7#z^J&@mOSaz*2q?zz3H>gM(q0-5djxAuY z@Yvb;eSPG4qEico^TALjwq2Uf^o5WB9GiA&x2F)K&p|~<WHjc!FZ^EU-wiqx5C$*a z5ombYjU4n%cWPL=jFD@-qPDzQS|EEAXY@s-XbNJ$yBYMYZ^Y_KP_x+P@YZi7<oR>m zJY}xMCO5uJ>2t0Ks|&$2&ByXQmO~ssTuDr>we4?^o;qQ_3J=!rMDFx(+u-!EH;$vs zL|U!$T!qO3)o0F3?%Skt%2FLUV6UUyh-UJX&@8===g;!I)3wW{L$px-mGMFQr7R^c zKv-~ejB$t8xII=b<nsG<qqRg_?(ybQ7)ObyO4=wfzXgz!)w4fjxN9}&YTLQ&(VM5v zVnC9HgQv87NIaI#s@3oACsK~H4BMWs;$R$coQsb-ZYvMnd!l9g(Lb>H0IlM9T2-DT zkV5Cg;T^%@U26VO%S3Qqb;v&VqHlqlIUsJ83sONgjF%z0h$aayAnznKMG;>8`YG2- zG@J$vl!~Iq%>3ZqOrseIPI9IpT=ZozBFkjS)nOxTyJ)KFtjynUw{%PA(i%hR6bleX zxcCy%sr+4!UfX&!5lGOq>LGLuSe~}Ar&(7M0U9b%yj{x|;Y2i(7(8H3bM*zY^fZ<w zv7t_1sY##}WMX3SC(v%W_5rOp!>OvBKdIJD^;Q}K-rr(=4c@F>URwC~WxHe0WdT&) zFg32&5@)UDz~<A^CeK@`wbdrx8bKiF8$#6+L~90`<bS~Qhnui^wYmara4ClF1-if% zYF)EW<H2cAkTn?ecNJfDkom{W^{@NDjrhEY<?0ZIgc23WbXBgXNs!~0P7>xT$U?^{ zeIk?0bf%04ll<gk2JG~!dSMp_GWmt+*&hAo4O~=xr35pcIWB;b(SB@U_1e_(v|`P2 zBM810cw_F5F<j7d=9z{?a~2PxH?OV{p?@9L8pRv6MGYZ@o0YFq-dHQ;0EJP6k{_!p zE!_X^JR!${TiylpN~2qT)b3(J?=lbjW`$;*UU8Xov4QaN?^iO9Zbo%{i466Qbs$4F z=>=r+<)lg@HjYs`7tS-G1o91nCah)IAuys+om|V)^V^%t<8bs3q*0CMAB*{oDg8?i z3~$r9eY|ZdMma@3V2ca~b)w<;*wi+|fg=7#I)9nb7h#66j)DBVJiJZ*5ZUounWlp^ znais$MM^K;;2do+uT5q0mKzW*#}Z6tyerIA>R|(rm1GpXeyaFRV6VV-qxIz+&U9BF z39Ed|w>#>(?~|#XvGp+W+s$8Ak6-JG(QT7+$qAUgFDrUMndn0at9dq`jZkPeV&-IA zx$Rzv12Aaw)qVHFl~kyabdtIFZ5zC7>-UId$G5R(=qoNl6D~jR@s-W)jE?o^(%IS` zLZ^N1vtXyrX9gUUbpCt#bj>%~Hu5=*4QD+6)PDlR<Fc2=$!ILH6|OpSFp46y07>B< z55oo$w)C})o1Yk0C4Lw}*69HVrosVnRADv)oN4S)P%f<*lT^sq{H{ijZ0mId-<Eex z#-yTq87RgeyS^i}P(%$P_MOzA-RZLeD_q~3U*+vwu8RYnNVRJ53VzFcEPZxe>36&` zvT~#9p7)Eio-4_}yHO5|htB;B?AuF?+8xN}N5;cdC61c`A}c<Hua?~?KxEFYa|t+3 zU`-g%8^n^V=h%-VfPf7n3Vm|Y@}5DnU!izZ;0j<ph}9rjS-J!mCI+^%U42wy?sHt! z|LGB1i{d?rVewuSliq-FL+^B#l9fMpx<~X$s@TgFPo>^Xo8m(cBHyr}m-t^mEI5Nc zMI&ra<On0PcAdK3zJ1&378nqY)G{Kam&8&0$^K<x(?pG6dmdW8gTo3haTFhSQYd49 z&Ck%c4(06_4z14a-`(EaG(I2Ka;lHOQwR^AoFw=Re!yB43^wD`DuM@S?YPnK6lPy_ z`P1reccz4ua6<RuiM{mcbcbs#(W7sBz~nV;{G={XeuVss{6Zh#tC_%t`)mb&J%vF_ zUa${Ajieml{_I%S<1kpRXQ-O^{9U`EwG$$<Z96J>d6SZn@ajFuRK`K`AD|6(xJMv~ z|BZz_Cqwq#cLl+)nh?pwN&OY?8EtOCK(W=cd<(yQA5`Vjk`_uFVeRKByuYwg##I_+ zW8EGugi+5FILozOvr_0noVQ1V7k90hNSOLp4777%&3zn&KiSoN?C9U;uu6k+vzwIi z_wY8|BJE|jBrGmQUrBhq&C7PXvLa3{Sf`9F6xZBq9sk?S1)>hUKp*anAb!nd?Ly<T zJf-YG#6RR9WWe6x*Rh2n0}<zLbsvcq1Uz6*7JP0v<UJBZ0J&48`=&a15<@f{XApqz zJN%3{mF{;fQFZdY31W8)ffu6}Z+bk%8*$}H{ne>hNg3wVWXaUasJVRK4}DUiA*~)2 zdmL6WHMVHD*dAzXRpjdo98Usha(V0H2gw)N9fBalN@>tp%rTAufcbv$dCLh&^`Z0M zxeITpNDCC9h7G;?S22DhQ=-p@ua^G`JGv0B@|QI+;PQeNC?basfCGt)JBTO2k?nzx zYfNOuY|e$>ToO~c<ryq(Q&qngk;C4NSp<(QUo(Zu1P1M}7;=*-aMn60TeVVtP@){| z8EtawnG}1BK@{M+R-`LDvv#GzVWsofJ)?VIK$rd_hHksgRyTaqG%rjt`l@oJnz0Ky z?(3al9RsV2c{<TZr$TF&S~Dkic2&6g)dNYQtlW~GZ+mutzx?{nTyv3_qwCYs><fj# z+36gaz{F^#pe$UP1N6y=)xssM`8Ru&Xg%(Cq;#Iqdg1#Ugd`7dc%iaob-mrjoL-sG zH%w7hjWmZ3O!vOwx|5{Lyl?mp9>AL7UWmqxtk9W~)VcPEDkxT2{v7wZ3dx2<6Ddj- zxDZ?Tw;7(rDHh@td}Sg3O{$p&=<t^6p6t>lQi?dkr~CuF3d)-cN8+gu<Hl&;el~c4 zv#w@o!)r673ixR^oYhyG=we1oZ%6Z)ptb-~Z*uVC_A7JmvV5-ZVsB={aW;^W43Smn zFy?ATTS3hKsIxzyvxW`sx~x@vsvOd!<bXX;$9{k)pqA1Cx+k|XV@V`+tBdi^!2F$$ zi<rd|XFjI1GAi0q<3Kvxm!-AF7nesGT_c@*YSIJze!++${_tOiZ7sIPP^xFv4ugEj z$^vw0PneDAgv*U!^yg#TG3cUZ9>yTqU9sP!0j#fS@pMWZ=Q3Xfbn+*Q<DdxD9aZAy zSGJf^ZDL%1lbZQbBT2&{VAHZ3;QHOo;o*(fP2Y4u^1D(|n`RJgnC<4~%Ys6Y=&3zg zGdBUd-;(EMZC3fYtE&qpGc(7OH6}18u#CrF@{rRve7h$je1{kze1+Nt=5?PuePIL$ z{DyHrT2nQj1IkOB3_A@*IO@3t;O^n9@?n1`Q?#8>@uBCc{i*muj^pvLZC#V8k?9MV zCyS#)T+30(5ICRA$*uz7u1b#UD#H4!HUK832f}?8k$3)Q1bdR%(f!5pmM|p<sz%di z<40!l9I7pRR&YW1yaNy3Y|H8V=d%MN+v0j>Z*yc|jmvs6TeSb&7poyoapukgWw@Sb z%<NGhNG%*t%Wua+ftXi?-kd)t|L8&wS8tG1VX@VKi}U_58PM3%#}F#XC^*0=dQ?eP z6N|9oqZ|3u5`v(k^k`ShSqVj7TWg17V@{wqcP>eCH|hN31ThmGkcU7iX-%MhD*2_Z zXwV*!6%NGy&okRU^w(Y>H3Z;zZ$sjt4XS7a&^;hmDL2mri&c{wN_uz0#QKQF&MK12 zi23c6vaKB$;PKI9>Qm1w@%AT1B|`qI9X@80x>WQ~q;AmWOxxCzDm;?Mi<ut6_jvzK zY4i({&8k3To}ThcYym$_new<ccxO*YN@EA_J#LZ?x9KuvWN^5}k~lxOw_~qBL}+UN zKeeeVUY9aKnC!I;hPAsFfIh`ov`#3klAR;sRLK~m77wV<8DB|qwTxttuIIp+<|3-b zISii2KR$O0@o{L>;PQYaJ&^Rk-w(6uqh^a@i-mQqMM>MAOV8pD=I2M!Nt(r)W2azK z4x8fFRvYEgZo55{l@!ez!Lfr~Y$||qMQFF>yg>bYAbVf;yN=_^YL7YdU;U-`R}|)< zpVOc+_N`SkQeQ1fEUNU%4NNR}>Y8iRi=hQ(<=;$RZW+O(s}Y#iStpSt>+I%aJ2nS+ zY;iVfn#)s_$%=<RwKcM*g<Cu&R3>A$cU;#&l!*%nS0r$kWK_ZsC?N{Aw*5{%PK11t za=YhpyIK|V+U$L*uWxF$FM4j4`;d17X0%T*UGrIp!KjTyM|`G49aa5eju45~Shp~- z=f|h5RYi{UiZLerFt6*xk<~|%@35SS9xT;4x6=Mzd~@qKZR=gVk`dP1U{WF_6$~dG zo2=&eq!9)P)g<nDUGg5s@0SmEsP}DUuT{Oq-%;!Ug}&rCB?p2|N`J=N8-{t7y_xTP z{=&PxZ64#iZ$+v2TUauw=jwd#GI`$5z42BbWL7vHO~x+&Fai@I9st{8fYg~tX*E32 z(Vn`P(wdYtT^St4AuJ4oL^gKiw$OdupB!r!V-hwXk^T{0-WBf`oE6csphpt&5sS<A zomK)ES)nq%7;nqCWp_#~`Z6uyhc^qTq`^|-+}*sYm=KIoGjIGyUUkAl{ND4WN7YvN zDAB{WMNgRm_%#VD5&q1%%1b!grl$RvVlPMqwS=AC&kf9)7up_+_|vqTr2*@%I;&gE z4H1Qu;a&Pvy%o-R!lnTL8~#Vl%;KY1VC*-M`!6ZU3S2f{CFzB6K)z&VX~-lL`h0i@ znEHgFY&qy5H3jZLpXjm>Y)Z)O#f$%UfLz69mfBEuH0O<9>XaY5O|*e*SjE-|H8iZ; zQEM-7F}Wk{fsUp40|z8kyQGzk)}lDH6z4iKUnU&Qe$$sz#Z*}(CbP?zecJvIJc`Qe z{x}b_V=DD}FZCIzT%iqVf3qR|>X$g&mT7-=HPI)ntu{Z+5(o-AJ1UN+k!?dH6#4$B z?pG%Vu+!7m?V;WM&Q&Ql8s!Yx9-|YmHM`?8nnKsYQc&HVJ%rb(c%x4iSMZaP47CbK z-l1WFuij6iz?AM7a{0-$(?1g(A3n}LMKAg3^IyKJ@N#hFWTko#w>q2(D6>9|r`1L? z?%C@Je)qc+Aly|Dyw>ryCaZYg;iigy(0Urr3Dq94X~MB!!)58-;^NDbIeoW6y0M3n zyo&>&qrGrYXIu~4Trgmj)AxKi=nuiD^JW8Sqz`cKA0p;w<)!{l7#>80Fa(YCXu*ox zX<;rJd+-xe4oXT9dpjA~ml8n<jf$-JS^jmuYP|Gka)9JRF$yKrrs8`1jq>|uJvpf! z+9p5e5rLo5HmfY~4Fo_&&UULcFLof>{E^EvYZD(Y#v-BT8ODmeYHfDsus>Q>s^#BJ zgpw}0TR3=F=ejeQDjmY!3g`d!Ng(evq*>bm-sT71fr_}nI|;X-it(ZZH?OK-d{@&x zN`1Qo*!CFF5Ci@oU7&_zar4d>igC6%owITaw#RPU1d~yUHZjDG!FaH&c5)|{O?)Wg zn@$<M@{89NyN9lDv1$EIPp~lSTnUpbUd)?PC!HJ_aGo&8gdkm=<hGhsRY*>vYm)!} yMGXB<PL1wQ{mlA4{5i2duJ}+SXp8Xh9>*W)lW><Z$on7~12j~0l&ch9hyM?2d(GSc diff --git a/apps/extension/public/dev-icon-32.png b/apps/extension/public/dev-icon-32.png deleted file mode 100755 index 704ca2d7d8f5cf42027afc024b6aac6a5b704a6a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1049 zcmV+!1m^pRP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px&(@8`@R9HvtmVHcBbr{D#_jb7#xV&DMGRVf;1(G6lOhVXLjc{bO)zV4I`-|8J zsb*WQ(uv7pzEGmJW$B<S*XR%D64XU4$bl7>aPJyib6pEExi?<!MYx`O+BvvWbG&u< z!?p3-?tZ_s-}8K*&+oa<^E*dVn0Q&_E)LBCz(NN8hZzv@h%P+dVJ76?5EH^M<G0s> z#)aNbL>xB~<G%r&#yDe-&AAJH8*3#n!9sYXkx)|*p{7t4Eiz#;nJ~%o<a0<!2s)jP zzCIfT+53qzY?(iRAdcgg+Q@rH>;BV?u`auUKzsT80KB4y8{=K1<PPx0j_rz=*Xw0; z)P=9F0li*7Wsu@cRfv~L)B{{|pX2h-VLV<J`30NUcc4}g+r6uXx&sHv&D%&%kA<kH zNNV@)Avic#vDy0KHbz?W0L`=zGuA+J|8sJoN#n_<p5S=HQAMc!;1PDdSxtUnF6rs3 zDJ|YYQE@)CdutRMY2VhAiH{~w9l+Uv9BvAOvh(V+R8F04QiM)?e3HtFmw2mY7n{pV zSov@YqA1e!Q>S9{?mLHQKN5>k7oiT|qGKohW8cco2?_DEU1(K=TAI(YF{gl6wpH=k z_E*WuT#MCW#s2#h#pa{NPiU&Xhfw`8bpW=}&*&L?Uv^%+_+BhO|ELIE?EH@GHBVDm zB&B^1E0R`m>y|*b^#{e~%oi=xSK3+P`-nOK=Y)lC{;ZOn^?Dz!_5XratCgQ^wkxD0 zrxO=%CM_+MV;?q9T2@5O+q;!u9UYy#p3}xs|15O?QVX8xe+EIEm<)Q=s#Lkt>BJnJ zh}j&=qmN|}9uY1B7z_p_SZ{A1C66}}6R=JlfaKMF<7EcNyJdmS@7sxvj*{1cAP^B2 zMSOf5U$>o~GJs>y!P*toJP=f@9>6;M0lgz9Wr5~%r%6s;sfZ<*lhFC-=(haxzyCXu zbXlcDZswt&t?B{(xOst&!5y-|iBB8IT9c`WRlHEe^5sdClod@IAmhO@VyILPAieEc z`qv{OQh&IP4LR$l6`N`!YgIY-xhvEINS>|Niy86Q<$jm?ezqZ9NN)EStzUjcZDkv= z#^>h;aOto8{OW2ZENqGVLXs-o;c((`IFJfWUdywC!P{%M+s(3|QlbN%MZ;w4PBrHA zJz#gY(B<4W+rj01A8i08eIiT!GYR+0nAK-a0As>+&JW}v;=MyqgHabI2Q(QH<vCDy zKg@9AJBj85AUR!iox(QyIR<SAq51?Rph0Vz^^`Li%o$)_QD$&n09eStUC+SZfcK^F TQ`#vS00000NkvXXu0mjfKZgO% diff --git a/apps/extension/src/assets/img/logo.svg b/apps/extension/src/assets/img/logo.svg deleted file mode 100644 index 6b60c104..00000000 --- a/apps/extension/src/assets/img/logo.svg +++ /dev/null @@ -1,7 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"> - <g fill="#61DAFB"> - <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/> - <circle cx="420.9" cy="296.5" r="45.7"/> - <path d="M520.5 78.1z"/> - </g> -</svg> diff --git a/apps/extension/src/assets/styles/tailwind.css b/apps/extension/src/assets/styles/tailwind.css deleted file mode 100644 index 4ff77402..00000000 --- a/apps/extension/src/assets/styles/tailwind.css +++ /dev/null @@ -1,13 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@theme { - --animate-spin-slow: spin 20s linear infinite; - - @keyframes spin { - to { - transform: rotate(360deg); - } - } -} diff --git a/apps/extension/src/background.ts b/apps/extension/src/background.ts new file mode 100644 index 00000000..e9dda89f --- /dev/null +++ b/apps/extension/src/background.ts @@ -0,0 +1,209 @@ +import { ExtensionRequest, GetCookiesResponse, ProviderInfo, StoredRefreshEntry } from './types/messages'; +import { getAllProviders, getProvider } from './providers/provider.registry'; +import { CookieProvider } from './providers/cookie-provider.interface'; + +const EXTENSION_VERSION = '2.0.0'; +const REFRESH_ALARM_NAME = 'cookie-refresh'; +const STORAGE_KEY = 'refreshEntries'; + +const ALLOWED_ORIGIN_PATTERNS = [ + /^https?:\/\/localhost(:\d+)?$/, + /^https?:\/\/([a-z0-9-]+\.)*postiz\.com$/, +]; + +function isOriginAllowed(origin: string | undefined): boolean { + if (!origin) return false; + return ALLOWED_ORIGIN_PATTERNS.some((pattern) => pattern.test(origin)); +} + +async function extractCookies(provider: CookieProvider): Promise<GetCookiesResponse> { + const allCookies = await chrome.cookies.getAll({ url: provider.url }); + + const extracted: Record<string, string> = {}; + const missingRequired: string[] = []; + + for (const def of provider.cookies) { + const found = allCookies.find((c) => c.name === def.name); + if (found) { + extracted[def.name] = found.value; + } else if (def.required) { + missingRequired.push(def.name); + } + } + + if (missingRequired.length > 0) { + return { + success: false, + provider: provider.identifier, + error: `Missing required cookies: ${missingRequired.join(', ')}. User may need to log in to ${provider.name}.`, + missingCookies: missingRequired, + }; + } + + return { + success: true, + provider: provider.identifier, + cookies: extracted, + }; +} + +// --- Refresh Token Storage Helpers --- + +async function getStoredEntries(): Promise<Record<string, StoredRefreshEntry>> { + const result = await chrome.storage.local.get(STORAGE_KEY); + return result[STORAGE_KEY] || {}; +} + +async function setStoredEntries(entries: Record<string, StoredRefreshEntry>): Promise<void> { + await chrome.storage.local.set({ [STORAGE_KEY]: entries }); +} + +async function ensureAlarm(): Promise<void> { + const existing = await chrome.alarms.get(REFRESH_ALARM_NAME); + if (!existing) { + chrome.alarms.create(REFRESH_ALARM_NAME, { periodInMinutes: 1440 }); + } +} + +async function clearAlarmIfEmpty(): Promise<void> { + const entries = await getStoredEntries(); + if (Object.keys(entries).length === 0) { + await chrome.alarms.clear(REFRESH_ALARM_NAME); + } +} + +// --- Background Cookie Refresh --- + +async function refreshAllCookies(): Promise<void> { + const entries = await getStoredEntries(); + for (const [integrationId, entry] of Object.entries(entries)) { + try { + const provider = getProvider(entry.provider); + if (!provider) continue; + + const cookieResult = await extractCookies(provider); + if (!cookieResult.success) continue; + + const base64Cookies = btoa(JSON.stringify(cookieResult.cookies)); + + await fetch(`${entry.backendUrl}/integrations/extension-refresh`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ jwt: entry.jwt, cookies: base64Cookies }), + }); + } catch { + // Silently skip — will retry next cycle + } + } +} + +// --- Alarm Listener --- + +chrome.alarms.onAlarm.addListener((alarm) => { + if (alarm.name === REFRESH_ALARM_NAME) { + refreshAllCookies(); + } +}); + +// --- Ensure alarm on startup --- + +(async () => { + const entries = await getStoredEntries(); + if (Object.keys(entries).length > 0) { + await ensureAlarm(); + } +})(); + +// --- Message Listener --- + +chrome.runtime.onMessageExternal.addListener( + ( + message: ExtensionRequest, + sender: chrome.runtime.MessageSender, + sendResponse: (response: unknown) => void + ) => { + const origin = sender.origin ?? sender.url; + if (!isOriginAllowed(origin)) { + sendResponse({ error: 'Unauthorized origin' }); + return true; + } + + switch (message.type) { + case 'PING': { + sendResponse({ status: 'ok', version: EXTENSION_VERSION }); + break; + } + + case 'GET_PROVIDERS': { + const providers = getAllProviders(); + const providerInfos: ProviderInfo[] = providers.map((p) => ({ + identifier: p.identifier, + name: p.name, + url: p.url, + cookieNames: p.cookies.map((c) => c.name), + })); + sendResponse({ providers: providerInfos }); + break; + } + + case 'GET_COOKIES': { + const provider = getProvider(message.provider); + if (!provider) { + sendResponse({ + success: false, + provider: message.provider, + error: `Unknown provider: ${message.provider}`, + }); + break; + } + + extractCookies(provider) + .then((result) => sendResponse(result)) + .catch((err) => + sendResponse({ + success: false, + provider: message.provider, + error: `Failed to extract cookies: ${err.message}`, + }) + ); + + return true; + } + + case 'STORE_REFRESH_TOKEN': { + (async () => { + const entries = await getStoredEntries(); + entries[message.integrationId] = { + jwt: message.jwt, + backendUrl: message.backendUrl, + provider: message.provider, + }; + await setStoredEntries(entries); + await ensureAlarm(); + sendResponse({ success: true }); + })().catch(() => sendResponse({ success: false })); + + return true; + } + + case 'REMOVE_REFRESH_TOKEN': { + (async () => { + const entries = await getStoredEntries(); + delete entries[message.integrationId]; + await setStoredEntries(entries); + await clearAlarmIfEmpty(); + sendResponse({ success: true }); + })().catch(() => sendResponse({ success: false })); + + return true; + } + + default: { + sendResponse({ error: `Unknown message type: ${(message as any).type}` }); + break; + } + } + + return true; + } +); diff --git a/apps/extension/src/global.d.ts b/apps/extension/src/global.d.ts deleted file mode 100644 index 7ad3edd1..00000000 --- a/apps/extension/src/global.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -declare module '*.svg' { - import React = require('react'); - export const ReactComponent: React.SFC<React.SVGProps<SVGSVGElement>>; - const src: string; - export default src; -} - -declare module '*.json' { - const content: string; - export default content; -} diff --git a/apps/extension/src/locales/en/messages.json b/apps/extension/src/locales/en/messages.json deleted file mode 100644 index f0941104..00000000 --- a/apps/extension/src/locales/en/messages.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extName": { - "message": "name in src/locales/en/messages.json", - "description": "Extension name" - }, - "extDescription": { - "message": "description in src/locales/en/messages.json", - "description": "Extension description" - } -} diff --git a/apps/extension/src/pages/background/index.ts b/apps/extension/src/pages/background/index.ts deleted file mode 100644 index ce643b60..00000000 --- a/apps/extension/src/pages/background/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { fetchRequestUtil } from '@gitroom/extension/utils/request.util'; - -const isDevelopment = process.env.NODE_ENV === 'development'; - -chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { - if (request.action === 'makeHttpRequest') { - fetchRequestUtil(request).then((response) => { - sendResponse(response); - }); - } - - if (request.action === 'loadStorage') { - chrome.storage.local.get([request.key], function (storage) { - sendResponse(storage[request.key]); - }); - } - - if (request.action === 'saveStorage') { - chrome.storage.local.set({ [request.key]: request.value }, function () { - sendResponse({ success: true }); - }); - } - - if (request.action === 'loadCookie') { - chrome.cookies.get( - { - url: import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL, - name: request.cookieName, - }, - function (cookies) { - sendResponse(cookies?.value); - } - ); - } - - return true; -}); diff --git a/apps/extension/src/pages/content/elements/action.component.tsx b/apps/extension/src/pages/content/elements/action.component.tsx deleted file mode 100644 index 109520b2..00000000 --- a/apps/extension/src/pages/content/elements/action.component.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { FC, memo, useCallback, useEffect, useState } from 'react'; -import { ProviderInterface } from '@gitroom/extension/providers/provider.interface'; -import { fetchCookie } from '@gitroom/extension/utils/load.cookie'; - -const Comp: FC<{ removeModal: () => void; platform: string; style: string }> = ( - props -) => { - const load = async () => { - const cookie = await fetchCookie(`auth`); - if (document.querySelector('iframe#modal-postiz')) { - return; - } - - const div = document.createElement('div'); - div.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; - div.style.position = 'fixed'; - div.style.top = '0'; - div.style.left = '0'; - div.style.zIndex = '9999'; - div.style.width = '100%'; - div.style.height = '100%'; - div.style.border = 'none'; - div.style.overflow = 'hidden'; - document.body.appendChild(div); - - const iframe = document.createElement('iframe'); - iframe.style.backgroundColor = 'transparent'; - // @ts-ignore - iframe.allowTransparency = 'true'; - iframe.src = - (import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL) + - `/modal/${props.style}/${props.platform}?loggedAuth=${cookie}`; - iframe.id = 'modal-postiz'; - iframe.style.width = '100%'; - iframe.style.height = '100%'; - iframe.style.position = 'fixed'; - iframe.style.top = '0'; - iframe.style.left = '0'; - iframe.style.zIndex = '9999'; - iframe.style.border = 'none'; - div.appendChild(iframe); - - window.addEventListener('message', (event) => { - if (event.data.action === 'closeIframe') { - const iframe = document.querySelector('iframe#modal-postiz'); - if (iframe) { - props.removeModal(); - div.remove(); - } - } - }); - }; - useEffect(() => { - load(); - }, []); - return <></>; -}; -export const ActionComponent: FC<{ - target: Node; - keyIndex: number; - actionType: string; - provider: ProviderInterface; - wrap: boolean; - selector: string; -}> = memo((props) => { - const { wrap, provider, selector, target, actionType } = props; - const [modal, showModal] = useState(false); - const handle = useCallback(async (e: any) => { - showModal(true); - e.preventDefault(); - e.stopPropagation(); - }, []); - - useEffect(() => { - const blockingDiv = document.createElement('div'); - if (document.querySelector(`.${selector}`)) { - console.log('already exists'); - return; - } - - setTimeout(() => { - // @ts-ignore - const targetInformation = target.getBoundingClientRect(); - blockingDiv.style.position = 'absolute'; - blockingDiv.id = 'blockingDiv'; - blockingDiv.style.cursor = 'pointer'; - blockingDiv.style.top = `${targetInformation.top}px`; - blockingDiv.style.left = `${targetInformation.left}px`; - blockingDiv.style.width = `${targetInformation.width}px`; - blockingDiv.style.height = `${targetInformation.height}px`; - blockingDiv.style.zIndex = '9999'; - blockingDiv.className = selector; - - document.body.appendChild(blockingDiv); - blockingDiv.addEventListener('click', handle); - }, 1000); - return () => { - blockingDiv.removeEventListener('click', handle); - blockingDiv.remove(); - }; - }, []); - - return ( - <div className="g-wrapper" style={{ position: 'relative' }}> - <div className="absolute start-0 top-0 z-[9999] w-full h-full" /> - {modal && ( - <Comp - platform={provider.identifier} - style={provider.style} - removeModal={() => showModal(false)} - /> - )} - </div> - ); -}); diff --git a/apps/extension/src/pages/content/index.tsx b/apps/extension/src/pages/content/index.tsx deleted file mode 100644 index d0456b43..00000000 --- a/apps/extension/src/pages/content/index.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { createRoot } from 'react-dom/client'; -import './style.css'; -import { MainContent } from '@gitroom/extension/pages/content/main.content'; -const div = document.createElement('div'); -div.id = '__root'; -document.body.appendChild(div); - -const rootContainer = document.querySelector('#__root'); -if (!rootContainer) throw new Error("Can't find Content root element"); -const root = createRoot(rootContainer); -root.render(<MainContent />); diff --git a/apps/extension/src/pages/content/main.content.tsx b/apps/extension/src/pages/content/main.content.tsx deleted file mode 100644 index a726f5ad..00000000 --- a/apps/extension/src/pages/content/main.content.tsx +++ /dev/null @@ -1,191 +0,0 @@ -import { - FC, - Fragment, - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { ProviderList } from '@gitroom/extension/providers/provider.list'; -import { createPortal } from 'react-dom'; -import { ActionComponent } from '@gitroom/extension/pages/content/elements/action.component'; - -// Define a type to track elements with their action types -interface ActionElement { - element: HTMLElement; - actionType: string; -} - -export const MainContent: FC = () => { - return <MainContentInner />; -}; - -export const MainContentInner: FC = (props) => { - const [actionElements, setActionElements] = useState<ActionElement[]>([]); - const actionSetRef = useRef(new Map<HTMLElement, string>()); - const provider = useMemo(() => { - return ProviderList.find((p) => { - return p.baseUrl.indexOf(new URL(window.location.href).hostname) > -1; - }); - }, []); - - useEffect(() => { - if (!provider) return; - - // Helper to scan DOM for existing matching elements - const scanDOMForExistingMatches = () => { - const action = { selector: provider.element, type: 'post' }; - const matches = document.querySelectorAll(action.selector); - matches.forEach((match) => { - const htmlMatch = match as HTMLElement; - if (!actionSetRef.current.has(htmlMatch)) { - actionSetRef.current.set(htmlMatch, action.type); - } - }); - - // Update state - const elements: ActionElement[] = []; - actionSetRef.current.forEach((actionType, element) => { - elements.push({ element, actionType }); - }); - setActionElements(elements); - }; - - // Initial scan before observing - scanDOMForExistingMatches(); - - const observer = new MutationObserver((mutationsList) => { - let addedSomething = false; - let removedSomething = false; - - for (const mutation of mutationsList) { - if (mutation.type === 'childList') { - for (const node of mutation.addedNodes) { - if (node.nodeType === Node.ELEMENT_NODE) { - const el = node as HTMLElement; - - const action = { selector: provider.element, type: 'post' }; - if ( - el.matches?.(action.selector) && - !actionSetRef.current.has(el) - ) { - actionSetRef.current.set(el, action.type); - addedSomething = true; - } - - if (el.querySelectorAll) { - const matches = el.querySelectorAll(action.selector); - matches.forEach((match) => { - const htmlMatch = match as HTMLElement; - if (!actionSetRef.current.has(htmlMatch)) { - actionSetRef.current.set(htmlMatch, action.type); - addedSomething = true; - } - }); - } - } - } - - for (const node of mutation.removedNodes) { - if (node.nodeType === Node.ELEMENT_NODE) { - const el = node as HTMLElement; - - if (actionSetRef.current.has(el)) { - actionSetRef.current.delete(el); - removedSomething = true; - } - - const action = { selector: provider.element, type: 'post' }; - if (el.querySelectorAll) { - const matches = el.querySelectorAll(action.selector); - matches.forEach((match) => { - const htmlMatch = match as HTMLElement; - if (actionSetRef.current.has(htmlMatch)) { - actionSetRef.current.delete(htmlMatch); - removedSomething = true; - } - }); - } - } - } - } - - if (mutation.type === 'attributes') { - const el = mutation.target; - if (el instanceof HTMLElement) { - const action = { selector: provider.element, type: 'post' }; - const matchesNow = el.matches(action.selector); - const wasTracked = actionSetRef.current.has(el); - - if (matchesNow && !wasTracked) { - actionSetRef.current.set(el, action.type); - addedSomething = true; - } else if (!matchesNow && wasTracked) { - actionSetRef.current.delete(el); - removedSomething = true; - } - } - } - } - - if (addedSomething || removedSomething) { - const elements: ActionElement[] = []; - actionSetRef.current.forEach((actionType, element) => { - elements.push({ element, actionType }); - }); - setActionElements(elements); - } - }); - - observer.observe(document.body, { - childList: true, - subtree: true, - attributes: true, - attributeOldValue: true, - }); - - return () => observer.disconnect(); - }, []); - - return actionElements.map((actionEl, index) => ( - <Fragment key={index}> - {createPortal( - <ActionComponent - target={actionEl.element} - keyIndex={index} - actionType={actionEl.actionType} - provider={provider} - wrap={true} - selector={stringToABC( - provider.element - .split(',') - .map((z) => z.trim()) - .find((p) => actionEl.element.matches(p)) || '' - )} - />, - actionEl.element - )} - </Fragment> - )); -}; - -function stringToABC(text: string, length = 8) { - // Simple DJB2-like hash (non-cryptographic!) - let hash = 5381; - for (let i = 0; i < text.length; i++) { - hash = (hash * 33) ^ text.charCodeAt(i); - } - - hash = Math.abs(hash); - - // Convert to base-26 string using a–z - const alphabet = 'abcdefghijklmnopqrstuvwxyz'; - let result = ''; - while (result.length < length) { - result = alphabet[hash % 26] + result; - hash = Math.floor(hash / 26); - } - - return result; -} diff --git a/apps/extension/src/pages/content/style.css b/apps/extension/src/pages/content/style.css deleted file mode 100644 index 7324e9b7..00000000 --- a/apps/extension/src/pages/content/style.css +++ /dev/null @@ -1,27 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -.my-wrapper { - left: 0 !important; - top: 0 !important; - position: fixed !important; - width: 100% !important; - height: 100% !important; - z-index: 999999 !important; - display: flex !important; - justify-content: center !important; - background: rgba(0, 0, 0, 0.5) !important; -} - -.my-wrapper > div { - background: white !important; - width: 600px !important; - height: 300px !important; - border-radius: 10px !important; - display: flex !important; - flex-direction: column !important; - justify-items: center !important; - margin-top: 100px !important; - color: black !important; -} diff --git a/apps/extension/src/pages/options/Options.css b/apps/extension/src/pages/options/Options.css deleted file mode 100644 index 1ea51cba..00000000 --- a/apps/extension/src/pages/options/Options.css +++ /dev/null @@ -1,8 +0,0 @@ -.container { - width: 100%; - height: 50vh; - font-size: 2rem; - display: flex; - align-items: center; - justify-content: center; -} diff --git a/apps/extension/src/pages/options/Options.tsx b/apps/extension/src/pages/options/Options.tsx deleted file mode 100644 index 7f5a709b..00000000 --- a/apps/extension/src/pages/options/Options.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react'; -import '@gitroom/extension/pages/options/Options.css'; - -export default function Options() { - return <div className="container">Options</div>; -} diff --git a/apps/extension/src/pages/options/index.css b/apps/extension/src/pages/options/index.css deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/extension/src/pages/options/index.html b/apps/extension/src/pages/options/index.html deleted file mode 100644 index db5fa745..00000000 --- a/apps/extension/src/pages/options/index.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="UTF-8" /> - <title>Options - - - -
- - - diff --git a/apps/extension/src/pages/options/index.tsx b/apps/extension/src/pages/options/index.tsx deleted file mode 100644 index feaad09b..00000000 --- a/apps/extension/src/pages/options/index.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import '@gitroom/extension/pages/options/index.css'; -import Options from '@gitroom/extension/pages/options/Options'; - -function init() { - const rootContainer = document.querySelector('#__root'); - if (!rootContainer) throw new Error("Can't find Options root element"); - const root = createRoot(rootContainer); - root.render(); -} - -init(); diff --git a/apps/extension/src/pages/panel/Panel.css b/apps/extension/src/pages/panel/Panel.css deleted file mode 100644 index 8a50bc80..00000000 --- a/apps/extension/src/pages/panel/Panel.css +++ /dev/null @@ -1,7 +0,0 @@ -body { - background-color: #242424; -} - -.container { - color: #ffffff; -} diff --git a/apps/extension/src/pages/panel/Panel.tsx b/apps/extension/src/pages/panel/Panel.tsx deleted file mode 100644 index 2157d549..00000000 --- a/apps/extension/src/pages/panel/Panel.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; -import '@pages/panel/Panel.css'; - -export default function Panel() { - return ( -
-

Side Panel

-
- ); -} diff --git a/apps/extension/src/pages/panel/index.css b/apps/extension/src/pages/panel/index.css deleted file mode 100644 index e69de29b..00000000 diff --git a/apps/extension/src/pages/panel/index.html b/apps/extension/src/pages/panel/index.html deleted file mode 100644 index 7aa306d0..00000000 --- a/apps/extension/src/pages/panel/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Devtools Panel - - - -
- - - diff --git a/apps/extension/src/pages/panel/index.tsx b/apps/extension/src/pages/panel/index.tsx deleted file mode 100644 index 797015a1..00000000 --- a/apps/extension/src/pages/panel/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import Panel from '@pages/panel/Panel'; -import '@pages/panel/index.css'; -import '@assets/styles/tailwind.css'; - -function init() { - const rootContainer = document.querySelector('#__root'); - if (!rootContainer) throw new Error("Can't find Panel root element"); - const root = createRoot(rootContainer); - root.render(); -} - -init(); diff --git a/apps/extension/src/pages/popup/Popup.tsx b/apps/extension/src/pages/popup/Popup.tsx deleted file mode 100644 index 604ba2bc..00000000 --- a/apps/extension/src/pages/popup/Popup.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; -import { ProviderList } from '@gitroom/extension/providers/provider.list'; -import { fetchCookie } from '@gitroom/extension/utils/load.cookie'; - -export const PopupContainerContainer: FC = () => { - const [url, setUrl] = useState(null); - useEffect(() => { - chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { - setUrl(tabs[0]?.url); - }); - }, []); - - if (!url) { - return ( -
This website is not supported by Postiz
- ); - } - - return ; -}; - -export const PopupContainer: FC<{ url: string }> = (props) => { - const { url } = props; - const [isLoggedIn, setIsLoggedIn] = useState(false); - const [isLoading, setIsLoading] = useState(true); - const provider = useMemo(() => { - return ProviderList.find((p) => { - return p.baseUrl.indexOf(new URL(url).hostname) > -1; - }); - }, [url]); - - const loadCookie = useCallback(async () => { - try { - if (!provider) { - setIsLoading(false); - return; - } - const auth = await fetchCookie(`auth`); - - if (auth) { - setIsLoggedIn(auth); - } - - setIsLoading(false); - } catch (e) { - setIsLoading(false); - } - }, []); - - useEffect(() => { - loadCookie(); - }, []); - - if (isLoading) { - return null; - } - - if (!provider) { - return ( -
This website is not supported by Postiz
- ); - } - - if (!isLoggedIn) { - return
You are not logged in to Postiz
; - } - - return
; -}; - -export default function Popup() { - return ( -
- -
- ); -} diff --git a/apps/extension/src/pages/popup/index.css b/apps/extension/src/pages/popup/index.css deleted file mode 100644 index d4638762..00000000 --- a/apps/extension/src/pages/popup/index.css +++ /dev/null @@ -1,16 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -body { - width: 300px; - height: 260px; - margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', - 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', - sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - position: relative; -} diff --git a/apps/extension/src/pages/popup/index.html b/apps/extension/src/pages/popup/index.html deleted file mode 100644 index b60f054f..00000000 --- a/apps/extension/src/pages/popup/index.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Popup - - - -
- - - diff --git a/apps/extension/src/pages/popup/index.tsx b/apps/extension/src/pages/popup/index.tsx deleted file mode 100644 index 606ffc55..00000000 --- a/apps/extension/src/pages/popup/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; -import { createRoot } from 'react-dom/client'; -import './index.css'; -import '@gitroom/extension/assets/styles/tailwind.css'; -import Popup from '@gitroom/extension/pages/popup/Popup'; - -function init() { - const rootContainer = document.querySelector('#__root'); - if (!rootContainer) throw new Error("Can't find Popup root element"); - const root = createRoot(rootContainer); - root.render(); -} - -init(); diff --git a/apps/extension/src/providers/cookie-provider.interface.ts b/apps/extension/src/providers/cookie-provider.interface.ts new file mode 100644 index 00000000..60912fe4 --- /dev/null +++ b/apps/extension/src/providers/cookie-provider.interface.ts @@ -0,0 +1,19 @@ +export interface CookieDefinition { + /** The cookie name to extract, e.g., 'client_id' */ + name: string; + /** Whether this cookie must exist for the extraction to be considered successful */ + required: boolean; +} + +export interface CookieProvider { + /** Unique identifier used in messages, e.g., 'skool' */ + identifier: string; + /** Human-readable name, e.g., 'Skool' */ + name: string; + /** URL to query cookies for, e.g., 'https://www.skool.com' — passed to chrome.cookies.getAll({ url }) */ + url: string; + /** URL pattern for host_permissions in manifest, e.g., '*://*.skool.com/*' */ + hostPermission: string; + /** List of cookies to extract from this site */ + cookies: CookieDefinition[]; +} diff --git a/apps/extension/src/providers/list/linkedin.provider.ts b/apps/extension/src/providers/list/linkedin.provider.ts deleted file mode 100644 index 6bf0d191..00000000 --- a/apps/extension/src/providers/list/linkedin.provider.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ProviderInterface } from '@gitroom/extension/providers/provider.interface'; - -export class LinkedinProvider implements ProviderInterface { - identifier = 'linkedin'; - baseUrl = 'https://www.linkedin.com'; - element = `.share-box-feed-entry__closed-share-box`; - attachTo = `[role="main"]`; - style = 'light' as 'light'; - findIdentifier = (element: HTMLElement) => { - return element.closest('[data-urn]').getAttribute('data-urn'); - }; -} diff --git a/apps/extension/src/providers/list/skool.provider.ts b/apps/extension/src/providers/list/skool.provider.ts new file mode 100644 index 00000000..6bfd51cf --- /dev/null +++ b/apps/extension/src/providers/list/skool.provider.ts @@ -0,0 +1,12 @@ +import { CookieProvider } from '../cookie-provider.interface'; + +export const skoolProvider: CookieProvider = { + identifier: 'skool', + name: 'Skool', + url: 'https://www.skool.com', + hostPermission: '*://*.skool.com/*', + cookies: [ + { name: 'client_id', required: true }, + { name: 'auth_token', required: true }, + ], +}; diff --git a/apps/extension/src/providers/list/x.provider.ts b/apps/extension/src/providers/list/x.provider.ts deleted file mode 100644 index 359a70de..00000000 --- a/apps/extension/src/providers/list/x.provider.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ProviderInterface } from '@gitroom/extension/providers/provider.interface'; - -export class XProvider implements ProviderInterface { - identifier = 'x'; - baseUrl = 'https://x.com'; - element = `[data-testid="primaryColumn"]:has([data-testid="toolBar"]) [data-testid="tweetTextarea_0_label"], [data-testid="SideNav_NewTweet_Button"]`; - attachTo = `#react-root`; - style = 'dark' as 'dark'; - findIdentifier = (element: HTMLElement) => { - return ( - Array.from( - ( - element?.closest('article') || - element?.closest(`[aria-labelledby="modal-header"]`) - )?.querySelectorAll('a') || [] - ) - ?.find((p) => { - return p?.getAttribute('href')?.includes('/status/'); - }) - ?.getAttribute('href') - ?.split('/status/')?.[1] || window.location.href.split('/status/')?.[1] - ); - }; -} diff --git a/apps/extension/src/providers/provider.interface.ts b/apps/extension/src/providers/provider.interface.ts deleted file mode 100644 index b7221211..00000000 --- a/apps/extension/src/providers/provider.interface.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface ProviderInterface { - identifier: string; - baseUrl: string; - element: string; - findIdentifier: (element: HTMLElement) => string; - attachTo: string; - style: 'dark' | 'light'; -} diff --git a/apps/extension/src/providers/provider.list.ts b/apps/extension/src/providers/provider.list.ts deleted file mode 100644 index b0faf078..00000000 --- a/apps/extension/src/providers/provider.list.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { XProvider } from './list/x.provider'; -import { ProviderInterface } from './provider.interface'; -import { LinkedinProvider } from './list/linkedin.provider'; - -export const ProviderList = [ - new XProvider(), - new LinkedinProvider(), -] satisfies ProviderInterface[] as ProviderInterface[]; diff --git a/apps/extension/src/providers/provider.registry.ts b/apps/extension/src/providers/provider.registry.ts new file mode 100644 index 00000000..aa992c9e --- /dev/null +++ b/apps/extension/src/providers/provider.registry.ts @@ -0,0 +1,18 @@ +import { CookieProvider } from './cookie-provider.interface'; +import { skoolProvider } from './list/skool.provider'; + +export const providers: CookieProvider[] = [ + skoolProvider, +]; + +const providerMap = new Map( + providers.map((p) => [p.identifier, p]) +); + +export function getAllProviders(): CookieProvider[] { + return providers; +} + +export function getProvider(identifier: string): CookieProvider | undefined { + return providerMap.get(identifier); +} diff --git a/apps/extension/src/types/messages.ts b/apps/extension/src/types/messages.ts new file mode 100644 index 00000000..1469b59c --- /dev/null +++ b/apps/extension/src/types/messages.ts @@ -0,0 +1,85 @@ +// ---- Request Types ---- + +export interface PingRequest { + type: 'PING'; +} + +export interface GetProvidersRequest { + type: 'GET_PROVIDERS'; +} + +export interface GetCookiesRequest { + type: 'GET_COOKIES'; + provider: string; +} + +export interface StoreRefreshTokenRequest { + type: 'STORE_REFRESH_TOKEN'; + provider: string; + integrationId: string; + jwt: string; + backendUrl: string; +} + +export interface RemoveRefreshTokenRequest { + type: 'REMOVE_REFRESH_TOKEN'; + integrationId: string; +} + +export type ExtensionRequest = + | PingRequest + | GetProvidersRequest + | GetCookiesRequest + | StoreRefreshTokenRequest + | RemoveRefreshTokenRequest; + +// ---- Response Types ---- + +export interface PingResponse { + status: 'ok'; + version: string; +} + +export interface ProviderInfo { + identifier: string; + name: string; + url: string; + cookieNames: string[]; +} + +export interface GetProvidersResponse { + providers: ProviderInfo[]; +} + +export interface GetCookiesSuccessResponse { + success: true; + provider: string; + cookies: Record; +} + +export interface GetCookiesErrorResponse { + success: false; + provider: string; + error: string; + missingCookies?: string[]; +} + +export type GetCookiesResponse = + | GetCookiesSuccessResponse + | GetCookiesErrorResponse; + +export interface StoredRefreshEntry { + jwt: string; + backendUrl: string; + provider: string; +} + +export interface ErrorResponse { + error: string; +} + +export type ExtensionResponse = + | PingResponse + | GetProvidersResponse + | GetCookiesResponse + | ErrorResponse; diff --git a/apps/extension/src/utils/load.cookie.ts b/apps/extension/src/utils/load.cookie.ts deleted file mode 100644 index 77164ab8..00000000 --- a/apps/extension/src/utils/load.cookie.ts +++ /dev/null @@ -1,13 +0,0 @@ -export const fetchCookie = (cookieName: string) => { - return chrome.runtime.sendMessage({ - action: 'loadCookie', - cookieName, - }); -}; - -export const getCookie = async ( - cookies: chrome.cookies.Cookie[], - cookie: string -) => { - // return cookies.find((c) => c.name === cookie).value; -}; diff --git a/apps/extension/src/utils/load.storage.ts b/apps/extension/src/utils/load.storage.ts deleted file mode 100644 index 0f5de843..00000000 --- a/apps/extension/src/utils/load.storage.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const fetchStorage = (key: string) => { - return chrome.runtime.sendMessage({ - action: 'loadStorage', - key, - }); -}; diff --git a/apps/extension/src/utils/request.util.ts b/apps/extension/src/utils/request.util.ts deleted file mode 100644 index 7da73fed..00000000 --- a/apps/extension/src/utils/request.util.ts +++ /dev/null @@ -1,33 +0,0 @@ -const isDev = process.env.NODE_ENV === 'development'; -export const sendRequest = ( - auth: string, - url: string, - method: 'GET' | 'POST', - body?: string -) => { - return chrome.runtime.sendMessage({ - action: 'makeHttpRequest', - url, - method, - body, - auth, - }); -}; - -export const fetchRequestUtil = async (request: any) => { - return ( - await fetch( - (import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL) + - request.url, - { - method: request.method || 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: request.auth, - // Add any auth headers here if needed - }, - ...(request.body ? { body: request.body } : {}), - } - ) - ).json(); -}; diff --git a/apps/extension/src/utils/save.storage.ts b/apps/extension/src/utils/save.storage.ts deleted file mode 100644 index f9e2fbdb..00000000 --- a/apps/extension/src/utils/save.storage.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const saveStorage = (key: string, value: any) => { - return chrome.runtime.sendMessage({ - action: 'saveStorage', - key, - value, - }); -}; diff --git a/apps/extension/src/vite-env.d.ts b/apps/extension/src/vite-env.d.ts deleted file mode 100644 index 11f02fe2..00000000 --- a/apps/extension/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/apps/extension/tsconfig.json b/apps/extension/tsconfig.json index 45dfbf4e..eab667fd 100644 --- a/apps/extension/tsconfig.json +++ b/apps/extension/tsconfig.json @@ -21,7 +21,6 @@ "src", "utils", "vite.config.base.ts", - "vite.config.chrome.ts", - "vite.config.firefox.ts" + "vite.config.chrome.ts" ] } diff --git a/apps/extension/vite.config.base.ts b/apps/extension/vite.config.base.ts index e3e81581..6a880897 100644 --- a/apps/extension/vite.config.base.ts +++ b/apps/extension/vite.config.base.ts @@ -1,38 +1,28 @@ import react from '@vitejs/plugin-react'; import { resolve } from 'path'; import { ManifestV3Export } from '@crxjs/vite-plugin'; -import tailwindcss from '@tailwindcss/vite'; import { defineConfig, BuildOptions } from 'vite'; import tsconfigPaths from 'vite-tsconfig-paths'; import { stripDevIcons, crxI18n } from './custom-vite-plugins'; import manifest from './manifest.json'; import devManifest from './manifest.dev.json'; import pkg from './package.json'; -import { ProviderList } from './src/providers/provider.list'; +import { providers } from './src/providers/provider.registry'; const isDev = process.env.NODE_ENV === 'development'; // set this flag to true, if you want localization support const localize = false; const merge = isDev ? devManifest : ({} as ManifestV3Export); -const { matches, ...rest } = manifest?.content_scripts?.[0] || {}; export const baseManifest = { ...manifest, host_permissions: [ - ...ProviderList.map((p) => p.baseUrl + '/'), import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL + '/*', + (import.meta.env?.NEXT_PUBLIC_BACKEND_URL || process?.env?.NEXT_PUBLIC_BACKEND_URL || '') + '/*', + ...providers.map(p => p.hostPermission) ], permissions: [...(manifest.permissions || [])], - content_scripts: [ - { - matches: ProviderList.reduce( - (all, p) => [...all, p.baseUrl + '/*'], - [import.meta.env?.FRONTEND_URL || process?.env?.FRONTEND_URL + '/*'] - ), - ...rest, - }, - ], version: pkg.version, ...merge, ...(localize @@ -50,9 +40,8 @@ export const baseBuildOptions: BuildOptions = { }; export default defineConfig({ - envPrefix: ['NEXT_PUBLIC_', 'FRONTEND_URL'], + envPrefix: ['NEXT_PUBLIC_', 'FRONTEND_URL', 'NEXT_PUBLIC_BACKEND_URL'], plugins: [ - tailwindcss(), tsconfigPaths(), react(), stripDevIcons(isDev), diff --git a/apps/extension/vite.config.chrome.ts b/apps/extension/vite.config.chrome.ts index 56fc94b8..1df4a600 100644 --- a/apps/extension/vite.config.chrome.ts +++ b/apps/extension/vite.config.chrome.ts @@ -15,7 +15,7 @@ export default mergeConfig( manifest: { ...baseManifest, background: { - service_worker: 'src/pages/background/index.ts', + service_worker: 'src/background.ts', type: 'module', }, } as ManifestV3Export, @@ -28,7 +28,7 @@ export default mergeConfig( ? [ hotReloadExtension({ log: true, - backgroundPath: 'src/pages/background/index.ts', + backgroundPath: 'src/background.ts', }), ] : []), diff --git a/apps/extension/vite.config.firefox.ts b/apps/extension/vite.config.firefox.ts deleted file mode 100644 index b1a8bf9b..00000000 --- a/apps/extension/vite.config.firefox.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { resolve } from 'path'; -import { mergeConfig, defineConfig } from 'vite'; -import { crx, ManifestV3Export } from '@crxjs/vite-plugin'; -import baseConfig, { baseManifest, baseBuildOptions } from './vite.config.base'; - -const outDir = resolve(__dirname, 'dist_firefox'); - -export default mergeConfig( - baseConfig, - defineConfig({ - plugins: [ - crx({ - manifest: { - ...baseManifest, - background: { - scripts: ['src/pages/background/index.ts'], - }, - } as ManifestV3Export, - browser: 'firefox', - contentScripts: { - injectCss: true, - }, - }), - ], - build: { - ...baseBuildOptions, - outDir, - }, - publicDir: resolve(__dirname, 'public'), - }) -); diff --git a/apps/extension/vite.config.ts b/apps/extension/vite.config.ts new file mode 100644 index 00000000..e69fb7f9 --- /dev/null +++ b/apps/extension/vite.config.ts @@ -0,0 +1,23 @@ +import { resolve } from 'path'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + build: { + outDir: resolve(__dirname, 'dist'), + emptyOutDir: true, + lib: { + entry: resolve(__dirname, 'src/background.ts'), + formats: ['es'], + fileName: () => 'background.js', + }, + rollupOptions: { + output: { + entryFileNames: 'background.js', + }, + }, + target: 'esnext', + minify: false, + sourcemap: process.env.NODE_ENV === 'development', + }, + publicDir: resolve(__dirname, 'public'), +}); diff --git a/apps/frontend/public/icons/platforms/skool.png b/apps/frontend/public/icons/platforms/skool.png new file mode 100644 index 0000000000000000000000000000000000000000..972a5339e15f9c655c4fe6f618345adf6222dda9 GIT binary patch literal 1805 zcmV+o2lDudP)pbtduX#CkGA1#N652SRDQ()+nnJ)f zD1#%e{cb z(lW?W(=y0X(=y0XlXb~0h#;;By8gC~5>R^PcNU;a3t|Y#pigPvzY(v-lnCCI4`HqD zSTs|Y+#{+S61;o@qv&3^X$a1Bz3(j$5fINpGW8x2Ll)J}6%*{sAWKbG49L8K-7vti zG?+Cv6~v(|=8*t~q*>x(Db;WuO{pai+tP>w)w1xPH*HS5Ym&r*X)1cV71ypw$QKpF z5QWmr*4)w{u8Asz=t^8a7A8o$i&*B2ug(c#JDM|tCeOa)(f^XmTLTuQsvwen9M^`m zy%D!wAF%C~fW8k$B(b0uKqFb3;MM<9e(E)JPy?bgl6Po(a0>`CdSzA+(}4Yl9lr6i zHr_pFVp-tYnnL!%Od2?T+UDp9o8LW`WBZm0-~OVHYtN5FEQ9#uFq4nI0+vyC&S(wA zof{F$qS+T}8teijTJz1Hw=ptqkk4u|jz$E+2uf9#u4G0ke3-&MAKISb>2-i?*Dah6`e0?aG)nJrR}A;EfYD z@0>GmZHN=tz1^p$OQBTj5Qb+n@#G=m<0FW3VM#ht7*`c2?%sq>W}mS6))IL7ui0q` zfG`$r>Z|g^_r@?yK*Kvj29H0V$Vb{fv*+^YDme5S$xV|sKY*(mJlSDyJm*QJ@`$XjurVT1-F%=tvSO(e<{JpOt zS-XY+bU7x!Ya`~$c8KS8Z_^mDr{=BsxL!uH@6S2*@Ao)2D%h?3Ziu_UJDEv5aC z@ousux@2uackg{HjkH84oei;F5@=VL1bKg(fx%2)`-X5?pTBzW3qS3KJC-n=~aYE_$ zhtqRp9xSt(46DNTzcRt@yUO&wKc*T9rK*sq`mK<0H2JLN^q|FeepR3v%x&20En=_h z#_Y&8jK#->s2)63-yZ)zAOM~Pdv;Fn?2m@|^@F3_wyA>WDkjT97}W#Wmgb*F9fro{ zm{x@B9qTFHx0%A%ZULvhev+JvzxM#iXt^PN77U_P>X~8=w%io(*aPGA|9FJ&?wVj# zPu!5DCVT_GVlbz9r9mmIRqZ$%J^*1&mJkqAC_jA!=ALP85D{=Zjg!?FmL{$WaVV_p zN!WQ;ncwUgqr0;)J+@rZYtW^L+~+=u3NK1`A_V_9N%Zy*(`j=dAOeU9kL=C!^`8_u z__|H3z|LyKN>?hTZunlgtfJtl*5g!bjEsf%scRsrhY>^Ye|s>ky=Ll9YSg^eDkvWM zWsygp%HukkXI}KU_Uedjn*(~f5{5>FKl~-j*%71egMDyCQdb?f#1bTUx342SbQ5+&>K$Li|Di$acau-$%^pl zn=5n_^t?Mpv)#k}#2Tv4yj~al;#!0UPLX_a9Y(=z`u>btOOtweMOVVOpJpcwxK3KG z=e_jTu{BCTH4rAs!mc}f?)`kJ@hsC#uG&YUgxvN%Xv?H!bj@>mGNAgm&J}lJVtHBJGCIoAQ`EkM$7dBi9*^u%nGNX`{9O zo=o|0y0#bMc$SnaAa>)H&~ft~W4c4UScz`hV6{{OD3#V;3zAN~GOt%!EZu8$#r#3H v46@X;46@X;46@X8#ei7A$mL$dBB=9!eS*|`4+}}u00000NkvXXu0mjf?FW81 literal 0 HcmV?d00001 diff --git a/apps/frontend/src/app/(app)/layout.tsx b/apps/frontend/src/app/(app)/layout.tsx index a6a62c02..c7cf60a4 100644 --- a/apps/frontend/src/app/(app)/layout.tsx +++ b/apps/frontend/src/app/(app)/layout.tsx @@ -78,6 +78,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { disableImageCompression={!!process.env.DISABLE_IMAGE_COMPRESSION} disableXAnalytics={!!process.env.DISABLE_X_ANALYTICS} sentryDsn={process.env.NEXT_PUBLIC_SENTRY_DSN!} + extensionId={process.env.EXTENSION_ID || ''} language={allHeaders.get(headerName)} transloadit={ process.env.TRANSLOADIT_AUTH && process.env.TRANSLOADIT_TEMPLATE diff --git a/apps/frontend/src/app/(extension)/layout.tsx b/apps/frontend/src/app/(extension)/layout.tsx index 4a02c5ea..3f3d83f7 100644 --- a/apps/frontend/src/app/(extension)/layout.tsx +++ b/apps/frontend/src/app/(extension)/layout.tsx @@ -49,6 +49,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { disableImageCompression={!!process.env.DISABLE_IMAGE_COMPRESSION} disableXAnalytics={!!process.env.DISABLE_X_ANALYTICS} sentryDsn={process.env.NEXT_PUBLIC_SENTRY_DSN!} + extensionId={process.env.EXTENSION_ID || ''} transloadit={ process.env.TRANSLOADIT_AUTH && process.env.TRANSLOADIT_TEMPLATE ? [ diff --git a/apps/frontend/src/chrome.d.ts b/apps/frontend/src/chrome.d.ts new file mode 100644 index 00000000..be91b792 --- /dev/null +++ b/apps/frontend/src/chrome.d.ts @@ -0,0 +1,15 @@ +/** + * Minimal Chrome extension API types for externally_connectable messaging. + * Web pages listed in an extension's externally_connectable can use + * chrome.runtime.sendMessage to communicate with the extension. + */ +declare namespace chrome { + namespace runtime { + const lastError: { message?: string } | undefined; + function sendMessage( + extensionId: string, + message: any, + callback: (response: any) => void + ): void; + } +} diff --git a/apps/frontend/src/components/launches/add.provider.component.tsx b/apps/frontend/src/components/launches/add.provider.component.tsx index db11ae2f..0442002a 100644 --- a/apps/frontend/src/components/launches/add.provider.component.tsx +++ b/apps/frontend/src/components/launches/add.provider.component.tsx @@ -251,6 +251,109 @@ export const CustomVariables: FC<{
); }; +const ExtensionNotFound: FC = () => { + const modals = useModals(); + const t = useT(); + return ( +
+

+ {t( + 'extension_not_available', + 'The Postiz browser extension is not installed. You need to install it before connecting this channel.' + )} +

+
+ + +
+
+ ); +}; + +const ChromeExtensionWarning: FC<{ + onConfirm: () => void; + onCancel: () => void; +}> = ({ onConfirm, onCancel }) => { + const modals = useModals(); + const t = useT(); + return ( +
+

+ {t( + 'chrome_extension_warning_intro', + 'This channel connects via the browser extension. Please be aware of the following:' + )} +

+
    +
  • + {t( + 'chrome_extension_warning_tos', + 'Using a browser extension to interact with a platform may violate its terms of service and could result in your account being suspended or banned.' + )} +
  • +
  • + {t( + 'chrome_extension_warning_unstable', + 'This method is not as reliable as native integrations and may experience random disconnections.' + )} +
  • +
  • + {t( + 'chrome_extension_warning_reconnect', + 'You may need to reconnect periodically if the session expires.' + )} +
  • +
  • + We will store your cookies securely to facilitate the connection. +
  • +
  • + Postiz does not take responsibility for any issues arising or account termination due to the use of this method. +
  • +
+
+ + +
+
+ ); +}; + export const AddProviderComponent: FC<{ social: Array<{ identifier: string; @@ -258,6 +361,11 @@ export const AddProviderComponent: FC<{ toolTip?: string; isExternal: boolean; isWeb3: boolean; + isChromeExtension?: boolean; + extensionCookies?: Array<{ + name: string; + domain: string; + }>; customFields?: Array<{ key: string; label: string; @@ -274,7 +382,7 @@ export const AddProviderComponent: FC<{ onboarding?: boolean; }> = (props) => { const { update, social, article, onboarding } = props; - const { isGeneral } = useVariables(); + const { isGeneral, extensionId } = useVariables(); const toaster = useToaster(); const router = useRouter(); const fetch = useFetch(); @@ -285,6 +393,7 @@ export const AddProviderComponent: FC<{ identifier: string, isExternal: boolean, isWeb3: boolean, + isChromeExtension?: boolean, customFields?: Array<{ key: string; label: string; @@ -364,6 +473,106 @@ export const AddProviderComponent: FC<{ openWeb3(); return; } + if (isChromeExtension) { + const confirmed = await new Promise((resolve) => { + modal.openModal({ + title: t('chrome_extension_notice', 'Browser Extension Notice'), + withCloseButton: true, + onClose: () => resolve(false), + children: ( + { + resolve(true); + }} + onCancel={() => { + resolve(false); + }} + /> + ), + }); + }); + if (!confirmed) { + return; + } + if (!extensionId || !chrome?.runtime?.sendMessage) { + modal.openModal({ + title: t('extension_not_available_title', 'Extension Not Found'), + withCloseButton: true, + children: , + }); + return; + } + try { + await new Promise((resolve, reject) => { + chrome.runtime.sendMessage( + extensionId, + { type: 'PING' }, + (response: any) => { + if (chrome.runtime.lastError || !response?.status) { + reject(new Error('Extension not reachable')); + } else { + resolve(); + } + } + ); + }); + } catch { + toaster.show( + t( + 'extension_not_installed', + 'Postiz browser extension is not installed or not reachable.' + ), + 'warning' + ); + return; + } + try { + const cookieResponse = await new Promise((resolve, reject) => { + chrome.runtime.sendMessage( + extensionId, + { type: 'GET_COOKIES', provider: identifier }, + (response: any) => { + if (chrome.runtime.lastError) { + reject(new Error(chrome.runtime.lastError.message)); + } else { + resolve(response); + } + } + ); + }); + if (!cookieResponse.success) { + toaster.show( + cookieResponse.error || + t( + 'extension_cookies_missing', + 'Could not get cookies. Please log in to the platform first.' + ), + 'warning' + ); + return; + } + const { url } = await ( + await fetch( + `/integrations/social/${identifier}${ + onboarding ? '?onboarding=true' : '' + }` + ) + ).json(); + modal.closeAll(); + window.location.href = `/integrations/social/${identifier}?state=${url}&code=${Buffer.from( + JSON.stringify(cookieResponse.cookies) + ).toString('base64')}${onboarding ? '&onboarding=true' : ''}`; + } catch { + toaster.show( + t( + 'extension_communication_error', + 'Failed to communicate with the browser extension.' + ), + 'warning' + ); + } + return; + } if (isExternal) { modal.openModal({ title: 'URL', @@ -415,7 +624,12 @@ export const AddProviderComponent: FC<{ return true; } - return !item.isExternal && !item.isWeb3 && !item.customFields; + return ( + !item.isExternal && + !item.isWeb3 && + !item.isChromeExtension && + !item.customFields + ); }) .map((item) => (
(null); const [twoStepState, setTwoStepState] = useState(null); @@ -135,9 +137,34 @@ export const ContinueIntegration: FC<{ onboarding: resOnboarding, pages, returnURL, + extensionToken, } = await data.json(); const onboarding = resOnboarding || searchParams.onboarding === 'true'; + // Store refresh token in extension for background cookie refresh + if ( + extensionToken && + extensionId && + typeof chrome !== 'undefined' && + chrome?.runtime?.sendMessage + ) { + try { + chrome.runtime.sendMessage( + extensionId, + { + type: 'STORE_REFRESH_TOKEN', + provider, + integrationId: id, + jwt: extensionToken, + backendUrl, + }, + () => {} + ); + } catch { + // Silently ignore — extension may not be available + } + } + // If it's a two-step provider, show the selection UI inline if (inBetweenSteps && !searchParams.refresh) { setTwoStepState({ diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index 8efd153a..03e8f792 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -25,6 +25,7 @@ import { Integration } from '@prisma/client'; import { SettingsModal } from '@gitroom/frontend/components/launches/settings.modal'; import { CustomVariables } from '@gitroom/frontend/components/launches/add.provider.component'; import { useRouter } from 'next/navigation'; +import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; import dayjs from 'dayjs'; @@ -59,6 +60,7 @@ export const Menu: FC<{ const fetch = useFetch(); const router = useRouter(); + const { extensionId } = useVariables(); const { integrations, reloadCalendarView } = useCalendar(); const toast = useToaster(); const modal = useModals(); @@ -146,10 +148,26 @@ export const Menu: FC<{ ); return; } + // Clean up extension refresh token if applicable + if ( + extensionId && + typeof chrome !== 'undefined' && + chrome?.runtime?.sendMessage + ) { + try { + chrome.runtime.sendMessage( + extensionId, + { type: 'REMOVE_REFRESH_TOKEN', integrationId: id }, + () => {} + ); + } catch { + // Silently ignore + } + } toast.show(t('channel_deleted', 'Channel Deleted'), 'success'); setShow(false); onChange(true); - }, [t]); + }, [t, extensionId, id]); const enableChannel = useCallback(async () => { await fetch('/integrations/enable', { diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index b7592150..511f25d8 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -526,7 +526,7 @@ export const EditorWrapper: FC<{ }; export const Editor: FC<{ - editorType?: 'normal' | 'markdown' | 'html'; + editorType?: 'none' | 'normal' | 'markdown' | 'html'; totalPosts: number; value: string; num?: number; @@ -777,14 +777,18 @@ export const Editor: FC<{ toolBar={
- - + {editorType !== 'none' && ( + <> + + + + )} {(editorType === 'markdown' || editorType === 'html') && identifier !== 'telegram' && ( <> @@ -854,7 +858,7 @@ export const Editor: FC<{ export const OnlyEditor = forwardRef< any, { - editorType: 'normal' | 'markdown' | 'html'; + editorType: 'none' | 'normal' | 'markdown' | 'html'; value: string; onChange: (value: string) => void; paste?: (event: ClipboardEvent | File[]) => void; diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 09fbc0fa..b9a3ec8f 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -36,6 +36,7 @@ import WordpressProvider from '@gitroom/frontend/components/new-launch/providers import ListmonkProvider from '@gitroom/frontend/components/new-launch/providers/listmonk/listmonk.provider'; import GmbProvider from '@gitroom/frontend/components/new-launch/providers/gmb/gmb.provider'; import MoltbookProvider from '@gitroom/frontend/components/new-launch/providers/moltbook/moltbook.provider'; +import SkoolProvider from '@gitroom/frontend/components/new-launch/providers/skool/skool.provider'; export const Providers = [ { @@ -157,6 +158,10 @@ export const Providers = [ { identifier: 'moltbook', component: MoltbookProvider, + }, + { + identifier: 'skool', + component: SkoolProvider, } ]; export const ShowAllProviders = forwardRef((props, ref) => { diff --git a/apps/frontend/src/components/new-launch/providers/skool/skool.group.select.tsx b/apps/frontend/src/components/new-launch/providers/skool/skool.group.select.tsx new file mode 100644 index 00000000..69dacd1e --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/skool/skool.group.select.tsx @@ -0,0 +1,57 @@ +'use client'; + +import { FC, useEffect, useState } from 'react'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { Select } from '@gitroom/react/form/select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; +export const SkoolGroupSelect: FC<{ + name: string; + onChange: (event: { + target: { + value: string; + name: string; + }; + }) => void; +}> = (props) => { + const { onChange, name } = props; + const t = useT(); + const customFunc = useCustomProviderFunction(); + const [groups, setGroups] = useState([]); + const { getValues } = useSettings(); + const [currentGroup, setCurrentGroup] = useState(); + const onChangeInner = (event: { + target: { + value: string; + name: string; + }; + }) => { + setCurrentGroup(event.target.value); + onChange(event); + }; + useEffect(() => { + customFunc.get('groups').then((data) => setGroups(data)); + const settings = getValues()[props.name]; + if (settings) { + setCurrentGroup(settings); + } + }, []); + if (!groups.length) { + return null; + } + return ( + + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/skool/skool.label.select.tsx b/apps/frontend/src/components/new-launch/providers/skool/skool.label.select.tsx new file mode 100644 index 00000000..037e5235 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/skool/skool.label.select.tsx @@ -0,0 +1,65 @@ +'use client'; + +import { FC, useEffect, useState } from 'react'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { Select } from '@gitroom/react/form/select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; +export const SkoolLabelSelect: FC<{ + name: string; + groupId: string | undefined; + onChange: (event: { + target: { + value: string; + name: string; + }; + }) => void; +}> = (props) => { + const { onChange, name, groupId } = props; + const t = useT(); + const customFunc = useCustomProviderFunction(); + const [labels, setLabels] = useState([]); + const { getValues } = useSettings(); + const [currentLabel, setCurrentLabel] = useState(); + const onChangeInner = (event: { + target: { + value: string; + name: string; + }; + }) => { + setCurrentLabel(event.target.value); + onChange(event); + }; + useEffect(() => { + if (!groupId) { + setLabels([]); + setCurrentLabel(undefined); + return; + } + customFunc.get('label', { id: groupId }).then((data) => setLabels(data)); + }, [groupId]); + useEffect(() => { + const settings = getValues()[name]; + if (settings) { + setCurrentLabel(settings); + } + }, []); + if (!groupId || !labels.length) { + return null; + } + return ( + + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx b/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx new file mode 100644 index 00000000..9c21abca --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx @@ -0,0 +1,37 @@ +'use client'; + +import { + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { FC, useState } from 'react'; +import { SkoolDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/skool.dto'; +import { SkoolGroupSelect } from '@gitroom/frontend/components/new-launch/providers/skool/skool.group.select'; +import { SkoolLabelSelect } from '@gitroom/frontend/components/new-launch/providers/skool/skool.label.select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { Input } from '@gitroom/react/form/input'; +const SkoolComponent: FC = () => { + const form = useSettings(); + const [selectedGroup, setSelectedGroup] = useState( + form.getValues().group + ); + const groupRegister = form.register('group'); + const onGroupChange = (event: { target: { value: string; name: string } }) => { + setSelectedGroup(event.target.value); + groupRegister.onChange(event); + }; + return ( +
+ + + +
+ ); +}; +export default withProvider({ + minimumCharacters: [], + SettingsComponent: SkoolComponent, + CustomPreviewComponent: undefined, + dto: SkoolDto, + checkValidity: undefined, + maximumCharacters: 5000, +}); diff --git a/apps/frontend/src/components/new-launch/store.ts b/apps/frontend/src/components/new-launch/store.ts index 879526ea..f848bc60 100644 --- a/apps/frontend/src/components/new-launch/store.ts +++ b/apps/frontend/src/components/new-launch/store.ts @@ -26,7 +26,7 @@ export interface SelectedIntegrations { } interface StoreState { - editor: undefined | 'normal' | 'markdown' | 'html'; + editor: undefined | 'none' | 'normal' | 'markdown' | 'html'; loaded: boolean; date: dayjs.Dayjs; postComment: PostComment; @@ -128,7 +128,7 @@ interface StoreState { setPostComment: (postComment: PostComment) => void; setActivateExitButton?: (activateExitButton: boolean) => void; setDummy: (dummy: boolean) => void; - setEditor: (editor: 'normal' | 'markdown' | 'html') => void; + setEditor: (editor: 'none' | 'normal' | 'markdown' | 'html') => void; setLoaded?: (loaded: boolean) => void; setChars: (id: string, chars: number) => void; chars: Record; @@ -613,7 +613,7 @@ export const useLaunchStore = create()((set) => ({ set((state) => ({ dummy, })), - setEditor: (editor: 'normal' | 'markdown' | 'html') => + setEditor: (editor: 'none' | 'normal' | 'markdown' | 'html') => set((state) => ({ editor, })), diff --git a/libraries/helpers/src/utils/strip.html.validation.ts b/libraries/helpers/src/utils/strip.html.validation.ts index 682fbbc5..7691bb46 100644 --- a/libraries/helpers/src/utils/strip.html.validation.ts +++ b/libraries/helpers/src/utils/strip.html.validation.ts @@ -145,6 +145,16 @@ export const stripHtmlValidation = ( const value = serialize(parseFragment(val)); + if (type === 'none') { + return striptags(value) + .replace(/>/gi, '>') + .replace(/</gi, '<') + .replace(/&/gi, '&') + .replace(/ /gi, ' ') + .replace(/"/gi, '"') + .replace(/'/gi, "'"); + } + if (type === 'html') { return striptags(convertMention(value, convertMentionFunction), [ 'ul', diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 2bce9a69..6bf17192 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -21,6 +21,7 @@ import { GmbSettingsDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-s import { FarcasterDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/farcaster.dto'; import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/facebook.dto'; import { MoltbookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/moltbook.dto'; +import { SkoolDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/skool.dto'; export type ProviderExtension = { __type: T } & M; export type AllProvidersSettings = @@ -53,7 +54,8 @@ export type AllProvidersSettings = | ProviderExtension<'telegram', None> | ProviderExtension<'nostr', None> | ProviderExtension<'moltbook', MoltbookDto> - | ProviderExtension<'vk', None>; + | ProviderExtension<'vk', None> + | ProviderExtension<'skool', SkoolDto>; type None = NonNullable; @@ -89,6 +91,7 @@ export const allProviders = (setEmpty?: any) => { { value: setEmpty, name: 'nostr' }, { value: setEmpty, name: 'vk' }, { value: MoltbookDto, name: 'moltbook' }, + { value: SkoolDto, name: 'skool' }, ].filter((f) => f.value); }; diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/skool.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/skool.dto.ts new file mode 100644 index 00000000..84eac005 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/skool.dto.ts @@ -0,0 +1,28 @@ +import { IsDefined, IsString, MinLength } from 'class-validator'; +import { JSONSchema } from 'class-validator-jsonschema'; + +export class SkoolDto { + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Group must be an id', + }) + group: string; + + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Label must be an id', + }) + label: string; + + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Title of the post', + }) + title: string; +} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 0bb78338..ba2ff607 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -33,6 +33,7 @@ import { KickProvider } from '@gitroom/nestjs-libraries/integrations/social/kick import { TwitchProvider } from '@gitroom/nestjs-libraries/integrations/social/twitch.provider'; import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { MoltbookProvider } from '@gitroom/nestjs-libraries/integrations/social/moltbook.provider'; +import { SkoolProvider } from '@gitroom/nestjs-libraries/integrations/social/skool.provider'; export const socialIntegrationList: Array = [ new XProvider(), @@ -65,6 +66,7 @@ export const socialIntegrationList: Array = [ new WordpressProvider(), new ListmonkProvider(), new MoltbookProvider(), + new SkoolProvider(), // new MastodonCustomProvider(), ]; @@ -80,6 +82,8 @@ export class IntegrationManager { editor: p.editor, isExternal: !!p.externalUrl, isWeb3: !!p.isWeb3, + isChromeExtension: !!p.isChromeExtension, + ...(p.extensionCookies ? { extensionCookies: p.extensionCookies } : {}), ...(p.customFields ? { customFields: await p.customFields() } : {}), })) ), diff --git a/libraries/nestjs-libraries/src/integrations/social/skool.provider.ts b/libraries/nestjs-libraries/src/integrations/social/skool.provider.ts new file mode 100644 index 00000000..78151dbf --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/skool.provider.ts @@ -0,0 +1,333 @@ +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '../social.abstract'; +import { + AuthTokenDetails, + MediaContent, + PostDetails, + PostResponse, + SocialProvider, +} from './social.integrations.interface'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; +import { SkoolDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/skool.dto'; +import { AuthService } from '@gitroom/helpers/auth/auth.service'; + +export class SkoolProvider extends SocialAbstract implements SocialProvider { + identifier = 'skool'; + name = 'Skool'; + isBetweenSteps = false; + isChromeExtension = true; + scopes = [] as string[]; + editor = 'normal' as const; + dto = SkoolDto; + + extensionCookies = [ + { name: 'client_id', domain: '.skool.com' }, + { name: 'auth_token', domain: '.skool.com' }, + ]; + + private getCookies(integration: Integration): { + client_id: string; + auth_token: string; + } { + return AuthService.verifyJWT(integration.customInstanceDetails!) as { + client_id: string; + auth_token: string; + }; + } + + override handleErrors( + body: string + ): + | { type: 'refresh-token' | 'bad-body' | 'retry'; value: string } + | undefined { + if (body.includes('must be admin or level')) { + return { type: 'bad-body', value: 'You can\'t post to this channel' }; + } + if (body.includes('cannot post to this label')) { + return { type: 'bad-body', value: 'Cannot post to this label' }; + } + return undefined; + } + + maxLength() { + return 5000; + } + + async refreshToken(refreshToken: string): Promise { + return { + refreshToken: '', + expiresIn: 0, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + }; + } + + async generateAuthUrl() { + const state = makeId(6); + return { + url: state, + codeVerifier: makeId(10), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + try { + const cookies: Record = JSON.parse( + Buffer.from(params.code, 'base64').toString() + ); + + const missing = this.extensionCookies + .map((c) => c.name) + .filter((name) => !cookies[name]); + + if (missing.length > 0) { + return `Missing required cookies: ${missing.join(', ')}`; + } + + const data = await ( + await fetch('https://api2.skool.com/self', { + method: 'GET', + headers: { + Cookie: `auth_token=${cookies.auth_token}; client_id=${cookies.client_id}`, + }, + }) + ).json(); + + return { + refreshToken: '', + expiresIn: dayjs().add(100, 'year').unix() - dayjs().unix(), + accessToken: AuthService.signJWT(cookies), + id: data.id, + name: data.first_name + ' ' + data.last_name, + picture: data.metadata.picture_profile || '', + username: data.name, + }; + } catch (e) { + return 'Invalid cookie data'; + } + } + + @Tool({ description: 'Groups', dataSchema: [] }) + async groups(accessToken: string, params: any, id: string, integration: Integration) { + try { + const { client_id, auth_token } = this.getCookies(integration); + const { groups } = await ( + await fetch( + `https://api2.skool.com/users/${id}/groups?offset=0&limit=30`, + { + headers: { + Cookie: `auth_token=${auth_token}; client_id=${client_id}`, + }, + } + ) + ).json(); + + return groups.map((p: any) => ({ + id: String(p.id), + name: p.metadata.display_name, + })); + } catch (err) { + return []; + } + } + + @Tool({ description: 'Label', dataSchema: [] }) + async label(accessToken: string, params: any, id: string, integration: Integration) { + try { + const { client_id, auth_token } = this.getCookies(integration); + const { metadata } = await ( + await this.fetch(`https://api2.skool.com/groups/${params.id}`, { + headers: { + Cookie: `auth_token=${auth_token}; client_id=${client_id}`, + }, + }) + ).json(); + + if (!metadata.labels || metadata.labels.length === 0) { + return [{ id: 'none', name: 'Default Label' }]; + } + + const labels = metadata.labels.split(','); + + if (labels.length === 0) { + return [{ id: 'none', name: 'Default Label' }]; + } + + const labelInformation = await Promise.all( + labels.map(async (labelId: string) => { + return ( + await this.fetch(`https://api2.skool.com/labels/${labelId}`, { + headers: { + Cookie: `auth_token=${auth_token}; client_id=${client_id}`, + }, + }) + ).json(); + }) + ); + + return labelInformation.map((p: any) => ({ + id: String(p.id), + name: p.metadata.display_name, + })); + } catch (err) { + return []; + } + } + + private async uploadMediaToSkool( + media: MediaContent[], + userId: string, + cookies: { client_id: string; auth_token: string } + ): Promise { + if (!media || media.length === 0) return ''; + + const fileIds: string[] = []; + + for (const item of media) { + const fileResponse = await fetch(item.path); + const fileBuffer = await fileResponse.arrayBuffer(); + const contentType = + fileResponse.headers.get('content-type') || 'application/octet-stream'; + const fileName = item.path.split('/').pop() || 'file'; + + const createFileResponse = await ( + await this.fetch('https://api2.skool.com/files', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Cookie: `auth_token=${cookies.auth_token}; client_id=${cookies.client_id}`, + }, + body: JSON.stringify({ + file_name: fileName, + content_type: contentType, + content_length: fileBuffer.byteLength, + content_disposition: '', + ref: '', + owner_id: userId, + large_thumbnail: false, + }), + }, 'create file record') + ).json(); + + await fetch(createFileResponse.write_url, { + method: 'PUT', + headers: { + 'Content-Type': createFileResponse.content_type, + 'x-amz-acl': createFileResponse.acl, + }, + body: fileBuffer, + }); + + fileIds.push(createFileResponse.file.id); + } + + return fileIds.join(','); + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const { client_id, auth_token } = this.getCookies(integration); + const [post] = postDetails; + + const attachments = await this.uploadMediaToSkool( + post.media || [], + id, + { client_id, auth_token } + ); + + const { id: postId, name } = await ( + await this.fetch('https://api2.skool.com/posts?follow=true', { + method: 'POST', + headers: { + Cookie: `auth_token=${auth_token}; client_id=${client_id}`, + }, + body: JSON.stringify({ + post_type: 'generic', + group_id: post.settings.group, + metadata: { + title: post.settings.title, + content: post.message, + attachments, + ...(post.settings.label && post.settings.label !== 'none' + ? { labels: post.settings.label } + : {}), + action: 0, + video_ids: '', + }, + }), + }) + ).json(); + + return [ + { + id: String(postId), + postId, + releaseURL: `https://www.skool.com/${post.settings.group}/${name}`, + status: 'success', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const { client_id, auth_token } = this.getCookies(integration); + const [post] = postDetails; + + const attachments = await this.uploadMediaToSkool( + post.media || [], + id, + { client_id, auth_token } + ); + + const { id: postIdFinal, name } = await ( + await this.fetch('https://api2.skool.com/posts?follow=true', { + method: 'POST', + headers: { + Cookie: `auth_token=${auth_token}; client_id=${client_id}`, + }, + body: JSON.stringify({ + post_type: 'comment', + group_id: post.settings.group, + root_id: postId, + parent_id: lastCommentId || postId, + metadata: { + title: '', + content: post.message, + attachments, + action: 0, + video_ids: '', + }, + }), + }) + ).json(); + + return [ + { + id: String(id), + postId: postIdFinal, + releaseURL: `https://www.skool.com/${post.settings.group}/${name}`, + status: 'success', + }, + ]; + } +} diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index 377621fb..bca6eb5f 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -141,7 +141,9 @@ export interface SocialProvider dto?: any; maxLength: (additionalSettings?: any) => number; isWeb3?: boolean; - editor: 'normal' | 'markdown' | 'html'; + isChromeExtension?: boolean; + extensionCookies?: { name: string; domain: string }[]; + editor: 'none' | 'normal' | 'markdown' | 'html'; customFields?: () => Promise< { key: string; diff --git a/libraries/react-shared-libraries/src/helpers/variable.context.tsx b/libraries/react-shared-libraries/src/helpers/variable.context.tsx index 1b81f7c2..5bbe64a0 100644 --- a/libraries/react-shared-libraries/src/helpers/variable.context.tsx +++ b/libraries/react-shared-libraries/src/helpers/variable.context.tsx @@ -25,6 +25,7 @@ interface VariableContextInterface { dub: boolean; transloadit: string[]; sentryDsn: string; + extensionId: string; } const VariableContext = createContext({ stripeClient: '', @@ -49,6 +50,7 @@ const VariableContext = createContext({ dub: false, transloadit: [], sentryDsn: '', + extensionId: '', } as VariableContextInterface); export const VariableContextComponent: FC< VariableContextInterface & { From f5ff72b706224ee2ac912481f4d1a4906d19a1c9 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 8 Feb 2026 21:10:11 +0700 Subject: [PATCH 228/340] feat: platforms with a chrome extension --- .../components/new-launch/providers/skool/skool.provider.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx b/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx index 9c21abca..f6b2b43a 100644 --- a/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/skool/skool.provider.tsx @@ -1,7 +1,7 @@ 'use client'; import { - withProvider, + PostComment, withProvider } from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; import { FC, useState } from 'react'; import { SkoolDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/skool.dto'; @@ -33,5 +33,6 @@ export default withProvider({ CustomPreviewComponent: undefined, dto: SkoolDto, checkValidity: undefined, - maximumCharacters: 5000, + maximumCharacters: 50000, + postComment: PostComment.ALL, }); From 5f5d746f8ace83ee45cc48280b8b0339b56410d0 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 9 Feb 2026 11:37:22 +0700 Subject: [PATCH 229/340] feat: show file name in uploads --- .../backend/src/api/routes/media.controller.ts | 18 +++++++++++++----- .../src/components/media/media.component.tsx | 1 + .../src/components/media/new.uploader.tsx | 6 ++++-- .../database/prisma/media/media.repository.ts | 6 +++++- .../src/database/prisma/media/media.service.ts | 4 ++-- .../src/database/prisma/schema.prisma | 1 + 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/apps/backend/src/api/routes/media.controller.ts b/apps/backend/src/api/routes/media.controller.ts index 95c4da0e..925f3e26 100644 --- a/apps/backend/src/api/routes/media.controller.ts +++ b/apps/backend/src/api/routes/media.controller.ts @@ -91,11 +91,13 @@ export class MediaController { @GetOrgFromRequest() org: Organization, @UploadedFile() file: Express.Multer.File ) { + const originalName = file.originalname; const uploadedFile = await this.storage.uploadFile(file); return this._mediaService.saveFile( org.id, uploadedFile.originalname, - uploadedFile.path + uploadedFile.path, + originalName ); } @@ -103,7 +105,8 @@ export class MediaController { async saveMedia( @GetOrgFromRequest() org: Organization, @Req() req: Request, - @Body('name') name: string + @Body('name') name: string, + @Body('originalName') originalName: string ) { if (!name) { return false; @@ -111,7 +114,8 @@ export class MediaController { return this._mediaService.saveFile( org.id, name, - process.env.CLOUDFLARE_BUCKET_URL + '/' + name + process.env.CLOUDFLARE_BUCKET_URL + '/' + name, + originalName || undefined ); } @@ -130,6 +134,7 @@ export class MediaController { @UploadedFile('file') file: Express.Multer.File, @Body('preventSave') preventSave: string = 'false' ) { + const originalName = file.originalname; const getFile = await this.storage.uploadFile(file); if (preventSave === 'true') { @@ -140,7 +145,8 @@ export class MediaController { return this._mediaService.saveFile( org.id, getFile.originalname, - getFile.path + getFile.path, + originalName ); } @@ -158,12 +164,14 @@ export class MediaController { // @ts-ignore const name = upload.Location.split('/').pop(); + const originalName = req.body?.file?.name; const saveFile = await this._mediaService.saveFile( org.id, name, // @ts-ignore - upload.Location + upload.Location, + originalName || undefined ); res.status(200).json({ ...upload, saved: saveFile }); diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index 97a10637..b3a37f76 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -534,6 +534,7 @@ export const MediaBox: FC<{ onClick={deleteImage(media)} /> )} +
{media.originalName}
0) { // @ts-ignore const allRes = result.transloadit[0].results; - const toSave = uniqBy<{ name: string; order: number }>( + const toSave = uniqBy<{ name: string; originalName: string; order: number }>( // @ts-ignore Object.values(allRes).flatMap((p: any[]) => { return p.flatMap((item) => ({ name: item.url.split('/').pop(), + originalName: item.name || '', order: +item.user_meta.addedOrder, })); }), @@ -238,12 +239,13 @@ export function useUppyUploader(props: { const loadAllMedia = ( await Promise.all( - toSave.map(async ({ name, order }) => ({ + toSave.map(async ({ name, originalName, order }) => ({ file: await ( await fetch('/media/save-media', { method: 'POST', body: JSON.stringify({ name, + originalName, }), }) ).json(), diff --git a/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts b/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts index 4c1d3e0b..d6389f06 100644 --- a/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/media/media.repository.ts @@ -6,7 +6,7 @@ import { SaveMediaInformationDto } from '@gitroom/nestjs-libraries/dtos/media/sa export class MediaRepository { constructor(private _media: PrismaRepository<'media'>) {} - saveFile(org: string, fileName: string, filePath: string) { + saveFile(org: string, fileName: string, filePath: string, originalName?: string) { return this._media.model.media.create({ data: { organization: { @@ -16,10 +16,12 @@ export class MediaRepository { }, name: fileName, path: filePath, + originalName: originalName || null, }, select: { id: true, name: true, + originalName: true, path: true, thumbnail: true, alt: true, @@ -61,6 +63,7 @@ export class MediaRepository { select: { id: true, name: true, + originalName: true, alt: true, thumbnail: true, path: true, @@ -90,6 +93,7 @@ export class MediaRepository { select: { id: true, name: true, + originalName: true, path: true, thumbnail: true, alt: true, diff --git a/libraries/nestjs-libraries/src/database/prisma/media/media.service.ts b/libraries/nestjs-libraries/src/database/prisma/media/media.service.ts index 29d72941..66bdac84 100644 --- a/libraries/nestjs-libraries/src/database/prisma/media/media.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/media/media.service.ts @@ -52,8 +52,8 @@ export class MediaService { return generating; } - saveFile(org: string, fileName: string, filePath: string) { - return this._mediaRepository.saveFile(org, fileName, filePath); + saveFile(org: string, fileName: string, filePath: string, originalName?: string) { + return this._mediaRepository.saveFile(org, fileName, filePath, originalName); } getMedia(org: string, page: number) { diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index b5ca2e7c..8740de0b 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -205,6 +205,7 @@ model Star { model Media { id String @id @default(uuid()) name String + originalName String? path String organizationId String createdAt DateTime @default(now()) From 029ddb29d118315e9e1db3e0bf2543938e18a115 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 9 Feb 2026 12:05:40 +0700 Subject: [PATCH 230/340] feat: fix linkedin carusel --- .../providers/linkedin/linkedin.provider.tsx | 11 ++ .../posts/providers-settings/linkedin.dto.ts | 6 +- .../integrations/social/linkedin.provider.ts | 108 ++++++++---------- 3 files changed, 63 insertions(+), 62 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx index d558ccb6..47818da8 100644 --- a/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/linkedin/linkedin.provider.tsx @@ -5,6 +5,7 @@ import { withProvider, } from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; import { Checkbox } from '@gitroom/react/form/checkbox'; +import { Input } from '@gitroom/react/form/input'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import { LinkedinDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/linkedin.dto'; @@ -13,6 +14,7 @@ import { LinkedinPreview } from '@gitroom/frontend/components/new-launch/provide const LinkedInSettings = () => { const t = useT(); const { watch, register, formState, control } = useSettings(); + const isCarousel = watch('post_as_images_carousel'); return (
@@ -23,6 +25,15 @@ const LinkedInSettings = () => { value: false, })} /> + {isCarousel && ( +
+ +
+ )}
); }; diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/linkedin.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/linkedin.dto.ts index b3840d14..f5e150e6 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/linkedin.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/linkedin.dto.ts @@ -1,7 +1,11 @@ -import { IsBoolean, IsOptional } from 'class-validator'; +import { IsBoolean, IsOptional, IsString } from 'class-validator'; export class LinkedinDto { @IsBoolean() @IsOptional() post_as_images_carousel: boolean; + + @IsString() + @IsOptional() + carousel_name?: string; } \ No newline at end of file diff --git a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts index bbab352c..81b2fded 100644 --- a/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/linkedin.provider.ts @@ -371,71 +371,51 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { postDetails: PostDetails[], firstPost: PostDetails ): Promise[]> { - // Collect all images from all posts - const allImages = postDetails.flatMap( - (post) => - post.media?.filter( - (media) => - media.path.toLowerCase().includes('.jpg') || - media.path.toLowerCase().includes('.jpeg') || - media.path.toLowerCase().includes('.png') - ) || [] - ); - - if (allImages.length === 0) { + if (!firstPost.media?.length) { return postDetails; } - // Convert images to buffers and get dimensions - const imageData = await Promise.all( - allImages.map(async (media) => { - const buffer = await readOrFetch(media.path); - const image = sharp(buffer, { - animated: lookup(media.path) === 'image/gif', - }); - const metadata = await image.metadata(); - - return { - buffer, - width: metadata.width || 0, - height: metadata.height || 0, - }; + // Fetch all images and get their dimensions + const images = await Promise.all( + firstPost.media.map(async (media) => { + const raw = await readOrFetch(media.path); + const image = sharp(raw, { animated: false }).toFormat('jpeg'); + const { width, height } = await image.metadata(); + const buffer = await image.toBuffer(); + return { buffer, width: width || 0, height: height || 0 }; }) ); - // Use the dimensions of the first image for the PDF page size - // You could also use the largest dimensions if you prefer - const firstImageDimensions = imageData[0]; - const pageSize = [firstImageDimensions.width, firstImageDimensions.height]; - - // Convert images to PDF with exact image dimensions - const pdfStream = imageToPDF( - imageData.map((data) => data.buffer), - pageSize + // Find the largest image by area to use as the PDF page size + const largest = images.reduce((max, img) => + img.width * img.height > max.width * max.height ? img : max ); - // Convert stream to buffer + const imageBuffers = images.map((img) => img.buffer); + + // Create a PDF sized to the largest image; it fills the page, + // smaller images are fitted and centered within the same dimensions + const pdfStream = imageToPDF( + imageBuffers, + [largest.width, largest.height] + ) as unknown as Readable; const pdfBuffer = await this.streamToBuffer(pdfStream); - // Create a temporary file-like object for the PDF - const pdfMedia = { - path: 'carousel.pdf', - buffer: pdfBuffer, - }; - - // Return modified post details with PDF instead of images - const modifiedFirstPost = { - ...firstPost, - media: [pdfMedia] as any[], - }; - - // Remove media from other posts since we're combining everything into one PDF - const modifiedRestPosts = postDetails.slice(1).map((post) => ({ - ...post, - media: [] as any[], - })); - - return [modifiedFirstPost, ...modifiedRestPosts]; + // Replace the first post's media with the single PDF + const [first, ...rest] = postDetails; + return [ + { + ...first, + media: [ + { + type: 'image' as const, + path: 'carousel.pdf', + buffer: pdfBuffer, + } as any, + ], + }, + ...rest, + ]; } private async streamToBuffer(stream: Readable): Promise { @@ -511,7 +491,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { .toBuffer(); } - private buildPostContent(isPdf: boolean, mediaIds: string[]) { + private buildPostContent(isPdf: boolean, mediaIds: string[], pdfTitle?: string) { if (mediaIds.length === 0) { return {}; } @@ -520,7 +500,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { return { content: { media: { - ...(isPdf ? { title: 'slides.pdf' } : {}), + ...(isPdf ? { title: pdfTitle || 'slides' } : {}), id: mediaIds[0], }, }, @@ -541,7 +521,8 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { type: 'company' | 'personal', message: string, mediaIds: string[], - isPdf: boolean + isPdf: boolean, + pdfTitle?: string ) { const author = type === 'personal' ? `urn:li:person:${id}` : `urn:li:organization:${id}`; @@ -555,7 +536,7 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { targetEntities: [] as string[], thirdPartyDistributionChannels: [] as string[], }, - ...this.buildPostContent(isPdf, mediaIds), + ...this.buildPostContent(isPdf, mediaIds, pdfTitle), lifecycleState: 'PUBLISHED', isReshareDisabledByAuthor: false, }; @@ -564,17 +545,22 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider { private async createMainPost( id: string, accessToken: string, - firstPost: PostDetails, + firstPost: PostDetails, mediaIds: string[], type: 'company' | 'personal', isPdf: boolean ): Promise { + const pdfTitle = isPdf + ? firstPost.settings?.carousel_name || 'slides' + : undefined; + const postPayload = this.createLinkedInPostPayload( id, type, firstPost.message, mediaIds, - isPdf + isPdf, + pdfTitle ); const response = await this.fetch(`https://api.linkedin.com/rest/posts`, { From 945dc09f480be15adafa49478bb38d35ae430aeb Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 9 Feb 2026 12:42:36 +0700 Subject: [PATCH 231/340] feat: fix ai agent --- .../src/chat/load.tools.service.ts | 2 +- .../chat/tools/generate.video.options.tool.ts | 12 +++++--- .../src/chat/tools/generate.video.tool.ts | 1 - .../chat/tools/integration.trigger.tool.ts | 1 - .../chat/tools/integration.validation.tool.ts | 4 +-- .../src/chat/validation.schemas.helper.ts | 30 +++++++++++++++++++ 6 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 libraries/nestjs-libraries/src/chat/validation.schemas.helper.ts diff --git a/libraries/nestjs-libraries/src/chat/load.tools.service.ts b/libraries/nestjs-libraries/src/chat/load.tools.service.ts index 15679f4b..d5908905 100644 --- a/libraries/nestjs-libraries/src/chat/load.tools.service.ts +++ b/libraries/nestjs-libraries/src/chat/load.tools.service.ts @@ -85,7 +85,7 @@ export class LoadToolsService { )} `; }, - model: openai('gpt-4.1'), + model: openai('gpt-5.2'), tools, memory: new Memory({ storage: pStore, diff --git a/libraries/nestjs-libraries/src/chat/tools/generate.video.options.tool.ts b/libraries/nestjs-libraries/src/chat/tools/generate.video.options.tool.ts index 7879c97c..453b7660 100644 --- a/libraries/nestjs-libraries/src/chat/tools/generate.video.options.tool.ts +++ b/libraries/nestjs-libraries/src/chat/tools/generate.video.options.tool.ts @@ -1,7 +1,10 @@ -import { AgentToolInterface, ToolReturn } from '@gitroom/nestjs-libraries/chat/agent.tool.interface'; +import { + AgentToolInterface, + ToolReturn, +} from '@gitroom/nestjs-libraries/chat/agent.tool.interface'; import { createTool } from '@mastra/core/tools'; import { Injectable } from '@nestjs/common'; -import { validationMetadatasToSchemas } from 'class-validator-jsonschema'; +import { getValidationSchemas } from '@gitroom/nestjs-libraries/chat/validation.schemas.helper'; import { VideoManager } from '@gitroom/nestjs-libraries/videos/video.manager'; import z from 'zod'; import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context'; @@ -42,7 +45,7 @@ export class GenerateVideoOptionsTool implements AgentToolInterface { type: p.identifier, output: 'vertical|horizontal', tools: p.tools, - customParams: validationMetadatasToSchemas()[p.dto.name], + customParams: getValidationSchemas()[p.dto.name], }; }), }, @@ -50,13 +53,14 @@ export class GenerateVideoOptionsTool implements AgentToolInterface { 2 ) ); + return { video: videos.map((p) => { return { type: p.identifier, output: 'vertical|horizontal', tools: p.tools, - customParams: validationMetadatasToSchemas()[p.dto.name], + customParams: getValidationSchemas()[p.dto.name], }; }), }; diff --git a/libraries/nestjs-libraries/src/chat/tools/generate.video.tool.ts b/libraries/nestjs-libraries/src/chat/tools/generate.video.tool.ts index 35740a1e..c8d4eb35 100644 --- a/libraries/nestjs-libraries/src/chat/tools/generate.video.tool.ts +++ b/libraries/nestjs-libraries/src/chat/tools/generate.video.tool.ts @@ -6,7 +6,6 @@ import { IntegrationManager, socialIntegrationList, } from '@gitroom/nestjs-libraries/integrations/integration.manager'; -import { validationMetadatasToSchemas } from 'class-validator-jsonschema'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { timer } from '@gitroom/helpers/utils/timer'; diff --git a/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts b/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts index 9de31dc2..b03e7c5b 100644 --- a/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts +++ b/libraries/nestjs-libraries/src/chat/tools/integration.trigger.tool.ts @@ -6,7 +6,6 @@ import { IntegrationManager, socialIntegrationList, } from '@gitroom/nestjs-libraries/integrations/integration.manager'; -import { validationMetadatasToSchemas } from 'class-validator-jsonschema'; import { IntegrationService } from '@gitroom/nestjs-libraries/database/prisma/integrations/integration.service'; import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { timer } from '@gitroom/helpers/utils/timer'; diff --git a/libraries/nestjs-libraries/src/chat/tools/integration.validation.tool.ts b/libraries/nestjs-libraries/src/chat/tools/integration.validation.tool.ts index 9d1e618c..1e1e28a0 100644 --- a/libraries/nestjs-libraries/src/chat/tools/integration.validation.tool.ts +++ b/libraries/nestjs-libraries/src/chat/tools/integration.validation.tool.ts @@ -6,7 +6,7 @@ import { IntegrationManager, socialIntegrationList, } from '@gitroom/nestjs-libraries/integrations/integration.manager'; -import { validationMetadatasToSchemas } from 'class-validator-jsonschema'; +import { getValidationSchemas } from '@gitroom/nestjs-libraries/chat/validation.schemas.helper'; import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context'; @Injectable() @@ -88,7 +88,7 @@ export class IntegrationValidationTool implements AgentToolInterface { const maxLength = integration.maxLength(context.isPremium); const schemas = !integration.dto ? false - : validationMetadatasToSchemas()[integration.dto.name]; + : getValidationSchemas()[integration.dto.name]; const tools = this._integrationManager.getAllTools(); const rules = this._integrationManager.getAllRulesDescription(); diff --git a/libraries/nestjs-libraries/src/chat/validation.schemas.helper.ts b/libraries/nestjs-libraries/src/chat/validation.schemas.helper.ts new file mode 100644 index 00000000..45a98bbe --- /dev/null +++ b/libraries/nestjs-libraries/src/chat/validation.schemas.helper.ts @@ -0,0 +1,30 @@ +import { + validationMetadatasToSchemas, + targetConstructorToSchema, +} from 'class-validator-jsonschema'; +import { ValidationTypes } from 'class-validator'; +// @ts-ignore +import { defaultMetadataStorage } from 'class-transformer/cjs/storage'; + +export function getValidationSchemas() { + return validationMetadatasToSchemas({ + classTransformerMetadataStorage: defaultMetadataStorage, + additionalConverters: { + [ValidationTypes.NESTED_VALIDATION]: (meta, options) => { + if (typeof meta.target === 'function') { + const typeMeta = options.classTransformerMetadataStorage + ? options.classTransformerMetadataStorage.findTypeMetadata( + meta.target, + meta.propertyName + ) + : null; + if (typeMeta) { + const childType = typeMeta.typeFunction(); + return targetConstructorToSchema(childType, options); + } + } + return {}; + }, + }, + }); +} From b3681d2ac1b081a8ce3ba533f065e47f9a8380bf Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 9 Feb 2026 17:02:28 +0700 Subject: [PATCH 232/340] feat: whop --- apps/frontend/public/icons/platforms/whop.png | Bin 0 -> 1390 bytes .../providers/show.all.providers.tsx | 5 + .../providers/whop/whop.company.select.tsx | 58 +++ .../providers/whop/whop.experience.select.tsx | 70 ++++ .../providers/whop/whop.provider.tsx | 47 +++ .../all.providers.settings.ts | 5 +- .../dtos/posts/providers-settings/whop.dto.ts | 24 ++ .../src/integrations/integration.manager.ts | 2 + .../src/integrations/social/whop.provider.ts | 375 ++++++++++++++++++ 9 files changed, 585 insertions(+), 1 deletion(-) create mode 100644 apps/frontend/public/icons/platforms/whop.png create mode 100644 apps/frontend/src/components/new-launch/providers/whop/whop.company.select.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/whop/whop.experience.select.tsx create mode 100644 apps/frontend/src/components/new-launch/providers/whop/whop.provider.tsx create mode 100644 libraries/nestjs-libraries/src/dtos/posts/providers-settings/whop.dto.ts create mode 100644 libraries/nestjs-libraries/src/integrations/social/whop.provider.ts diff --git a/apps/frontend/public/icons/platforms/whop.png b/apps/frontend/public/icons/platforms/whop.png new file mode 100644 index 0000000000000000000000000000000000000000..2944dd501741f4d06047fadcacbfe19c520bc755 GIT binary patch literal 1390 zcmV-!1(EuRP)AW1|)RA_HcNZafO13e2=>cy=mOj>b3v z* z`agCDMvcSTuty{ai9XgiFoe+?2bZ?WI2Ec=LnKLodLA`SDARRy3UhcL5iF>5{rpOd>1t=}V!HQpi*WNq5dhUjH^tOIGPMcR&l zKY(nh*Gi15%F|9^xO@Rwu>kq`m_jVyriqrT5|L(P^{Wu=(7E)Z%<yXNWZF9l1o2=}NiUtoV{O_8 zkB-g*;v%x58aaIo^HL3_!K*|rh)6TCb}=NPc|kzl?^>dZGNd^#moN6;HW-F0X%Mpu zfH;v35y%Uo01!{M107e9mGhCd!x^GE4WgkMIpbAgdA<-WeIW8zfv@r6ZGas}<05D~ zk_M9wu|Oea6;O$jExIPUdhAq~ia3vW(Hy;nyKV`zw<=5^1lO-3O-qo|M={gqVd}Sf z5t{?zrz6O^B?YaC0RZWN3B&E{xXb6knF#!S9jTv-L|QPj7GWB9g5RHepE%iqtXiN! zQX~ZAomyGDYdj7ci5WT^X+Mscu?Vwrdtr8pN44kU?49aYzMAgz+JTv`SDu~fKXUt z)CuU%!4PtIpV}#Q|8`2(09dQn*XdE$SeV&MF_UM)&=G*>iSpPt$i5w5nXE_FqDLN- zf4zyTPuy`JRH46%cIsLRR`h#`P^@y_q7)-z>7|4SB)s@_{Ce{;NE#fF9vL$zPd}3$ w(3{2*RvDz!R0b(El|f2PWsp)+8Kl(o4+>&WO0;lHxBvhE07*qoM6N<$f|6Bu+5i9m literal 0 HcmV?d00001 diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index b9a3ec8f..3ade8870 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -37,6 +37,7 @@ import ListmonkProvider from '@gitroom/frontend/components/new-launch/providers/ import GmbProvider from '@gitroom/frontend/components/new-launch/providers/gmb/gmb.provider'; import MoltbookProvider from '@gitroom/frontend/components/new-launch/providers/moltbook/moltbook.provider'; import SkoolProvider from '@gitroom/frontend/components/new-launch/providers/skool/skool.provider'; +import WhopProvider from '@gitroom/frontend/components/new-launch/providers/whop/whop.provider'; export const Providers = [ { @@ -162,6 +163,10 @@ export const Providers = [ { identifier: 'skool', component: SkoolProvider, + }, + { + identifier: 'whop', + component: WhopProvider, } ]; export const ShowAllProviders = forwardRef((props, ref) => { diff --git a/apps/frontend/src/components/new-launch/providers/whop/whop.company.select.tsx b/apps/frontend/src/components/new-launch/providers/whop/whop.company.select.tsx new file mode 100644 index 00000000..3dc88a3f --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/whop/whop.company.select.tsx @@ -0,0 +1,58 @@ +'use client'; + +import { FC, useEffect, useState } from 'react'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { Select } from '@gitroom/react/form/select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +export const WhopCompanySelect: FC<{ + name: string; + onChange: (event: { + target: { + value: string; + name: string; + }; + }) => void; +}> = (props) => { + const { onChange, name } = props; + const t = useT(); + const customFunc = useCustomProviderFunction(); + const [companies, setCompanies] = useState([]); + const { getValues } = useSettings(); + const [currentCompany, setCurrentCompany] = useState(); + const onChangeInner = (event: { + target: { + value: string; + name: string; + }; + }) => { + setCurrentCompany(event.target.value); + onChange(event); + }; + useEffect(() => { + customFunc.get('companies').then((data) => setCompanies(data)); + const settings = getValues()[props.name]; + if (settings) { + setCurrentCompany(settings); + } + }, []); + if (!companies.length) { + return null; + } + return ( + + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/whop/whop.experience.select.tsx b/apps/frontend/src/components/new-launch/providers/whop/whop.experience.select.tsx new file mode 100644 index 00000000..93c0cb74 --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/whop/whop.experience.select.tsx @@ -0,0 +1,70 @@ +'use client'; + +import { FC, useEffect, useState } from 'react'; +import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; +import { Select } from '@gitroom/react/form/select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +export const WhopExperienceSelect: FC<{ + name: string; + companyId: string | undefined; + onChange: (event: { + target: { + value: string; + name: string; + }; + }) => void; +}> = (props) => { + const { onChange, name, companyId } = props; + const t = useT(); + const customFunc = useCustomProviderFunction(); + const [experiences, setExperiences] = useState([]); + const { getValues } = useSettings(); + const [currentExperience, setCurrentExperience] = useState< + string | undefined + >(); + const onChangeInner = (event: { + target: { + value: string; + name: string; + }; + }) => { + setCurrentExperience(event.target.value); + onChange(event); + }; + useEffect(() => { + if (!companyId) { + setExperiences([]); + setCurrentExperience(undefined); + return; + } + customFunc + .get('experiences', { id: companyId }) + .then((data) => setExperiences(data)); + }, [companyId]); + useEffect(() => { + const settings = getValues()[name]; + if (settings) { + setCurrentExperience(settings); + } + }, []); + if (!companyId || !experiences.length) { + return null; + } + return ( + + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/whop/whop.provider.tsx b/apps/frontend/src/components/new-launch/providers/whop/whop.provider.tsx new file mode 100644 index 00000000..3c08938e --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/whop/whop.provider.tsx @@ -0,0 +1,47 @@ +'use client'; + +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { WhopDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/whop.dto'; +import { Input } from '@gitroom/react/form/input'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { WhopCompanySelect } from '@gitroom/frontend/components/new-launch/providers/whop/whop.company.select'; +import { WhopExperienceSelect } from '@gitroom/frontend/components/new-launch/providers/whop/whop.experience.select'; +import { FC, useState } from 'react'; + +const WhopSettings: FC = () => { + const form = useSettings(); + const [selectedCompany, setSelectedCompany] = useState( + form.getValues().company + ); + const companyRegister = form.register('company'); + const onCompanyChange = (event: { + target: { value: string; name: string }; + }) => { + setSelectedCompany(event.target.value); + companyRegister.onChange(event); + }; + + return ( +
+ + + +
+ ); +}; + +export default withProvider({ + postComment: PostComment.COMMENT, + minimumCharacters: [], + SettingsComponent: WhopSettings, + CustomPreviewComponent: undefined, + dto: WhopDto, + checkValidity: undefined, + maximumCharacters: 50000, +}); diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 6bf17192..071be782 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -22,6 +22,7 @@ import { FarcasterDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-set import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/facebook.dto'; import { MoltbookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/moltbook.dto'; import { SkoolDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/skool.dto'; +import { WhopDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/whop.dto'; export type ProviderExtension = { __type: T } & M; export type AllProvidersSettings = @@ -55,7 +56,8 @@ export type AllProvidersSettings = | ProviderExtension<'nostr', None> | ProviderExtension<'moltbook', MoltbookDto> | ProviderExtension<'vk', None> - | ProviderExtension<'skool', SkoolDto>; + | ProviderExtension<'skool', SkoolDto> + | ProviderExtension<'whop', WhopDto>; type None = NonNullable; @@ -92,6 +94,7 @@ export const allProviders = (setEmpty?: any) => { { value: setEmpty, name: 'vk' }, { value: MoltbookDto, name: 'moltbook' }, { value: SkoolDto, name: 'skool' }, + { value: WhopDto, name: 'whop' }, ].filter((f) => f.value); }; diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/whop.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/whop.dto.ts new file mode 100644 index 00000000..c7d5ae5e --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/whop.dto.ts @@ -0,0 +1,24 @@ +import { IsDefined, IsOptional, IsString, MinLength } from 'class-validator'; +import { JSONSchema } from 'class-validator-jsonschema'; + +export class WhopDto { + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Company ID', + }) + company: string; + + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Experience ID for the Whop forum', + }) + experience: string; + + @IsOptional() + @IsString() + title?: string; +} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index ba2ff607..731bdbe5 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -34,6 +34,7 @@ import { TwitchProvider } from '@gitroom/nestjs-libraries/integrations/social/tw import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; import { MoltbookProvider } from '@gitroom/nestjs-libraries/integrations/social/moltbook.provider'; import { SkoolProvider } from '@gitroom/nestjs-libraries/integrations/social/skool.provider'; +import { WhopProvider } from '@gitroom/nestjs-libraries/integrations/social/whop.provider'; export const socialIntegrationList: Array = [ new XProvider(), @@ -66,6 +67,7 @@ export const socialIntegrationList: Array = [ new WordpressProvider(), new ListmonkProvider(), new MoltbookProvider(), + new WhopProvider(), new SkoolProvider(), // new MastodonCustomProvider(), ]; diff --git a/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts b/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts new file mode 100644 index 00000000..604f9ece --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts @@ -0,0 +1,375 @@ +import { createHash, randomBytes } from 'crypto'; +import { + AuthTokenDetails, + MediaContent, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { timer } from '@gitroom/helpers/utils/timer'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { WhopDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/whop.dto'; +import { Integration } from '@prisma/client'; +import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; + +export class WhopProvider extends SocialAbstract implements SocialProvider { + identifier = 'whop'; + name = 'Whop'; + isBetweenSteps = false; + scopes = ['openid', 'profile', 'email', 'forum:post:create', 'forum:read', 'company:basic:read']; + refreshCron = true; + editor = 'markdown' as const; + dto = WhopDto; + toolTip = 'Schedule posts to forums'; + + maxLength() { + return 50000; + } + + private generateCodeChallenge(codeVerifier: string): string { + return createHash('sha256').update(codeVerifier).digest('base64url'); + } + + override handleErrors( + body: string + ): + | { type: 'refresh-token' | 'bad-body'; value: string } + | undefined { + if (body.includes('invalid_grant')) { + return { + type: 'refresh-token' as const, + value: 'Invalid token, please re-authenticate', + }; + } + + if (body.includes('insufficient_scope')) { + return { + type: 'refresh-token' as const, + value: + 'Insufficient permissions, please re-authenticate with required scopes', + }; + } + + if (body.includes('invalid_request')) { + return { + type: 'bad-body' as const, + value: 'Invalid request parameters', + }; + } + + if (body.includes('not_found')) { + return { + type: 'bad-body' as const, + value: 'Forum or experience not found', + }; + } + + return undefined; + } + + async refreshToken(refreshToken: string): Promise { + const response = await ( + await fetch('https://api.whop.com/oauth/token', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + grant_type: 'refresh_token', + refresh_token: refreshToken, + client_id: process.env.WHOP_CLIENT_ID, + }), + }) + ).json(); + + const userInfo = await ( + await fetch('https://api.whop.com/oauth/userinfo', { + headers: { Authorization: `Bearer ${response.access_token}` }, + }) + ).json(); + + return { + id: userInfo.sub, + name: userInfo.name || userInfo.preferred_username || '', + accessToken: response.access_token, + refreshToken: response.refresh_token, + expiresIn: response.expires_in || 3600, + picture: userInfo.picture || '', + username: userInfo.preferred_username || '', + }; + } + + async generateAuthUrl() { + const state = makeId(6); + const codeVerifier = randomBytes(32).toString('base64url'); + const codeChallenge = this.generateCodeChallenge(codeVerifier); + const nonce = makeId(16); + + return { + url: + 'https://api.whop.com/oauth/authorize' + + `?response_type=code` + + `&client_id=${process.env.WHOP_CLIENT_ID}` + + `&redirect_uri=${encodeURIComponent( + `${process.env.FRONTEND_URL}/integrations/social/whop` + )}` + + `&scope=${encodeURIComponent(this.scopes.join(' '))}` + + `&state=${state}` + + `&nonce=${nonce}` + + `&code_challenge=${codeChallenge}` + + `&code_challenge_method=S256`, + codeVerifier, + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const redirectUri = `${process.env.FRONTEND_URL}/integrations/social/whop${ + params.refresh ? `?refresh=${params.refresh}` : '' + }`; + + const tokenResponse = await ( + await fetch('https://api.whop.com/oauth/token', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + grant_type: 'authorization_code', + code: params.code, + redirect_uri: redirectUri, + client_id: process.env.WHOP_CLIENT_ID, + code_verifier: params.codeVerifier, + }), + }) + ).json(); + + if (tokenResponse.error) { + return `Authentication failed: ${ + tokenResponse.error_description || tokenResponse.error + }`; + } + + const userInfo = await ( + await fetch('https://api.whop.com/oauth/userinfo', { + headers: { Authorization: `Bearer ${tokenResponse.access_token}` }, + }) + ).json(); + + return { + id: userInfo.sub, + name: userInfo.name || userInfo.preferred_username || '', + accessToken: tokenResponse.access_token, + refreshToken: tokenResponse.refresh_token, + expiresIn: tokenResponse.expires_in || 3600, + picture: userInfo.picture || '', + username: userInfo.preferred_username || '', + }; + } + + @Tool({ description: 'Companies', dataSchema: [] }) + async companies(accessToken: string, params: any, id: string) { + try { + const response = await fetch( + 'https://api.whop.com/api/v1/companies?first=50', + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + + const { data } = await response.json(); + + return (data || []).map((company: any) => ({ + id: company.id, + name: company.title, + })); + } catch { + return []; + } + } + + @Tool({ description: 'Experiences', dataSchema: [] }) + async experiences(accessToken: string, params: any, id: string) { + try { + if (!params?.id) return []; + + const response = await fetch( + `https://api.whop.com/api/v1/forums?company_id=${params.id}&first=50`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + + const { data } = await response.json(); + + return (data || []).map((forum: any) => ({ + id: forum.experience?.id || forum.id, + name: forum.experience?.name || forum.id, + })); + } catch { + return []; + } + } + + private async uploadMediaToWhop( + media: MediaContent[], + accessToken: string + ): Promise<{ id: string }[]> { + if (!media || media.length === 0) return []; + + const attachments: { id: string }[] = []; + + for (const item of media) { + const fileResponse = await fetch(item.path); + const fileBuffer = await fileResponse.arrayBuffer(); + const fileName = item.path.split('/').pop() || 'file'; + + const createFileResponse = await ( + await this.fetch( + 'https://api.whop.com/api/v1/files', + { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + filename: fileName, + }), + }, + 'create file record' + ) + ).json(); + + if (createFileResponse.upload_url) { + await fetch(createFileResponse.upload_url, { + method: 'PUT', + headers: createFileResponse.upload_headers || {}, + body: fileBuffer, + }); + + let uploadStatus = 'pending'; + while (uploadStatus !== 'ready') { + const fileStatus = await ( + await this.fetch( + `https://api.whop.com/api/v1/files/${createFileResponse.id}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + 'check file status', + 0, + true + ) + ).json(); + uploadStatus = fileStatus.upload_status; + if (uploadStatus === 'failed') { + throw new Error('File upload failed'); + } + if (uploadStatus !== 'ready') { + await timer(5000); + } + } + } + + attachments.push({ id: createFileResponse.id }); + } + + return attachments; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const [post] = postDetails; + + const attachments = await this.uploadMediaToWhop( + post.media || [], + accessToken + ); + + const data = await ( + await this.fetch( + 'https://api.whop.com/api/v1/forum_posts', + { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + experience_id: post.settings.experience, + content: post.message, + ...(post.settings.title ? { title: post.settings.title } : {}), + ...(attachments.length ? { attachments } : {}), + }), + }, + 'create forum post' + ) + ).json(); + + return [ + { + id: post.id, + postId: data.id, + releaseURL: `https://whop.com/experiences/${post.settings.experience}/${data.id}`, + status: 'success', + }, + ]; + } + + async comment( + id: string, + postId: string, + lastCommentId: string | undefined, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const [post] = postDetails; + const replyToId = lastCommentId || postId; + + const attachments = await this.uploadMediaToWhop( + post.media || [], + accessToken + ); + + const data = await ( + await this.fetch( + 'https://api.whop.com/api/v1/forum_posts', + { + method: 'POST', + headers: { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + experience_id: post.settings.experience, + content: post.message, + parent_id: replyToId, + ...(attachments.length ? { attachments } : {}), + }), + }, + 'create comment' + ) + ).json(); + + return [ + { + id: post.id, + postId: data.id, + releaseURL: `https://whop.com/experiences/${post.settings.experience}/${postId}`, + status: 'success', + }, + ]; + } +} From 5869300e64aa6bd49a8b92d7cf7ea54605b558ff Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 9 Feb 2026 18:50:13 +0700 Subject: [PATCH 233/340] feat: dont refresh --- .../nestjs-libraries/src/integrations/social/whop.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts b/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts index 604f9ece..a1c71b86 100644 --- a/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/whop.provider.ts @@ -18,7 +18,7 @@ export class WhopProvider extends SocialAbstract implements SocialProvider { name = 'Whop'; isBetweenSteps = false; scopes = ['openid', 'profile', 'email', 'forum:post:create', 'forum:read', 'company:basic:read']; - refreshCron = true; + refreshCron = false; editor = 'markdown' as const; dto = WhopDto; toolTip = 'Schedule posts to forums'; From 0ceaa3d04d195b3d48148e84cd514ce8ae48cc7d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 10 Feb 2026 10:21:38 +0700 Subject: [PATCH 234/340] feat: trial reels --- .../instagram/instagram.collaborators.tsx | 50 +++++++++++++++++++ .../posts/providers-settings/instagram.dto.ts | 7 +++ .../integrations/social/instagram.provider.ts | 20 ++++++-- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx b/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx index 6ca7b088..7bad425f 100644 --- a/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx +++ b/apps/frontend/src/components/new-launch/providers/instagram/instagram.collaborators.tsx @@ -6,6 +6,7 @@ import { } from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; import { FC } from 'react'; import { Select } from '@gitroom/react/form/select'; +import { Checkbox } from '@gitroom/react/form/checkbox'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import { InstagramDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/instagram.dto'; import { InstagramCollaboratorsTags } from '@gitroom/frontend/components/new-launch/providers/instagram/instagram.tags'; @@ -21,12 +22,24 @@ const postType = [ label: 'Story', }, ]; + +const graduationStrategies = [ + { + value: 'MANUAL', + label: 'Manual', + }, + { + value: 'SS_PERFORMANCE', + label: 'Auto (based on performance)', + }, +]; const InstagramCollaborators: FC<{ values?: any; }> = (props) => { const t = useT(); const { watch, register, formState, control } = useSettings(); const postCurrentType = watch('post_type'); + const isTrialReel = watch('is_trial_reel'); return ( <> + {graduationStrategies.map((item) => ( + + ))} + + )} +
+ )} ); }; @@ -67,6 +106,17 @@ export default withProvider({ if ((firstPost?.length ?? 0) > 1 && settings?.post_type === 'story') { return 'Stories can only have one media'; } + if (settings?.is_trial_reel) { + if ((firstPost?.length ?? 0) > 1) { + return 'Trial Reels can only have one video'; + } + const hasVideo = firstPost?.some( + (f) => (f?.path?.indexOf?.('mp4') ?? -1) > -1 + ); + if (!hasVideo) { + return 'Trial Reels must be a video'; + } + } const checkVideosLength = await Promise.all( firstPost ?.filter((f) => (f?.path?.indexOf?.('mp4') ?? -1) > -1) diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/instagram.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/instagram.dto.ts index da9a2481..20b25446 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/instagram.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/instagram.dto.ts @@ -18,6 +18,13 @@ export class InstagramDto { @IsDefined() post_type: 'post' | 'story'; + @IsOptional() + is_trial_reel?: boolean; + + @IsIn(['MANUAL', 'SS_PERFORMANCE']) + @IsOptional() + graduation_strategy?: 'MANUAL' | 'SS_PERFORMANCE'; + @Type(() => Collaborators) @ValidateNested({ each: true }) @IsArray() diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index e0568663..d0f8ab46 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -64,6 +64,12 @@ export class InstagramProvider value: 'An unknown error occurred, please try again later', }; } + if (body.indexOf('2207081') > -1) { + return { + type: 'bad-body' as const, + value: 'This account doesn\'t support Trial Reels', + }; + } if (body.indexOf('REVOKED_ACCESS_TOKEN') > -1) { return { @@ -457,6 +463,7 @@ export class InstagramProvider const [firstPost] = postDetails; console.log('in progress', id); const isStory = firstPost.settings.post_type === 'story'; + const isTrialReel = !!firstPost.settings.is_trial_reel; const medias = await Promise.all( firstPost?.media?.map(async (m) => { const caption = @@ -481,7 +488,15 @@ export class InstagramProvider : isStory ? `image_url=${m.path}&media_type=STORIES` : `image_url=${m.path}`; - console.log('in progress1'); + + const trialParams = isTrialReel + ? `&trial_params=${encodeURIComponent( + JSON.stringify({ + graduation_strategy: + firstPost.settings.graduation_strategy || 'MANUAL', + }) + )}` + : ``; const collaborators = firstPost?.settings?.collaborators?.length && !isStory @@ -490,10 +505,9 @@ export class InstagramProvider )}` : ``; - console.log(collaborators); const { id: photoId } = await ( await this.fetch( - `https://${type}/v20.0/${id}/media?${mediaType}${isCarousel}${collaborators}&access_token=${accessToken}${caption}`, + `https://${type}/v20.0/${id}/media?${mediaType}${isCarousel}${collaborators}${trialParams}&access_token=${accessToken}${caption}`, { method: 'POST', } From 82c8b5323648a1a1bfb03d78afd852fd3d6df7ab Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 13:29:11 +0700 Subject: [PATCH 235/340] feat: fix bold and underline for gt and lt --- libraries/helpers/src/utils/strip.html.validation.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/helpers/src/utils/strip.html.validation.ts b/libraries/helpers/src/utils/strip.html.validation.ts index 7691bb46..d780fc71 100644 --- a/libraries/helpers/src/utils/strip.html.validation.ts +++ b/libraries/helpers/src/utils/strip.html.validation.ts @@ -255,7 +255,11 @@ export const stripHtmlValidation = ( return striptags(processedHtml) .replace(/>/gi, '>') - .replace(/</gi, '<'); + .replace(/</gi, '<') + .replace(/&𝗹𝘁;/gi, '<') + .replace(/&𝗴𝘁;/gi, '>') + .replace(/&g̲t̲;/gi, '>') + .replace(/&l̲t̲;/gi, '<'); } // Strip all other tags From f7d36b36e75a0f53fd1718d704e1f66d38984d0f Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 14:48:32 +0700 Subject: [PATCH 236/340] feat: wordpress insufficent permission --- .../src/integrations/social/wordpress.provider.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts index 84d19919..a1094e3a 100644 --- a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts @@ -13,6 +13,7 @@ import slugify from 'slugify'; // import FormData from 'form-data'; import axios from 'axios'; import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; +import { string } from 'yup'; export class WordpressProvider extends SocialAbstract @@ -49,6 +50,19 @@ export class WordpressProvider username: '', }; } + override handleErrors( + body: string + ): + | { type: 'refresh-token' | 'bad-body' | 'retry'; value: string } + | undefined { + if (body.indexOf('rest_cannot_create') > -1) { + return { + type: 'bad-body', + value: 'The connect user has insufficient permissions to create posts', + }; + } + return undefined; + } async customFields() { return [ From 95956fe345ff8e5daeb585ffc377bebe9ca33504 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 16:57:55 +0700 Subject: [PATCH 237/340] feat: throw wordpress --- .../src/integrations/social/wordpress.provider.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts index a1094e3a..cda50772 100644 --- a/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/wordpress.provider.ts @@ -101,7 +101,7 @@ export class WordpressProvider const auth = Buffer.from(`${body.username}:${body.password}`).toString( 'base64' ); - const { id, name, avatar_urls } = await ( + const { id, name, avatar_urls, code } = await ( await fetch(`${body.domain}/wp-json/wp/v2/users/me`, { headers: { Authorization: `Basic ${auth}`, @@ -109,6 +109,10 @@ export class WordpressProvider }) ).json(); + if (code) { + throw "Invalid credentials"; + } + const biggestImage = Object.entries(avatar_urls || {}).reduce( (all, current) => { if (all > Number(current[0])) { From d055e693357b1d7606fc9f6aca71393d4af7ea6d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 17:41:13 +0700 Subject: [PATCH 238/340] feat: delete post --- .../routes/v1/public.integrations.controller.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 2655f699..7f310b0c 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -137,13 +137,22 @@ export class PublicIntegrationsController { @Delete('/posts/:id') async deletePost( @GetOrgFromRequest() org: Organization, - @Param() body: { id: string } + @Param('id') id: string ) { Sentry.metrics.count("public_api-request", 1); - const getPostById = await this._postsService.getPost(org.id, body.id); + const getPostById = await this._postsService.getPost(org.id, id); return this._postsService.deletePost(org.id, getPostById.group); } + @Delete('/posts/group/:group') + deletePostByGroup( + @GetOrgFromRequest() org: Organization, + @Param('group') group: string + ) { + Sentry.metrics.count("public_api-request", 1); + return this._postsService.deletePost(org.id, group); + } + @Get('/is-connected') async getActiveIntegrations(@GetOrgFromRequest() org: Organization) { Sentry.metrics.count("public_api-request", 1); From 7f7c336939b6831840d9b191f4220451c2601e5a Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 20:17:32 +0700 Subject: [PATCH 239/340] feat: restricted --- .../src/integrations/social/instagram.provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index d0f8ab46..42aa7303 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -98,7 +98,7 @@ export class InstagramProvider if (body.indexOf('2207050') > -1) { return { - type: 'refresh-token' as const, + type: 'bad-body' as const, value: 'Instagram user is restricted', }; } From d929c96792ccf1584a3e7402b7c4014ba82fa0f5 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 20:31:14 +0700 Subject: [PATCH 240/340] feat: map more errors, rate limits --- .../src/integrations/social/instagram.provider.ts | 11 +++++++++-- .../src/integrations/social/tiktok.provider.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 42aa7303..111a3616 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -33,7 +33,7 @@ export class InstagramProvider 'instagram_manage_comments', 'instagram_manage_insights', ]; - override maxConcurrentJob = 200; + override maxConcurrentJob = 400; editor = 'normal' as const; dto = InstagramDto; maxLength() { @@ -67,7 +67,7 @@ export class InstagramProvider if (body.indexOf('2207081') > -1) { return { type: 'bad-body' as const, - value: 'This account doesn\'t support Trial Reels', + value: "This account doesn't support Trial Reels", }; } @@ -269,6 +269,13 @@ export class InstagramProvider }; } + if (body.indexOf('190,') > -1) { + return { + type: 'bad-body' as const, + value: 'The account is missing some permissions to perform this action, please re-add the account and allow all permissions', + }; + } + if (body.indexOf('36001') > -1) { return { type: 'bad-body' as const, diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 032ee1db..f21f88bc 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -31,7 +31,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { 'user.info.profile', 'user.info.stats', ]; - override maxConcurrentJob = 1; // TikTok has strict video upload limits + override maxConcurrentJob = 300; dto = TikTokDto; editor = 'normal' as const; maxLength() { From 3b5767943083baca7dc4a089f326c55c10d79a03 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 21:05:56 +0700 Subject: [PATCH 241/340] feat: notifications --- .../v1/public.integrations.controller.ts | 41 +++++++++++++------ .../notifications/notification.service.ts | 7 ++++ .../notifications/notifications.repository.ts | 36 ++++++++++++++++ .../notifications/get.notifications.dto.ts | 10 +++++ 4 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 libraries/nestjs-libraries/src/dtos/notifications/get.notifications.dto.ts diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 7f310b0c..86b412f5 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -27,6 +27,8 @@ import { import { VideoDto } from '@gitroom/nestjs-libraries/dtos/videos/video.dto'; import { VideoFunctionDto } from '@gitroom/nestjs-libraries/dtos/videos/video.function.dto'; import { UploadDto } from '@gitroom/nestjs-libraries/dtos/media/upload.dto'; +import { NotificationService } from '@gitroom/nestjs-libraries/database/prisma/notifications/notification.service'; +import { GetNotificationsDto } from '@gitroom/nestjs-libraries/dtos/notifications/get.notifications.dto'; import axios from 'axios'; import { Readable } from 'stream'; import { lookup } from 'mime-types'; @@ -36,11 +38,12 @@ import * as Sentry from '@sentry/nestjs'; @Controller('/public/v1') export class PublicIntegrationsController { private storage = UploadFactory.createStorage(); - + constructor( private _integrationService: IntegrationService, private _postsService: PostsService, - private _mediaService: MediaService + private _mediaService: MediaService, + private _notificationService: NotificationService ) {} @Post('/upload') @@ -49,7 +52,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @UploadedFile('file') file: Express.Multer.File ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); if (!file) { throw new HttpException({ msg: 'No file provided' }, 400); } @@ -67,7 +70,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Body() body: UploadDto ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); const response = await axios.get(body.url, { responseType: 'arraybuffer', }); @@ -99,7 +102,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Param('id') id?: string ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); return { date: await this._postsService.findFreeDateTime(org.id, id) }; } @@ -108,7 +111,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Query() query: GetPostsDto ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); const posts = await this._postsService.getPosts(org.id, query); return { posts, @@ -122,7 +125,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Body() rawBody: any ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); const body = await this._postsService.mapTypeToPost( rawBody, org.id, @@ -139,7 +142,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Param('id') id: string ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); const getPostById = await this._postsService.getPost(org.id, id); return this._postsService.deletePost(org.id, getPostById.group); } @@ -149,19 +152,19 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Param('group') group: string ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); return this._postsService.deletePost(org.id, group); } @Get('/is-connected') async getActiveIntegrations(@GetOrgFromRequest() org: Organization) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); return { connected: true }; } @Get('/integrations') async listIntegration(@GetOrgFromRequest() org: Organization) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); return (await this._integrationService.getIntegrationsList(org.id)).map( (org) => ({ id: org.id, @@ -180,18 +183,30 @@ export class PublicIntegrationsController { ); } + @Get('/notifications') + async getNotifications( + @GetOrgFromRequest() org: Organization, + @Query() query: GetNotificationsDto + ) { + Sentry.metrics.count('public_api-request', 1); + return this._notificationService.getNotificationsPaginated( + org.id, + query.page ?? 0 + ); + } + @Post('/generate-video') generateVideo( @GetOrgFromRequest() org: Organization, @Body() body: VideoDto ) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); return this._mediaService.generateVideo(org, body); } @Post('/video/function') videoFunction(@Body() body: VideoFunctionDto) { - Sentry.metrics.count("public_api-request", 1); + Sentry.metrics.count('public_api-request', 1); return this._mediaService.videoFunction( body.identifier, body.functionName, diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts index 6608ba17..31cd52fb 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notification.service.ts @@ -24,6 +24,13 @@ export class NotificationService { ); } + getNotificationsPaginated(organizationId: string, page: number) { + return this._notificationRepository.getNotificationsPaginated( + organizationId, + page + ); + } + getNotifications(organizationId: string, userId: string) { return this._notificationRepository.getNotifications( organizationId, diff --git a/libraries/nestjs-libraries/src/database/prisma/notifications/notifications.repository.ts b/libraries/nestjs-libraries/src/database/prisma/notifications/notifications.repository.ts index 86cb37ce..958fe58a 100644 --- a/libraries/nestjs-libraries/src/database/prisma/notifications/notifications.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/notifications/notifications.repository.ts @@ -56,6 +56,42 @@ export class NotificationsRepository { }); } + async getNotificationsPaginated(organizationId: string, page: number) { + const limit = 100; + const skip = page * limit; + + const where = { + organizationId, + deletedAt: null as Date | null, + }; + + const [notifications, total] = await Promise.all([ + this._notifications.model.notifications.findMany({ + where, + orderBy: { + createdAt: 'desc', + }, + skip, + take: limit, + select: { + id: true, + content: true, + link: true, + createdAt: true, + }, + }), + this._notifications.model.notifications.count({ where }), + ]); + + return { + notifications, + total, + page, + limit, + hasMore: skip + notifications.length < total, + }; + } + async getNotifications(organizationId: string, userId: string) { const { lastReadNotifications } = (await this.getLastReadNotification( userId diff --git a/libraries/nestjs-libraries/src/dtos/notifications/get.notifications.dto.ts b/libraries/nestjs-libraries/src/dtos/notifications/get.notifications.dto.ts new file mode 100644 index 00000000..d7305bc5 --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/notifications/get.notifications.dto.ts @@ -0,0 +1,10 @@ +import { IsOptional, IsNumber, Min } from 'class-validator'; +import { Transform } from 'class-transformer'; + +export class GetNotificationsDto { + @IsOptional() + @IsNumber() + @Min(0) + @Transform(({ value }) => parseInt(value, 10)) + page?: number = 0; +} From a098d612af67473a080510c1a2f6bbc027800e06 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 11 Feb 2026 22:04:47 +0700 Subject: [PATCH 242/340] feat: no posts with a deleted integration --- .../src/database/prisma/posts/posts.repository.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 4d7ab250..26454661 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -132,10 +132,7 @@ export class PostsRepository { OR: [ { organizationId: orgId, - }, - { - submittedForOrganizationId: orgId, - }, + } ], }, { @@ -154,6 +151,9 @@ export class PostsRepository { ], }, ], + integration: { + deletedAt: null, + }, deletedAt: null, parentPostId: null, ...(query.customer From 80d8804f5ac9d2c2df5855cd48c835e2b41b334a Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 13 Feb 2026 21:49:08 +0700 Subject: [PATCH 243/340] feat: tiktok fixes --- .../providers/tiktok/tiktok.provider.tsx | 1 + .../integrations/social/tiktok.provider.ts | 24 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx index 5059223f..9fc74e7f 100644 --- a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx @@ -118,6 +118,7 @@ const TikTokSettings: FC<{ ))} + {isUploadMode &&
After posting you fill find a notification inside your Inbox about your post (not content studio)
} + + {groups.map((group: any) => ( + + ))} + + ); +}; diff --git a/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx b/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx new file mode 100644 index 00000000..8aadca6c --- /dev/null +++ b/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx @@ -0,0 +1,30 @@ +'use client'; + +import { + PostComment, + withProvider, +} from '@gitroom/frontend/components/new-launch/providers/high.order.provider'; +import { FC } from 'react'; +import { MeweDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/mewe.dto'; +import { MeweGroupSelect } from '@gitroom/frontend/components/new-launch/providers/mewe/mewe.group.select'; +import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; + +const MeweComponent: FC = () => { + const form = useSettings(); + return ( +
+ +
+ ); +}; + +export default withProvider({ + postComment: PostComment.POST, + comments: false, + minimumCharacters: [], + SettingsComponent: MeweComponent, + CustomPreviewComponent: undefined, + dto: MeweDto, + checkValidity: undefined, + maximumCharacters: 63206, +}); diff --git a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx index 3ade8870..91844429 100644 --- a/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx +++ b/apps/frontend/src/components/new-launch/providers/show.all.providers.tsx @@ -38,6 +38,7 @@ import GmbProvider from '@gitroom/frontend/components/new-launch/providers/gmb/g import MoltbookProvider from '@gitroom/frontend/components/new-launch/providers/moltbook/moltbook.provider'; import SkoolProvider from '@gitroom/frontend/components/new-launch/providers/skool/skool.provider'; import WhopProvider from '@gitroom/frontend/components/new-launch/providers/whop/whop.provider'; +import MeweProvider from '@gitroom/frontend/components/new-launch/providers/mewe/mewe.provider'; export const Providers = [ { @@ -167,7 +168,11 @@ export const Providers = [ { identifier: 'whop', component: WhopProvider, - } + }, + { + identifier: 'mewe', + component: MeweProvider, + }, ]; export const ShowAllProviders = forwardRef((props, ref) => { const { date, current, global, selectedIntegrations, allIntegrations } = diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts index 071be782..a66261e1 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts @@ -23,6 +23,7 @@ import { FacebookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-sett import { MoltbookDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/moltbook.dto'; import { SkoolDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/skool.dto'; import { WhopDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/whop.dto'; +import { MeweDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/mewe.dto'; export type ProviderExtension = { __type: T } & M; export type AllProvidersSettings = @@ -57,6 +58,7 @@ export type AllProvidersSettings = | ProviderExtension<'moltbook', MoltbookDto> | ProviderExtension<'vk', None> | ProviderExtension<'skool', SkoolDto> + | ProviderExtension<'mewe', MeweDto> | ProviderExtension<'whop', WhopDto>; type None = NonNullable; @@ -95,6 +97,7 @@ export const allProviders = (setEmpty?: any) => { { value: MoltbookDto, name: 'moltbook' }, { value: SkoolDto, name: 'skool' }, { value: WhopDto, name: 'whop' }, + { value: MeweDto, name: 'mewe' }, ].filter((f) => f.value); }; diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts new file mode 100644 index 00000000..f1039bae --- /dev/null +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts @@ -0,0 +1,12 @@ +import { IsDefined, IsString, MinLength } from 'class-validator'; +import { JSONSchema } from 'class-validator-jsonschema'; + +export class MeweDto { + @MinLength(1) + @IsDefined() + @IsString() + @JSONSchema({ + description: 'Group must be an id', + }) + group: string; +} diff --git a/libraries/nestjs-libraries/src/integrations/integration.manager.ts b/libraries/nestjs-libraries/src/integrations/integration.manager.ts index 731bdbe5..a09ea2f6 100644 --- a/libraries/nestjs-libraries/src/integrations/integration.manager.ts +++ b/libraries/nestjs-libraries/src/integrations/integration.manager.ts @@ -35,6 +35,7 @@ import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.ab import { MoltbookProvider } from '@gitroom/nestjs-libraries/integrations/social/moltbook.provider'; import { SkoolProvider } from '@gitroom/nestjs-libraries/integrations/social/skool.provider'; import { WhopProvider } from '@gitroom/nestjs-libraries/integrations/social/whop.provider'; +import { MeweProvider } from '@gitroom/nestjs-libraries/integrations/social/mewe.provider'; export const socialIntegrationList: Array = [ new XProvider(), @@ -69,6 +70,7 @@ export const socialIntegrationList: Array = [ new MoltbookProvider(), new WhopProvider(), new SkoolProvider(), + new MeweProvider(), // new MastodonCustomProvider(), ]; diff --git a/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts new file mode 100644 index 00000000..55640527 --- /dev/null +++ b/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts @@ -0,0 +1,297 @@ +import { + AuthTokenDetails, + PostDetails, + PostResponse, + SocialProvider, +} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; +import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; +import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import dayjs from 'dayjs'; +import { Integration } from '@prisma/client'; +import { MeweDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/mewe.dto'; +import { Tool } from '@gitroom/nestjs-libraries/integrations/tool.decorator'; + +export class MeweProvider extends SocialAbstract implements SocialProvider { + identifier = 'mewe'; + name = 'MeWe'; + isBetweenSteps = false; + scopes = [] as string[]; + editor = 'normal' as const; + dto = MeweDto; + + private get meweHost() { + return process.env.MEWE_HOST || 'https://mewe.com'; + } + + private authHeaders(apiToken: string) { + return { + 'X-App-Id': process.env.MEWE_APP_ID!, + 'X-Api-Key': process.env.MEWE_API_KEY!, + Authorization: `Bearer ${apiToken}`, + 'Content-Type': 'application/json', + }; + } + + maxLength() { + return 63206; + } + + override handleErrors( + body: string + ): + | { type: 'refresh-token' | 'bad-body' | 'retry'; value: string } + | undefined { + if (body.indexOf('Unauthorized') > -1) { + return { + type: 'refresh-token' as const, + value: 'Access token expired, please re-authenticate', + }; + } + + if (body.indexOf('Enhance Your Calm') > -1 || body.indexOf('420') > -1) { + return { + type: 'retry' as const, + value: 'Rate limited, retrying...', + }; + } + + if (body.indexOf('Forbidden') > -1) { + return { + type: 'bad-body' as const, + value: 'Insufficient permissions for this action', + }; + } + + return undefined; + } + + async refreshToken(refreshToken: string): Promise { + return { + refreshToken: '', + expiresIn: 0, + accessToken: '', + id: '', + name: '', + picture: '', + username: '', + }; + } + + async generateAuthUrl() { + const state = makeId(6); + return { + url: + `${this.meweHost}/login` + + `?client_id=${process.env.MEWE_APP_ID}` + + `&redirect_uri=${encodeURIComponent( + `${process.env.FRONTEND_URL}/integrations/social/mewe` + )}` + + `&state=${state}`, + codeVerifier: makeId(10), + state, + }; + } + + async authenticate(params: { + code: string; + codeVerifier: string; + refresh?: string; + }) { + const loginRequestToken = params.code; + + if (!loginRequestToken) { + return 'No login request token received. Please try again.'; + } + + try { + // Exchange loginRequestToken for apiToken + const tokenResponse = await fetch( + `${this.meweHost}/api/dev/token?loginRequestToken=${loginRequestToken}`, + { + method: 'GET', + headers: { + 'X-App-Id': process.env.MEWE_APP_ID!, + 'X-Api-Key': process.env.MEWE_API_KEY!, + }, + } + ); + + if (!tokenResponse.ok) { + return 'Failed to exchange token. Please try again.'; + } + + const tokenData = await tokenResponse.json(); + + if (tokenData.pending) { + return 'Login request is still pending. Please approve on MeWe and try again.'; + } + + if (!tokenData.apiToken) { + return 'No API token received. Please try again.'; + } + + const apiToken = tokenData.apiToken; + const expiresAt = tokenData.expiresAt; + + // Fetch user profile + const profileResponse = await fetch(`${this.meweHost}/api/dev/me`, { + method: 'GET', + headers: this.authHeaders(apiToken), + }); + + if (!profileResponse.ok) { + return 'Failed to fetch MeWe profile.'; + } + + const profile = await profileResponse.json(); + + const expiresIn = expiresAt + ? dayjs(expiresAt).unix() - dayjs().unix() + : dayjs().add(30, 'days').unix() - dayjs().unix(); + + return { + id: profile.userId, + name: + profile.name || + `${profile.firstName || ''} ${profile.lastName || ''}`.trim(), + accessToken: apiToken, + refreshToken: '', + expiresIn, + picture: '', + username: profile.handle || '', + }; + } catch (e) { + console.log(e); + return 'MeWe authentication failed. Please try again.'; + } + } + + @Tool({ description: 'Groups', dataSchema: [] }) + async groups( + accessToken: string, + params: any, + id: string, + integration: Integration + ) { + try { + const allGroups: any[] = []; + let nextUrl: string | null = `${this.meweHost}/api/dev/groups`; + + while (nextUrl) { + const response = await fetch(nextUrl, { + method: 'GET', + headers: this.authHeaders(accessToken), + }); + + if (!response.ok) break; + + const data = await response.json(); + allGroups.push(...(data.groups || [])); + nextUrl = data.nextPage ? `${this.meweHost}${data.nextPage}` : null; + } + + return allGroups.map((group: any) => ({ + id: String(group.groupId), + name: group.name, + })); + } catch (err) { + return []; + } + } + + private async uploadPhoto( + accessToken: string, + mediaPath: string + ): Promise { + const mediaResponse = await fetch(mediaPath); + const blob = await mediaResponse.blob(); + const fileName = mediaPath.split('/').pop() || 'photo.jpg'; + + const form = new FormData(); + form.append('file', blob, fileName); + + const uploadResponse = await fetch( + `${this.meweHost}/api/dev/photo/upload`, + { + method: 'POST', + headers: { + 'X-App-Id': process.env.MEWE_APP_ID!, + 'X-Api-Key': process.env.MEWE_API_KEY!, + Authorization: `Bearer ${accessToken}`, + }, + body: form, + } + ); + + if (!uploadResponse.ok) { + const errorText = await uploadResponse.text(); + throw new Error(`Photo upload failed: ${errorText}`); + } + + const uploadData = await uploadResponse.json(); + return uploadData.id; + } + + async post( + id: string, + accessToken: string, + postDetails: PostDetails[], + integration: Integration + ): Promise { + const [firstPost] = postDetails; + const groupId = firstPost.settings.group; + + // Upload photos if present (exclude videos) + const imageMedia = + firstPost.media?.filter((m) => !m.path || m.path.indexOf('mp4') === -1) || + []; + + const uploadedPhotoIds: string[] = []; + for (const media of imageMedia) { + const photoId = await this.uploadPhoto(accessToken, media.path); + uploadedPhotoIds.push(photoId); + } + + const postBody: Record = { text: firstPost.message }; + if (uploadedPhotoIds.length > 0) { + postBody.uploadedPhotoIds = uploadedPhotoIds; + } + + // MeWe post endpoint may return 204 (no content), so use raw fetch + const postResponse = await fetch( + `${this.meweHost}/api/dev/group/${groupId}/post`, + { + method: 'POST', + headers: this.authHeaders(accessToken), + body: JSON.stringify(postBody), + } + ); + + if (!postResponse.ok) { + const errorText = await postResponse.text(); + console.log(errorText); + const handleError = this.handleErrors(errorText); + if (handleError) { + throw new Error(handleError.value); + } + throw new Error('Failed to create MeWe post'); + } + + let postId = ''; + try { + const responseData = await postResponse.json(); + postId = responseData.postId || responseData.id || makeId(12); + } catch { + postId = makeId(12); + } + + return [ + { + id: firstPost.id, + postId, + releaseURL: `${this.meweHost}/group/${groupId}`, + status: 'success', + }, + ]; + } +} From 0c0255406447d76a54b1f6d47e6e984408dd5420 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 09:48:10 +0700 Subject: [PATCH 246/340] feat: cli --- apps/cli/HOW_TO_RUN.md | 300 ++++++++++++++++++++++++++++++++ apps/cli/PUBLISHING.md | 377 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 677 insertions(+) create mode 100644 apps/cli/HOW_TO_RUN.md create mode 100644 apps/cli/PUBLISHING.md diff --git a/apps/cli/HOW_TO_RUN.md b/apps/cli/HOW_TO_RUN.md new file mode 100644 index 00000000..ef003c3b --- /dev/null +++ b/apps/cli/HOW_TO_RUN.md @@ -0,0 +1,300 @@ +# How to Run the Postiz CLI + +There are several ways to run the CLI, depending on your needs. + +## Option 1: Direct Execution (Quick Test) ⚡ + +The built file at `apps/cli/dist/index.js` is already executable! + +```bash +# From the monorepo root +node apps/cli/dist/index.js --help + +# Or run it directly (it has a shebang) +./apps/cli/dist/index.js --help + +# Example command +export POSTIZ_API_KEY=your_key +node apps/cli/dist/index.js posts:list +``` + +## Option 2: Link Globally (Recommended for Development) 🔗 + +This creates a global `postiz` command you can use anywhere: + +```bash +# From the monorepo root +cd apps/cli +pnpm link --global + +# Now you can use it anywhere! +postiz --help +postiz posts:list +postiz posts:create -c "Hello!" -i "twitter-123" + +# To unlink later +pnpm unlink --global +``` + +**After linking, you can use `postiz` from any directory!** + +## Option 3: Use pnpm Filter (From Root) 📦 + +```bash +# From the monorepo root +pnpm --filter postiz start -- --help +pnpm --filter postiz start -- posts:list +pnpm --filter postiz start -- posts:create -c "Hello" -i "twitter-123" +``` + +## Option 4: Use npm/npx (After Publishing) 🌐 + +Once published to npm: + +```bash +# Install globally +npm install -g postiz + +# Or use with npx (no install) +npx postiz --help +npx postiz posts:list +``` + +## Quick Setup Guide + +### Step 1: Build the CLI + +```bash +# From monorepo root +pnpm run build:cli +``` + +### Step 2: Set Your API Key + +```bash +export POSTIZ_API_KEY=your_api_key_here + +# To make it permanent, add to your shell profile: +echo 'export POSTIZ_API_KEY=your_api_key' >> ~/.bashrc +# or ~/.zshrc if you use zsh +``` + +### Step 3: Choose Your Method + +**For quick testing:** +```bash +node apps/cli/dist/index.js --help +``` + +**For regular use (recommended):** +```bash +cd apps/cli +pnpm link --global +postiz --help +``` + +## Troubleshooting + +### "Command not found: postiz" + +If you linked globally but still get this error: + +```bash +# Check if it's linked +which postiz + +# If not found, try linking again +cd apps/cli +pnpm link --global + +# Or check your PATH +echo $PATH +``` + +### "POSTIZ_API_KEY is not set" + +```bash +export POSTIZ_API_KEY=your_key + +# Verify it's set +echo $POSTIZ_API_KEY +``` + +### Permission Denied + +If you get permission errors: + +```bash +# Make the file executable +chmod +x apps/cli/dist/index.js + +# Then try again +./apps/cli/dist/index.js --help +``` + +### Rebuild After Changes + +After making code changes, rebuild: + +```bash +pnpm run build:cli +``` + +If you linked globally, the changes will be reflected immediately (no need to re-link). + +## Testing the CLI + +### Test Help Command + +```bash +postiz --help +postiz posts:create --help +``` + +### Test with Sample Command (requires API key) + +```bash +export POSTIZ_API_KEY=your_key + +# List integrations +postiz integrations:list + +# Create a test post +postiz posts:create \ + -c "Test post from CLI" \ + -i "your-integration-id" +``` + +## Development Workflow + +### 1. Make Changes + +Edit files in `apps/cli/src/` + +### 2. Rebuild + +```bash +pnpm run build:cli +``` + +### 3. Test + +```bash +# If linked globally +postiz --help + +# Or direct execution +node apps/cli/dist/index.js --help +``` + +### 4. Watch Mode (Auto-rebuild) + +```bash +# From apps/cli directory +pnpm run dev + +# In another terminal, test your changes +postiz --help +``` + +## Environment Variables + +### Required + +- `POSTIZ_API_KEY` - Your Postiz API key (required for all operations) + +### Optional + +- `POSTIZ_API_URL` - Custom API endpoint (default: `https://api.postiz.com`) + +### Setting Environment Variables + +**Temporary (current session):** +```bash +export POSTIZ_API_KEY=your_key +export POSTIZ_API_URL=https://custom-api.com +``` + +**Permanent (add to shell profile):** +```bash +# For bash +echo 'export POSTIZ_API_KEY=your_key' >> ~/.bashrc +source ~/.bashrc + +# For zsh +echo 'export POSTIZ_API_KEY=your_key' >> ~/.zshrc +source ~/.zshrc +``` + +## Using Aliases + +Create a convenient alias: + +```bash +# Add to ~/.bashrc or ~/.zshrc +alias pz='postiz' + +# Now you can use +pz posts:list +pz posts:create -c "Quick post" -i "twitter-123" +``` + +## Production Deployment + +### Publish to npm + +```bash +# From monorepo root +pnpm run publish-cli + +# Or from apps/cli +cd apps/cli +pnpm run publish +``` + +### Install from npm + +```bash +# Global install +npm install -g postiz + +# Project-specific +npm install postiz +npx postiz --help +``` + +## Summary of Methods + +| Method | Command | Use Case | +|--------|---------|----------| +| **Direct Node** | `node apps/cli/dist/index.js` | Quick testing, no installation | +| **Direct Execution** | `./apps/cli/dist/index.js` | Same as above, slightly shorter | +| **Global Link** | `postiz` (after `pnpm link --global`) | **Recommended** for development | +| **pnpm Filter** | `pnpm --filter postiz start --` | From monorepo root | +| **npm Global** | `postiz` (after `npm i -g postiz`) | After publishing to npm | +| **npx** | `npx postiz` | One-off usage without installing | + +## Recommended Setup + +For the best development experience: + +```bash +# 1. Build +pnpm run build:cli + +# 2. Link globally +cd apps/cli +pnpm link --global + +# 3. Set API key +export POSTIZ_API_KEY=your_key + +# 4. Test +postiz --help +postiz integrations:list + +# 5. Start using! +postiz posts:create -c "My first post" -i "twitter-123" +``` + +Now you can use `postiz` from anywhere! 🚀 diff --git a/apps/cli/PUBLISHING.md b/apps/cli/PUBLISHING.md new file mode 100644 index 00000000..5c4d5d34 --- /dev/null +++ b/apps/cli/PUBLISHING.md @@ -0,0 +1,377 @@ +# Publishing the Postiz CLI to npm + +## Quick Publish (Current Name: "postiz") + +```bash +# From apps/cli directory +pnpm run build +pnpm publish --access public +``` + +Then users can install: +```bash +npm install -g postiz +# or +pnpm install -g postiz + +# And use: +postiz --help +``` + +## Publishing with a Different Package Name + +If you want to publish as a different npm package name (e.g., "agent-postiz"): + +### 1. Change Package Name + +Edit `apps/cli/package.json`: + +```json +{ + "name": "agent-postiz", // ← Changed package name + "version": "1.0.0", + "bin": { + "postiz": "./dist/index.js" // ← Keep command name! + } +} +``` + +**Important:** The `bin` field determines the command name, NOT the package name! + +### 2. Publish + +```bash +cd apps/cli +pnpm run build +pnpm publish --access public +``` + +### 3. Users Install + +```bash +npm install -g agent-postiz +# or +pnpm install -g agent-postiz +``` + +### 4. Users Use + +Even though the package is called "agent-postiz", the command is still: + +```bash +postiz --help # ← Command name from "bin" field +postiz posts:create -c "Hello!" -i "twitter-123" +``` + +## Package Name vs Command Name + +| Field | Purpose | Example | +|-------|---------|---------| +| `"name"` | npm package name (what you install) | `"agent-postiz"` | +| `"bin"` | Command name (what you type) | `"postiz"` | + +**Examples:** + +1. **Same name:** + ```json + "name": "postiz", + "bin": { "postiz": "./dist/index.js" } + ``` + Install: `npm i -g postiz` + Use: `postiz` + +2. **Different names:** + ```json + "name": "agent-postiz", + "bin": { "postiz": "./dist/index.js" } + ``` + Install: `npm i -g agent-postiz` + Use: `postiz` + +3. **Multiple commands:** + ```json + "name": "agent-postiz", + "bin": { + "postiz": "./dist/index.js", + "pz": "./dist/index.js" + } + ``` + Install: `npm i -g agent-postiz` + Use: `postiz` or `pz` + +## Publishing Checklist + +### Before First Publish + +- [ ] Verify package name is available on npm + ```bash + npm view postiz + # If error "404 Not Found" - name is available! + ``` + +- [ ] Update version if needed + ```json + "version": "1.0.0" + ``` + +- [ ] Review files to include + ```json + "files": [ + "dist", + "README.md", + "SKILL.md" + ] + ``` + +- [ ] Build the package + ```bash + pnpm run build + ``` + +- [ ] Test locally + ```bash + pnpm link --global + postiz --help + ``` + +### Publish to npm + +```bash +# Login to npm (first time only) +npm login + +# From apps/cli +pnpm run build +pnpm publish --access public + +# Or use the root script +cd /path/to/monorepo/root +pnpm run publish-cli +``` + +### After Publishing + +Verify it's published: +```bash +npm view postiz +# Should show your package info +``` + +Test installation: +```bash +npm install -g postiz +postiz --version +``` + +## Using from Monorepo Root + +The root `package.json` already has: + +```json +{ + "scripts": { + "publish-cli": "pnpm run --filter ./apps/cli publish" + } +} +``` + +So you can publish from the root: + +```bash +# From monorepo root +pnpm run publish-cli +``` + +## Version Updates + +### Patch Release (1.0.0 → 1.0.1) + +```bash +cd apps/cli +npm version patch +pnpm publish --access public +``` + +### Minor Release (1.0.0 → 1.1.0) + +```bash +cd apps/cli +npm version minor +pnpm publish --access public +``` + +### Major Release (1.0.0 → 2.0.0) + +```bash +cd apps/cli +npm version major +pnpm publish --access public +``` + +## Scoped Packages + +If you want to publish under an organization scope: + +```json +{ + "name": "@yourorg/postiz", + "bin": { + "postiz": "./dist/index.js" + } +} +``` + +Install: +```bash +npm install -g @yourorg/postiz +``` + +Use: +```bash +postiz --help +``` + +## Testing Before Publishing + +### Test the Build + +```bash +pnpm run build +node dist/index.js --help +``` + +### Test Linking + +```bash +pnpm link --global +postiz --help +pnpm unlink --global +``` + +### Test Publishing (Dry Run) + +```bash +npm publish --dry-run +# Shows what would be published +``` + +### Test with `npm pack` + +```bash +npm pack +# Creates a .tgz file + +# Test installing the tarball +npm install -g ./postiz-1.0.0.tgz +postiz --help +npm uninstall -g postiz +``` + +## Continuous Publishing + +### Using GitHub Actions + +Create `.github/workflows/publish-cli.yml`: + +```yaml +name: Publish CLI to npm + +on: + push: + tags: + - 'cli-v*' + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: pnpm/action-setup@v2 + - uses: actions/setup-node@v3 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - run: pnpm install + - run: pnpm run build:cli + + - name: Publish to npm + run: pnpm --filter ./apps/cli publish --access public + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} +``` + +Then publish with: +```bash +git tag cli-v1.0.0 +git push origin cli-v1.0.0 +``` + +## Common Issues + +### "You do not have permission to publish" + +- Make sure you're logged in: `npm login` +- Check package name isn't taken: `npm view postiz` +- If scoped, ensure org access: `npm org ls yourorg` + +### "Package name too similar to existing package" + +- Choose a more unique name +- Or use a scoped package: `@yourorg/postiz` + +### "Missing required files" + +- Check `"files"` field in package.json +- Run `npm pack` to see what would be included +- Make sure `dist/` exists and is built + +### Command not found after install + +- Check `"bin"` field is correct +- Ensure `dist/index.js` has shebang: `#!/usr/bin/env node` +- Try reinstalling: `npm uninstall -g postiz && npm install -g postiz` + +## Recommended Names + +If "postiz" is taken, consider: + +- `@postiz/cli` +- `postiz-cli` +- `postiz-agent` +- `agent-postiz` +- `@yourorg/postiz` + +Remember: The package name is just for installation. The command can still be `postiz`! + +## Summary + +✅ Current setup works perfectly! +✅ `bin` field defines the command name +✅ `name` field defines the npm package name +✅ They can be different! + +**To publish now:** + +```bash +cd apps/cli +pnpm run build +pnpm publish --access public +``` + +**Users install:** + +```bash +npm install -g postiz +# or +pnpm install -g postiz +``` + +**Users use:** + +```bash +postiz --help +postiz posts:create -c "Hello!" -i "twitter-123" +``` + +🚀 **Ready to publish!** From 33c8adbef11884f9a9d9fd2ede43b3089a4ce3c8 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 10:27:02 +0700 Subject: [PATCH 247/340] feat: get settings --- .../v1/public.integrations.controller.ts | 41 +++ apps/cli/SUPPORTED_FILE_TYPES.md | 305 ++++++++++++++++++ apps/cli/package.json | 4 +- apps/cli/src/api.ts | 50 ++- apps/cli/src/commands/posts.ts | 21 +- apps/cli/src/index.ts | 37 +-- 6 files changed, 425 insertions(+), 33 deletions(-) create mode 100644 apps/cli/SUPPORTED_FILE_TYPES.md diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 86b412f5..67f2b0f6 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -33,6 +33,9 @@ import axios from 'axios'; import { Readable } from 'stream'; import { lookup } from 'mime-types'; import * as Sentry from '@sentry/nestjs'; +import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context'; +import { socialIntegrationList } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { getValidationSchemas } from '@gitroom/nestjs-libraries/chat/validation.schemas.helper'; @ApiTags('Public API') @Controller('/public/v1') @@ -213,4 +216,42 @@ export class PublicIntegrationsController { body.params ); } + + @Get('/integration-settings/:id') + async getIntegrationSettings( + @GetOrgFromRequest() org: Organization, + @Param('providerName') providerName: string + ) { + const loadIntegration = await this._integrationService.getIntegrationById( + org.id, + providerName + ); + + const verified = + JSON.parse(loadIntegration.additionalSettings || '[]')?.find( + (p: any) => p?.title === 'Verified' + )?.value || false; + + const integration = socialIntegrationList.find( + (p) => p.identifier === loadIntegration.providerIdentifier + )!; + + if (!integration) { + return { + output: { rules: '', maxLength: 0, settings: {}, tools: [] as any[] }, + }; + } + + const maxLength = integration.maxLength(verified); + const schemas = !integration.dto + ? false + : getValidationSchemas()[integration.dto.name]; + + return { + output: { + maxLength, + settings: !schemas ? 'No additional settings required' : schemas, + }, + }; + } } diff --git a/apps/cli/SUPPORTED_FILE_TYPES.md b/apps/cli/SUPPORTED_FILE_TYPES.md new file mode 100644 index 00000000..e0610013 --- /dev/null +++ b/apps/cli/SUPPORTED_FILE_TYPES.md @@ -0,0 +1,305 @@ +# Supported File Types for Upload + +The Postiz CLI now correctly detects and uploads various media types. + +## How It Works + +The CLI automatically detects the MIME type based on the file extension: + +```bash +postiz upload video.mp4 +# ✅ Detected as: video/mp4 + +postiz upload image.png +# ✅ Detected as: image/png + +postiz upload audio.mp3 +# ✅ Detected as: audio/mpeg +``` + +## Supported File Types + +### Images + +| Extension | MIME Type | Supported | +|-----------|-----------|-----------| +| `.png` | `image/png` | ✅ Yes | +| `.jpg`, `.jpeg` | `image/jpeg` | ✅ Yes | +| `.gif` | `image/gif` | ✅ Yes | +| `.webp` | `image/webp` | ✅ Yes | +| `.svg` | `image/svg+xml` | ✅ Yes | +| `.bmp` | `image/bmp` | ✅ Yes | +| `.ico` | `image/x-icon` | ✅ Yes | + +**Examples:** +```bash +postiz upload photo.jpg +postiz upload logo.png +postiz upload animation.gif +postiz upload icon.svg +``` + +### Videos + +| Extension | MIME Type | Supported | +|-----------|-----------|-----------| +| `.mp4` | `video/mp4` | ✅ Yes | +| `.mov` | `video/quicktime` | ✅ Yes | +| `.avi` | `video/x-msvideo` | ✅ Yes | +| `.mkv` | `video/x-matroska` | ✅ Yes | +| `.webm` | `video/webm` | ✅ Yes | +| `.flv` | `video/x-flv` | ✅ Yes | +| `.wmv` | `video/x-ms-wmv` | ✅ Yes | +| `.m4v` | `video/x-m4v` | ✅ Yes | +| `.mpeg`, `.mpg` | `video/mpeg` | ✅ Yes | +| `.3gp` | `video/3gpp` | ✅ Yes | + +**Examples:** +```bash +postiz upload video.mp4 +postiz upload clip.mov +postiz upload recording.webm +postiz upload movie.mkv +``` + +### Audio + +| Extension | MIME Type | Supported | +|-----------|-----------|-----------| +| `.mp3` | `audio/mpeg` | ✅ Yes | +| `.wav` | `audio/wav` | ✅ Yes | +| `.ogg` | `audio/ogg` | ✅ Yes | +| `.aac` | `audio/aac` | ✅ Yes | +| `.flac` | `audio/flac` | ✅ Yes | +| `.m4a` | `audio/mp4` | ✅ Yes | + +**Examples:** +```bash +postiz upload podcast.mp3 +postiz upload song.wav +postiz upload audio.ogg +``` + +### Documents + +| Extension | MIME Type | Supported | +|-----------|-----------|-----------| +| `.pdf` | `application/pdf` | ✅ Yes | +| `.doc` | `application/msword` | ✅ Yes | +| `.docx` | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` | ✅ Yes | + +**Examples:** +```bash +postiz upload document.pdf +postiz upload report.docx +``` + +### Other Files + +For file types not listed above, the CLI uses: +- MIME type: `application/octet-stream` +- This is a generic binary file type + +## Usage Examples + +### Upload an Image + +```bash +postiz upload ./images/photo.jpg +``` + +Response: +```json +{ + "id": "upload-123", + "path": "https://cdn.postiz.com/uploads/photo.jpg", + "url": "https://cdn.postiz.com/uploads/photo.jpg" +} +``` + +### Upload a Video (MP4) + +```bash +postiz upload ./videos/promo.mp4 +``` + +Response: +```json +{ + "id": "upload-456", + "path": "https://cdn.postiz.com/uploads/promo.mp4", + "url": "https://cdn.postiz.com/uploads/promo.mp4" +} +``` + +### Upload and Use in Post + +```bash +# 1. Upload the file +RESULT=$(postiz upload video.mp4) +echo $RESULT + +# 2. Extract the path (you'll need jq or similar) +PATH=$(echo $RESULT | jq -r '.path') + +# 3. Use in a post +postiz posts:create \ + -c "Check out my video!" \ + -m "$PATH" \ + -i "tiktok-123" +``` + +### Upload Multiple Files + +```bash +# Upload images +postiz upload image1.jpg +postiz upload image2.png +postiz upload image3.gif + +# Upload videos +postiz upload video1.mp4 +postiz upload video2.mov +``` + +## What Changed (Fix) + +### Before (❌ Bug) + +```bash +postiz upload video.mp4 +# ❌ Was detected as: image/jpeg (WRONG!) +``` + +The problem: The CLI defaulted to `image/jpeg` for any unknown file type. + +### After (✅ Fixed) + +```bash +postiz upload video.mp4 +# ✅ Correctly detected as: video/mp4 + +postiz upload audio.mp3 +# ✅ Correctly detected as: audio/mpeg + +postiz upload document.pdf +# ✅ Correctly detected as: application/pdf +``` + +## Platform-Specific Notes + +### TikTok +- Supports: MP4, MOV, WEBM +- Recommended: MP4 + +### YouTube +- Supports: MP4, MOV, AVI, WMV, FLV, 3GP, WEBM +- Recommended: MP4 + +### Instagram +- Images: JPG, PNG +- Videos: MP4, MOV +- Recommended: MP4 for videos, JPG for images + +### Twitter/X +- Images: PNG, JPG, GIF, WEBP +- Videos: MP4, MOV +- Max video size: 512MB + +### LinkedIn +- Images: PNG, JPG, GIF +- Videos: MP4, MOV, AVI +- Documents: PDF, DOC, DOCX, PPT + +## Troubleshooting + +### "Upload failed: Unsupported file type" + +Some platforms may not accept certain file types. Check the platform's documentation. + +**Solution:** Convert the file to a supported format: + +```bash +# Convert video to MP4 +ffmpeg -i video.avi video.mp4 + +# Then upload +postiz upload video.mp4 +``` + +### File Size Limits + +Different platforms have different file size limits: + +- **Twitter/X**: Max 512MB for videos +- **Instagram**: Max 100MB for videos +- **TikTok**: Max 287.6MB for videos +- **YouTube**: Max 128GB (but 256GB for verified) + +### "MIME type mismatch" + +If you renamed a file with the wrong extension: + +```bash +# ❌ Wrong: PNG file renamed to .jpg +mv image.png image.jpg +postiz upload image.jpg # Might fail + +# ✅ Correct: Keep original extension +postiz upload image.png +``` + +## Testing File Upload + +```bash +# Set API key +export POSTIZ_API_KEY=your_key + +# Test image upload +postiz upload test-image.jpg + +# Test video upload +postiz upload test-video.mp4 + +# Test audio upload +postiz upload test-audio.mp3 +``` + +## Error Messages + +### File Not Found +``` +❌ ENOENT: no such file or directory +``` + +**Solution:** Check the file path is correct. + +### No Permission +``` +❌ EACCES: permission denied +``` + +**Solution:** Check file permissions: +```bash +chmod 644 your-file.mp4 +``` + +### Invalid API Key +``` +❌ Upload failed (401): Unauthorized +``` + +**Solution:** Set your API key: +```bash +export POSTIZ_API_KEY=your_key +``` + +## Summary + +✅ **30+ file types supported** +✅ **Automatic MIME type detection** +✅ **Images, videos, audio, documents** +✅ **Correct handling of MP4, MOV, MP3, etc.** +✅ **No more defaulting to JPEG!** + +**The upload bug is fixed!** 🎉 diff --git a/apps/cli/package.json b/apps/cli/package.json index b171f162..34afd55d 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "postiz", - "version": "1.0.0", + "version": "2.0.2", "description": "Postiz CLI - Command line interface for the Postiz social media scheduling API", "main": "dist/index.js", "bin": { @@ -10,7 +10,7 @@ "dev": "tsup --watch", "build": "tsup", "start": "node ./dist/index.js", - "publish": "tsup && pnpm publish --access public" + "publish": "tsup && pnpm publish --access public --no-git-checks" }, "files": [ "dist", diff --git a/apps/cli/src/api.ts b/apps/cli/src/api.ts index b9c9c7d6..c32c0d87 100644 --- a/apps/cli/src/api.ts +++ b/apps/cli/src/api.ts @@ -73,16 +73,48 @@ export class PostizAPI { async upload(file: Buffer, filename: string) { const formData = new FormData(); - const extension = filename.split('.').pop() || 'jpg'; + const extension = filename.split('.').pop()?.toLowerCase() || ''; - const type = - extension === 'png' - ? 'image/png' - : extension === 'jpg' || extension === 'jpeg' - ? 'image/jpeg' - : extension === 'gif' - ? 'image/gif' - : 'image/jpeg'; + // Determine MIME type based on file extension + const mimeTypes: Record = { + // Images + 'png': 'image/png', + 'jpg': 'image/jpeg', + 'jpeg': 'image/jpeg', + 'gif': 'image/gif', + 'webp': 'image/webp', + 'svg': 'image/svg+xml', + 'bmp': 'image/bmp', + 'ico': 'image/x-icon', + + // Videos + 'mp4': 'video/mp4', + 'mov': 'video/quicktime', + 'avi': 'video/x-msvideo', + 'mkv': 'video/x-matroska', + 'webm': 'video/webm', + 'flv': 'video/x-flv', + 'wmv': 'video/x-ms-wmv', + 'm4v': 'video/x-m4v', + 'mpeg': 'video/mpeg', + 'mpg': 'video/mpeg', + '3gp': 'video/3gpp', + + // Audio + 'mp3': 'audio/mpeg', + 'wav': 'audio/wav', + 'ogg': 'audio/ogg', + 'aac': 'audio/aac', + 'flac': 'audio/flac', + 'm4a': 'audio/mp4', + + // Documents + 'pdf': 'application/pdf', + 'doc': 'application/msword', + 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + }; + + const type = mimeTypes[extension] || 'application/octet-stream'; const blob = new Blob([file], { type }); formData.append('file', blob, filename); diff --git a/apps/cli/src/commands/posts.ts b/apps/cli/src/commands/posts.ts index feaef7f4..8131d0b9 100644 --- a/apps/cli/src/commands/posts.ts +++ b/apps/cli/src/commands/posts.ts @@ -118,10 +118,23 @@ export async function listPosts(args: any) { const config = getConfig(); const api = new PostizAPI(config); - const filters: any = {}; - if (args.page) filters.page = args.page; - if (args.limit) filters.limit = args.limit; - if (args.search) filters.search = args.search; + // Set default date range: last 30 days to 30 days in the future + const defaultStartDate = new Date(); + defaultStartDate.setDate(defaultStartDate.getDate() - 30); + + const defaultEndDate = new Date(); + defaultEndDate.setDate(defaultEndDate.getDate() + 30); + + // Only send fields that are in GetPostsDto + const filters: any = { + startDate: args.startDate || defaultStartDate.toISOString(), + endDate: args.endDate || defaultEndDate.toISOString(), + }; + + // customer is optional in the DTO + if (args.customer) { + filters.customer = args.customer; + } try { const result = await api.listPosts(filters); diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index d2dd4d7c..e59a4b7e 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -111,26 +111,27 @@ yargs(hideBin(process.argv)) 'List all posts', (yargs: Argv) => { return yargs - .option('page', { - alias: 'p', - describe: 'Page number', - type: 'number', - default: 1, - }) - .option('limit', { - alias: 'l', - describe: 'Number of posts per page', - type: 'number', - default: 10, - }) - .option('search', { - alias: 's', - describe: 'Search query', + .option('startDate', { + describe: 'Start date (ISO 8601 format). Default: 30 days ago', type: 'string', }) - .example('$0 posts:list', 'List all posts') - .example('$0 posts:list -p 2 -l 20', 'List posts with pagination') - .example('$0 posts:list -s "hello"', 'Search posts'); + .option('endDate', { + describe: 'End date (ISO 8601 format). Default: 30 days from now', + type: 'string', + }) + .option('customer', { + describe: 'Customer ID (optional)', + type: 'string', + }) + .example('$0 posts:list', 'List all posts (last 30 days to next 30 days)') + .example( + '$0 posts:list --startDate "2024-01-01T00:00:00Z" --endDate "2024-12-31T23:59:59Z"', + 'List posts for a specific date range' + ) + .example( + '$0 posts:list --customer "customer-id"', + 'List posts for a specific customer' + ); }, listPosts as any ) From 2c475d12381e4cd145f9680f799149f249a251b5 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 10:28:16 +0700 Subject: [PATCH 248/340] feat: postiz cli --- pnpm-lock.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7046d4cd..7a509ec3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -802,6 +802,8 @@ importers: apps/backend: {} + apps/cli: {} + apps/commands: {} apps/extension: {} From 2feadf467358e9d367a5615c05d16353e3e7bc7c Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 10:33:33 +0700 Subject: [PATCH 249/340] feat: platforms settings --- apps/cli/INTEGRATION_SETTINGS_DISCOVERY.md | 418 +++++++++++++++++++++ apps/cli/src/api.ts | 6 + apps/cli/src/commands/integrations.ts | 20 + apps/cli/src/index.ts | 22 +- 4 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 apps/cli/INTEGRATION_SETTINGS_DISCOVERY.md diff --git a/apps/cli/INTEGRATION_SETTINGS_DISCOVERY.md b/apps/cli/INTEGRATION_SETTINGS_DISCOVERY.md new file mode 100644 index 00000000..a2a4b5db --- /dev/null +++ b/apps/cli/INTEGRATION_SETTINGS_DISCOVERY.md @@ -0,0 +1,418 @@ +# Integration Settings Discovery + +The CLI now has a powerful feature to discover what settings are available for each integration! + +## New Command: `integrations:settings` + +Get the settings schema, validation rules, and maximum character limits for any integration. + +## Usage + +```bash +postiz integrations:settings +``` + +## What It Returns + +```json +{ + "output": { + "maxLength": 280, + "settings": { + "properties": { + "who_can_reply_post": { + "enum": ["everyone", "following", "mentionedUsers", "subscribers", "verified"], + "description": "Who can reply to this post" + }, + "community": { + "pattern": "^(https://x.com/i/communities/\\d+)?$", + "description": "X community URL" + } + }, + "required": ["who_can_reply_post"] + } + } +} +``` + +## Workflow + +### 1. List Your Integrations + +```bash +postiz integrations:list +``` + +Output: +```json +[ + { + "id": "reddit-abc123", + "name": "My Reddit Account", + "identifier": "reddit", + "provider": "reddit" + }, + { + "id": "youtube-def456", + "name": "My YouTube Channel", + "identifier": "youtube", + "provider": "youtube" + }, + { + "id": "twitter-ghi789", + "name": "@myhandle", + "identifier": "x", + "provider": "x" + } +] +``` + +### 2. Get Settings for Specific Integration + +```bash +postiz integrations:settings reddit-abc123 +``` + +Output: +```json +{ + "output": { + "maxLength": 40000, + "settings": { + "properties": { + "subreddit": { + "type": "array", + "items": { + "properties": { + "value": { + "properties": { + "subreddit": { + "type": "string", + "minLength": 2, + "description": "Subreddit name" + }, + "title": { + "type": "string", + "minLength": 2, + "description": "Post title" + }, + "type": { + "type": "string", + "description": "Post type (text or link)" + }, + "url": { + "type": "string", + "description": "URL for link posts" + }, + "is_flair_required": { + "type": "boolean", + "description": "Whether flair is required" + }, + "flair": { + "properties": { + "id": "string", + "name": "string" + } + } + }, + "required": ["subreddit", "title", "type", "is_flair_required"] + } + } + } + } + }, + "required": ["subreddit"] + } + } +} +``` + +### 3. Use the Settings in Your Post + +Now you know what settings are available and required! + +```bash +postiz posts:create \ + -c "My post content" \ + -p reddit \ + --settings '{ + "subreddit": [{ + "value": { + "subreddit": "programming", + "title": "Check this out!", + "type": "text", + "url": "", + "is_flair_required": false + } + }] + }' \ + -i "reddit-abc123" +``` + +## Examples by Platform + +### Reddit + +```bash +postiz integrations:settings reddit-abc123 +``` + +Returns: +- Max length: 40,000 characters +- Required settings: subreddit, title, type +- Optional: flair + +### YouTube + +```bash +postiz integrations:settings youtube-def456 +``` + +Returns: +- Max length: 5,000 characters (description) +- Required settings: title, type (public/private/unlisted) +- Optional: tags, thumbnail, selfDeclaredMadeForKids + +### X (Twitter) + +```bash +postiz integrations:settings twitter-ghi789 +``` + +Returns: +- Max length: 280 characters (or 4,000 for verified) +- Required settings: who_can_reply_post +- Optional: community + +### LinkedIn + +```bash +postiz integrations:settings linkedin-jkl012 +``` + +Returns: +- Max length: 3,000 characters +- Optional settings: post_as_images_carousel, carousel_name + +### TikTok + +```bash +postiz integrations:settings tiktok-mno345 +``` + +Returns: +- Max length: 150 characters (caption) +- Required settings: privacy_level, duet, stitch, comment, autoAddMusic, brand_content_toggle, brand_organic_toggle, content_posting_method +- Optional: title, video_made_with_ai + +### Instagram + +```bash +postiz integrations:settings instagram-pqr678 +``` + +Returns: +- Max length: 2,200 characters +- Required settings: post_type (post or story) +- Optional: is_trial_reel, graduation_strategy, collaborators + +## No Additional Settings Required + +Some platforms don't require specific settings: + +```bash +postiz integrations:settings threads-stu901 +``` + +Returns: +```json +{ + "output": { + "maxLength": 500, + "settings": "No additional settings required" + } +} +``` + +Platforms with no additional settings: +- Threads +- Mastodon +- Bluesky +- Telegram +- Nostr +- VK + +## Use Cases + +### 1. Discovery + +Find out what settings are available before posting: + +```bash +# What settings does YouTube support? +postiz integrations:settings youtube-123 + +# What settings does Reddit support? +postiz integrations:settings reddit-456 +``` + +### 2. Validation + +Check maximum character limits: + +```bash +postiz integrations:settings twitter-789 | jq '.output.maxLength' +# Output: 280 +``` + +### 3. AI Agent Integration + +AI agents can call this endpoint to: +- Discover available settings dynamically +- Validate settings before posting +- Adapt to platform-specific requirements + +```javascript +// Get settings schema +const settings = await execSync( + `postiz integrations:settings ${integrationId}`, + { encoding: 'utf-8' } +); +const schema = JSON.parse(settings); + +// Check max length +if (content.length > schema.output.maxLength) { + content = content.substring(0, schema.output.maxLength); +} + +// Use required settings +const requiredSettings = schema.output.settings.required || []; +``` + +### 4. Form Generation + +Use the schema to generate UI forms: + +```javascript +const settings = await getIntegrationSettings('reddit-123'); +const schema = settings.output.settings; + +// Generate form fields from schema +schema.properties.subreddit.items.properties.value.properties +// → subreddit (text, minLength: 2) +// → title (text, minLength: 2) +// → type (select: text/link) +// → etc. +``` + +## Combined Workflow + +Complete workflow for posting with correct settings: + +```bash +#!/bin/bash +export POSTIZ_API_KEY=your_key + +# 1. List integrations +echo "📋 Available integrations:" +postiz integrations:list + +# 2. Get settings for Reddit +echo "" +echo "⚙️ Reddit settings:" +SETTINGS=$(postiz integrations:settings reddit-123) +echo $SETTINGS | jq '.output.maxLength' +echo $SETTINGS | jq '.output.settings' + +# 3. Create post with correct settings +echo "" +echo "📝 Creating post..." +postiz posts:create \ + -c "My post content" \ + -p reddit \ + --settings '{ + "subreddit": [{ + "value": { + "subreddit": "programming", + "title": "Interesting post", + "type": "text", + "url": "", + "is_flair_required": false + } + }] + }' \ + -i "reddit-123" +``` + +## API Endpoint + +The command calls: +``` +GET /public/v1/integration-settings/:id +``` + +Returns: +```typescript +{ + output: { + maxLength: number; + settings: ValidationSchema | "No additional settings required"; + } +} +``` + +## Error Handling + +### Integration Not Found + +```bash +postiz integrations:settings invalid-id +# ❌ Failed to get integration settings: Integration not found +``` + +### API Key Not Set + +```bash +postiz integrations:settings reddit-123 +# ❌ Error: POSTIZ_API_KEY environment variable is required +``` + +## Tips + +1. **Always check settings first** before creating posts with custom settings +2. **Use the schema** to validate your settings object +3. **Check maxLength** to avoid exceeding character limits +4. **For AI agents**: Cache the settings to avoid repeated API calls +5. **Required fields** must be included in your settings object + +## Comparison: Before vs After + +### Before ❌ + +```bash +# Had to guess what settings are available +# Had to read documentation or source code +# Didn't know character limits +``` + +### After ✅ + +```bash +# Discover settings programmatically +postiz integrations:settings reddit-123 + +# See exactly what's required and optional +# Know the exact character limits +# Get validation schemas +``` + +## Summary + +✅ **Discover settings for any integration** +✅ **Get character limits** +✅ **See validation schemas** +✅ **Know required vs optional fields** +✅ **Perfect for AI agents** +✅ **No more guesswork!** + +**Now you can discover what settings each platform supports!** 🎉 diff --git a/apps/cli/src/api.ts b/apps/cli/src/api.ts index c32c0d87..96ec93ed 100644 --- a/apps/cli/src/api.ts +++ b/apps/cli/src/api.ts @@ -142,4 +142,10 @@ export class PostizAPI { method: 'GET', }); } + + async getIntegrationSettings(integrationId: string) { + return this.request(`/public/v1/integration-settings/${integrationId}`, { + method: 'GET', + }); + } } diff --git a/apps/cli/src/commands/integrations.ts b/apps/cli/src/commands/integrations.ts index 743a1834..ced8f98e 100644 --- a/apps/cli/src/commands/integrations.ts +++ b/apps/cli/src/commands/integrations.ts @@ -15,3 +15,23 @@ export async function listIntegrations() { process.exit(1); } } + +export async function getIntegrationSettings(args: any) { + const config = getConfig(); + const api = new PostizAPI(config); + + if (!args.id) { + console.error('❌ Integration ID is required'); + process.exit(1); + } + + try { + const result = await api.getIntegrationSettings(args.id); + console.log(`⚙️ Settings for integration: ${args.id}`); + console.log(JSON.stringify(result, null, 2)); + return result; + } catch (error: any) { + console.error('❌ Failed to get integration settings:', error.message); + process.exit(1); + } +} diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index e59a4b7e..14c5d037 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,7 +1,7 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { createPost, listPosts, deletePost } from './commands/posts'; -import { listIntegrations } from './commands/integrations'; +import { listIntegrations, getIntegrationSettings } from './commands/integrations'; import { uploadFile } from './commands/upload'; import type { Argv } from 'yargs'; @@ -154,6 +154,26 @@ yargs(hideBin(process.argv)) {}, listIntegrations as any ) + .command( + 'integrations:settings ', + 'Get settings schema for a specific integration', + (yargs: Argv) => { + return yargs + .positional('id', { + describe: 'Integration ID', + type: 'string', + }) + .example( + '$0 integrations:settings reddit-123', + 'Get settings schema for Reddit integration' + ) + .example( + '$0 integrations:settings youtube-456', + 'Get settings schema for YouTube integration' + ); + }, + getIntegrationSettings as any + ) .command( 'upload ', 'Upload a file', From 6aa6dfb6149b6a1df083c1449a11099781f95c68 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 10:37:26 +0700 Subject: [PATCH 250/340] feat: fix settings --- .../public-api/routes/v1/public.integrations.controller.ts | 4 ++-- apps/cli/package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 67f2b0f6..03667e67 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -220,11 +220,11 @@ export class PublicIntegrationsController { @Get('/integration-settings/:id') async getIntegrationSettings( @GetOrgFromRequest() org: Organization, - @Param('providerName') providerName: string + @Param('id') id: string ) { const loadIntegration = await this._integrationService.getIntegrationById( org.id, - providerName + id ); const verified = diff --git a/apps/cli/package.json b/apps/cli/package.json index 34afd55d..b1c98e7f 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "postiz", - "version": "2.0.2", + "version": "2.0.3", "description": "Postiz CLI - Command line interface for the Postiz social media scheduling API", "main": "dist/index.js", "bin": { From af08ae118703b654f32f621d8d4427edcc324e2c Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 11:00:14 +0700 Subject: [PATCH 251/340] feat: tools in public api --- .../v1/public.integrations.controller.ts | 96 +++- apps/cli/INTEGRATION_TOOLS_WORKFLOW.md | 435 ++++++++++++++++++ apps/cli/src/api.ts | 11 + apps/cli/src/commands/integrations.ts | 36 ++ apps/cli/src/index.ts | 35 +- 5 files changed, 609 insertions(+), 4 deletions(-) create mode 100644 apps/cli/INTEGRATION_TOOLS_WORKFLOW.md diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 03667e67..ac8b12db 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -33,9 +33,11 @@ import axios from 'axios'; import { Readable } from 'stream'; import { lookup } from 'mime-types'; import * as Sentry from '@sentry/nestjs'; -import { checkAuth } from '@gitroom/nestjs-libraries/chat/auth.context'; -import { socialIntegrationList } from '@gitroom/nestjs-libraries/integrations/integration.manager'; +import { socialIntegrationList, IntegrationManager } from '@gitroom/nestjs-libraries/integrations/integration.manager'; import { getValidationSchemas } from '@gitroom/nestjs-libraries/chat/validation.schemas.helper'; +import { RefreshIntegrationService } from '@gitroom/nestjs-libraries/integrations/refresh.integration.service'; +import { RefreshToken } from '@gitroom/nestjs-libraries/integrations/social.abstract'; +import { timer } from '@gitroom/helpers/utils/timer'; @ApiTags('Public API') @Controller('/public/v1') @@ -46,7 +48,9 @@ export class PublicIntegrationsController { private _integrationService: IntegrationService, private _postsService: PostsService, private _mediaService: MediaService, - private _notificationService: NotificationService + private _notificationService: NotificationService, + private _integrationManager: IntegrationManager, + private _refreshIntegrationService: RefreshIntegrationService ) {} @Post('/upload') @@ -222,6 +226,7 @@ export class PublicIntegrationsController { @GetOrgFromRequest() org: Organization, @Param('id') id: string ) { + Sentry.metrics.count('public_api-request', 1); const loadIntegration = await this._integrationService.getIntegrationById( org.id, id @@ -246,12 +251,97 @@ export class PublicIntegrationsController { const schemas = !integration.dto ? false : getValidationSchemas()[integration.dto.name]; + const tools = this._integrationManager.getAllTools(); + const rules = this._integrationManager.getAllRulesDescription(); return { output: { + rules: rules[integration.identifier], maxLength, settings: !schemas ? 'No additional settings required' : schemas, + tools: tools[integration.identifier], }, }; } + + @Post('/integration-trigger/:id') + async triggerIntegrationTool( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string, + @Body() body: { methodName: string; data: Record } + ) { + Sentry.metrics.count('public_api-request', 1); + const getIntegration = await this._integrationService.getIntegrationById( + org.id, + id + ); + + if (!getIntegration) { + throw new HttpException({ msg: 'Integration not found' }, 404); + } + + const integrationProvider = socialIntegrationList.find( + (p) => p.identifier === getIntegration.providerIdentifier + )!; + + if (!integrationProvider) { + throw new HttpException({ msg: 'Integration provider not found' }, 404); + } + + const tools = this._integrationManager.getAllTools(); + if ( + // @ts-ignore + !tools[integrationProvider.identifier]?.some( + (p: any) => p.methodName === body.methodName + ) || + // @ts-ignore + !integrationProvider[body.methodName] + ) { + throw new HttpException({ msg: 'Tool not found' }, 404); + } + + while (true) { + try { + // @ts-ignore + const result = await integrationProvider[body.methodName]( + getIntegration.token, + body.data || {}, + getIntegration.internalId, + getIntegration + ); + + return { output: result }; + } catch (err) { + if (err instanceof RefreshToken) { + const data = await this._refreshIntegrationService.refresh( + getIntegration + ); + + if (!data) { + await this._integrationService.disconnectChannel( + org.id, + getIntegration + ); + throw new HttpException( + { msg: 'Channel disconnected due to expired token' }, + 401 + ); + } + + const { accessToken } = data; + + if (accessToken) { + getIntegration.token = accessToken; + + if (integrationProvider.refreshWait) { + await timer(10000); + } + + continue; + } + } + throw new HttpException({ msg: 'Unexpected error' }, 500); + } + } + } } diff --git a/apps/cli/INTEGRATION_TOOLS_WORKFLOW.md b/apps/cli/INTEGRATION_TOOLS_WORKFLOW.md new file mode 100644 index 00000000..e6986e5d --- /dev/null +++ b/apps/cli/INTEGRATION_TOOLS_WORKFLOW.md @@ -0,0 +1,435 @@ +# Integration Tools Workflow + +Some integrations require additional data (like IDs, tags, playlists, etc.) before you can post. The CLI supports a complete workflow to discover and use these tools. + +## The Complete Workflow + +### Step 1: List Integrations + +```bash +postiz integrations:list +``` + +Get your integration IDs. + +### Step 2: Get Integration Settings + +```bash +postiz integrations:settings +``` + +This returns: +- `maxLength` - Character limit +- `settings` - Required/optional fields +- **`tools`** - Callable methods to fetch additional data + +### Step 3: Trigger Tools (If Needed) + +If settings require IDs/data you don't have, use the tools: + +```bash +postiz integrations:trigger -d '{"key":"value"}' +``` + +### Step 4: Create Post with Complete Settings + +Use the data from Step 3 in your post settings. + +## Real-World Example: Reddit + +### 1. Get Reddit Integration Settings + +```bash +postiz integrations:settings reddit-abc123 +``` + +**Output:** +```json +{ + "output": { + "maxLength": 40000, + "settings": { + "properties": { + "subreddit": { + "type": "array", + "items": { + "properties": { + "subreddit": { "type": "string" }, + "title": { "type": "string" }, + "flair": { + "properties": { + "id": { "type": "string" } // ← Need flair ID! + } + } + } + } + } + } + }, + "tools": [ + { + "methodName": "getFlairs", + "description": "Get available flairs for a subreddit", + "dataSchema": [ + { + "key": "subreddit", + "description": "The subreddit name", + "type": "string" + } + ] + }, + { + "methodName": "searchSubreddits", + "description": "Search for subreddits", + "dataSchema": [ + { + "key": "query", + "description": "Search query", + "type": "string" + } + ] + } + ] + } +} +``` + +### 2. Get Flairs for the Subreddit + +```bash +postiz integrations:trigger reddit-abc123 getFlairs -d '{"subreddit":"programming"}' +``` + +**Output:** +```json +{ + "output": [ + { + "id": "flair-12345", + "name": "Discussion" + }, + { + "id": "flair-67890", + "name": "Tutorial" + } + ] +} +``` + +### 3. Create Post with Flair ID + +```bash +postiz posts:create \ + -c "Check out my project!" \ + -p reddit \ + --settings '{ + "subreddit": [{ + "value": { + "subreddit": "programming", + "title": "My Cool Project", + "type": "text", + "url": "", + "is_flair_required": true, + "flair": { + "id": "flair-12345", + "name": "Discussion" + } + } + }] + }' \ + -i "reddit-abc123" +``` + +## Example: YouTube Playlists + +### 1. Get YouTube Settings + +```bash +postiz integrations:settings youtube-123 +``` + +**Output includes tools:** +```json +{ + "tools": [ + { + "methodName": "getPlaylists", + "description": "Get your YouTube playlists", + "dataSchema": [] + }, + { + "methodName": "getCategories", + "description": "Get available video categories", + "dataSchema": [] + } + ] +} +``` + +### 2. Get Playlists + +```bash +postiz integrations:trigger youtube-123 getPlaylists +``` + +**Output:** +```json +{ + "output": [ + { + "id": "PLxxxxxx", + "title": "My Tutorials" + }, + { + "id": "PLyyyyyy", + "title": "Product Demos" + } + ] +} +``` + +### 3. Post to Specific Playlist + +```bash +postiz posts:create \ + -c "Video description" \ + -p youtube \ + --settings '{ + "title": "My Video", + "type": "public", + "playlistId": "PLxxxxxx" + }' \ + -i "youtube-123" +``` + +## Example: LinkedIn Companies + +### 1. Get LinkedIn Settings + +```bash +postiz integrations:settings linkedin-123 +``` + +**Output includes tools:** +```json +{ + "tools": [ + { + "methodName": "getCompanies", + "description": "Get companies you can post to", + "dataSchema": [] + } + ] +} +``` + +### 2. Get Companies + +```bash +postiz integrations:trigger linkedin-123 getCompanies +``` + +**Output:** +```json +{ + "output": [ + { + "id": "company-123", + "name": "My Company" + }, + { + "id": "company-456", + "name": "Other Company" + } + ] +} +``` + +### 3. Post as Company + +```bash +postiz posts:create \ + -c "Company announcement" \ + -p linkedin \ + --settings '{ + "companyId": "company-123" + }' \ + -i "linkedin-123" +``` + +## Understanding Tools + +### Tool Structure + +```json +{ + "methodName": "getFlairs", + "description": "Get available flairs for a subreddit", + "dataSchema": [ + { + "key": "subreddit", + "description": "The subreddit name", + "type": "string" + } + ] +} +``` + +- **methodName** - Use this in `integrations:trigger` +- **description** - What the tool does +- **dataSchema** - Required input parameters + +### Calling Tools + +```bash +# No parameters +postiz integrations:trigger + +# With parameters +postiz integrations:trigger -d '{"key":"value"}' +``` + +## Common Tool Methods + +### Reddit +- `getFlairs` - Get flairs for a subreddit +- `searchSubreddits` - Search for subreddits +- `getSubreddits` - Get subscribed subreddits + +### YouTube +- `getPlaylists` - Get your playlists +- `getCategories` - Get video categories +- `getChannels` - Get your channels + +### LinkedIn +- `getCompanies` - Get companies you manage +- `getOrganizations` - Get organizations + +### Twitter/X +- `getListsowned` - Get your Twitter lists +- `getCommunities` - Get communities you're in + +### Pinterest +- `getBoards` - Get your Pinterest boards +- `getBoardSections` - Get sections in a board + +## AI Agent Workflow + +For AI agents, this enables dynamic discovery and usage: + +```javascript +// 1. Get settings and tools +const settings = JSON.parse( + execSync(`postiz integrations:settings ${integrationId}`) +); + +// 2. Check if tools are needed +const tools = settings.output.tools || []; + +// 3. Call tools to get required data +for (const tool of tools) { + if (needsThisTool(tool)) { + const data = buildDataForTool(tool.dataSchema); + const result = JSON.parse( + execSync( + `postiz integrations:trigger ${integrationId} ${tool.methodName} -d '${JSON.stringify(data)}'` + ) + ); + + // Use result.output in your settings + updateSettings(result.output); + } +} + +// 4. Create post with complete settings +execSync(`postiz posts:create -c "${content}" --settings '${JSON.stringify(settings)}' -i "${integrationId}"`); +``` + +## Error Handling + +### Tool Not Found + +```bash +postiz integrations:trigger reddit-123 invalidMethod +# ❌ Failed to trigger tool: Tool not found +``` + +### Missing Required Data + +```bash +postiz integrations:trigger reddit-123 getFlairs +# ❌ Missing required parameter: subreddit +``` + +### Integration Not Found + +```bash +postiz integrations:trigger invalid-id getFlairs +# ❌ Failed to trigger tool: Integration not found +``` + +## Tips + +1. **Always check tools first** - Run `integrations:settings` to see available tools +2. **Read dataSchema** - Know what parameters each tool needs +3. **Parse JSON output** - Use `jq` or similar to extract data +4. **Cache results** - Tool results don't change often +5. **For AI agents** - Automate the entire workflow + +## Complete Example Script + +```bash +#!/bin/bash +export POSTIZ_API_KEY=your_key +INTEGRATION_ID="reddit-abc123" + +# 1. Get settings +echo "📋 Getting settings..." +SETTINGS=$(postiz integrations:settings $INTEGRATION_ID) +echo $SETTINGS | jq '.output.tools' + +# 2. Get flairs +echo "" +echo "🏷️ Getting flairs..." +FLAIRS=$(postiz integrations:trigger $INTEGRATION_ID getFlairs -d '{"subreddit":"programming"}') +FLAIR_ID=$(echo $FLAIRS | jq -r '.output[0].id') +FLAIR_NAME=$(echo $FLAIRS | jq -r '.output[0].name') + +echo "Selected flair: $FLAIR_NAME ($FLAIR_ID)" + +# 3. Create post +echo "" +echo "📝 Creating post..." +postiz posts:create \ + -c "My post content" \ + -p reddit \ + --settings "{ + \"subreddit\": [{ + \"value\": { + \"subreddit\": \"programming\", + \"title\": \"My Post Title\", + \"type\": \"text\", + \"url\": \"\", + \"is_flair_required\": true, + \"flair\": { + \"id\": \"$FLAIR_ID\", + \"name\": \"$FLAIR_NAME\" + } + } + }] + }" \ + -i "$INTEGRATION_ID" + +echo "✅ Done!" +``` + +## Summary + +✅ **Discover available tools** with `integrations:settings` +✅ **Call tools** to fetch required data with `integrations:trigger` +✅ **Use tool results** in post settings +✅ **Complete workflow** from discovery to posting +✅ **Perfect for AI agents** - fully automated +✅ **No guesswork** - know exactly what data you need + +**The CLI now supports the complete integration tools workflow!** 🎉 diff --git a/apps/cli/src/api.ts b/apps/cli/src/api.ts index 96ec93ed..0daa21c6 100644 --- a/apps/cli/src/api.ts +++ b/apps/cli/src/api.ts @@ -148,4 +148,15 @@ export class PostizAPI { method: 'GET', }); } + + async triggerIntegrationTool( + integrationId: string, + methodName: string, + data: Record + ) { + return this.request(`/public/v1/integration-trigger/${integrationId}`, { + method: 'POST', + body: JSON.stringify({ methodName, data }), + }); + } } diff --git a/apps/cli/src/commands/integrations.ts b/apps/cli/src/commands/integrations.ts index ced8f98e..60cdc203 100644 --- a/apps/cli/src/commands/integrations.ts +++ b/apps/cli/src/commands/integrations.ts @@ -35,3 +35,39 @@ export async function getIntegrationSettings(args: any) { process.exit(1); } } + +export async function triggerIntegrationTool(args: any) { + const config = getConfig(); + const api = new PostizAPI(config); + + if (!args.id) { + console.error('❌ Integration ID is required'); + process.exit(1); + } + + if (!args.method) { + console.error('❌ Method name is required'); + process.exit(1); + } + + // Parse data from JSON string or use empty object + let data: Record = {}; + if (args.data) { + try { + data = JSON.parse(args.data); + } catch (error: any) { + console.error('❌ Failed to parse data JSON:', error.message); + process.exit(1); + } + } + + try { + const result = await api.triggerIntegrationTool(args.id, args.method, data); + console.log(`🔧 Tool result for ${args.method}:`); + console.log(JSON.stringify(result, null, 2)); + return result; + } catch (error: any) { + console.error('❌ Failed to trigger tool:', error.message); + process.exit(1); + } +} diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 14c5d037..3b410b9c 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,7 +1,7 @@ import yargs from 'yargs'; import { hideBin } from 'yargs/helpers'; import { createPost, listPosts, deletePost } from './commands/posts'; -import { listIntegrations, getIntegrationSettings } from './commands/integrations'; +import { listIntegrations, getIntegrationSettings, triggerIntegrationTool } from './commands/integrations'; import { uploadFile } from './commands/upload'; import type { Argv } from 'yargs'; @@ -174,6 +174,39 @@ yargs(hideBin(process.argv)) }, getIntegrationSettings as any ) + .command( + 'integrations:trigger ', + 'Trigger an integration tool to fetch additional data', + (yargs: Argv) => { + return yargs + .positional('id', { + describe: 'Integration ID', + type: 'string', + }) + .positional('method', { + describe: 'Method name from the integration tools', + type: 'string', + }) + .option('data', { + alias: 'd', + describe: 'Data to pass to the tool as JSON string', + type: 'string', + }) + .example( + '$0 integrations:trigger reddit-123 getSubreddits', + 'Get list of subreddits' + ) + .example( + '$0 integrations:trigger reddit-123 searchSubreddits -d \'{"query":"programming"}\'', + 'Search for subreddits' + ) + .example( + '$0 integrations:trigger youtube-123 getPlaylists', + 'Get YouTube playlists' + ); + }, + triggerIntegrationTool as any + ) .command( 'upload ', 'Upload a file', From 6a69ac7eb44d4465e98d70dbeed132d49ef435e4 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 11:42:56 +0700 Subject: [PATCH 252/340] feat: skill --- apps/cli/README.md | 757 ++++++++++++++++++--------- apps/cli/SKILL.md | 911 +++++++++++++++++++-------------- apps/cli/package.json | 2 +- apps/cli/src/commands/posts.ts | 21 +- apps/cli/src/index.ts | 43 +- 5 files changed, 1069 insertions(+), 665 deletions(-) diff --git a/apps/cli/README.md b/apps/cli/README.md index e679e88e..31c86fc0 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -1,254 +1,461 @@ # Postiz CLI -> Command-line interface for the Postiz social media scheduling platform +**Social media automation CLI for AI agents** - Schedule posts across 28+ platforms programmatically. -## Overview +The Postiz CLI provides a command-line interface to the Postiz API, enabling developers and AI agents to automate social media posting, manage content, and handle media uploads across platforms like Twitter/X, LinkedIn, Reddit, YouTube, TikTok, Instagram, Facebook, and more. -The Postiz CLI allows you to interact with the Postiz API from the command line, making it easy for developers and AI agents to automate social media scheduling, manage posts, and upload media. +--- -## Quick Start +## Installation -### Installation +### From npm (Recommended) ```bash -# Install dependencies +npm install -g postiz +# or +pnpm install -g postiz +``` + +### From Source + +```bash +git clone https://github.com/gitroomhq/postiz-app.git +cd postiz-app/apps/cli pnpm install - -# Build the CLI pnpm run build - -# Run locally (development) -pnpm run start -- [command] - -# Or link globally pnpm link --global ``` -### Setup +### For Development -Before using the CLI, you need to set your Postiz API key: +```bash +cd apps/cli +pnpm install +pnpm run build +pnpm link --global + +# Or run directly without linking +pnpm run start -- posts:list +``` + +--- + +## Setup + +**Required:** Set your Postiz API key ```bash export POSTIZ_API_KEY=your_api_key_here ``` -Optionally, you can set a custom API URL: +**Optional:** Custom API endpoint ```bash export POSTIZ_API_URL=https://your-custom-api.com ``` -## Usage +--- -```bash -postiz [options] -``` +## Commands -### Commands - -#### Create a Post - -```bash -postiz posts:create -c "Your content here" -i "integration-id-1,integration-id-2" -``` - -**Options:** -- `-c, --content ` - Post/comment content (can be used multiple times) -- `-m, --media ` - Comma-separated media URLs for the corresponding `-c` (can be used multiple times) -- `-i, --integrations ` - Comma-separated integration IDs (required) -- `-s, --schedule ` - Schedule date (ISO 8601) -- `-d, --delay ` - Delay between comments in milliseconds (default: 5000) -- `-p, --provider-type ` - Provider type for platform-specific settings (e.g., reddit, youtube, x, tiktok) -- `--settings ` - Provider-specific settings as JSON string - -**Examples:** - -```bash -# Simple post -postiz posts:create -c "Hello World!" -i "twitter-123" - -# Post with multiple images -postiz posts:create \ - -c "Check these out!" \ - -m "img1.jpg,img2.jpg,img3.jpg" \ - -i "twitter-123" - -# Post with comments, each having their own media -postiz posts:create \ - -c "Main post 🚀" -m "main.jpg,main2.jpg" \ - -c "First comment 📸" -m "comment1.jpg" \ - -c "Second comment 🎨" -m "comment2.jpg" \ - -i "twitter-123" - -# Comments can contain semicolons! -postiz posts:create \ - -c "Main post" \ - -c "Comment with semicolon; see, it works!" \ - -c "Another comment; multiple; semicolons!" \ - -i "twitter-123" - -# Twitter thread with custom delay -postiz posts:create \ - -c "Thread 1/3" \ - -c "Thread 2/3" \ - -c "Thread 3/3" \ - -d 2000 \ - -i "twitter-123" - -# Scheduled post -postiz posts:create \ - -c "Future post" \ - -s "2024-12-31T12:00:00Z" \ - -i "twitter-123" - -# With provider-specific settings -postiz posts:create \ - -c "Video description" \ - -p youtube \ - --settings '{"title":"My Video","type":"public"}' \ - -i "youtube-123" -``` - -### Provider-Specific Settings - -Many platforms support specific settings (Reddit subreddits, YouTube visibility, TikTok privacy, etc.): - -```bash -# Reddit with subreddit settings -postiz posts:create \ - -c "Post content" \ - -p reddit \ - --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"My Title","type":"text","url":"","is_flair_required":false}}]}' \ - -i "reddit-123" - -# YouTube with title and visibility -postiz posts:create \ - -c "Video description" \ - -p youtube \ - --settings '{"title":"My Video","type":"public","tags":[{"value":"tech","label":"Tech"}]}' \ - -i "youtube-123" - -# X (Twitter) with reply settings -postiz posts:create \ - -c "Tweet" \ - -p x \ - --settings '{"who_can_reply_post":"everyone"}' \ - -i "twitter-123" -``` - -See **[PROVIDER_SETTINGS.md](./PROVIDER_SETTINGS.md)** for complete documentation on all platform-specific settings. -``` - -#### List Posts - -```bash -postiz posts:list [options] -``` - -**Options:** -- `-p, --page ` - Page number (default: 1) -- `-l, --limit ` - Posts per page (default: 10) -- `-s, --search ` - Search query - -**Examples:** - -```bash -# List all posts -postiz posts:list - -# With pagination -postiz posts:list -p 2 -l 20 - -# Search posts -postiz posts:list -s "keyword" -``` - -#### Delete a Post - -```bash -postiz posts:delete -``` - -**Example:** - -```bash -postiz posts:delete abc123xyz -``` - -#### List Integrations +### Discovery & Settings +**List all connected integrations** ```bash postiz integrations:list ``` -Shows all connected social media accounts. +Returns integration IDs, provider names, and metadata. -#### Upload a File +**Get integration settings schema** +```bash +postiz integrations:settings +``` +Returns character limits, required settings, and available tools for fetching dynamic data. + +**Trigger integration tools** +```bash +postiz integrations:trigger +postiz integrations:trigger -d '{"key":"value"}' +``` + +Fetch dynamic data like Reddit flairs, YouTube playlists, LinkedIn companies, etc. + +**Examples:** +```bash +# Get Reddit flairs +postiz integrations:trigger reddit-123 getFlairs -d '{"subreddit":"programming"}' + +# Get YouTube playlists +postiz integrations:trigger youtube-456 getPlaylists + +# Get LinkedIn companies +postiz integrations:trigger linkedin-789 getCompanies +``` + +--- + +### Creating Posts + +**Simple scheduled post** +```bash +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -i "integration-id" +``` + +**Draft post** +```bash +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -t draft -i "integration-id" +``` + +**Post with media** +```bash +postiz posts:create -c "Content" -m "img1.jpg,img2.jpg" -s "2024-12-31T12:00:00Z" -i "integration-id" +``` + +**Post with comments** (each comment can have its own media) +```bash +postiz posts:create \ + -c "Main post" -m "main.jpg" \ + -c "First comment" -m "comment1.jpg" \ + -c "Second comment" -m "comment2.jpg,comment3.jpg" \ + -s "2024-12-31T12:00:00Z" \ + -i "integration-id" +``` + +**Multi-platform post** +```bash +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -i "twitter-id,linkedin-id,facebook-id" +``` + +**Platform-specific settings** +```bash +postiz posts:create \ + -c "Content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"Post Title","type":"text"}}]}' \ + -i "reddit-id" +``` + +**Complex post from JSON file** +```bash +postiz posts:create --json post.json +``` + +**Options:** +- `-c, --content` - Post/comment content (use multiple times for posts with comments) +- `-s, --date` - Schedule date in ISO 8601 format (REQUIRED) +- `-t, --type` - Post type: "schedule" or "draft" (default: "schedule") +- `-m, --media` - Comma-separated media URLs for corresponding `-c` +- `-i, --integrations` - Comma-separated integration IDs (required) +- `-d, --delay` - Delay between comments in milliseconds (default: 5000) +- `--settings` - Platform-specific settings as JSON string +- `-j, --json` - Path to JSON file with full post structure +- `--shortLink` - Use short links (default: true) + +--- + +### Managing Posts + +**List posts** +```bash +postiz posts:list +postiz posts:list --startDate "2024-01-01T00:00:00Z" --endDate "2024-12-31T23:59:59Z" +postiz posts:list --customer "customer-id" +``` + +Defaults to last 30 days to next 30 days if dates not specified. + +**Delete post** +```bash +postiz posts:delete +``` + +--- + +### Media Upload + +**Upload file and get URL** ```bash postiz upload ``` +**Supported formats:** +- **Images:** PNG, JPG, JPEG, GIF, WEBP, SVG, BMP, ICO +- **Videos:** MP4, MOV, AVI, MKV, WEBM, FLV, WMV, M4V, MPEG, MPG, 3GP +- **Audio:** MP3, WAV, OGG, AAC, FLAC, M4A +- **Documents:** PDF, DOC, DOCX + **Example:** +```bash +RESULT=$(postiz upload video.mp4) +PATH=$(echo "$RESULT" | jq -r '.path') +postiz posts:create -c "Check out my video!" -m "$PATH" -i "tiktok-id" +``` + +--- + +## Platform-Specific Features + +### Reddit +```bash +# Get available flairs +postiz integrations:trigger reddit-id getFlairs -d '{"subreddit":"programming"}' + +# Post with subreddit and flair +postiz posts:create \ + -c "Content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"My Post","type":"text","is_flair_required":true,"flair":{"id":"flair-123","name":"Discussion"}}}]}' \ + -i "reddit-id" +``` + +### YouTube +```bash +# Get playlists +postiz integrations:trigger youtube-id getPlaylists + +# Upload video with metadata +postiz posts:create \ + -c "Video description" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"title":"Video Title","type":"public","tags":[{"value":"tech","label":"Tech"}],"playlistId":"playlist-id"}' \ + -m "video.mp4" \ + -i "youtube-id" +``` + +### TikTok +```bash +postiz posts:create \ + -c "Video caption #fyp" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"privacy":"PUBLIC_TO_EVERYONE","duet":true,"stitch":true}' \ + -m "video.mp4" \ + -i "tiktok-id" +``` + +### LinkedIn +```bash +# Get companies you can post to +postiz integrations:trigger linkedin-id getCompanies + +# Post as company +postiz posts:create \ + -c "Company announcement" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"companyId":"company-123"}' \ + -i "linkedin-id" +``` + +### X (Twitter) +```bash +# Create thread +postiz posts:create \ + -c "Thread 1/3 🧵" \ + -c "Thread 2/3" \ + -c "Thread 3/3" \ + -s "2024-12-31T12:00:00Z" \ + -d 2000 \ + -i "twitter-id" + +# With reply settings +postiz posts:create \ + -c "Tweet content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"who_can_reply_post":"everyone"}' \ + -i "twitter-id" +``` + +### Instagram +```bash +# Regular post +postiz posts:create \ + -c "Caption #hashtag" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"post_type":"post"}' \ + -m "image.jpg" \ + -i "instagram-id" + +# Story +postiz posts:create \ + -c "" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"post_type":"story"}' \ + -m "story.jpg" \ + -i "instagram-id" +``` + +**See [PROVIDER_SETTINGS.md](./PROVIDER_SETTINGS.md) for all 28+ platforms.** + +--- + +## Features for AI Agents + +### Discovery Workflow +The CLI enables dynamic discovery of integration capabilities: + +1. **List integrations** - Get available social media accounts +2. **Get settings** - Retrieve character limits, required fields, and available tools +3. **Trigger tools** - Fetch dynamic data (flairs, playlists, boards, etc.) +4. **Create posts** - Use discovered data in posts + +This allows AI agents to adapt to different platforms without hardcoded knowledge. + +### JSON Mode +For complex posts with multiple platforms and settings: ```bash -postiz upload ./images/photo.png +postiz posts:create --json complex-post.json ``` -## Development - -### Project Structure - -``` -apps/cli/ -├── src/ -│ ├── index.ts # CLI entry point -│ ├── api.ts # API client -│ ├── config.ts # Configuration handler -│ └── commands/ -│ ├── posts.ts # Post commands -│ ├── integrations.ts # Integration commands -│ └── upload.ts # Upload commands -├── package.json -├── tsconfig.json -├── tsup.config.ts -├── README.md -└── SKILL.md # AI agent usage guide +JSON structure: +```json +{ + "integrations": ["twitter-123", "linkedin-456"], + "posts": [ + { + "provider": "twitter", + "post": [ + { + "content": "Tweet version", + "image": ["twitter-image.jpg"] + } + ] + }, + { + "provider": "linkedin", + "post": [ + { + "content": "LinkedIn version with more context...", + "image": ["linkedin-image.jpg"] + } + ], + "settings": { + "__type": "linkedin", + "companyId": "company-123" + } + } + ] +} ``` -### Scripts - -- `pnpm run dev` - Watch mode for development -- `pnpm run build` - Build the CLI -- `pnpm run start` - Run the built CLI - -### Building - -The CLI uses `tsup` for building: +### All Output is JSON +Every command outputs JSON for easy parsing: ```bash -pnpm run build +INTEGRATIONS=$(postiz integrations:list | jq -r '.') +REDDIT_ID=$(echo "$INTEGRATIONS" | jq -r '.[] | select(.identifier=="reddit") | .id') ``` -This creates a `dist/` directory with: -- Compiled JavaScript -- Type declarations -- Source maps -- Executable shebang for Node.js +### Threading Support +Comments are automatically converted to threads/replies based on platform: +- **Twitter/X**: Thread of tweets +- **Reddit**: Comment replies +- **LinkedIn**: Comment on post +- **Instagram**: First comment -## For AI Agents +```bash +postiz posts:create \ + -c "Main post" \ + -c "Comment 1" \ + -c "Comment 2" \ + -i "integration-id" +``` -See [SKILL.md](./SKILL.md) for detailed usage patterns and examples for AI agents. +--- -## API Reference +## Common Workflows + +### Reddit Post with Flair +```bash +#!/bin/bash +REDDIT_ID=$(postiz integrations:list | jq -r '.[] | select(.identifier=="reddit") | .id') +FLAIRS=$(postiz integrations:trigger "$REDDIT_ID" getFlairs -d '{"subreddit":"programming"}') +FLAIR_ID=$(echo "$FLAIRS" | jq -r '.output[0].id') + +postiz posts:create \ + -c "My post content" \ + -s "2024-12-31T12:00:00Z" \ + --settings "{\"subreddit\":[{\"value\":{\"subreddit\":\"programming\",\"title\":\"Post Title\",\"type\":\"text\",\"is_flair_required\":true,\"flair\":{\"id\":\"$FLAIR_ID\",\"name\":\"Discussion\"}}}]}" \ + -i "$REDDIT_ID" +``` + +### YouTube Video Upload +```bash +#!/bin/bash +VIDEO=$(postiz upload video.mp4) +VIDEO_PATH=$(echo "$VIDEO" | jq -r '.path') + +postiz posts:create \ + -c "Video description..." \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"title":"My Video","type":"public","tags":[{"value":"tech","label":"Tech"}]}' \ + -m "$VIDEO_PATH" \ + -i "youtube-id" +``` + +### Multi-Platform Campaign +```bash +#!/bin/bash +postiz posts:create \ + -c "Same content everywhere" \ + -s "2024-12-31T12:00:00Z" \ + -m "image.jpg" \ + -i "twitter-id,linkedin-id,facebook-id" +``` + +### Batch Scheduling +```bash +#!/bin/bash +DATES=("2024-02-14T09:00:00Z" "2024-02-15T09:00:00Z" "2024-02-16T09:00:00Z") +CONTENT=("Monday motivation 💪" "Tuesday tips 💡" "Wednesday wisdom 🧠") + +for i in "${!DATES[@]}"; do + postiz posts:create \ + -c "${CONTENT[$i]}" \ + -s "${DATES[$i]}" \ + -i "twitter-id" +done +``` + +--- + +## Documentation + +**For AI Agents:** +- **[SKILL.md](./SKILL.md)** - Complete skill reference with patterns and examples + +**Deep-Dive Guides:** +- **[HOW_TO_RUN.md](./HOW_TO_RUN.md)** - Installation and setup methods +- **[COMMAND_LINE_GUIDE.md](./COMMAND_LINE_GUIDE.md)** - Complete command syntax reference +- **[PROVIDER_SETTINGS.md](./PROVIDER_SETTINGS.md)** - All platform settings schemas +- **[INTEGRATION_TOOLS_WORKFLOW.md](./INTEGRATION_TOOLS_WORKFLOW.md)** - Tools workflow guide +- **[INTEGRATION_SETTINGS_DISCOVERY.md](./INTEGRATION_SETTINGS_DISCOVERY.md)** - Settings discovery +- **[SUPPORTED_FILE_TYPES.md](./SUPPORTED_FILE_TYPES.md)** - Media format reference +- **[PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md)** - Code architecture +- **[PUBLISHING.md](./PUBLISHING.md)** - npm publishing guide + +**Examples:** +- **[examples/EXAMPLES.md](./examples/EXAMPLES.md)** - Comprehensive examples +- **[examples/](./examples/)** - Ready-to-use scripts and JSON files + +--- + +## API Endpoints The CLI interacts with these Postiz API endpoints: -- `POST /public/v1/posts` - Create a post -- `GET /public/v1/posts` - List posts -- `DELETE /public/v1/posts/:id` - Delete a post -- `GET /public/v1/integrations` - List integrations -- `POST /public/v1/upload` - Upload media +| Endpoint | Method | Purpose | +|----------|--------|---------| +| `/public/v1/posts` | POST | Create a post | +| `/public/v1/posts` | GET | List posts | +| `/public/v1/posts/:id` | DELETE | Delete a post | +| `/public/v1/integrations` | GET | List integrations | +| `/public/v1/integration-settings/:id` | GET | Get integration settings | +| `/public/v1/integration-trigger/:id` | POST | Trigger integration tool | +| `/public/v1/upload` | POST | Upload media | + +--- ## Environment Variables @@ -257,65 +464,147 @@ The CLI interacts with these Postiz API endpoints: | `POSTIZ_API_KEY` | ✅ Yes | - | Your Postiz API key | | `POSTIZ_API_URL` | No | `https://api.postiz.com` | Custom API endpoint | +--- + ## Error Handling -The CLI provides user-friendly error messages: +The CLI provides clear error messages with exit codes: -- ✅ Success messages with green checkmarks -- ❌ Error messages with red X marks -- 📋 Info messages with emojis -- Exit code 0 for success, 1 for errors +- **Exit code 0**: Success +- **Exit code 1**: Error occurred -## Examples +**Common errors:** -### Basic Workflow +| Error | Solution | +|-------|----------| +| `POSTIZ_API_KEY is not set` | Set environment variable: `export POSTIZ_API_KEY=key` | +| `Integration not found` | Run `integrations:list` to get valid IDs | +| `startDate/endDate required` | Use ISO 8601 format: `"2024-12-31T12:00:00Z"` | +| `Invalid settings` | Check `integrations:settings` for required fields | +| `Tool not found` | Check available tools in `integrations:settings` output | +| `Upload failed` | Verify file exists and format is supported | + +--- + +## Development + +### Project Structure + +``` +apps/cli/ +├── src/ +│ ├── index.ts # CLI entry point with yargs +│ ├── api.ts # PostizAPI client class +│ ├── config.ts # Environment configuration +│ └── commands/ +│ ├── posts.ts # Post management commands +│ ├── integrations.ts # Integration commands +│ └── upload.ts # Media upload command +├── examples/ # Example scripts and JSON files +├── package.json +├── tsconfig.json +├── tsup.config.ts # Build configuration +├── README.md # This file +└── SKILL.md # AI agent reference +``` + +### Scripts ```bash -# 1. Set API key +pnpm run dev # Watch mode for development +pnpm run build # Build the CLI +pnpm run start # Run the built CLI +``` + +### Building + +The CLI uses `tsup` for bundling: + +```bash +pnpm run build +``` + +Output in `dist/`: +- `index.js` - Bundled executable with shebang +- `index.js.map` - Source map + +--- + +## Quick Reference + +```bash +# Environment setup export POSTIZ_API_KEY=your_key -# 2. Check connected integrations -postiz integrations:list +# Discovery +postiz integrations:list # List integrations +postiz integrations:settings # Get settings +postiz integrations:trigger -d '{}' # Fetch data -# 3. Create a post -postiz posts:create -c "Hello from CLI!" -i "twitter-123" +# Posting (date is required) +postiz posts:create -c "text" -s "2024-12-31T12:00:00Z" -i "id" # Simple +postiz posts:create -c "text" -s "2024-12-31T12:00:00Z" -t draft -i "id" # Draft +postiz posts:create -c "text" -m "img.jpg" -s "2024-12-31T12:00:00Z" -i "id" # With media +postiz posts:create -c "main" -c "comment" -s "2024-12-31T12:00:00Z" -i "id" # With comment +postiz posts:create -c "text" -s "2024-12-31T12:00:00Z" --settings '{}' -i "id" # Platform-specific +postiz posts:create --json file.json # Complex -# 4. List posts -postiz posts:list +# Management +postiz posts:list # List posts +postiz posts:delete # Delete post +postiz upload # Upload media -# 5. Delete a post -postiz posts:delete post-id-123 +# Help +postiz --help # Show help +postiz posts:create --help # Command help ``` -### Scheduled Posting - -```bash -# Schedule posts for different times -postiz posts:create -c "Morning post" -s "2024-01-15T09:00:00Z" -postiz posts:create -c "Afternoon post" -s "2024-01-15T15:00:00Z" -postiz posts:create -c "Evening post" -s "2024-01-15T20:00:00Z" -``` - -### Media Upload Workflow - -```bash -# Upload an image -postiz upload ./image.png - -# The response includes the URL, use it in a post -postiz posts:create -c "Check this out!" --image "url-from-upload" -``` +--- ## Contributing This CLI is part of the [Postiz monorepo](https://github.com/gitroomhq/postiz-app). +To contribute: +1. Fork the repository +2. Create a feature branch +3. Make your changes in `apps/cli/` +4. Run tests: `pnpm run build` +5. Submit a pull request + +--- + ## License AGPL-3.0 +--- + ## Links -- [Postiz Website](https://postiz.com) -- [API Documentation](https://postiz.com/api-docs) -- [GitHub Repository](https://github.com/gitroomhq/postiz-app) +- **Website:** [postiz.com](https://postiz.com) +- **API Docs:** [postiz.com/api-docs](https://postiz.com/api-docs) +- **GitHub:** [gitroomhq/postiz-app](https://github.com/gitroomhq/postiz-app) +- **Issues:** [Report bugs](https://github.com/gitroomhq/postiz-app/issues) + +--- + +## Supported Platforms + +28+ platforms including: + +| Platform | Integration Tools | Settings | +|----------|------------------|----------| +| Twitter/X | getLists, getCommunities | who_can_reply_post | +| LinkedIn | getCompanies | companyId, carousel | +| Reddit | getFlairs, searchSubreddits | subreddit, title, flair | +| YouTube | getPlaylists, getCategories | title, type, tags, playlistId | +| TikTok | - | privacy, duet, stitch | +| Instagram | - | post_type (post/story) | +| Facebook | getPages | - | +| Pinterest | getBoards, getBoardSections | - | +| Discord | getChannels | - | +| Slack | getChannels | - | +| And 18+ more... | | | + +**See [PROVIDER_SETTINGS.md](./PROVIDER_SETTINGS.md) for complete documentation.** diff --git a/apps/cli/SKILL.md b/apps/cli/SKILL.md index 2156620c..ba272887 100644 --- a/apps/cli/SKILL.md +++ b/apps/cli/SKILL.md @@ -1,468 +1,585 @@ -# Postiz CLI Skill +| Property | Value | +|----------|-------| +| **name** | postiz | +| **description** | Social media automation CLI for scheduling posts across 28+ platforms | +| **allowed-tools** | Bash(postiz:*) | -## Description +--- -The Postiz CLI is a command-line interface for interacting with the Postiz social media scheduling API. It allows AI agents and developers to programmatically manage posts, integrations, and media uploads. +## Core Workflow -## Prerequisites +The fundamental pattern for using Postiz CLI: -- API Key: You need a valid Postiz API key -- Set the environment variable: `export POSTIZ_API_KEY=your_api_key` -- Optional: Set custom API URL with `export POSTIZ_API_URL=https://your-api-url.com` - -## Installation +1. **Discover** - List integrations and get their settings +2. **Fetch** - Use integration tools to retrieve dynamic data (flairs, playlists, companies) +3. **Prepare** - Upload media files if needed +4. **Post** - Create posts with content, media, and platform-specific settings ```bash -# From the monorepo root -pnpm install +# 1. Discover +postiz integrations:list +postiz integrations:settings -# Build the CLI -pnpm --filter postiz run build +# 2. Fetch (if needed) +postiz integrations:trigger -d '{"key":"value"}' -# Link globally (optional) -cd apps/cli -pnpm link --global +# 3. Prepare +postiz upload image.jpg + +# 4. Post +postiz posts:create -c "Content" -m "image.jpg" -i "" ``` -## Available Commands +--- -### Posts Management +## Essential Commands -#### Create a Post - -The CLI supports both **simple** and **complex** post creation: - -##### Simple Post Creation (Command-line) +### Setup ```bash -postiz posts:create -c "Your post content" -i "integration-id-1,integration-id-2" +# Required environment variable +export POSTIZ_API_KEY=your_api_key_here + +# Optional custom API URL +export POSTIZ_API_URL=https://custom-api-url.com ``` -Options: -- `-c, --content `: Post content/text -- `-i, --integrations `: Comma-separated integration IDs (required) -- `-s, --schedule `: Schedule date in ISO 8601 format -- `--image `: Comma-separated image URLs or paths (multiple images supported) -- `--comments `: Semicolon-separated comments -- `--shortLink`: Use URL shortener (default: true) - -Examples: -```bash -# Create immediate post -postiz posts:create -c "Hello World!" -i "twitter-123,linkedin-456" - -# Create scheduled post -postiz posts:create -c "Future post" -s "2024-12-31T12:00:00Z" -i "twitter-123" - -# Create post with multiple images -postiz posts:create -c "Check these out!" --image "url1.jpg,url2.jpg,url3.jpg" -i "twitter-123" - -# Create post with comments (simple) -postiz posts:create -c "Main post" --comments "First comment;Second comment" -i "twitter-123" -``` - -##### Complex Post Creation (JSON File) - -For posts with **comments that have their own media**, use JSON files: +### Integration Discovery ```bash -postiz posts:create --json ./my-post.json +# List all connected integrations +postiz integrations:list + +# Get settings schema for specific integration +postiz integrations:settings + +# Trigger integration tool to fetch dynamic data +postiz integrations:trigger +postiz integrations:trigger -d '{"param":"value"}' ``` -**JSON Structure:** -```json -{ - "type": "now", - "date": "2024-01-15T12:00:00Z", - "shortLink": true, - "tags": [], - "posts": [ - { - "integration": { "id": "twitter-123" }, - "value": [ - { - "content": "Main post with media 🚀", - "image": [ - { "id": "img1", "path": "https://example.com/main1.jpg" }, - { "id": "img2", "path": "https://example.com/main2.jpg" } - ] - }, - { - "content": "First comment with its own media 📸", - "image": [ - { "id": "img3", "path": "https://example.com/comment1.jpg" } - ], - "delay": 5000 - }, - { - "content": "Second comment with different media 🎨", - "image": [ - { "id": "img4", "path": "https://example.com/comment2.jpg" } - ], - "delay": 10000 - } - ], - "settings": { "__type": "EmptySettings" } - } - ] -} -``` +### Creating Posts -**Key Features:** -- ✅ **Multiple posts** to different platforms in one request -- ✅ **Each post can have multiple values** (main post + comments/thread) -- ✅ **Each value can have multiple images** (array of MediaDto) -- ✅ **Delays between comments** (in milliseconds) -- ✅ **Platform-specific content** (different content per integration) - -See `examples/` directory for: -- `post-with-comments.json` - Post with comments, each having their own media -- `multi-platform-post.json` - Post to multiple platforms with different content -- `thread-post.json` - Create a Twitter thread with 5 tweets -- `EXAMPLES.md` - Comprehensive guide with all use cases - -#### List Posts ```bash -postiz posts:list +# Simple post (date is REQUIRED) +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -i "integration-id" + +# Draft post +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -t draft -i "integration-id" + +# Post with media +postiz posts:create -c "Content" -m "img1.jpg,img2.jpg" -s "2024-12-31T12:00:00Z" -i "integration-id" + +# Post with comments (each with own media) +postiz posts:create \ + -c "Main post" -m "main.jpg" \ + -c "First comment" -m "comment1.jpg" \ + -c "Second comment" -m "comment2.jpg,comment3.jpg" \ + -s "2024-12-31T12:00:00Z" \ + -i "integration-id" + +# Multi-platform post +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -i "twitter-id,linkedin-id,facebook-id" + +# Platform-specific settings +postiz posts:create \ + -c "Content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"My Post","type":"text"}}]}' \ + -i "reddit-id" + +# Complex post from JSON file +postiz posts:create --json post.json ``` -Options: -- `-p, --page `: Page number (default: 1) -- `-l, --limit `: Posts per page (default: 10) -- `-s, --search `: Search query +### Managing Posts -Examples: ```bash -# List all posts +# List posts (defaults to last 30 days to next 30 days) postiz posts:list -# List with pagination -postiz posts:list -p 2 -l 20 +# List posts in date range +postiz posts:list --startDate "2024-01-01T00:00:00Z" --endDate "2024-12-31T23:59:59Z" -# Search posts -postiz posts:list -s "hello" -``` - -#### Delete a Post -```bash +# Delete post postiz posts:delete ``` -Example: -```bash -postiz posts:delete abc123xyz -``` - -### Integrations - -#### List Connected Integrations -```bash -postiz integrations:list -``` - -This command shows all connected social media accounts and their IDs, which can be used when creating posts. - ### Media Upload -#### Upload a File ```bash -postiz upload +# Upload file and get URL +postiz upload image.jpg + +# Supports: images (PNG, JPG, GIF, WEBP, SVG), videos (MP4, MOV, AVI, MKV, WEBM), +# audio (MP3, WAV, OGG, AAC), documents (PDF, DOC, DOCX) ``` -Example: +--- + +## Common Patterns + +### Pattern 1: Discover & Use Integration Tools + +**Reddit - Get flairs for a subreddit:** ```bash -postiz upload ./images/photo.png +# Get Reddit integration ID +REDDIT_ID=$(postiz integrations:list | jq -r '.[] | select(.identifier=="reddit") | .id') + +# Fetch available flairs +FLAIRS=$(postiz integrations:trigger "$REDDIT_ID" getFlairs -d '{"subreddit":"programming"}') +FLAIR_ID=$(echo "$FLAIRS" | jq -r '.output[0].id') + +# Use in post +postiz posts:create \ + -c "My post content" \ + -s "2024-12-31T12:00:00Z" \ + --settings "{\"subreddit\":[{\"value\":{\"subreddit\":\"programming\",\"title\":\"Post Title\",\"type\":\"text\",\"is_flair_required\":true,\"flair\":{\"id\":\"$FLAIR_ID\",\"name\":\"Discussion\"}}}]}" \ + -i "$REDDIT_ID" ``` -Supported formats: PNG, JPG, JPEG, GIF +**YouTube - Get playlists:** +```bash +YOUTUBE_ID=$(postiz integrations:list | jq -r '.[] | select(.identifier=="youtube") | .id') +PLAYLISTS=$(postiz integrations:trigger "$YOUTUBE_ID" getPlaylists) +PLAYLIST_ID=$(echo "$PLAYLISTS" | jq -r '.output[0].id') -## Usage for AI Agents +postiz posts:create \ + -c "Video description" \ + -s "2024-12-31T12:00:00Z" \ + --settings "{\"title\":\"My Video\",\"type\":\"public\",\"playlistId\":\"$PLAYLIST_ID\"}" \ + -m "video.mp4" \ + -i "$YOUTUBE_ID" +``` -AI agents can use this CLI to automate social media scheduling. The CLI supports both simple and advanced post structures, including posts with comments where each has its own media. +**LinkedIn - Post as company:** +```bash +LINKEDIN_ID=$(postiz integrations:list | jq -r '.[] | select(.identifier=="linkedin") | .id') +COMPANIES=$(postiz integrations:trigger "$LINKEDIN_ID" getCompanies) +COMPANY_ID=$(echo "$COMPANIES" | jq -r '.output[0].id') -### Understanding the Post Structure +postiz posts:create \ + -c "Company announcement" \ + -s "2024-12-31T12:00:00Z" \ + --settings "{\"companyId\":\"$COMPANY_ID\"}" \ + -i "$LINKEDIN_ID" +``` -```typescript -CreatePostDto { - type: 'now' | 'schedule' | 'draft' | 'update', - date: string, // ISO 8601 timestamp - shortLink: boolean, // Enable URL shortening - tags: Tag[], // Array of tags - posts: [ // Can post to multiple platforms +### Pattern 2: Upload Media Before Posting + +```bash +# Upload multiple files +VIDEO_RESULT=$(postiz upload video.mp4) +VIDEO_PATH=$(echo "$VIDEO_RESULT" | jq -r '.path') + +THUMB_RESULT=$(postiz upload thumbnail.jpg) +THUMB_PATH=$(echo "$THUMB_RESULT" | jq -r '.path') + +# Use in post +postiz posts:create \ + -c "Check out my video!" \ + -s "2024-12-31T12:00:00Z" \ + -m "$VIDEO_PATH" \ + -i "tiktok-id" +``` + +### Pattern 3: Twitter Thread + +```bash +postiz posts:create \ + -c "🧵 Thread starter (1/4)" -m "intro.jpg" \ + -c "Point one (2/4)" -m "point1.jpg" \ + -c "Point two (3/4)" -m "point2.jpg" \ + -c "Conclusion (4/4)" -m "outro.jpg" \ + -s "2024-12-31T12:00:00Z" \ + -d 2000 \ + -i "twitter-id" +``` + +### Pattern 4: Multi-Platform Campaign + +```bash +# Create JSON file with platform-specific content +cat > campaign.json << 'EOF' +{ + "integrations": ["twitter-123", "linkedin-456", "facebook-789"], + "posts": [ { - integration: { id: string }, // Platform integration ID - value: [ // Main post + comments/thread + "provider": "twitter", + "post": [ { - content: string, // Text content - image: MediaDto[], // Array of media (multiple images) - delay?: number, // Delay in ms (for comments) - id?: string // Optional ID + "content": "Short tweet version #tech", + "image": ["twitter-image.jpg"] } - ], - settings: { __type: 'EmptySettings' } + ] + }, + { + "provider": "linkedin", + "post": [ + { + "content": "Professional LinkedIn version with more context...", + "image": ["linkedin-image.jpg"] + } + ] } ] } -``` - -### Common Patterns - -### Pattern 1: Create and Schedule Multiple Posts -```bash -# Set API key once -export POSTIZ_API_KEY=your_key - -# Create posts programmatically -postiz posts:create -c "Morning update" -s "2024-01-15T09:00:00Z" -i "twitter-123" -postiz posts:create -c "Afternoon update" -s "2024-01-15T15:00:00Z" -i "twitter-123" -postiz posts:create -c "Evening update" -s "2024-01-15T20:00:00Z" -i "twitter-123" -``` - -### Pattern 2: Upload Media and Create Post -```bash -# First, upload the media -UPLOAD_RESULT=$(postiz upload ./image.png) - -# Extract the URL from the result (you'll need to parse the JSON) -# Then create post with the media URL -postiz posts:create -c "Check out this image!" --image "" -``` - -### Pattern 3: Check Integrations Before Posting -```bash -# List available integrations -postiz integrations:list - -# Use the integration IDs from the response to create posts -postiz posts:create -c "Multi-platform post" -i "twitter-123,linkedin-456,facebook-789" -``` - -### Pattern 4: Manage Existing Posts -```bash -# List all posts to get IDs -postiz posts:list - -# Delete a specific post -postiz posts:delete -``` - -### Pattern 5: Create Post with Comments and Media (Advanced) - -```bash -# Create a JSON file programmatically -cat > post.json << 'EOF' -{ - "type": "now", - "date": "2024-01-15T12:00:00Z", - "shortLink": true, - "tags": [], - "posts": [{ - "integration": { "id": "twitter-123" }, - "value": [ - { - "content": "Main post with 2 images 🚀", - "image": [ - { "id": "1", "path": "https://example.com/img1.jpg" }, - { "id": "2", "path": "https://example.com/img2.jpg" } - ] - }, - { - "content": "First comment with its own image 📸", - "image": [ - { "id": "3", "path": "https://example.com/comment-img.jpg" } - ], - "delay": 5000 - } - ], - "settings": { "__type": "EmptySettings" } - }] -} EOF -# Post it -postiz posts:create --json post.json +postiz posts:create --json campaign.json ``` -### Pattern 6: Multi-Platform Campaign +### Pattern 5: Validate Settings Before Posting ```javascript -// AI Agent: Create coordinated multi-platform posts -const campaign = { - type: "schedule", - date: "2024-12-25T12:00:00Z", - shortLink: true, - tags: [{ value: "campaign", label: "Campaign" }], - posts: [ - { - integration: { id: "twitter-123" }, - value: [{ - content: "Twitter-optimized content 🐦", - image: [{ id: "t1", path: "twitter-image.jpg" }] - }] - }, - { - integration: { id: "linkedin-456" }, - value: [{ - content: "Professional LinkedIn content 💼", - image: [{ id: "l1", path: "linkedin-image.jpg" }] - }] - }, - { - integration: { id: "facebook-789" }, - value: [ - { - content: "Facebook main post 📱", - image: [{ id: "f1", path: "facebook-main.jpg" }] - }, - { - content: "Additional context in comments", - image: [{ id: "f2", path: "facebook-comment.jpg" }], - delay: 300000 // 5 minutes later - } - ] - } - ] -}; +const { execSync } = require('child_process'); -require('fs').writeFileSync('campaign.json', JSON.stringify(campaign, null, 2)); -execSync('postiz posts:create --json campaign.json'); -``` +function validateAndPost(content, integrationId, settings) { + // Get integration settings + const settingsResult = execSync( + `postiz integrations:settings ${integrationId}`, + { encoding: 'utf-8' } + ); + const schema = JSON.parse(settingsResult); -### Pattern 7: Twitter Thread Creation - -```bash -# Create a thread with multiple tweets, each with media -postiz posts:create --json - << 'EOF' -{ - "type": "now", - "date": "2024-01-15T12:00:00Z", - "shortLink": true, - "tags": [], - "posts": [{ - "integration": { "id": "twitter-123" }, - "value": [ - { - "content": "🧵 Thread about X (1/3)", - "image": [{ "id": "1", "path": "https://example.com/thread-1.jpg" }] - }, - { - "content": "Key point number 1 (2/3)", - "image": [{ "id": "2", "path": "https://example.com/thread-2.jpg" }], - "delay": 2000 - }, - { - "content": "Conclusion and CTA (3/3)", - "image": [{ "id": "3", "path": "https://example.com/thread-3.jpg" }], - "delay": 2000 - } - ], - "settings": { "__type": "EmptySettings" } - }] -} -EOF -``` - -### Pattern 8: Upload Media, Then Post - -```bash -# 1. Upload images first -IMG1=$(postiz upload ./image1.jpg | jq -r '.path') -IMG2=$(postiz upload ./image2.jpg | jq -r '.path') - -# 2. Create post with uploaded images -cat > post.json << EOF -{ - "type": "now", - "date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", - "shortLink": true, - "tags": [], - "posts": [{ - "integration": { "id": "twitter-123" }, - "value": [{ - "content": "Check out these images!", - "image": [ - { "id": "img1", "path": "$IMG1" }, - { "id": "img2", "path": "$IMG2" } - ] - }], - "settings": { "__type": "EmptySettings" } - }] -} -EOF - -postiz posts:create --json post.json -``` - -## Output Format - -All commands return JSON output, making it easy for AI agents to parse and process results: - -```json -{ - "success": true, - "data": { - "id": "post-123", - "content": "Hello World!", - "scheduledDate": "2024-01-15T12:00:00Z" + // Check character limit + if (content.length > schema.output.maxLength) { + console.warn(`Content exceeds ${schema.output.maxLength} chars, truncating...`); + content = content.substring(0, schema.output.maxLength - 3) + '...'; } + + // Create post + const result = execSync( + `postiz posts:create -c "${content}" -s "2024-12-31T12:00:00Z" --settings '${JSON.stringify(settings)}' -i "${integrationId}"`, + { encoding: 'utf-8' } + ); + + return JSON.parse(result); } ``` -## Error Handling - -The CLI provides clear error messages: - -- Missing API key: `❌ Error: POSTIZ_API_KEY environment variable is required` -- API errors: `❌ API Error (status): message` -- File not found: `❌ Failed to upload file: message` - -Exit codes: -- `0`: Success -- `1`: Error occurred - -## Environment Variables - -| Variable | Required | Default | Description | -|----------|----------|---------|-------------| -| `POSTIZ_API_KEY` | Yes | - | Your Postiz API key | -| `POSTIZ_API_URL` | No | `https://api.postiz.com` | Custom API endpoint | - -## Tips for AI Agents - -1. **Always set the API key** before running commands -2. **Parse JSON output** using tools like `jq` for scripting -3. **Check exit codes** to determine if commands succeeded -4. **Use integrations:list** first to get valid integration IDs -5. **Schedule posts** in the future using ISO 8601 date format -6. **Upload media first** before referencing in posts - -## Example Workflow Script +### Pattern 6: Batch Scheduling ```bash #!/bin/bash -# Set API key -export POSTIZ_API_KEY="your-api-key-here" +# Schedule posts for the week +DATES=( + "2024-02-14T09:00:00Z" + "2024-02-15T09:00:00Z" + "2024-02-16T09:00:00Z" +) -# Get integrations -INTEGRATIONS=$(postiz integrations:list) -echo "Available integrations: $INTEGRATIONS" +CONTENT=( + "Monday motivation 💪" + "Tuesday tips 💡" + "Wednesday wisdom 🧠" +) -# Create a post -postiz posts:create \ - -c "Automated post from AI agent" \ - -s "2024-12-25T12:00:00Z" \ - -i "twitter-123,linkedin-456" - -# List all posts -postiz posts:list -l 5 - -echo "✅ Workflow completed!" +for i in "${!DATES[@]}"; do + postiz posts:create \ + -c "${CONTENT[$i]}" \ + -s "${DATES[$i]}" \ + -i "twitter-id" \ + -m "post-${i}.jpg" + echo "Scheduled: ${CONTENT[$i]} for ${DATES[$i]}" +done ``` -## Support +### Pattern 7: Error Handling & Retry -For issues or questions: -- GitHub: https://github.com/gitroomhq/postiz-app -- Documentation: https://postiz.com/docs -- API Reference: https://postiz.com/api-docs +```javascript +const { execSync } = require('child_process'); + +async function postWithRetry(content, integrationId, date, maxRetries = 3) { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + const result = execSync( + `postiz posts:create -c "${content}" -s "${date}" -i "${integrationId}"`, + { encoding: 'utf-8', stdio: 'pipe' } + ); + console.log('✅ Post created successfully'); + return JSON.parse(result); + } catch (error) { + console.error(`❌ Attempt ${attempt} failed: ${error.message}`); + + if (attempt < maxRetries) { + const delay = Math.pow(2, attempt) * 1000; // Exponential backoff + console.log(`⏳ Retrying in ${delay}ms...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } else { + throw new Error(`Failed after ${maxRetries} attempts`); + } + } + } +} +``` + +--- + +## Technical Concepts + +### Integration Tools Workflow + +Many integrations require dynamic data (IDs, tags, playlists) that can't be hardcoded. The tools workflow enables discovery and usage: + +1. **Check available tools** - `integrations:settings` returns a `tools` array +2. **Review tool schema** - Each tool has `methodName`, `description`, and `dataSchema` +3. **Trigger tool** - Call `integrations:trigger` with required parameters +4. **Use output** - Tool returns data to use in post settings + +**Example tools by platform:** +- **Reddit**: `getFlairs`, `searchSubreddits`, `getSubreddits` +- **YouTube**: `getPlaylists`, `getCategories`, `getChannels` +- **LinkedIn**: `getCompanies`, `getOrganizations` +- **Twitter/X**: `getListsowned`, `getCommunities` +- **Pinterest**: `getBoards`, `getBoardSections` + +### Provider Settings Structure + +Platform-specific settings use a discriminator pattern with `__type` field: + +```json +{ + "posts": [ + { + "provider": "reddit", + "post": [{ "content": "...", "image": [...] }], + "settings": { + "__type": "reddit", + "subreddit": [{ + "value": { + "subreddit": "programming", + "title": "Post Title", + "type": "text", + "url": "", + "is_flair_required": false + } + }] + } + } + ] +} +``` + +Pass settings directly: +```bash +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" --settings '{"subreddit":[...]}' -i "reddit-id" +# Backend automatically adds "__type" based on integration ID +``` + +### Comments and Threading + +Posts can have comments (threads on Twitter/X, replies elsewhere). Each comment can have its own media: + +```bash +# Using multiple -c and -m flags +postiz posts:create \ + -c "Main post" -m "image1.jpg,image2.jpg" \ + -c "Comment 1" -m "comment-img.jpg" \ + -c "Comment 2" -m "another.jpg,more.jpg" \ + -s "2024-12-31T12:00:00Z" \ + -d 5000 \ # Delay between comments in ms + -i "integration-id" +``` + +Internally creates: +```json +{ + "posts": [{ + "value": [ + { "content": "Main post", "image": ["image1.jpg", "image2.jpg"] }, + { "content": "Comment 1", "image": ["comment-img.jpg"], "delay": 5000 }, + { "content": "Comment 2", "image": ["another.jpg", "more.jpg"], "delay": 5000 } + ] + }] +} +``` + +### Date Handling + +All dates use ISO 8601 format: +- Schedule posts: `-s "2024-12-31T12:00:00Z"` +- List posts: `--startDate "2024-01-01T00:00:00Z" --endDate "2024-12-31T23:59:59Z"` +- Defaults: `posts:list` uses 30 days ago to 30 days from now + +### Media Upload Response + +Upload returns JSON with path and metadata: +```json +{ + "path": "https://cdn.postiz.com/uploads/abc123.jpg", + "size": 123456, + "type": "image/jpeg" +} +``` + +Extract path for use in posts: +```bash +RESULT=$(postiz upload image.jpg) +PATH=$(echo "$RESULT" | jq -r '.path') +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -m "$PATH" -i "integration-id" +``` + +### JSON Mode vs CLI Flags + +**CLI flags** - Quick posts: +```bash +postiz posts:create -c "Content" -m "img.jpg" -i "twitter-id" +``` + +**JSON mode** - Complex posts with multiple platforms and settings: +```bash +postiz posts:create --json post.json +``` + +JSON mode supports: +- Multiple platforms with different content per platform +- Complex provider-specific settings +- Scheduled posts +- Posts with many comments +- Custom delay between comments + +--- + +## Platform-Specific Examples + +### Reddit +```bash +postiz posts:create \ + -c "Post content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"My Title","type":"text","url":"","is_flair_required":false}}]}' \ + -i "reddit-id" +``` + +### YouTube +```bash +postiz posts:create \ + -c "Video description" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"title":"Video Title","type":"public","tags":[{"value":"tech","label":"Tech"}]}' \ + -m "video.mp4" \ + -i "youtube-id" +``` + +### TikTok +```bash +postiz posts:create \ + -c "Video caption #fyp" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"privacy":"PUBLIC_TO_EVERYONE","duet":true,"stitch":true}' \ + -m "video.mp4" \ + -i "tiktok-id" +``` + +### X (Twitter) +```bash +postiz posts:create \ + -c "Tweet content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"who_can_reply_post":"everyone"}' \ + -i "twitter-id" +``` + +### LinkedIn +```bash +# Personal post +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -i "linkedin-id" + +# Company post +postiz posts:create \ + -c "Content" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"companyId":"company-123"}' \ + -i "linkedin-id" +``` + +### Instagram +```bash +# Regular post +postiz posts:create \ + -c "Caption #hashtag" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"post_type":"post"}' \ + -m "image.jpg" \ + -i "instagram-id" + +# Story +postiz posts:create \ + -c "" \ + -s "2024-12-31T12:00:00Z" \ + --settings '{"post_type":"story"}' \ + -m "story.jpg" \ + -i "instagram-id" +``` + +--- + +## Supporting Resources + +**Deep-dive documentation:** +- [HOW_TO_RUN.md](./HOW_TO_RUN.md) - Installation and setup methods +- [COMMAND_LINE_GUIDE.md](./COMMAND_LINE_GUIDE.md) - Complete command syntax reference +- [PROVIDER_SETTINGS.md](./PROVIDER_SETTINGS.md) - All 28+ platform settings schemas +- [INTEGRATION_TOOLS_WORKFLOW.md](./INTEGRATION_TOOLS_WORKFLOW.md) - Complete tools workflow guide +- [INTEGRATION_SETTINGS_DISCOVERY.md](./INTEGRATION_SETTINGS_DISCOVERY.md) - Settings discovery workflow +- [SUPPORTED_FILE_TYPES.md](./SUPPORTED_FILE_TYPES.md) - All supported media formats +- [PROJECT_STRUCTURE.md](./PROJECT_STRUCTURE.md) - Code architecture +- [PUBLISHING.md](./PUBLISHING.md) - npm publishing guide + +**Ready-to-use examples:** +- [examples/EXAMPLES.md](./examples/EXAMPLES.md) - Comprehensive examples +- [examples/basic-usage.sh](./examples/basic-usage.sh) - Shell script basics +- [examples/ai-agent-example.js](./examples/ai-agent-example.js) - Node.js agent +- [examples/post-with-comments.json](./examples/post-with-comments.json) - Threading example +- [examples/multi-platform-with-settings.json](./examples/multi-platform-with-settings.json) - Campaign example +- [examples/youtube-video.json](./examples/youtube-video.json) - YouTube with tags +- [examples/reddit-post.json](./examples/reddit-post.json) - Reddit with subreddit +- [examples/tiktok-video.json](./examples/tiktok-video.json) - TikTok with privacy + +--- + +## Common Gotchas + +1. **API Key not set** - Always `export POSTIZ_API_KEY=key` before using CLI +2. **Invalid integration ID** - Run `integrations:list` to get current IDs +3. **Settings schema mismatch** - Check `integrations:settings` for required fields +4. **Media upload before posting** - Upload returns URL to use in `-m` flag +5. **JSON escaping in shell** - Use single quotes for JSON: `--settings '{...}'` +6. **Date format** - Must be ISO 8601: `"2024-12-31T12:00:00Z"` +7. **Tool not found** - Check available tools in `integrations:settings` output +8. **Character limits** - Each platform has different limits, check `maxLength` in settings +9. **Required settings** - Some platforms require specific settings (Reddit needs title, YouTube needs title) +10. **Media MIME types** - CLI auto-detects from file extension, ensure correct extension + +--- + +## Quick Reference + +```bash +# Environment +export POSTIZ_API_KEY=key + +# Discovery +postiz integrations:list # Get integration IDs +postiz integrations:settings # Get settings schema +postiz integrations:trigger -d '{}' # Fetch dynamic data + +# Posting (date is REQUIRED) +postiz posts:create -c "text" -s "2024-12-31T12:00:00Z" -i "id" # Simple +postiz posts:create -c "text" -s "2024-12-31T12:00:00Z" -t draft -i "id" # Draft +postiz posts:create -c "text" -m "img.jpg" -s "2024-12-31T12:00:00Z" -i "id" # With media +postiz posts:create -c "main" -c "comment" -s "2024-12-31T12:00:00Z" -i "id" # With comment +postiz posts:create -c "text" -s "2024-12-31T12:00:00Z" --settings '{}' -i "id" # Platform-specific +postiz posts:create --json file.json # Complex + +# Management +postiz posts:list # List posts +postiz posts:delete # Delete post +postiz upload # Upload media + +# Help +postiz --help # Show help +postiz posts:create --help # Command help +``` diff --git a/apps/cli/package.json b/apps/cli/package.json index b1c98e7f..8fb64cc4 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "postiz", - "version": "2.0.3", + "version": "2.0.5", "description": "Postiz CLI - Command line interface for the Postiz social media scheduling API", "main": "dist/index.js", "bin": { diff --git a/apps/cli/src/commands/posts.ts b/apps/cli/src/commands/posts.ts index 8131d0b9..2cc9c316 100644 --- a/apps/cli/src/commands/posts.ts +++ b/apps/cli/src/commands/posts.ts @@ -64,35 +64,24 @@ export async function createPost(args: any) { }); // Parse provider-specific settings if provided - let settings: any = { __type: 'EmptySettings' }; + // Note: __type is automatically added by the backend based on integration ID + let settings: any = undefined; if (args.settings) { try { - const parsedSettings = typeof args.settings === 'string' + settings = typeof args.settings === 'string' ? JSON.parse(args.settings) : args.settings; - - // If provider type is specified, add it to settings - if (args.providerType) { - settings = { - __type: args.providerType, - ...parsedSettings, - }; - } else { - settings = parsedSettings; - } } catch (error: any) { console.error('❌ Failed to parse settings JSON:', error.message); process.exit(1); } - } else if (args.providerType) { - settings = { __type: args.providerType }; } // Build the proper post structure postData = { - type: args.schedule ? 'schedule' : 'now', - date: args.schedule || new Date().toISOString(), + type: args.type || 'schedule', // 'schedule' or 'draft' + date: args.date, // Required date field shortLink: args.shortLink !== false, tags: [], posts: integrations.map((integrationId: string) => ({ diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 3b410b9c..3de9dd76 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -28,11 +28,18 @@ yargs(hideBin(process.argv)) describe: 'Comma-separated list of integration IDs', type: 'string', }) - .option('schedule', { + .option('date', { alias: 's', - describe: 'Schedule date (ISO 8601 format)', + describe: 'Schedule date (ISO 8601 format) - REQUIRED', type: 'string', }) + .option('type', { + alias: 't', + describe: 'Post type: "schedule" or "draft"', + type: 'string', + choices: ['schedule', 'draft'], + default: 'schedule', + }) .option('delay', { alias: 'd', describe: 'Delay in milliseconds between comments (default: 5000)', @@ -49,13 +56,8 @@ yargs(hideBin(process.argv)) type: 'boolean', default: true, }) - .option('provider-type', { - alias: 'p', - describe: 'Provider type for settings (e.g., reddit, youtube, tiktok, x, linkedin, instagram)', - type: 'string', - }) .option('settings', { - describe: 'Provider-specific settings as JSON string', + describe: 'Platform-specific settings as JSON string', type: 'string', }) .check((argv) => { @@ -65,26 +67,33 @@ yargs(hideBin(process.argv)) if (!argv.json && !argv.integrations) { throw new Error('--integrations is required when not using --json'); } + if (!argv.json && !argv.date) { + throw new Error('--date is required when not using --json'); + } return true; }) .example( - '$0 posts:create -c "Hello World!" -i "twitter-123"', - 'Simple post' + '$0 posts:create -c "Hello World!" -s "2024-12-31T12:00:00Z" -i "twitter-123"', + 'Simple scheduled post' ) .example( - '$0 posts:create -c "Main post" -m "img1.jpg,img2.jpg" -i "twitter-123"', + '$0 posts:create -c "Draft post" -s "2024-12-31T12:00:00Z" -t draft -i "twitter-123"', + 'Create draft post' + ) + .example( + '$0 posts:create -c "Main post" -m "img1.jpg,img2.jpg" -s "2024-12-31T12:00:00Z" -i "twitter-123"', 'Post with multiple images' ) .example( - '$0 posts:create -c "Main post" -m "img1.jpg" -c "First comment" -m "img2.jpg" -c "Second comment" -m "img3.jpg,img4.jpg" -i "twitter-123"', + '$0 posts:create -c "Main post" -m "img1.jpg" -c "First comment" -m "img2.jpg" -c "Second comment" -m "img3.jpg,img4.jpg" -s "2024-12-31T12:00:00Z" -i "twitter-123"', 'Post with comments, each having their own media' ) .example( - '$0 posts:create -c "Main" -c "Comment with semicolon; see?" -c "Another!" -i "twitter-123"', + '$0 posts:create -c "Main" -c "Comment with semicolon; see?" -c "Another!" -s "2024-12-31T12:00:00Z" -i "twitter-123"', 'Comments can contain semicolons' ) .example( - '$0 posts:create -c "Thread 1/3" -c "Thread 2/3" -c "Thread 3/3" -d 2000 -i "twitter-123"', + '$0 posts:create -c "Thread 1/3" -c "Thread 2/3" -c "Thread 3/3" -d 2000 -s "2024-12-31T12:00:00Z" -i "twitter-123"', 'Twitter thread with 2s delay' ) .example( @@ -92,15 +101,15 @@ yargs(hideBin(process.argv)) 'Complex post from JSON file' ) .example( - '$0 posts:create -c "Post to subreddit" -p reddit --settings \'{"subreddit":[{"value":{"subreddit":"programming","title":"My Title","type":"text","url":"","is_flair_required":false}}]}\' -i "reddit-123"', + '$0 posts:create -c "Post to subreddit" -s "2024-12-31T12:00:00Z" --settings \'{"subreddit":[{"value":{"subreddit":"programming","title":"My Title","type":"text","url":"","is_flair_required":false}}]}\' -i "reddit-123"', 'Reddit post with specific subreddit settings' ) .example( - '$0 posts:create -c "Video description" -p youtube --settings \'{"title":"My Video","type":"public","tags":[{"value":"tech","label":"Tech"}]}\' -i "youtube-123"', + '$0 posts:create -c "Video description" -s "2024-12-31T12:00:00Z" --settings \'{"title":"My Video","type":"public","tags":[{"value":"tech","label":"Tech"}]}\' -i "youtube-123"', 'YouTube post with title and tags' ) .example( - '$0 posts:create -c "Tweet content" -p x --settings \'{"who_can_reply_post":"everyone"}\' -i "twitter-123"', + '$0 posts:create -c "Tweet content" -s "2024-12-31T12:00:00Z" --settings \'{"who_can_reply_post":"everyone"}\' -i "twitter-123"', 'X (Twitter) post with reply settings' ); }, From dc8a380176c66073807669fd9aeb7eec6d28754f Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 14 Feb 2026 11:57:12 +0700 Subject: [PATCH 253/340] feat: README and SKILL --- apps/cli/README.md | 47 +++++++++++++++++++++++++++++++++++++++------- apps/cli/SKILL.md | 34 +++++++++++++++++++++++++++------ 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/apps/cli/README.md b/apps/cli/README.md index 31c86fc0..1c7983d8 100644 --- a/apps/cli/README.md +++ b/apps/cli/README.md @@ -180,6 +180,15 @@ postiz posts:delete postiz upload ``` +**⚠️ IMPORTANT: Upload Files Before Posting** + +You **must** upload media files to Postiz before using them in posts. Many platforms (especially TikTok, Instagram, and YouTube) require verified/trusted URLs and will reject external links. + +**Workflow:** +1. Upload your file using `postiz upload` +2. Extract the returned URL +3. Use that URL in your post's `-m` parameter + **Supported formats:** - **Images:** PNG, JPG, JPEG, GIF, WEBP, SVG, BMP, ICO - **Videos:** MP4, MOV, AVI, MKV, WEBM, FLV, WMV, M4V, MPEG, MPG, 3GP @@ -188,11 +197,19 @@ postiz upload **Example:** ```bash +# 1. Upload the file first RESULT=$(postiz upload video.mp4) PATH=$(echo "$RESULT" | jq -r '.path') -postiz posts:create -c "Check out my video!" -m "$PATH" -i "tiktok-id" + +# 2. Use the Postiz URL in your post +postiz posts:create -c "Check out my video!" -s "2024-12-31T12:00:00Z" -m "$PATH" -i "tiktok-id" ``` +**Why this is required:** +- **TikTok, Instagram, YouTube** only accept URLs from trusted domains +- **Security:** Platforms verify media sources to prevent abuse +- **Reliability:** Postiz ensures your media is always accessible + --- ## Platform-Specific Features @@ -215,22 +232,31 @@ postiz posts:create \ # Get playlists postiz integrations:trigger youtube-id getPlaylists -# Upload video with metadata +# Upload video FIRST (required!) +VIDEO=$(postiz upload video.mp4) +VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') + +# Post with uploaded video URL postiz posts:create \ -c "Video description" \ -s "2024-12-31T12:00:00Z" \ --settings '{"title":"Video Title","type":"public","tags":[{"value":"tech","label":"Tech"}],"playlistId":"playlist-id"}' \ - -m "video.mp4" \ + -m "$VIDEO_URL" \ -i "youtube-id" ``` ### TikTok ```bash +# Upload video FIRST (TikTok only accepts verified URLs!) +VIDEO=$(postiz upload video.mp4) +VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') + +# Post with uploaded video URL postiz posts:create \ -c "Video caption #fyp" \ -s "2024-12-31T12:00:00Z" \ --settings '{"privacy":"PUBLIC_TO_EVERYONE","duet":true,"stitch":true}' \ - -m "video.mp4" \ + -m "$VIDEO_URL" \ -i "tiktok-id" ``` @@ -268,20 +294,27 @@ postiz posts:create \ ### Instagram ```bash +# Upload image FIRST (Instagram requires verified URLs!) +IMAGE=$(postiz upload image.jpg) +IMAGE_URL=$(echo "$IMAGE" | jq -r '.path') + # Regular post postiz posts:create \ -c "Caption #hashtag" \ -s "2024-12-31T12:00:00Z" \ --settings '{"post_type":"post"}' \ - -m "image.jpg" \ + -m "$IMAGE_URL" \ -i "instagram-id" -# Story +# Story (upload first) +STORY=$(postiz upload story.jpg) +STORY_URL=$(echo "$STORY" | jq -r '.path') + postiz posts:create \ -c "" \ -s "2024-12-31T12:00:00Z" \ --settings '{"post_type":"story"}' \ - -m "story.jpg" \ + -m "$STORY_URL" \ -i "instagram-id" ``` diff --git a/apps/cli/SKILL.md b/apps/cli/SKILL.md index ba272887..fea39a91 100644 --- a/apps/cli/SKILL.md +++ b/apps/cli/SKILL.md @@ -107,12 +107,19 @@ postiz posts:delete ### Media Upload +**⚠️ IMPORTANT:** Always upload files to Postiz before using them in posts. Many platforms (TikTok, Instagram, YouTube) **require verified URLs** and will reject external links. + ```bash # Upload file and get URL postiz upload image.jpg # Supports: images (PNG, JPG, GIF, WEBP, SVG), videos (MP4, MOV, AVI, MKV, WEBM), # audio (MP3, WAV, OGG, AAC), documents (PDF, DOC, DOCX) + +# Workflow: Upload → Extract URL → Use in post +VIDEO=$(postiz upload video.mp4) +VIDEO_PATH=$(echo "$VIDEO" | jq -r '.path') +postiz posts:create -c "Content" -s "2024-12-31T12:00:00Z" -m "$VIDEO_PATH" -i "tiktok-id" ``` --- @@ -455,21 +462,29 @@ postiz posts:create \ ### YouTube ```bash +# Upload video first (required!) +VIDEO=$(postiz upload video.mp4) +VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') + postiz posts:create \ -c "Video description" \ -s "2024-12-31T12:00:00Z" \ --settings '{"title":"Video Title","type":"public","tags":[{"value":"tech","label":"Tech"}]}' \ - -m "video.mp4" \ + -m "$VIDEO_URL" \ -i "youtube-id" ``` ### TikTok ```bash +# Upload video first (TikTok only accepts verified URLs!) +VIDEO=$(postiz upload video.mp4) +VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') + postiz posts:create \ -c "Video caption #fyp" \ -s "2024-12-31T12:00:00Z" \ --settings '{"privacy":"PUBLIC_TO_EVERYONE","duet":true,"stitch":true}' \ - -m "video.mp4" \ + -m "$VIDEO_URL" \ -i "tiktok-id" ``` @@ -497,20 +512,27 @@ postiz posts:create \ ### Instagram ```bash +# Upload image first (Instagram requires verified URLs!) +IMAGE=$(postiz upload image.jpg) +IMAGE_URL=$(echo "$IMAGE" | jq -r '.path') + # Regular post postiz posts:create \ -c "Caption #hashtag" \ -s "2024-12-31T12:00:00Z" \ --settings '{"post_type":"post"}' \ - -m "image.jpg" \ + -m "$IMAGE_URL" \ -i "instagram-id" # Story +STORY=$(postiz upload story.jpg) +STORY_URL=$(echo "$STORY" | jq -r '.path') + postiz posts:create \ -c "" \ -s "2024-12-31T12:00:00Z" \ --settings '{"post_type":"story"}' \ - -m "story.jpg" \ + -m "$STORY_URL" \ -i "instagram-id" ``` @@ -545,9 +567,9 @@ postiz posts:create \ 1. **API Key not set** - Always `export POSTIZ_API_KEY=key` before using CLI 2. **Invalid integration ID** - Run `integrations:list` to get current IDs 3. **Settings schema mismatch** - Check `integrations:settings` for required fields -4. **Media upload before posting** - Upload returns URL to use in `-m` flag +4. **Media MUST be uploaded to Postiz first** - ⚠️ **CRITICAL:** TikTok, Instagram, YouTube, and many platforms only accept verified URLs. Upload files via `postiz upload` first, then use the returned URL in `-m`. External URLs will be rejected! 5. **JSON escaping in shell** - Use single quotes for JSON: `--settings '{...}'` -6. **Date format** - Must be ISO 8601: `"2024-12-31T12:00:00Z"` +6. **Date format** - Must be ISO 8601: `"2024-12-31T12:00:00Z"` and is REQUIRED 7. **Tool not found** - Check available tools in `integrations:settings` output 8. **Character limits** - Each platform has different limits, check `maxLength` in settings 9. **Required settings** - Some platforms require specific settings (Reddit needs title, YouTube needs title) From c908813d0f0d8e7ad5cd81011a30e36aa0a8a099 Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Sat, 14 Feb 2026 13:45:26 +0700 Subject: [PATCH 254/340] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c4637f7f..eb30259e 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@

+

NEW: check out Postiz agent CLI! perfect for OpenClaw and other agents

Your ultimate AI social media scheduling tool


From 844449b3788ebaf1d2ae1a8cfd7d6c2c169306e3 Mon Sep 17 00:00:00 2001 From: Enno Gelhaus Date: Sat, 14 Feb 2026 18:59:40 +0100 Subject: [PATCH 255/340] Update checkout action version to v6 --- .github/workflows/pr-docker-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-docker-build.yml b/.github/workflows/pr-docker-build.yml index 17eb5c04..2c1832e6 100644 --- a/.github/workflows/pr-docker-build.yml +++ b/.github/workflows/pr-docker-build.yml @@ -15,7 +15,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ github.event.pull_request.head.sha }} From 56efd49d8fdfb68d13a750bbd970d82654cb40e2 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 15 Feb 2026 11:41:39 +0700 Subject: [PATCH 256/340] feat: tiktok fixes --- .../integrations/social/tiktok.provider.ts | 160 +++++++++--------- 1 file changed, 79 insertions(+), 81 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index b36b68d5..9e628cfd 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -439,6 +439,82 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { } } + private buildTikokPostInfoBody(firstPost: PostDetails) { + const isPhoto = (firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) === -1; + const method = firstPost?.settings?.content_posting_method; + + if (method === 'DIRECT_POST') { + return { + post_info: { + ...(isPhoto && firstPost.settings.title + ? { title: firstPost.settings.title } + : {}), + ...(!isPhoto && firstPost.message + ? { title: firstPost.message } + : {}), + ...(isPhoto ? { description: firstPost.message } : {}), + privacy_level: + firstPost.settings.privacy_level || 'PUBLIC_TO_EVERYONE', + disable_duet: !firstPost.settings.duet || false, + disable_comment: !firstPost.settings.comment || false, + disable_stitch: !firstPost.settings.stitch || false, + is_aigc: firstPost.settings.video_made_with_ai || false, + brand_content_toggle: + firstPost.settings.brand_content_toggle || false, + brand_organic_toggle: + firstPost.settings.brand_organic_toggle || false, + ...((firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) === -1 + ? { + auto_add_music: firstPost.settings.autoAddMusic === 'yes', + } + : {}), + }, + }; + } + + return { + post_info: { + ...(isPhoto && firstPost.settings.title + ? { title: firstPost.settings.title } + : {}), + ...(!isPhoto && firstPost.message ? { title: firstPost.message } : {}), + ...(isPhoto ? { description: firstPost.message } : {}), + }, + }; + } + + private buildTikokSourceInfoBody(firstPost: PostDetails) { + const isPhoto = (firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) === -1; + + if (isPhoto) { + return { + post_mode: + firstPost?.settings?.content_posting_method === 'DIRECT_POST' + ? 'DIRECT_POST' + : 'MEDIA_UPLOAD', + media_type: 'PHOTO', + source_info: { + source: 'PULL_FROM_URL', + photo_cover_index: 0, + photo_images: firstPost.media?.map((p) => p.path), + }, + }; + } + + return { + source_info: { + source: 'PULL_FROM_URL', + video_url: firstPost?.media?.[0]?.path!, + ...(firstPost?.media?.[0]?.thumbnailTimestamp! + ? { + video_cover_timestamp_ms: + firstPost?.media?.[0]?.thumbnailTimestamp!, + } + : {}), + }, + }; + } + async post( id: string, accessToken: string, @@ -447,6 +523,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { ): Promise { const [firstPost] = postDetails; const isPhoto = (firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) === -1; + const { data: { publish_id }, } = await ( @@ -462,87 +539,8 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ - ...((firstPost?.settings?.content_posting_method || - 'DIRECT_POST') === 'DIRECT_POST' - ? { - post_info: { - ...((firstPost?.settings?.title && isPhoto) || - (firstPost.message && !isPhoto) - ? { - title: isPhoto - ? firstPost.settings.title - : firstPost.message, - } - : {}), - ...(isPhoto ? { description: firstPost.message } : {}), - privacy_level: - firstPost.settings.privacy_level || 'PUBLIC_TO_EVERYONE', - disable_duet: !firstPost.settings.duet || false, - disable_comment: !firstPost.settings.comment || false, - disable_stitch: !firstPost.settings.stitch || false, - is_aigc: firstPost.settings.video_made_with_ai || false, - brand_content_toggle: - firstPost.settings.brand_content_toggle || false, - brand_organic_toggle: - firstPost.settings.brand_organic_toggle || false, - ...((firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) === - -1 - ? { - auto_add_music: - firstPost.settings.autoAddMusic === 'yes', - } - : {}), - }, - } - : {}), - ...((firstPost?.media?.[0]?.path?.indexOf('mp4') || -1) > -1 - ? { - post_info: { - ...((firstPost?.settings?.title && isPhoto) || - (firstPost.message && !isPhoto) - ? { - title: isPhoto - ? firstPost.settings.title - : firstPost.message, - } - : {}), - ...(isPhoto ? { description: firstPost.message } : {}), - }, - source_info: { - source: 'PULL_FROM_URL', - video_url: firstPost?.media?.[0]?.path!, - ...(firstPost?.media?.[0]?.thumbnailTimestamp! - ? { - video_cover_timestamp_ms: - firstPost?.media?.[0]?.thumbnailTimestamp!, - } - : {}), - }, - } - : { - post_info: { - ...((firstPost?.settings?.title && isPhoto) || - (firstPost.message && !isPhoto) - ? { - title: isPhoto - ? firstPost.settings.title - : firstPost.message, - } - : {}), - ...(isPhoto ? { description: firstPost.message } : {}), - }, - source_info: { - source: 'PULL_FROM_URL', - photo_cover_index: 0, - photo_images: firstPost.media?.map((p) => p.path), - }, - post_mode: - firstPost?.settings?.content_posting_method === - 'DIRECT_POST' - ? 'DIRECT_POST' - : 'MEDIA_UPLOAD', - media_type: 'PHOTO', - }), + ...this.buildTikokPostInfoBody(firstPost), + ...this.buildTikokSourceInfoBody(firstPost), }), } ) From ecb1b55ce15c79b35225b2a3844bdde4b38c69de Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 15 Feb 2026 12:40:02 +0700 Subject: [PATCH 257/340] feat: analytics public api --- .../v1/public.integrations.controller.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index ac8b12db..d457caa0 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -264,6 +264,26 @@ export class PublicIntegrationsController { }; } + @Get('/analytics/:integration') + async getAnalytics( + @GetOrgFromRequest() org: Organization, + @Param('integration') integration: string, + @Query('date') date: string + ) { + Sentry.metrics.count('public_api-request', 1); + return this._integrationService.checkAnalytics(org, integration, date); + } + + @Get('/analytics/post/:postId') + async getPostAnalytics( + @GetOrgFromRequest() org: Organization, + @Param('postId') postId: string, + @Query('date') date: string + ) { + Sentry.metrics.count('public_api-request', 1); + return this._postsService.checkPostAnalytics(org.id, postId, +date); + } + @Post('/integration-trigger/:id') async triggerIntegrationTool( @GetOrgFromRequest() org: Organization, From 38b1cb2ce5b8b22eff8ee8c97f78e60626c9d782 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 15 Feb 2026 14:09:02 +0700 Subject: [PATCH 258/340] feat: local store fixed when trying to download pictures --- libraries/nestjs-libraries/src/upload/local.storage.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/nestjs-libraries/src/upload/local.storage.ts b/libraries/nestjs-libraries/src/upload/local.storage.ts index 87cf3cef..3f672625 100644 --- a/libraries/nestjs-libraries/src/upload/local.storage.ts +++ b/libraries/nestjs-libraries/src/upload/local.storage.ts @@ -3,16 +3,14 @@ import { mkdirSync, unlink, writeFileSync } from 'fs'; // @ts-ignore import mime from 'mime'; import { extname } from 'path'; -import axios from 'axios'; - export class LocalStorage implements IUploadProvider { constructor(private uploadDirectory: string) {} async uploadSimple(path: string) { - const loadImage = await axios.get(path, { responseType: 'arraybuffer' }); + const loadImage = await fetch(path); const contentType = - loadImage?.headers?.['content-type'] || - loadImage?.headers?.['Content-Type']; + loadImage?.headers?.get('content-type') || + loadImage?.headers?.get('Content-Type'); const findExtension = mime.getExtension(contentType)!; const now = new Date(); @@ -32,7 +30,7 @@ export class LocalStorage implements IUploadProvider { const filePath = `${dir}/${randomName}.${findExtension}`; const publicPath = `${innerPath}/${randomName}.${findExtension}`; // Logic to save the file to the filesystem goes here - writeFileSync(filePath, loadImage.data); + writeFileSync(filePath, Buffer.from(await loadImage.arrayBuffer())); return process.env.FRONTEND_URL + '/uploads' + publicPath; } From 6f54180768d0f436e8fd447548a5b627f259088e Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 15 Feb 2026 16:31:51 +0700 Subject: [PATCH 259/340] feat: same refresh token --- .../src/integrations/social/reddit.provider.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts index d58911a7..1e8048de 100644 --- a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts @@ -34,7 +34,6 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { async refreshToken(refreshToken: string): Promise { const { access_token: accessToken, - refresh_token: newRefreshToken, expires_in: expiresIn, } = await ( await this.fetch('https://www.reddit.com/api/v1/access_token', { @@ -64,7 +63,7 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { id, name, accessToken, - refreshToken: newRefreshToken, + refreshToken: refreshToken, expiresIn, picture: icon_img?.split?.('?')?.[0] || '', username: name, From 16946b1c0ff6451e1479313b0b81b8cf6758062e Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 15 Feb 2026 19:04:08 +0700 Subject: [PATCH 260/340] feat: fix reddit --- .../new-launch/providers/reddit/subreddit.tsx | 6 +++--- .../posts/providers-settings/reddit.dto.ts | 4 ++++ .../integrations/social/reddit.provider.ts | 19 ++++++++++++------- package.json | 1 + 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/reddit/subreddit.tsx b/apps/frontend/src/components/new-launch/providers/reddit/subreddit.tsx index 9e002e1f..c3bdd53f 100644 --- a/apps/frontend/src/components/new-launch/providers/reddit/subreddit.tsx +++ b/apps/frontend/src/components/new-launch/providers/reddit/subreddit.tsx @@ -21,7 +21,7 @@ export const RenderOptions: FC<{ }> = (props) => { const { options, onClick, value } = props; const mapValues = useMemo(() => { - return options.map((p) => ({ + return options?.map((p) => ({ children: ( <> {p === 'self' @@ -35,7 +35,7 @@ export const RenderOptions: FC<{ ), id: p, onClick: () => onClick(p), - })); + })) || []; }, [options]); return (
@@ -216,7 +216,7 @@ export const Subreddit: FC<{ name="flair" > - {value.flairs.map((f: any) => ( + {value?.flairs?.map((f: any) => ( diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/reddit.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/reddit.dto.ts index 49c7462f..07e1f8e1 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/reddit.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/reddit.dto.ts @@ -10,6 +10,7 @@ import { ValidateNested, } from 'class-validator'; import { Type } from 'class-transformer'; +import { JSONSchema } from 'class-validator-jsonschema'; export class RedditFlairDto { @IsString() @@ -25,6 +26,9 @@ export class RedditSettingsDtoInner { @IsString() @MinLength(2) @IsDefined() + @JSONSchema({ + description: 'Subreddit must start with /r', + }) subreddit: string; @IsString() diff --git a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts index 1e8048de..08da96be 100644 --- a/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/reddit.provider.ts @@ -32,10 +32,7 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { } async refreshToken(refreshToken: string): Promise { - const { - access_token: accessToken, - expires_in: expiresIn, - } = await ( + const { access_token: accessToken, expires_in: expiresIn } = await ( await this.fetch('https://www.reddit.com/api/v1/access_token', { method: 'POST', headers: { @@ -221,7 +218,10 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { } : {}), text: post.message, - sr: firstPostSettings.value.subreddit, + sr: + firstPostSettings.value.subreddit.indexOf('/r/') > -1 + ? firstPostSettings.value.subreddit + : `/r/${firstPostSettings.value.subreddit}`, }; const all = await ( @@ -235,7 +235,11 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { }) ).json(); - const { id: redditId, name, url } = await new Promise<{ + const { + id: redditId, + name, + url, + } = await new Promise<{ id: string; name: string; url: string; @@ -399,7 +403,8 @@ export class RedditProvider extends SocialAbstract implements SocialProvider { { key: 'subreddit', type: 'string', - description: 'Search flairs and restrictions by subreddit key should be "/r/[name]"', + description: + 'Search flairs and restrictions by subreddit key should be "/r/[name]"', }, ], }) diff --git a/package.json b/package.json index 16fd3ba6..8b8d743f 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "packageManager": "pnpm@10.6.1", "scripts": { "dev": "pnpm run --filter ./apps/extension --filter ./apps/orchestrator --filter ./apps/backend --filter ./apps/frontend --parallel dev", + "dev-backend": "pnpm run --filter ./apps/backend --filter ./apps/frontend --parallel dev", "pm2": "pnpm run pm2-run", "publish-sdk": "pnpm run --filter ./apps/sdk publish", "publish-cli": "pnpm run --filter ./apps/cli publish", From cfa52b23363686797edac349a931d0b1fceaddfa Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 15 Feb 2026 19:08:46 +0700 Subject: [PATCH 261/340] feat: bot nickname --- apps/frontend/src/components/launches/menu/menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx index 03e8f792..a426c85c 100644 --- a/apps/frontend/src/components/launches/menu/menu.tsx +++ b/apps/frontend/src/components/launches/menu/menu.tsx @@ -504,7 +504,7 @@ export const Menu: FC<{
- {t('change_bot', 'Change Bot')} + {t('change_bot', 'Change Bot')}{' '} {[ canChangeProfilePicture && t('picture', 'Picture'), canChangeNickName && t('label_nickname', 'Nickname'), From 613a4285ffc53778b07bc3e319b541f140c65c59 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 16 Feb 2026 00:17:59 +0700 Subject: [PATCH 262/340] feat: post missing modal --- .../src/api/routes/posts.controller.ts | 17 +++ .../v1/public.integrations.controller.ts | 20 +++ .../src/components/launches/calendar.tsx | 44 +++++- .../launches/missing-release.modal.tsx | 129 ++++++++++++++++++ .../src/components/launches/statistics.tsx | 9 +- .../database/prisma/posts/posts.repository.ts | 13 ++ .../database/prisma/posts/posts.service.ts | 70 +++++++++- .../social/social.integrations.interface.ts | 4 + .../integrations/social/tiktok.provider.ts | 82 ++++++++--- 9 files changed, 359 insertions(+), 29 deletions(-) create mode 100644 apps/frontend/src/components/launches/missing-release.modal.tsx diff --git a/apps/backend/src/api/routes/posts.controller.ts b/apps/backend/src/api/routes/posts.controller.ts index 2f4e0858..7be708ba 100644 --- a/apps/backend/src/api/routes/posts.controller.ts +++ b/apps/backend/src/api/routes/posts.controller.ts @@ -45,6 +45,23 @@ export class PostsController { return this._postsService.getStatistics(org.id, id); } + @Get('/:id/missing') + async getMissingContent( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string + ) { + return this._postsService.getMissingContent(org.id, id); + } + + @Put('/:id/release-id') + async updateReleaseId( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string, + @Body('releaseId') releaseId: string + ) { + return this._postsService.updateReleaseId(org.id, id, releaseId); + } + @Post('/should-shortlink') async shouldShortlink(@Body() body: { messages: string[] }) { return { ask: this._shortLinkService.askShortLinkedin(body.messages) }; diff --git a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts index 2f289756..6184aff8 100644 --- a/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts +++ b/apps/backend/src/public-api/routes/v1/public.integrations.controller.ts @@ -6,6 +6,7 @@ import { HttpException, Param, Post, + Put, Query, UploadedFile, UseInterceptors, @@ -268,6 +269,25 @@ export class PublicIntegrationsController { }; } + @Get('/posts/:id/missing') + async getMissingContent( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string + ) { + Sentry.metrics.count('public_api-request', 1); + return this._postsService.getMissingContent(org.id, id); + } + + @Put('/posts/:id/release-id') + async updateReleaseId( + @GetOrgFromRequest() org: Organization, + @Param('id') id: string, + @Body('releaseId') releaseId: string + ) { + Sentry.metrics.count('public_api-request', 1); + return this._postsService.updateReleaseId(org.id, id, releaseId); + } + @Get('/analytics/:integration') async getAnalytics( @GetOrgFromRequest() org: Organization, diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 88c27f38..43ce7be5 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -47,6 +47,7 @@ import { extend } from 'dayjs'; import { isUSCitizen } from './helpers/isuscitizen.utils'; import { useInterval } from '@mantine/hooks'; import { StatisticsModal } from '@gitroom/frontend/components/launches/statistics'; +import { MissingReleaseModal } from '@gitroom/frontend/components/launches/missing-release.modal'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import i18next from 'i18next'; import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; @@ -214,7 +215,26 @@ const usePostActions = (onMutate?: () => void) => { [modal, t] ); - return { editPost, deletePost, openStatistics }; + const openMissingRelease = useCallback( + (id: string) => () => { + modal.openModal({ + title: t('connect_post', 'Connect Post'), + closeOnClickOutside: true, + closeOnEscape: true, + withCloseButton: true, + classNames: { + modal: 'w-[100%] max-w-[800px]', + }, + children: ( + + ), + size: '60%', + }); + }, + [modal, t, mutate] + ); + + return { editPost, deletePost, openStatistics, openMissingRelease }; }; export const DayView = () => { @@ -452,7 +472,7 @@ export const ListView = () => { const { integrations, loading, listPosts } = useCalendar(); // Use shared post actions hook - const { editPost, deletePost, openStatistics } = usePostActions(); + const { editPost, deletePost, openStatistics, openMissingRelease } = usePostActions(); // Group posts by date const groupedPosts = useMemo(() => { @@ -502,6 +522,7 @@ export const ListView = () => { date={newDayjs(post.publishDate)} state={post.state} statistics={openStatistics(post.id)} + missingRelease={openMissingRelease(post.id)} editPost={editPost(post, false)} duplicatePost={editPost(post, true)} post={post} @@ -557,7 +578,7 @@ export const CalendarColumn: FC<{ const fetch = useFetch(); // Use shared post actions hook - const { editPost, deletePost, openStatistics } = usePostActions(); + const { editPost, deletePost, openStatistics, openMissingRelease } = usePostActions(); const postList = useMemo(() => { return posts.filter((post) => { const pList = dayjs.utc(post.publishDate).local(); @@ -820,6 +841,7 @@ export const CalendarColumn: FC<{ date={getDate} state={post.state} statistics={openStatistics(post.id)} + missingRelease={openMissingRelease(post.id)} editPost={editPost(post, false)} duplicatePost={editPost(post, true)} post={post} @@ -929,6 +951,7 @@ const CalendarItem: FC<{ duplicatePost: () => void; deletePost: () => void; statistics: () => void; + missingRelease?: () => void; integrations: Integrations[]; state: State; display: 'day' | 'week' | 'month'; @@ -952,6 +975,7 @@ const CalendarItem: FC<{ display, deletePost, showTime, + missingRelease, } = props; const { disableXAnalytics } = useVariables(); const preview = useCallback(() => { @@ -1016,7 +1040,17 @@ const CalendarItem: FC<{
{' '} {((post.integration.providerIdentifier === 'x' && disableXAnalytics) || !post.releaseId) ? ( <> - ) : ( + ) : post.releaseId === 'missing' && missingRelease ? ( +
+ +
+ ) : post.releaseId !== 'missing' ? (
+ ) : ( + <> )}{' '}
void; +}> = ({ postId, onSuccess }) => { + const t = useT(); + const fetch = useFetch(); + const modal = useModals(); + const toaster = useToaster(); + const [selected, setSelected] = useState(null); + const [saving, setSaving] = useState(false); + + const loadMissingContent = useCallback(async () => { + return (await fetch(`/posts/${postId}/missing`)).json(); + }, [postId, fetch]); + + const { data, isLoading } = useSWR( + `/posts/${postId}/missing`, + loadMissingContent + ); + + const handleSave = useCallback(async () => { + if (!selected) return; + setSaving(true); + try { + await fetch(`/posts/${postId}/release-id`, { + method: 'PUT', + body: JSON.stringify({ releaseId: selected }), + }); + onSuccess(); + modal.closeAll(); + modal.openModal({ + title: t('statistics', 'Statistics'), + closeOnClickOutside: true, + closeOnEscape: true, + withCloseButton: true, + classNames: { + modal: 'w-[100%] max-w-[1400px]', + }, + children: , + size: '80%', + }); + } catch { + toaster.show( + t('release_id_update_failed', 'Failed to connect post'), + 'warning' + ); + } finally { + setSaving(false); + } + }, [selected, postId, fetch, toaster, t, onSuccess, modal]); + + if (isLoading) { + return ( +
+ +
+ ); + } + + if (!data || data.length === 0) { + return ( +
+ {t( + 'no_missing_content', + 'No content found from this provider. The provider may not support this feature.' + )} +
+ ); + } + + return ( +
+
+ {t( + 'select_matching_content', + 'Select the content that matches this post:' + )} +
+
+ {data.map((item: { id: string; url: string }) => ( +
setSelected(item.id)} + className={`cursor-pointer rounded-[8px] overflow-hidden border-2 transition-all ${ + selected === item.id + ? 'border-[#612BD3] scale-[1.02]' + : 'border-transparent hover:border-textColor/20' + }`} + > + {item.id} +
+ ))} +
+
+ + +
+
+ ); +}; diff --git a/apps/frontend/src/components/launches/statistics.tsx b/apps/frontend/src/components/launches/statistics.tsx index ff136f87..16d4b47d 100644 --- a/apps/frontend/src/components/launches/statistics.tsx +++ b/apps/frontend/src/components/launches/statistics.tsx @@ -1,10 +1,11 @@ import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; -import useSWR from 'swr'; +import useSWR, { useSWRConfig } from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { ChartSocial } from '@gitroom/frontend/components/analytics/chart-social'; import { Select } from '@gitroom/react/form/select'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; +import { MissingReleaseModal } from '@gitroom/frontend/components/launches/missing-release.modal'; interface AnalyticsData { label: string; @@ -34,7 +35,7 @@ export const StatisticsModal: FC<{ loadStatistics ); - const { data: analyticsData, isLoading: isLoadingAnalytics } = useSWR( + const { data: analyticsData, isLoading: isLoadingAnalytics, mutate: mutateAnalytics } = useSWR( `/analytics/post/${postId}?date=${dateRange}`, loadPostAnalytics, { @@ -47,6 +48,8 @@ export const StatisticsModal: FC<{ } ); + const isMissing = analyticsData && !Array.isArray(analyticsData) && analyticsData.missing; + const dateOptions = useMemo(() => { return [ { key: 7, value: t('7_days', '7 Days') }, @@ -76,6 +79,8 @@ export const StatisticsModal: FC<{
+ ) : isMissing ? ( + mutateAnalytics()} /> ) : (
{/* Post Analytics Section */} diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 26454661..12db7861 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -372,6 +372,19 @@ export class PostsRepository { }); } + updateReleaseId(id: string, orgId: string, releaseId: string) { + return this._post.model.post.update({ + where: { + id, + organizationId: orgId, + releaseId: 'missing', + }, + data: { + releaseId, + }, + }); + } + async changeState(id: string, state: State, err?: any, body?: any) { const update = await this._post.model.post.update({ where: { diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts index 5a084c7e..39c77653 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts @@ -64,17 +64,85 @@ export class PostsService { return this._postRepository.updatePost(id, postId, releaseURL); } + async getMissingContent( + orgId: string, + postId: string, + forceRefresh = false + ): Promise<{ id: string; url: string }[]> { + const post = await this._postRepository.getPostById(postId, orgId); + if (!post || post.releaseId !== 'missing') { + return []; + } + + const integrationProvider = this._integrationManager.getSocialIntegration( + post.integration.providerIdentifier + ); + + if (!integrationProvider.missing) { + return []; + } + + const getIntegration = post.integration!; + + if ( + dayjs(getIntegration?.tokenExpiration).isBefore(dayjs()) || + forceRefresh + ) { + const data = await this._refreshIntegrationService.refresh( + getIntegration + ); + if (!data) { + return []; + } + + const { accessToken } = data; + + if (accessToken) { + getIntegration.token = accessToken; + + if (integrationProvider.refreshWait) { + await timer(10000); + } + } else { + await this._integrationService.disconnectChannel(orgId, getIntegration); + return []; + } + } + + try { + return await integrationProvider.missing( + getIntegration.internalId, + getIntegration.token + ); + } catch (e) { + console.log(e); + if (e instanceof RefreshToken) { + return this.getMissingContent(orgId, postId, true); + } + } + + return []; + } + + async updateReleaseId(orgId: string, postId: string, releaseId: string) { + return this._postRepository.updateReleaseId(postId, orgId, releaseId); + } + async checkPostAnalytics( orgId: string, postId: string, date: number, forceRefresh = false - ): Promise { + ): Promise { const post = await this._postRepository.getPostById(postId, orgId); if (!post || !post.releaseId) { return []; } + if (post.releaseId === 'missing') { + return { missing: true }; + } + const integrationProvider = this._integrationManager.getSocialIntegration( post.integration.providerIdentifier ); diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts index bca6eb5f..29d8356b 100644 --- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts +++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts @@ -44,6 +44,10 @@ export interface IAuthenticator { accessToken: string, url: string ): Promise<{ url: string }>; + missing?( + id: string, + accessToken: string + ): Promise<{ id: string; url: string }[]>; } export interface AnalyticsData { diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index 9e628cfd..011dc3d8 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -369,7 +369,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { id: string, publishId: string, accessToken: string - ): Promise<{ url: string; id: number }> { + ): Promise<{ url: string; id: string }> { // eslint-disable-next-line no-constant-condition while (true) { const post = await ( @@ -396,7 +396,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { if (status === 'SEND_TO_USER_INBOX') { return { url: 'https://www.tiktok.com/messages?lang=en', - id: Math.floor(Math.random() * 1000000 + 100000), + id: 'missing', }; } @@ -703,6 +703,40 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { } } + async missing( + id: string, + accessToken: string + ): Promise<{ id: string; url: string }[]> { + try { + const videoListResponse = await this.fetch( + 'https://open.tiktokapis.com/v2/video/list/?fields=id,cover_image_url,title', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ max_count: 20 }), + } + ); + + const videoListData = await videoListResponse.json(); + const videos = videoListData?.data?.videos; + + if (!videos || videos.length === 0) { + return []; + } + + return videos.map((v: { id: string; cover_image_url: string }) => ({ + id: String(v.id), + url: v.cover_image_url, + })); + } catch (err) { + console.error('Error fetching TikTok missing content:', err); + return []; + } + } + async postAnalytics( integrationId: string, accessToken: string, @@ -711,27 +745,31 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { ): Promise { const today = dayjs().format('YYYY-MM-DD'); - const post = await ( - await this.fetch( - 'https://open.tiktokapis.com/v2/post/publish/status/fetch/', - { - method: 'POST', - headers: { - 'Content-Type': 'application/json; charset=UTF-8', - Authorization: `Bearer ${accessToken}`, + if (postId.indexOf('v_pub_url') > -1) { + const post = await ( + await this.fetch( + 'https://open.tiktokapis.com/v2/post/publish/status/fetch/', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json; charset=UTF-8', + Authorization: `Bearer ${accessToken}`, + }, + body: JSON.stringify({ + publish_id: postId, + }), }, - body: JSON.stringify({ - publish_id: postId, - }), - }, - '', - 0, - true - ) - ).json(); + '', + 0, + true + ) + ).json(); - if (!post?.data?.publicaly_available_post_id?.[0]) { - return []; + if (!post?.data?.publicaly_available_post_id?.[0]) { + return []; + } + + postId = post.data.publicaly_available_post_id[0]; } try { @@ -746,7 +784,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { }, body: JSON.stringify({ filters: { - video_ids: post?.data?.publicaly_available_post_id.map(String), + video_ids: [postId], }, }), } From 009f2df074c845ea149c327e1fbea8cba7f3da2d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 16 Feb 2026 16:06:06 +0700 Subject: [PATCH 263/340] feat: original name --- apps/backend/src/api/routes/media.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/src/api/routes/media.controller.ts b/apps/backend/src/api/routes/media.controller.ts index 925f3e26..0da66316 100644 --- a/apps/backend/src/api/routes/media.controller.ts +++ b/apps/backend/src/api/routes/media.controller.ts @@ -91,7 +91,7 @@ export class MediaController { @GetOrgFromRequest() org: Organization, @UploadedFile() file: Express.Multer.File ) { - const originalName = file.originalname; + const originalName = file?.originalname || ''; const uploadedFile = await this.storage.uploadFile(file); return this._mediaService.saveFile( org.id, From b854430f7d28c09c3560f0dab2a242bc73565572 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 16 Feb 2026 18:20:12 +0700 Subject: [PATCH 264/340] feat: release id must be string --- .../src/database/prisma/posts/posts.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts index 12db7861..d8128bc2 100644 --- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts +++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts @@ -380,7 +380,7 @@ export class PostsRepository { releaseId: 'missing', }, data: { - releaseId, + releaseId: String(releaseId), }, }); } From f7f5899d01c507c4951b6a39bda982b6d4325554 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 16 Feb 2026 20:32:54 +0700 Subject: [PATCH 265/340] Feat: tiktok fixes --- .../providers/tiktok/tiktok.provider.tsx | 2 +- .../integrations/social/tiktok.provider.ts | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx index 9fc74e7f..7ed473af 100644 --- a/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/tiktok/tiktok.provider.tsx @@ -83,7 +83,7 @@ const TikTokSettings: FC<{ return (
{/**/} - {isTitle && } + {isTitle && } ({ + id: charge.id, + amount: charge.amount, + currency: charge.currency, + created: charge.created, + status: charge.status, + refunded: charge.refunded, + amount_refunded: charge.amount_refunded, + description: charge.description, + })); + } + + async refundCharges(organizationId: string, chargeIds: string[]) { + const org = await this._organizationService.getOrgById(organizationId); + if (!org?.paymentId) { + throw new Error('No payment customer found for this organization'); + } + + const refunded: string[] = []; + const failed: string[] = []; + + for (const chargeId of chargeIds) { + try { + await stripe.refunds.create({ charge: chargeId }); + refunded.push(chargeId); + } catch (err) { + failed.push(chargeId); + } + } + + return { refunded, failed }; + } + + async cancelSubscription(organizationId: string) { + const org = await this._organizationService.getOrgById(organizationId); + if (!org?.paymentId) { + throw new Error('No payment customer found for this organization'); + } + + const customer = org.paymentId; + + const subscriptions = ( + await stripe.subscriptions.list({ + customer, + status: 'all', + }) + ).data.filter((f) => f.status !== 'canceled'); + + if (!subscriptions.length) { + throw new Error('No active subscription found'); + } + + await stripe.subscriptions.cancel(subscriptions[0].id); + await this._subscriptionService.deleteSubscription(customer); + + return { cancelled: true }; + } + async lifetimeDeal(organizationId: string, code: string) { const getCurrentSubscription = await this._subscriptionService.getSubscriptionByOrganizationId( From 7bfa3e5ad93f24a8ecff4afdbc82f69c45353cd2 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 27 Feb 2026 18:58:17 +0700 Subject: [PATCH 285/340] feat: only success charges --- .../src/services/stripe.service.ts | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index 31f336de..455dae07 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -831,16 +831,18 @@ export class StripeService { limit: 100, }); - return charges.data.map((charge) => ({ - id: charge.id, - amount: charge.amount, - currency: charge.currency, - created: charge.created, - status: charge.status, - refunded: charge.refunded, - amount_refunded: charge.amount_refunded, - description: charge.description, - })); + return charges.data + .filter((f) => f.status === 'succeeded') + .map((charge) => ({ + id: charge.id, + amount: charge.amount, + currency: charge.currency, + created: charge.created, + status: charge.status, + refunded: charge.refunded, + amount_refunded: charge.amount_refunded, + description: charge.description, + })); } async refundCharges(organizationId: string, chargeIds: string[]) { From ff89a21c988bc7284f815868ce729d2842715934 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 27 Feb 2026 19:41:20 +0700 Subject: [PATCH 286/340] feat: redis throttler --- apps/backend/src/app.module.ts | 17 +++++++++++------ package.json | 1 + pnpm-lock.yaml | 23 +++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/apps/backend/src/app.module.ts b/apps/backend/src/app.module.ts index 64135061..7a0be2d0 100644 --- a/apps/backend/src/app.module.ts +++ b/apps/backend/src/app.module.ts @@ -15,6 +15,8 @@ import { ChatModule } from '@gitroom/nestjs-libraries/chat/chat.module'; import { getTemporalModule } from '@gitroom/nestjs-libraries/temporal/temporal.module'; import { TemporalRegisterMissingSearchAttributesModule } from '@gitroom/nestjs-libraries/temporal/temporal.register'; import { InfiniteWorkflowRegisterModule } from '@gitroom/nestjs-libraries/temporal/infinite.workflow.register'; +import { ThrottlerStorageRedisService } from '@nest-lab/throttler-storage-redis'; +import { ioRedis } from '@gitroom/nestjs-libraries/redis/redis.service'; @Global() @Module({ @@ -30,12 +32,15 @@ import { InfiniteWorkflowRegisterModule } from '@gitroom/nestjs-libraries/tempor getTemporalModule(false), TemporalRegisterMissingSearchAttributesModule, InfiniteWorkflowRegisterModule, - ThrottlerModule.forRoot([ - { - ttl: 3600000, - limit: process.env.API_LIMIT ? Number(process.env.API_LIMIT) : 30, - }, - ]), + ThrottlerModule.forRoot({ + throttlers: [ + { + ttl: 3600000, + limit: process.env.API_LIMIT ? Number(process.env.API_LIMIT) : 30, + }, + ], + storage: new ThrottlerStorageRedisService(ioRedis), + }), ], controllers: [], providers: [ diff --git a/package.json b/package.json index ae995bb3..787c795c 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "@mastra/memory": "^0.15.6", "@mastra/pg": "^0.17.2", "@modelcontextprotocol/sdk": "^1.22.0", + "@nest-lab/throttler-storage-redis": "^1.2.0", "@nestjs/cli": "10.0.2", "@nestjs/common": "^10.0.2", "@nestjs/core": "^10.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d41077cb..447aa9eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,6 +84,9 @@ importers: '@modelcontextprotocol/sdk': specifier: ^1.22.0 version: 1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76) + '@nest-lab/throttler-storage-redis': + specifier: ^1.2.0 + version: 1.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/throttler@6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14))(ioredis@5.8.2)(reflect-metadata@0.1.14) '@nestjs/cli': specifier: 10.0.2 version: 10.0.2(@swc/cli@0.3.14(@swc/core@1.5.7(@swc/helpers@0.5.13))(chokidar@3.5.3))(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) @@ -3748,6 +3751,15 @@ packages: '@neon-rs/load@0.1.82': resolution: {integrity: sha512-H4Gu2o5kPp+JOEhRrOQCnJnf7X6sv9FBLttM/wSbb4efsgFWeHzfU/ItZ01E5qqEk+U6QGdeVO7lxXIAtYHr5A==} + '@nest-lab/throttler-storage-redis@1.2.0': + resolution: {integrity: sha512-tMkUyo68NCKTR+zILk+EC35SMYBtDPZY2mCj7ZaCietWGVTnuP4zwq9ERYfvU6kJv6h8teNZrC6MJCmY6/dljw==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/core': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 + '@nestjs/throttler': '>=6.0.0' + ioredis: '>=5.0.0' + reflect-metadata: ^0.2.1 + '@nestjs/axios@4.0.1': resolution: {integrity: sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==} peerDependencies: @@ -16734,10 +16746,12 @@ packages: whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation whatwg-fetch@3.6.20: resolution: {integrity: sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==} @@ -21037,6 +21051,15 @@ snapshots: '@neon-rs/load@0.1.82': {} + '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/throttler@6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14))(ioredis@5.8.2)(reflect-metadata@0.1.14)': + dependencies: + '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/throttler': 6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14) + ioredis: 5.8.2 + reflect-metadata: 0.1.14 + tslib: 2.8.1 + '@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': dependencies: '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) From 71833f3f39dbde204b67b55152f8fafddd684f15 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 28 Feb 2026 15:01:54 +0700 Subject: [PATCH 287/340] feat: mewe --- .../providers/mewe/mewe.provider.tsx | 15 +++++++++++- .../dtos/posts/providers-settings/mewe.dto.ts | 13 ++++++++--- .../src/integrations/social/mewe.provider.ts | 23 +++++++++++-------- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx b/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx index 8aadca6c..82e27c38 100644 --- a/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx +++ b/apps/frontend/src/components/new-launch/providers/mewe/mewe.provider.tsx @@ -8,12 +8,25 @@ import { FC } from 'react'; import { MeweDto } from '@gitroom/nestjs-libraries/dtos/posts/providers-settings/mewe.dto'; import { MeweGroupSelect } from '@gitroom/frontend/components/new-launch/providers/mewe/mewe.group.select'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; +import { Select } from '@gitroom/react/form/select'; +import { useWatch } from 'react-hook-form'; const MeweComponent: FC = () => { const form = useSettings(); + const postType = useWatch({ control: form.control, name: 'postType' }); + return (
- + + {postType === 'group' && ( + + )}
); }; diff --git a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts index f1039bae..ca4d9647 100644 --- a/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts +++ b/libraries/nestjs-libraries/src/dtos/posts/providers-settings/mewe.dto.ts @@ -1,12 +1,19 @@ -import { IsDefined, IsString, MinLength } from 'class-validator'; +import { IsIn, IsOptional, IsString, MinLength, ValidateIf } from 'class-validator'; import { JSONSchema } from 'class-validator-jsonschema'; export class MeweDto { + @IsIn(['timeline', 'group']) + @JSONSchema({ + description: 'Where to post: timeline or group', + }) + postType: 'timeline' | 'group'; + + @ValidateIf((o) => o.postType === 'group') @MinLength(1) - @IsDefined() @IsString() @JSONSchema({ description: 'Group must be an id', }) - group: string; + @IsOptional() + group?: string; } diff --git a/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts b/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts index 55640527..07e2e675 100644 --- a/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/mewe.provider.ts @@ -239,6 +239,7 @@ export class MeweProvider extends SocialAbstract implements SocialProvider { integration: Integration ): Promise { const [firstPost] = postDetails; + const postType = firstPost.settings.postType || 'group'; const groupId = firstPost.settings.group; // Upload photos if present (exclude videos) @@ -257,15 +258,17 @@ export class MeweProvider extends SocialAbstract implements SocialProvider { postBody.uploadedPhotoIds = uploadedPhotoIds; } + const postUrl = + postType === 'timeline' + ? `${this.meweHost}/api/dev/me/post` + : `${this.meweHost}/api/dev/group/${groupId}/post`; + // MeWe post endpoint may return 204 (no content), so use raw fetch - const postResponse = await fetch( - `${this.meweHost}/api/dev/group/${groupId}/post`, - { - method: 'POST', - headers: this.authHeaders(accessToken), - body: JSON.stringify(postBody), - } - ); + const postResponse = await fetch(postUrl, { + method: 'POST', + headers: this.authHeaders(accessToken), + body: JSON.stringify(postBody), + }); if (!postResponse.ok) { const errorText = await postResponse.text(); @@ -285,11 +288,13 @@ export class MeweProvider extends SocialAbstract implements SocialProvider { postId = makeId(12); } + const releaseURL = `${this.meweHost}/post/show/${postId}`; + return [ { id: firstPost.id, postId, - releaseURL: `${this.meweHost}/group/${groupId}`, + releaseURL, status: 'success', }, ]; From 6c39e810dbb492acd29e40fa04781a529a2bfc94 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sat, 28 Feb 2026 16:53:03 +0700 Subject: [PATCH 288/340] feat: seperate mcp url --- apps/frontend/src/app/(app)/layout.tsx | 1 + apps/frontend/src/app/(extension)/layout.tsx | 1 + .../src/components/public-api/public.component.tsx | 10 +++++----- .../src/helpers/variable.context.tsx | 2 ++ 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/frontend/src/app/(app)/layout.tsx b/apps/frontend/src/app/(app)/layout.tsx index c7cf60a4..861814c2 100644 --- a/apps/frontend/src/app/(app)/layout.tsx +++ b/apps/frontend/src/app/(app)/layout.tsx @@ -70,6 +70,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { oauthLogoUrl={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL!} oauthDisplayName={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME!} uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!} + mcpUrl={process.env.MCP_URL} dub={!!process.env.STRIPE_PUBLISHABLE_KEY} facebookPixel={process.env.NEXT_PUBLIC_FACEBOOK_PIXEL!} telegramBotName={process.env.TELEGRAM_BOT_NAME!} diff --git a/apps/frontend/src/app/(extension)/layout.tsx b/apps/frontend/src/app/(extension)/layout.tsx index 3f3d83f7..b58b9e0b 100644 --- a/apps/frontend/src/app/(extension)/layout.tsx +++ b/apps/frontend/src/app/(extension)/layout.tsx @@ -41,6 +41,7 @@ export default async function AppLayout({ children }: { children: ReactNode }) { oauthLogoUrl={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL!} oauthDisplayName={process.env.NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME!} uploadDirectory={process.env.NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY!} + mcpUrl={process.env.MCP_URL} dub={false} facebookPixel={process.env.NEXT_PUBLIC_FACEBOOK_PIXEL!} telegramBotName={process.env.TELEGRAM_BOT_NAME!} diff --git a/apps/frontend/src/components/public-api/public.component.tsx b/apps/frontend/src/components/public-api/public.component.tsx index ef3f9b7e..92dad801 100644 --- a/apps/frontend/src/components/public-api/public.component.tsx +++ b/apps/frontend/src/components/public-api/public.component.tsx @@ -12,7 +12,7 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useDecisionModal } from '@gitroom/frontend/components/layout/new-modal'; export const PublicComponent = () => { const user = useUser(); - const { backendUrl, frontEndUrl } = useVariables(); + const { backendUrl, frontEndUrl, mcpUrl } = useVariables(); const toaster = useToaster(); const fetch = useFetch(); const decision = useDecisionModal(); @@ -25,7 +25,7 @@ export const PublicComponent = () => { }, [user]); const copyToClipboard2 = useCallback(() => { toaster.show('MCP copied to clipboard', 'success'); - copy(`${backendUrl}/mcp/` + user?.publicApi); + copy(`${mcpUrl || backendUrl}/mcp/` + user?.publicApi); }, [user]); const rotateKey = useCallback(async () => { @@ -121,13 +121,13 @@ export const PublicComponent = () => {
{reveal2 ? ( - `${backendUrl}/mcp/` + user.publicApi + `${mcpUrl || backendUrl}/mcp/` + user.publicApi ) : ( <>
- {(`${backendUrl}/mcp/` + user.publicApi).slice(0, -5)} + {(`${mcpUrl || backendUrl}/mcp/` + user.publicApi).slice(0, -5)}
-
{(`${backendUrl}/mcp/` + user.publicApi).slice(-5)}
+
{(`${mcpUrl || backendUrl}/mcp/` + user.publicApi).slice(-5)}
)}
diff --git a/libraries/react-shared-libraries/src/helpers/variable.context.tsx b/libraries/react-shared-libraries/src/helpers/variable.context.tsx index 5bbe64a0..30f2dcdf 100644 --- a/libraries/react-shared-libraries/src/helpers/variable.context.tsx +++ b/libraries/react-shared-libraries/src/helpers/variable.context.tsx @@ -8,6 +8,7 @@ interface VariableContextInterface { genericOauth: boolean; oauthLogoUrl: string; oauthDisplayName: string; + mcpUrl?: string; frontEndUrl: string; plontoKey: string; storageProvider: 'local' | 'cloudflare'; @@ -34,6 +35,7 @@ const VariableContext = createContext({ genericOauth: false, oauthLogoUrl: '', oauthDisplayName: '', + mcpUrl: '', frontEndUrl: '', storageProvider: 'local', plontoKey: '', From d0a6ee330dcbaf54af799cbb7ea205e5dddd2b1b Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 1 Mar 2026 16:40:51 +0700 Subject: [PATCH 289/340] feat: re-add sse option for mcp --- .../nestjs-libraries/src/chat/start.mcp.ts | 186 +- pnpm-lock.yaml | 10414 ++++++++-------- 2 files changed, 5494 insertions(+), 5106 deletions(-) diff --git a/libraries/nestjs-libraries/src/chat/start.mcp.ts b/libraries/nestjs-libraries/src/chat/start.mcp.ts index 75eb4258..7e8fab5c 100644 --- a/libraries/nestjs-libraries/src/chat/start.mcp.ts +++ b/libraries/nestjs-libraries/src/chat/start.mcp.ts @@ -20,44 +20,85 @@ export const startMcp = async (app: INestApplication) => { agents: { postiz: agent }, }); - app.use( - '/mcp', - async (req: Request, res: Response, next: () => void) => { - // Skip if this is the /mcp/:id route - if (req.path !== '/' && req.path !== '') { - next(); - return; - } + app.use('/mcp', async (req: Request, res: Response, next: () => void) => { + // Skip if this is the /mcp/:id route + if (req.path !== '/' && req.path !== '') { + next(); + return; + } + // @ts-ignore + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', '*'); + res.setHeader('Access-Control-Allow-Headers', '*'); + res.setHeader('Access-Control-Expose-Headers', '*'); + + if (req.method === 'OPTIONS') { + res.sendStatus(200); + return; + } + + const token = req.headers.authorization?.replace('Bearer ', ''); + if (!token) { + res.status(401).send('Missing Authorization header'); + return; + } + + // @ts-ignore + req.auth = await organizationService.getOrgByApiKey(token); + // @ts-ignore + if (!req.auth) { + res.status(401).send('Invalid API Key'); + return; + } + + const url = new URL('/mcp', process.env.NEXT_PUBLIC_BACKEND_URL); + + // @ts-ignore + await runWithContext({ requestId: token, auth: req.auth }, async () => { + await server.startHTTP({ + url, + httpPath: url.pathname, + options: { + sessionIdGenerator: () => { + return randomUUID(); + }, + }, + req, + res, + }); + }); + }); + + app.use('/mcp/:id', async (req: Request, res: Response) => { + // @ts-ignore + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', '*'); + res.setHeader('Access-Control-Allow-Headers', '*'); + res.setHeader('Access-Control-Expose-Headers', '*'); + + if (req.method === 'OPTIONS') { + res.sendStatus(200); + return; + } + + // @ts-ignore + req.auth = await organizationService.getOrgByApiKey(req.params.id); + // @ts-ignore + if (!req.auth) { + res.status(400).send('Invalid API Key'); + return; + } + + const url = new URL( + `/mcp/${req.params.id}`, + process.env.NEXT_PUBLIC_BACKEND_URL + ); + + await runWithContext( // @ts-ignore - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', '*'); - res.setHeader('Access-Control-Allow-Headers', '*'); - res.setHeader('Access-Control-Expose-Headers', '*'); - - if (req.method === 'OPTIONS') { - res.sendStatus(200); - return; - } - - const token = req.headers.authorization?.replace('Bearer ', ''); - if (!token) { - res.status(401).send('Missing Authorization header'); - return; - } - - // @ts-ignore - req.auth = await organizationService.getOrgByApiKey(token); - // @ts-ignore - if (!req.auth) { - res.status(401).send('Invalid API Key'); - return; - } - - const url = new URL('/mcp', process.env.NEXT_PUBLIC_BACKEND_URL); - - // @ts-ignore - await runWithContext({ requestId: token, auth: req.auth }, async () => { + { requestId: req.params.id, auth: req.auth }, + async () => { await server.startHTTP({ url, httpPath: url.pathname, @@ -69,51 +110,44 @@ export const startMcp = async (app: INestApplication) => { req, res, }); - }); + } + ); + }); + + app.use(['/sse/:id', '/message/:id'], async (req: Request, res: Response) => { + // @ts-ignore + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Access-Control-Allow-Methods', '*'); + res.setHeader('Access-Control-Allow-Headers', '*'); + res.setHeader('Access-Control-Expose-Headers', '*'); + + if (req.method === 'OPTIONS') { + res.sendStatus(200); + return; } - ); - app.use( - '/mcp/:id', - async (req: Request, res: Response) => { + // @ts-ignore + req.auth = await organizationService.getOrgByApiKey(req.params.id); + // @ts-ignore + if (!req.auth) { + res.status(400).send('Invalid API Key'); + return; + } + + const url = new URL(req.originalUrl, process.env.NEXT_PUBLIC_BACKEND_URL); + + await runWithContext( // @ts-ignore - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Access-Control-Allow-Methods', '*'); - res.setHeader('Access-Control-Allow-Headers', '*'); - res.setHeader('Access-Control-Expose-Headers', '*'); - - if (req.method === 'OPTIONS') { - res.sendStatus(200); - return; - } - - // @ts-ignore - req.auth = await organizationService.getOrgByApiKey(req.params.id); - // @ts-ignore - if (!req.auth) { - res.status(400).send('Invalid API Key'); - return ; - } - - const url = new URL( - `/mcp/${req.params.id}`, - process.env.NEXT_PUBLIC_BACKEND_URL - ); - - // @ts-ignore - await runWithContext({ requestId: req.params.id, auth: req.auth }, async () => { - await server.startHTTP({ + { requestId: req.params.id, auth: req.auth }, + async () => { + await server.startSSE({ url, - httpPath: url.pathname, - options: { - sessionIdGenerator: () => { - return randomUUID(); - }, - }, + ssePath: `/sse/${req.params.id}`, + messagePath: `/message/${req.params.id}`, req, res, }); - }); - } - ); + } + ); + }); }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 447aa9eb..e19f84f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,52 +14,52 @@ importers: dependencies: '@ag-ui/mastra': specifier: 0.2.0 - version: 0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@copilotkit/runtime@1.10.6(c9e743140f0883eda50bf44e0628c06c))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) + version: 0.2.0(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(9413ba786a25fdda1f6d87c0f5568c1b))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@ai-sdk/openai': specifier: ^2.0.52 - version: 2.0.88(zod@3.25.76) + version: 2.0.95(zod@3.25.76) '@atproto/api': specifier: ^0.15.15 version: 0.15.27 '@aws-sdk/client-s3': specifier: ^3.787.0 - version: 3.956.0 + version: 3.1000.0 '@aws-sdk/s3-request-presigner': specifier: ^3.787.0 - version: 3.956.0 + version: 3.1000.0 '@casl/ability': specifier: ^6.5.0 - version: 6.7.5 + version: 6.8.0 '@copilotkit/react-core': specifier: 1.10.6 - version: 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@copilotkit/react-textarea': specifier: 1.10.6 - version: 1.10.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.10.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@copilotkit/react-ui': specifier: 1.10.6 - version: 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@copilotkit/runtime': specifier: 1.10.6 - version: 1.10.6(c9e743140f0883eda50bf44e0628c06c) + version: 1.10.6(9413ba786a25fdda1f6d87c0f5568c1b) '@dub/analytics': specifier: ^0.0.32 version: 0.0.32 '@hookform/resolvers': specifier: ^3.3.4 - version: 3.10.0(react-hook-form@7.69.0(react@18.3.1)) + version: 3.10.0(react-hook-form@7.71.2(react@18.3.1)) '@langchain/community': specifier: ^0.3.40 - version: 0.3.58(d743da2b976a18eaa6701515aaffafa9) + version: 0.3.59(5e7fc56f823c1fb62b8d797ade7587fc) '@langchain/core': specifier: ^0.3.44 - version: 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + version: 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) '@langchain/langgraph': specifier: ^0.2.63 - version: 0.2.74(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.0(zod@3.25.76)) + version: 0.2.74(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.1(zod@3.25.76)) '@langchain/openai': specifier: ^0.5.5 - version: 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + version: 0.5.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@mantine/core': specifier: ^5.10.5 version: 5.10.5(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@mantine/hooks@5.10.5(react@18.3.1))(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -83,73 +83,73 @@ importers: version: 0.17.10(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(pg-query-stream@4.10.3(pg@8.16.3)) '@modelcontextprotocol/sdk': specifier: ^1.22.0 - version: 1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76) + version: 1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76) '@nest-lab/throttler-storage-redis': specifier: ^1.2.0 - version: 1.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/throttler@6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14))(ioredis@5.8.2)(reflect-metadata@0.1.14) + version: 1.2.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(@nestjs/throttler@6.5.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(reflect-metadata@0.1.14))(ioredis@5.10.0)(reflect-metadata@0.1.14) '@nestjs/cli': specifier: 10.0.2 version: 10.0.2(@swc/cli@0.3.14(@swc/core@1.5.7(@swc/helpers@0.5.13))(chokidar@3.5.3))(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) '@nestjs/common': specifier: ^10.0.2 - version: 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/core': specifier: ^10.0.2 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/microservices': specifier: ^10.3.1 - version: 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 10.4.22(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(cache-manager@7.2.8)(ioredis@5.10.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/platform-express': specifier: ^10.0.2 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22) '@nestjs/schedule': specifier: ^4.0.0 - version: 4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 4.1.2(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22) '@nestjs/swagger': specifier: ^7.3.0 - version: 7.4.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14) + version: 7.4.2(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14) '@nestjs/throttler': specifier: ^6.3.0 - version: 6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14) + version: 6.5.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(reflect-metadata@0.1.14) '@neynar/nodejs-sdk': specifier: ^3.112.0 - version: 3.112.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 3.137.0(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@neynar/react': specifier: ^0.9.7 - version: 0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1)) + version: 0.9.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.58.2)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3)(swr@2.4.1(react@18.3.1)) '@pigment-css/react': specifier: ^0.0.30 version: 0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) '@postiz/wallets': specifier: ^0.0.1 - version: 0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 0.0.1(@babel/runtime@7.28.6)(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@prisma/client': specifier: 6.5.0 version: 6.5.0(prisma@6.5.0(typescript@5.5.4))(typescript@5.5.4) '@sentry/nestjs': specifier: ^10.26.0 - version: 10.32.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + version: 10.40.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22) '@sentry/nextjs': specifier: ^10.26.0 - version: 10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + version: 10.40.0(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3))(react@18.3.1)(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) '@sentry/profiling-node': specifier: ^10.25.0 - version: 10.32.1 + version: 10.40.0 '@sentry/react': specifier: ^10.25.0 - version: 10.32.1(react@18.3.1) + version: 10.40.0(react@18.3.1) '@solana/wallet-adapter-react': specifier: ^0.15.35 - version: 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/wallet-adapter-react-ui': specifier: ^0.9.35 - version: 0.9.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 0.9.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@stripe/react-stripe-js': specifier: ^5.4.1 - version: 5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.6.0(@stripe/stripe-js@8.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@stripe/stripe-js': specifier: ^8.6.0 - version: 8.6.0 + version: 8.8.0 '@swc/helpers': specifier: 0.5.13 version: 0.5.13 @@ -158,64 +158,64 @@ importers: version: 5.0.27 '@tailwindcss/postcss': specifier: ^4.1.7 - version: 4.1.18 + version: 4.2.1 '@temporalio/activity': specifier: ^1.14.0 - version: 1.14.0 + version: 1.15.0 '@temporalio/client': specifier: ^1.14.0 - version: 1.14.0 + version: 1.15.0 '@temporalio/common': specifier: ^1.14.0 - version: 1.14.0 + version: 1.15.0 '@temporalio/worker': specifier: ^1.14.0 - version: 1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12) + version: 1.15.0(@swc/helpers@0.5.13)(esbuild@0.25.12)(tslib@2.8.1) '@temporalio/workflow': specifier: ^1.14.0 - version: 1.14.0 + version: 1.15.0 '@tiptap/extension-bold': specifier: ^3.0.6 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) '@tiptap/extension-document': specifier: ^3.0.6 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) '@tiptap/extension-heading': specifier: ^3.0.7 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) '@tiptap/extension-history': specifier: ^3.0.7 - version: 3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) '@tiptap/extension-link': specifier: ^3.0.9 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) '@tiptap/extension-list': specifier: ^3.0.7 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) '@tiptap/extension-mention': specifier: ^3.0.7 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@tiptap/suggestion@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)(@tiptap/suggestion@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) '@tiptap/extension-paragraph': specifier: ^3.0.6 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) '@tiptap/extension-text': specifier: ^3.0.6 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) '@tiptap/extension-underline': specifier: ^3.0.6 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) '@tiptap/pm': specifier: ^3.0.6 - version: 3.14.0 + version: 3.20.0 '@tiptap/react': specifier: ^3.0.6 - version: 3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.20.0(@floating-ui/dom@1.7.5)(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@tiptap/starter-kit': specifier: ^3.0.6 - version: 3.14.0 + version: 3.20.0 '@tiptap/suggestion': specifier: ^3.0.7 - version: 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + version: 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) '@types/bcrypt': specifier: ^5.0.2 version: 5.0.2 @@ -230,7 +230,7 @@ importers: version: 9.0.10 '@types/lodash': specifier: ^4.14.202 - version: 4.17.21 + version: 4.17.24 '@types/md5': specifier: ^2.3.5 version: 2.3.6 @@ -245,7 +245,7 @@ importers: version: 1.4.13 '@types/nodemailer': specifier: ^6.4.16 - version: 6.4.21 + version: 6.4.23 '@types/parse5': specifier: v6.0.1 version: 6.0.1 @@ -317,7 +317,7 @@ importers: version: 0.5.0 axios: specifier: ^1.7.7 - version: 1.13.2(debug@4.4.3) + version: 1.13.6(debug@4.4.3) bcrypt: specifier: ^5.1.1 version: 5.1.1 @@ -341,10 +341,10 @@ importers: version: 0.5.1 class-validator: specifier: ^0.14.1 - version: 0.14.3 + version: 0.14.4 class-validator-jsonschema: specifier: ^5.1.0 - version: 5.1.0(class-transformer@0.5.1)(class-validator@0.14.3) + version: 5.1.0(class-transformer@0.5.1)(class-validator@0.14.4) clsx: specifier: ^2.1.0 version: 2.1.1 @@ -374,7 +374,7 @@ importers: version: 8.0.0 emoji-picker-react: specifier: ^4.12.0 - version: 4.16.1(react@18.3.1) + version: 4.18.0(react@18.3.1) evp_bytestokey: specifier: ^1.0.3 version: 1.0.3 @@ -383,7 +383,7 @@ importers: version: 21.0.5 fast-xml-parser: specifier: ^4.5.1 - version: 4.5.3 + version: 4.5.4 google-auth-library: specifier: ^9.11.0 version: 9.15.1 @@ -398,10 +398,10 @@ importers: version: 7.14.0 i18next: specifier: ^25.2.1 - version: 25.7.3(typescript@5.5.4) + version: 25.8.13(typescript@5.5.4) i18next-browser-languagedetector: specifier: ^8.1.0 - version: 8.2.0 + version: 8.2.1 i18next-resources-to-backend: specifier: ^1.2.1 version: 1.2.1 @@ -410,7 +410,7 @@ importers: version: 3.0.2 ioredis: specifier: ^5.3.2 - version: 5.8.2 + version: 5.10.0 json-to-graphql-query: specifier: ^2.2.5 version: 2.3.0 @@ -419,10 +419,10 @@ importers: version: 9.0.3 lodash: specifier: ^4.17.21 - version: 4.17.21 + version: 4.17.23 mastra: specifier: ^0.13.2 - version: 0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(hono@4.11.1)(typescript@5.5.4)(zod@3.25.76) + version: 0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(typescript@5.5.4)(zod@3.25.76) md5: specifier: ^2.3.0 version: 2.3.0 @@ -440,19 +440,19 @@ importers: version: 7.14.0 nestjs-command: specifier: ^3.1.4 - version: 3.1.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(yargs@17.7.2) + version: 3.1.5(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(yargs@17.7.2) nestjs-real-ip: specifier: ^3.0.1 - version: 3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)) + version: 3.0.1(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2)) nestjs-temporal-core: specifier: ^3.2.0 - version: 3.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@temporalio/client@1.14.0)(@temporalio/common@1.14.0)(@temporalio/worker@1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12))(@temporalio/workflow@1.14.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) + version: 3.2.3(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(@temporalio/client@1.15.0)(@temporalio/common@1.15.0)(@temporalio/worker@1.15.0(@swc/helpers@0.5.13)(esbuild@0.25.12)(tslib@2.8.1))(@temporalio/workflow@1.15.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) next: specifier: 14.2.35 - version: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) + version: 14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3) next-plausible: specifier: ^3.12.0 - version: 3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 3.12.5(next@14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) node-fetch: specifier: ^3.3.2 version: 3.3.2 @@ -461,13 +461,13 @@ importers: version: 0.66.0(request@2.88.2) nodemailer: specifier: ^7.0.11 - version: 7.0.11 + version: 7.0.13 nostr-tools: specifier: ^2.18.2 - version: 2.19.4(typescript@5.5.4) + version: 2.23.3(typescript@5.5.4) openai: specifier: ^6.2.0 - version: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + version: 6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) p-limit: specifier: ^3.1.0 version: 3.1.0 @@ -476,10 +476,10 @@ importers: version: 6.0.1 polotno: specifier: ^2.10.5 - version: 2.34.1(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 2.36.10(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) posthog-js: specifier: ^1.178.0 - version: 1.309.1 + version: 1.356.1 react: specifier: 18.3.1 version: 18.3.1 @@ -500,22 +500,22 @@ importers: version: 18.3.1(react@18.3.1) react-dropzone: specifier: ^14.3.5 - version: 14.3.8(react@18.3.1) + version: 14.4.1(react@18.3.1) react-hook-form: specifier: ^7.58.1 - version: 7.69.0(react@18.3.1) + version: 7.71.2(react@18.3.1) react-hotkeys-hook: specifier: ^5.1.0 - version: 5.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-i18next: specifier: ^15.5.2 - version: 15.7.4(i18next@25.7.3(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + version: 15.7.4(i18next@25.8.13(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) react-loading: specifier: ^2.0.3 version: 2.0.3(prop-types@15.8.1)(react@18.3.1) react-sortablejs: specifier: ^6.1.4 - version: 6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6) + version: 6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.7) react-tag-autocomplete: specifier: ^7.2.0 version: 7.5.1(react@18.3.1) @@ -548,7 +548,7 @@ importers: version: 7.8.2 sass: specifier: ^1.89.2 - version: 1.97.1 + version: 1.97.3 sha256: specifier: ^0.2.0 version: 0.2.0 @@ -575,7 +575,7 @@ importers: version: 11.4.8 swr: specifier: ^2.2.5 - version: 2.3.8(react@18.3.1) + version: 2.4.1(react@18.3.1) tailwind-scrollbar: specifier: ^3.1.0 version: 3.1.0(tailwindcss@3.4.17(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4))) @@ -605,13 +605,13 @@ importers: version: 1.0.3 twitter-api-v2: specifier: ^1.24.0 - version: 1.28.0 + version: 1.29.0 twitter-text: specifier: ^3.1.0 version: 3.1.0 use-debounce: specifier: ^10.0.0 - version: 10.0.6(react@18.3.1) + version: 10.1.0(react@18.3.1) utf-8-validate: specifier: ^5.0.10 version: 5.0.10 @@ -620,13 +620,13 @@ importers: version: 10.0.0 viem: specifier: ^2.22.9 - version: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) webextension-polyfill: specifier: ^0.12.0 version: 0.12.0 ws: specifier: ^8.18.0 - version: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + version: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) yargs: specifier: ^17.7.2 version: 17.7.2 @@ -638,7 +638,7 @@ importers: version: 3.25.76 zustand: specifier: ^5.0.5 - version: 5.0.9(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) + version: 5.0.11(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)) devDependencies: '@crxjs/vite-plugin': specifier: ^2.0.0-beta.32 @@ -648,10 +648,10 @@ importers: version: 10.2.3(chokidar@3.5.3)(typescript@5.5.4) '@nestjs/testing': specifier: ^10.0.2 - version: 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20) + version: 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22) '@pmmmwh/react-refresh-webpack-plugin': specifier: ^0.5.7 - version: 0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + version: 0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) '@svgr/webpack': specifier: ^8.0.1 version: 8.1.0(typescript@5.5.4) @@ -666,7 +666,7 @@ importers: version: 1.5.7(@swc/helpers@0.5.13) '@tailwindcss/vite': specifier: ^4.0.17 - version: 4.1.18(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) + version: 4.2.1(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)) '@testing-library/react': specifier: 15.0.6 version: 15.0.6(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -690,7 +690,7 @@ importers: version: 18.16.9 '@types/node-telegram-bot-api': specifier: ^0.64.7 - version: 0.64.13 + version: 0.64.14 '@types/react': specifier: 18.3.1 version: 18.3.1 @@ -702,7 +702,7 @@ importers: version: 9.0.8 '@types/webextension-polyfill': specifier: ^0.12.3 - version: 0.12.4 + version: 0.12.5 '@types/yargs': specifier: ^17.0.32 version: 17.0.35 @@ -714,7 +714,7 @@ importers: version: 7.18.0(eslint@8.57.0)(typescript@5.5.4) '@vitejs/plugin-react': specifier: ^4.2.0 - version: 4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) + version: 4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)) '@vitest/coverage-v8': specifier: 1.6.0 version: 1.6.0(vitest@3.1.4) @@ -723,10 +723,10 @@ importers: version: 1.6.0(vitest@3.1.4) autoprefixer: specifier: ^10.4.17 - version: 10.4.23(postcss@8.4.38) + version: 10.4.27(postcss@8.4.38) babel-jest: specifier: 29.7.0 - version: 29.7.0(@babel/core@7.28.5) + version: 29.7.0(@babel/core@7.29.0) cross-env: specifier: ^10.0.0 version: 10.1.0 @@ -774,7 +774,7 @@ importers: version: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) nodemon: specifier: ^3.1.9 - version: 3.1.11 + version: 3.1.14 postcss: specifier: 8.4.38 version: 8.4.38 @@ -789,7 +789,7 @@ importers: version: 0.10.0 ts-jest: specifier: ^29.1.0 - version: 29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4) + version: 29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4) ts-node: specifier: 10.9.2 version: 10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4) @@ -801,13 +801,13 @@ importers: version: 5.5.4 vite: specifier: ^6.3.5 - version: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + version: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) + version: 5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)) vitest: specifier: 3.1.4 - version: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + version: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) apps/backend: {} @@ -844,20 +844,20 @@ packages: '@adraffy/ens-normalize@1.11.1': resolution: {integrity: sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==} - '@ag-ui/client@0.0.42': - resolution: {integrity: sha512-zAbP+sZJImR5bUpR2ni7RtuuNZMuesaxviynyIgzKlr1k2VCM49mFpbDUKU4TH4Cneu+Xe7OEnO8qCOCIzBAww==} + '@ag-ui/client@0.0.46': + resolution: {integrity: sha512-9Bl6GN6N3NWa3Ewqgl8E3nJzo88prIB2LS50bTNgw35h5BxC1UY21c0SImqQWZ+VV5kbhs6AUrriypKEBB7F5A==} '@ag-ui/core@0.0.37': resolution: {integrity: sha512-7bmjPn1Ol0Zo00F+MrPr0eOwH4AFZbhmq/ZMhCsrMILtVYBiBLcLU9QFBpBL3Zm9MCHha8b79N7JE2FzwcMaVA==} - '@ag-ui/core@0.0.42': - resolution: {integrity: sha512-C2hMg4Gs5oiUDgK9cA2RsTwSSmFZdIsqPklDrFw/Ue+quH6EU3vKp5YoOq7nuaQYO4pO8Em+Z+l5/M5PpcvP1g==} + '@ag-ui/core@0.0.46': + resolution: {integrity: sha512-5/gC9n20ImA10LMFLLYKOowqn2Btrr3UYXWGosmLc1+KJqREI0t35NXnwqoKlw7TWySznF1bpwY6uIvMtO/ZUg==} - '@ag-ui/encoder@0.0.42': - resolution: {integrity: sha512-97B5MMCSs82t/y41uk2NrLBYFhbvn4kYsKQHMCfy8tjSWubyxh3zP7N9yHo8zJeSPe3WvzTvclyXNiGxSOsorg==} + '@ag-ui/encoder@0.0.46': + resolution: {integrity: sha512-XU6dTgUOFZsXeO+CxCMNl5R8NCbdUyifWP7sRNIi61Et3F/0d0JotLo1y1/9GMGfsJNnP7bjb4YYsx21R7YMlw==} - '@ag-ui/langgraph@0.0.21': - resolution: {integrity: sha512-QGGbBB4rPnMtCdflhew7KSGdlVgm/2GzpssuUJ//Gld6xJmCK0yzO8heUggIOwNLcabLnKbvAm0vkxKss7rjWg==} + '@ag-ui/langgraph@0.0.24': + resolution: {integrity: sha512-ebTYpUw28fvbmhqbpAbmfsDTfEqm1gSeZaBcnxMGHFivJLCzsJ/C9hYw6aV8yRKV3lMFBwh/QFxn1eRcr7yRkQ==} peerDependencies: '@ag-ui/client': '>=0.0.42' '@ag-ui/core': '>=0.0.42' @@ -871,8 +871,8 @@ packages: '@mastra/core': '>=0.20.1' zod: ^3.25.67 - '@ag-ui/proto@0.0.42': - resolution: {integrity: sha512-NDUwSgMnGEqxZGkWIJ1ge5t3Q7Kiddj360x2JAWaIfv9w+7tDJ0pmgyzf3/SXp605aY2wZiDLBtJ6jKZeg1lFg==} + '@ag-ui/proto@0.0.46': + resolution: {integrity: sha512-+FfVhB1OP5A1+5BrEccQnwfODTbfBRWT3+NVnbW4RDFUDVmO9EUA+XPuO1ZxWcDfziTvQriwm0vNyaXGidSIhw==} '@ai-sdk/anthropic@2.0.23': resolution: {integrity: sha512-ZEBiiv1UhjGjBwUU63pFhLK5LCSlNDb1idY9K1oZHm5/Fda1cuTojf32tOp0opH0RPbPAN/F8fyyNjbU33n9Kw==} @@ -904,8 +904,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/openai@2.0.88': - resolution: {integrity: sha512-LlOf83haeZIiRUH1Zw1oEmqUfw5y54227CvndFoBpIkMJwQDGAB3VARUeOJ6iwAWDJjXSz06GdnEnhRU67Yatw==} + '@ai-sdk/openai@2.0.95': + resolution: {integrity: sha512-2CABPaa1UNh7dPyZUIB/Dc4AbvJioFnmryRx45sx7ezBSOdR0zxG6gbrSd/fZ0GVbptSZeLmF9omu10d/GxmJA==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -922,8 +922,8 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 - '@ai-sdk/provider-utils@3.0.19': - resolution: {integrity: sha512-W41Wc9/jbUVXVwCN/7bWa4IKe8MtxO3EyA0Hfhx6grnmiYlCvpI8neSYWFE0zScXJkgA/YK3BRybzgyiXuu6JA==} + '@ai-sdk/provider-utils@3.0.21': + resolution: {integrity: sha512-veuMwTLxsgh31Jjn0SnBABnM1f7ebHhRWcV2ZuY3hP3iJDCZ8VXBaYqcHXoOQDqUXTCas08sKQcHyWK+zl882Q==} engines: {node: '>=18'} peerDependencies: zod: ^3.25.76 || ^4.1.8 @@ -936,6 +936,10 @@ packages: resolution: {integrity: sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==} engines: {node: '>=18'} + '@ai-sdk/provider@2.0.1': + resolution: {integrity: sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==} + engines: {node: '>=18'} + '@ai-sdk/react@1.2.12': resolution: {integrity: sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==} engines: {node: '>=18'} @@ -1014,32 +1018,26 @@ packages: peerDependencies: '@types/json-schema': ^7.0.15 - '@apm-js-collab/code-transformer@0.8.2': - resolution: {integrity: sha512-YRjJjNq5KFSjDUoqu5pFUWrrsvGOxl6c3bu+uMFc9HNNptZ2rNU/TI2nLw4jnhQNtka972Ee2m3uqbvDQtPeCA==} - - '@apm-js-collab/tracing-hooks@0.3.1': - resolution: {integrity: sha512-Vu1CbmPURlN5fTboVuKMoJjbO5qcq9fA5YXpskx3dXe/zTBvjODFoerw+69rVBlRLrJpwPqSDqEuJDEKIrTldw==} - '@atproto/api@0.15.27': resolution: {integrity: sha512-ok/WGafh1nz4t8pEQGtAF/32x2E2VDWU4af6BajkO5Gky2jp2q6cv6aB2A5yuvNNcc3XkYMYipsqVHVwLPMF9g==} - '@atproto/common-web@0.4.7': - resolution: {integrity: sha512-vjw2+81KPo2/SAbbARGn64Ln+6JTI0FTI4xk8if0ebBfDxFRmHb2oSN1y77hzNq/ybGHqA2mecfhS03pxC5+lg==} + '@atproto/common-web@0.4.17': + resolution: {integrity: sha512-sfxD8NGxyoxhxmM9EUshEFbWcJ3+JHEOZF4Quk6HsCh1UxpHBmLabT/vEsAkDWl+C/8U0ine0+c/gHyE/OZiQQ==} - '@atproto/lex-data@0.0.3': - resolution: {integrity: sha512-ivo1IpY/EX+RIpxPgCf4cPhQo5bfu4nrpa1vJCt8hCm9SfoonJkDFGa0n4SMw4JnXZoUcGcrJ46L+D8bH6GI2g==} + '@atproto/lex-data@0.0.12': + resolution: {integrity: sha512-aekJudcK1p6sbTqUv2bJMJBAGZaOJS0mgDclpK3U6VuBREK/au4B6ffunBFWgrDfg0Vwj2JGyEA7E51WZkJcRw==} - '@atproto/lex-json@0.0.3': - resolution: {integrity: sha512-ZVcY7XlRfdPYvQQ2WroKUepee0+NCovrSXgXURM3Xv+n5jflJCoczguROeRr8sN0xvT0ZbzMrDNHCUYKNnxcjw==} + '@atproto/lex-json@0.0.12': + resolution: {integrity: sha512-XlEpnWWZdDJ5BIgG25GyH+6iBfyrFL18BI5JSE6rUfMObbFMrQRaCuRLQfryRXNysVz3L3U+Qb9y8KcXbE8AcA==} '@atproto/lexicon@0.4.14': resolution: {integrity: sha512-jiKpmH1QER3Gvc7JVY5brwrfo+etFoe57tKPQX/SmPwjvUsFnJAow5xLIryuBaJgFAhnTZViXKs41t//pahGHQ==} - '@atproto/lexicon@0.6.0': - resolution: {integrity: sha512-5veb8aD+J5M0qszLJ+73KSFsFrJBgAY/nM1TSAJvGY7fNc9ZAT+PSUlmIyrdye9YznAZ07yktalls/TwNV7cHQ==} + '@atproto/lexicon@0.6.1': + resolution: {integrity: sha512-/vI1kVlY50Si+5MXpvOucelnYwb0UJ6Qto5mCp+7Q5C+Jtp+SoSykAPVvjVtTnQUH2vrKOFOwpb3C375vSKzXw==} - '@atproto/syntax@0.4.2': - resolution: {integrity: sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==} + '@atproto/syntax@0.4.3': + resolution: {integrity: sha512-YoZUz40YAJr5nPwvCDWgodEOlt5IftZqPJvA0JDWjuZKD8yXddTwSzXSaKQAzGOpuM+/A3uXRtPzJJqlScc+iA==} '@atproto/xrpc@0.7.7': resolution: {integrity: sha512-K1ZyO/BU8JNtXX5dmPp7b5UrkLMMqpsIa/Lrj5D3Su+j1Xwq1m6QJ2XJ1AgjEjkI1v4Muzm7klianLE6XGxtmA==} @@ -1067,204 +1065,204 @@ packages: '@aws-crypto/util@5.2.0': resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} - '@aws-sdk/client-bedrock-agent-runtime@3.956.0': - resolution: {integrity: sha512-Y4u1BE+aoEPOcX2EdPeuAgrrc8YBAG50tUUkEL8VeLA8h7eCO4fxnuazRZNwlq3PksH3rTOHQoXbcRculqVM4w==} - engines: {node: '>=18.0.0'} + '@aws-sdk/client-bedrock-agent-runtime@3.1000.0': + resolution: {integrity: sha512-XnkQvShSdTwkCxVQBo5v0Ozs3jsjAqW1dWnanp/nLXwrS3upSSk+3CjeaT7aDCLVDACA3MfV9IhKP3vNYmTlbw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/client-bedrock-runtime@3.956.0': - resolution: {integrity: sha512-8cD53FBKn7uvc4QZtYZr87KcuSrEFMwS/O3HC+Y7+disgePlTXxtOo0naCj5O1yVZPuU3FCVLGzxFk5fhbzAJg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/client-bedrock-runtime@3.1000.0': + resolution: {integrity: sha512-GA96wgTFB4Z5vhysm+hErbgiEWZ9JqAl09BxARajL7Oanpf0KvdIjxuLp2rD/XqEIks9yG/5Rh9XIAoCUUTZXw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/client-kendra@3.956.0': - resolution: {integrity: sha512-hnIaKXGpxg/yA21HQZRMZiSsVe2MhY1JQ7kGJzn22y+GzvrvsLI6dyD4/NUMHNi9+wp2saLb4kBaNguW2OxzWA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/client-kendra@3.1000.0': + resolution: {integrity: sha512-H9xDUFxlDpJZ5c15zWuK9J5UIQIAYJ7SoJgzC+GqrBAhmJZhO9Kz5jli7DFeDFBUfyMDPnjoeKQxBOPG561z+w==} + engines: {node: '>=20.0.0'} - '@aws-sdk/client-s3@3.956.0': - resolution: {integrity: sha512-O+Z7PSY9TjaqJcZSDMvVmXBuV/jmFRJIu7ga+9XgWv4+qfjhAX2N2s4kgsRnIdjIO4xgkN3O/BugTCyjIRrIDQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/client-s3@3.1000.0': + resolution: {integrity: sha512-7kPy33qNGq3NfwHC0412T6LDK1bp4+eiPzetX0sVd9cpTSXuQDKpoOFnB0Njj6uZjJDcLS3n2OeyarwwgkQ0Ow==} + engines: {node: '>=20.0.0'} - '@aws-sdk/client-ses@3.956.0': - resolution: {integrity: sha512-pot5tKpQcL8EC4MIKicF684FNrVWU6ZUmxdeJpEHb6b0x2B2ggIUGgNEo0nyd7/ga74azKcTmP7rDpE/9Z3yYA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/core@3.973.15': + resolution: {integrity: sha512-AlC0oQ1/mdJ8vCIqu524j5RB7M8i8E24bbkZmya1CuiQxkY7SdIZAyw7NDNMGaNINQFq/8oGRMX0HeOfCVsl/A==} + engines: {node: '>=20.0.0'} - '@aws-sdk/client-sso@3.956.0': - resolution: {integrity: sha512-TCxCa9B1IMILvk/7sig0fRQzff+M2zBQVZGWOJL8SAZq/gfElIMAf/nYjQwMhXjyq8PFDRGm4GN8ZhNKPeNleQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/crc64-nvme@3.972.3': + resolution: {integrity: sha512-UExeK+EFiq5LAcbHm96CQLSia+5pvpUVSAsVApscBzayb7/6dJBJKwV4/onsk4VbWSmqxDMcfuTD+pC4RxgZHg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.956.0': - resolution: {integrity: sha512-BMOCXZNz5z4cR3/SaNHUfeoZQUG/y39bLscdLUgg3RL6mDOhuINIqMc0qc6G3kpwDTLVdXikF4nmx2UrRK9y5A==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-env@3.972.13': + resolution: {integrity: sha512-6ljXKIQ22WFKyIs1jbORIkGanySBHaPPTOI4OxACP5WXgbcR0nDYfqNJfXEGwCK7IzHdNbCSFsNKKs0qCexR8Q==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.956.0': - resolution: {integrity: sha512-aLJavJMPVTvhmggJ0pcdCKEWJk3sL9QkJkUIEoTzOou7HnxWS66N4sC5e8y27AF2nlnYfIxq3hkEiZlGi/vlfA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-http@3.972.15': + resolution: {integrity: sha512-dJuSTreu/T8f24SHDNTjd7eQ4rabr0TzPh2UTCwYexQtzG3nTDKm1e5eIdhiroTMDkPEJeY+WPkA6F9wod/20A==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.956.0': - resolution: {integrity: sha512-VsKzBNhwT6XJdW3HQX6o4KOHj1MAzSwA8/zCsT9mOGecozw1yeCcQPtlWDSlfsfygKVCXz7fiJzU03yl11NKMA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-ini@3.972.13': + resolution: {integrity: sha512-JKSoGb7XeabZLBJptpqoZIFbROUIS65NuQnEHGOpuT9GuuZwag2qciKANiDLFiYk4u8nSrJC9JIOnWKVvPVjeA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.956.0': - resolution: {integrity: sha512-TlDy+IGr0JIRBwnPdV31J1kWXEcfsR3OzcNVWQrguQdHeTw2lU5eft16kdizo6OruqcZRF/LvHBDwAWx4u51ww==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-login@3.972.13': + resolution: {integrity: sha512-RtYcrxdnJHKY8MFQGLltCURcjuMjnaQpAxPE6+/QEdDHHItMKZgabRe/KScX737F9vJMQsmJy9EmMOkCnoC1JQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.956.0': - resolution: {integrity: sha512-p2Y62mdIlUpiyi5tvn8cKTja5kq1e3Rm5gm4wpNQ9caTayfkIEXyKrbP07iepTv60Coaylq9Fx6b5En/siAeGA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-node@3.972.14': + resolution: {integrity: sha512-WqoC2aliIjQM/L3oFf6j+op/enT2i9Cc4UTxxMEKrJNECkq4/PlKE5BOjSYFcq6G9mz65EFbXJh7zOU4CvjSKQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.956.0': - resolution: {integrity: sha512-ITjp7uAQh17ljUsCWkPRmLjyFfupGlJVUfTLHnZJ+c7G0P0PDRquaM+fBSh0y33tauHsBa5fGnCCLRo5hy9sGQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-process@3.972.13': + resolution: {integrity: sha512-rsRG0LQA4VR+jnDyuqtXi2CePYSmfm5GNL9KxiW8DSe25YwJSr06W8TdUfONAC+rjsTI+aIH2rBGG5FjMeANrw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.956.0': - resolution: {integrity: sha512-wpAex+/LGVWkHPchsn9FWy1ahFualIeSYq3ADFc262ljJjrltOWGh3+cu3OK3gTMkX6VEsl+lFvy1P7Bk7cgXA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-sso@3.972.13': + resolution: {integrity: sha512-fr0UU1wx8kNHDhTQBXioc/YviSW8iXuAxHvnH7eQUtn8F8o/FU3uu6EUMvAQgyvn7Ne5QFnC0Cj0BFlwCk+RFw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.956.0': - resolution: {integrity: sha512-IRFSDF32x8TpOEYSGMcGQVJUiYuJaFkek0aCjW0klNIZHBF1YpflVpUarK9DJe4v4ryfVq3c0bqR/JFui8QFmw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/credential-provider-web-identity@3.972.13': + resolution: {integrity: sha512-a6iFMh1pgUH0TdcouBppLJUfPM7Yd3R9S1xFodPtCRoLqCz2RQFA3qjA8x4112PVYXEd4/pHX2eihapq39w0rA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.956.0': - resolution: {integrity: sha512-4YkmjwZC+qoUKlVOY9xNx7BTKRdJ1R1/Zjk2QSW5aWtwkk2e07ZUQvUpbW4vGpAxGm1K4EgRcowuSpOsDTh44Q==} - engines: {node: '>=18.0.0'} + '@aws-sdk/eventstream-handler-node@3.972.9': + resolution: {integrity: sha512-mKPiiVssgFDWkAXdEDh8+wpr2pFSX/fBn2onXXnrfIAYbdZhYb4WilKbZ3SJMUnQi+Y48jZMam5J0RrgARluaA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/eventstream-handler-node@3.956.0': - resolution: {integrity: sha512-OdnzsiCyMcK9fkMI3II7+q8qu3hY5iK4coV8VjXI5u05UEbg3iosQynlkv0FXztSodPYYwnuZ0lFtIthsUy0Tw==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-bucket-endpoint@3.972.6': + resolution: {integrity: sha512-3H2bhvb7Cb/S6WFsBy/Dy9q2aegC9JmGH1inO8Lb2sWirSqpLJlZmvQHPE29h2tIxzv6el/14X/tLCQ8BQU6ZQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-bucket-endpoint@3.956.0': - resolution: {integrity: sha512-+iHH9cnkNZgKkTBnPP9rbapHliKDrOuj7MDz6+wL0NV4N/XGB5tbrd+uDP608FXVeMHcWIIZtWkANADUmAI49w==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-eventstream@3.972.6': + resolution: {integrity: sha512-mB2+3G/oxRC+y9WRk0KCdradE2rSfxxJpcOSmAm+vDh3ex3WQHVLZ1catNIe1j5NQ+3FLBsNMRPVGkZ43PRpjw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-eventstream@3.956.0': - resolution: {integrity: sha512-xBhNmBCJxB4ho7ATmhniv3PK3qN5ZEgknUI+7XTM/cnQBzuYG5twAQSkGdInzEjygTSTmKpkdBVh67AxKTotAQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-expect-continue@3.972.6': + resolution: {integrity: sha512-QMdffpU+GkSGC+bz6WdqlclqIeCsOfgX8JFZ5xvwDtX+UTj4mIXm3uXu7Ko6dBseRcJz1FA6T9OmlAAY6JgJUg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-expect-continue@3.956.0': - resolution: {integrity: sha512-97rmalK9x09Darcl6AbShZRXYxWiyCeO8ll1C9rx1xyZMs2DeIKAZ/xuAJ/bywB3l25ls6VqXO4/EuDFJHL8eA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-flexible-checksums@3.973.1': + resolution: {integrity: sha512-QLXsxsI6VW8LuGK+/yx699wzqP/NMCGk/hSGP+qtB+Lcff+23UlbahyouLlk+nfT7Iu021SkXBhnAuVd6IZcPw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-flexible-checksums@3.956.0': - resolution: {integrity: sha512-Rd/VeVKuw+lQ1oJmJOyXV0flIkp9ouMGAS9QT28ogdQVxXriaheNo754N4z0+8+R6uTcmeojN7dN4jzt51WV2g==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-host-header@3.972.6': + resolution: {integrity: sha512-5XHwjPH1lHB+1q4bfC7T8Z5zZrZXfaLcjSMwTd1HPSPrCmPFMbg3UQ5vgNWcVj0xoX4HWqTGkSf2byrjlnRg5w==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-host-header@3.956.0': - resolution: {integrity: sha512-JujNJDp/dj1DbsI0ntzhrz2uJ4jpumcKtr743eMpEhdboYjuu/UzY8/7n1h5JbgU9TNXgqE9lgQNa5QPG0Tvsg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-location-constraint@3.972.6': + resolution: {integrity: sha512-XdZ2TLwyj3Am6kvUc67vquQvs6+D8npXvXgyEUJAdkUDx5oMFJKOqpK+UpJhVDsEL068WAJl2NEGzbSik7dGJQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-location-constraint@3.956.0': - resolution: {integrity: sha512-eANhYRFcVO/lI9tliitSW0DK5H1d9J7BK/9RrRz86bd5zPWteVqqzQRbMUdErVi1nwSbSIAa6YGv/ItYPswe0w==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-logger@3.972.6': + resolution: {integrity: sha512-iFnaMFMQdljAPrvsCVKYltPt2j40LQqukAbXvW7v0aL5I+1GO7bZ/W8m12WxW3gwyK5p5u1WlHg8TSAizC5cZw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-logger@3.956.0': - resolution: {integrity: sha512-Qff39yEOPYgRsm4SrkHOvS0nSoxXILYnC8Akp0uMRi2lOcZVyXL3WCWqIOtI830qVI4GPa796sleKguxx50RHg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-recursion-detection@3.972.6': + resolution: {integrity: sha512-dY4v3of5EEMvik6+UDwQ96KfUFDk8m1oZDdkSc5lwi4o7rFrjnv0A+yTV+gu230iybQZnKgDLg/rt2P3H+Vscw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-recursion-detection@3.956.0': - resolution: {integrity: sha512-/f4JxL2kSCYhy63wovqts6SJkpalSLvuFe78ozt3ClrGoHGyr69o7tPRYx5U7azLgvrIGjsWUyTayeAk3YHIVQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-sdk-s3@3.972.15': + resolution: {integrity: sha512-WDLgssevOU5BFx1s8jA7jj6cE5HuImz28sy9jKOaVtz0AW1lYqSzotzdyiybFaBcQTs5zxXOb2pUfyMxgEKY3Q==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-sdk-s3@3.956.0': - resolution: {integrity: sha512-U/+jYb4iowqqpLjB6cSYan0goAMOlh2xg2CPIdSy550o8mYnJtuajBUQ20A9AA9PYKLlEAoCNEysNZkn4o/63g==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-ssec@3.972.6': + resolution: {integrity: sha512-acvMUX9jF4I2Ew+Z/EA6gfaFaz9ehci5wxBmXCZeulLuv8m+iGf6pY9uKz8TPjg39bdAz3hxoE0eLP8Qz+IYlA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-ssec@3.956.0': - resolution: {integrity: sha512-1Et0vPoIzfhkUAdNRzu0pC25ZawFqXo5T8xpvbwkfDgfIkeVj+sm9t01iXO3pCOK52OSuLRAy7fiAo/AoHjOYg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/middleware-user-agent@3.972.15': + resolution: {integrity: sha512-ABlFVcIMmuRAwBT+8q5abAxOr7WmaINirDJBnqGY5b5jSDo00UMlg/G4a0xoAgwm6oAECeJcwkvDlxDwKf58fQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-user-agent@3.956.0': - resolution: {integrity: sha512-azH8OJ0AIe3NafaTNvJorG/ALaLNTYwVKtyaSeQKOvaL8TNuBVuDnM5iHCiWryIaRgZotomqycwyfNKLw2D3JQ==} - engines: {node: '>=18.0.0'} - - '@aws-sdk/middleware-websocket@3.956.0': - resolution: {integrity: sha512-yH8D1z5stLDPZPXoDsgzyMIwziMUj6v5riHARJ4cECJBtdREJwDmp56c+iCzkhvtWKqeL/viAlj4Kwe2fAmTxw==} + '@aws-sdk/middleware-websocket@3.972.10': + resolution: {integrity: sha512-uNqRpbL6djE+XXO4cQ+P8ra37cxNNBP+2IfkVOXu1xFdGMfW+uOTxBQuDPpP43i40PBRBXK5un79l/oYpbzYkA==} engines: {node: '>= 14.0.0'} - '@aws-sdk/nested-clients@3.956.0': - resolution: {integrity: sha512-GHDQMkxoWpi3eTrhWGmghw0gsZJ5rM1ERHfBFhlhduCdtV3TyhKVmDgFG84KhU8v18dcVpSp3Pu3KwH7j1tgIg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/nested-clients@3.996.3': + resolution: {integrity: sha512-AU5TY1V29xqwg/MxmA2odwysTez+ccFAhmfRJk+QZT5HNv90UTA9qKd1J9THlsQkvmH7HWTEV1lDNxkQO5PzNw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/region-config-resolver@3.956.0': - resolution: {integrity: sha512-byU5XYekW7+rZ3e067y038wlrpnPkdI4fMxcHCHrv+TAfzl8CCk5xLyzerQtXZR8cVPVOXuaYWe1zKW0uCnXUA==} - engines: {node: '>=18.0.0'} + '@aws-sdk/region-config-resolver@3.972.6': + resolution: {integrity: sha512-Aa5PusHLXAqLTX1UKDvI3pHQJtIsF7Q+3turCHqfz/1F61/zDMWfbTC8evjhrrYVAtz9Vsv3SJ/waSUeu7B6gw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/s3-request-presigner@3.956.0': - resolution: {integrity: sha512-LoqSwHIIYobnly7E6+8ASwlGT1qAAnBzW1xPUlA4gD9ePiV9L9TXwxRwW3WXeaLLQrR3+bi2nXiDxYEiiTsrow==} - engines: {node: '>=18.0.0'} + '@aws-sdk/s3-request-presigner@3.1000.0': + resolution: {integrity: sha512-DP6EbwCD0CKzBwBnT1X6STB5i+bY765CxjMbWCATDhCgOB343Q6AHM9c1S/300Uc5waXWtI/Wdeak9Ru56JOvg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/signature-v4-multi-region@3.956.0': - resolution: {integrity: sha512-gejlXPmor08VydGC8bx0Bv4/tPT92eK0WLe2pUPR0AaMXL+5ycDpThAi1vLWjWr0aUjCA7lXx0pMENWlJlYK3A==} - engines: {node: '>=18.0.0'} + '@aws-sdk/signature-v4-multi-region@3.996.3': + resolution: {integrity: sha512-gQYI/Buwp0CAGQxY7mR5VzkP56rkWq2Y1ROkFuXh5XY94DsSjJw62B3I0N0lysQmtwiL2ht2KHI9NylM/RP4FA==} + engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.956.0': - resolution: {integrity: sha512-I01Q9yDeG9oXge14u/bubtSdBpok/rTsPp2AQwy5xj/5PatRTHPbUTP6tef3AH/lFCAqkI0nncIcgx6zikDdUQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/token-providers@3.1000.0': + resolution: {integrity: sha512-eOI+8WPtWpLdlYBGs8OCK3k5uIMUHVsNG3AFO4kaRaZcKReJ/2OO6+2O2Dd/3vTzM56kRjSKe7mBOCwa4PdYqg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/types@3.956.0': - resolution: {integrity: sha512-DMRU/p9wAlAJxEjegnLwduCA8YP2pcT/sIJ+17KSF38c5cC6CbBhykwbZLECTo+zYzoFrOqeLbqE6paH8Gx3ug==} - engines: {node: '>=18.0.0'} + '@aws-sdk/token-providers@3.999.0': + resolution: {integrity: sha512-cx0hHUlgXULfykx4rdu/ciNAJaa3AL5xz3rieCz7NKJ68MJwlj3664Y8WR5MGgxfyYJBdamnkjNSx5Kekuc0cg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-arn-parser@3.953.0': - resolution: {integrity: sha512-9hqdKkn4OvYzzaLryq2xnwcrPc8ziY34i9szUdgBfSqEC6pBxbY9/lLXmrgzfwMSL2Z7/v2go4Od0p5eukKLMQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.973.4': + resolution: {integrity: sha512-RW60aH26Bsc016Y9B98hC0Plx6fK5P2v/iQYwMzrSjiDh1qRMUCP6KrXHYEHe3uFvKiOC93Z9zk4BJsUi6Tj1Q==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-endpoints@3.956.0': - resolution: {integrity: sha512-xZ5CBoubS4rs9JkFniKNShDtfqxaMUnwaebYMoybZm070q9+omFkQkJYXl7kopTViEgZgQl1sAsAkrawBM8qEQ==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-arn-parser@3.972.2': + resolution: {integrity: sha512-VkykWbqMjlSgBFDyrY3nOSqupMc6ivXuGmvci6Q3NnLq5kC+mKQe2QBZ4nrWRE/jqOxeFP2uYzLtwncYYcvQDg==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-format-url@3.956.0': - resolution: {integrity: sha512-Piap0XvvmZMtCjeCStuAG/Meq7/U5SR3X+ZDduRYMKlkNtkLcc98e9Sih5AThIJLUdffRS/M+gQRiWvc1sm1ww==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-endpoints@3.996.3': + resolution: {integrity: sha512-yWIQSNiCjykLL+ezN5A+DfBb1gfXTytBxm57e64lYmwxDHNmInYHRJYYRAGWG1o77vKEiWaw4ui28e3yb1k5aQ==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-locate-window@3.953.0': - resolution: {integrity: sha512-mPxK+I1LcrgC/RSa3G5AMAn8eN2Ay0VOgw8lSRmV1jCtO+iYvNeCqOdxoJUjOW6I5BA4niIRWqVORuRP07776Q==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-format-url@3.972.6': + resolution: {integrity: sha512-0YNVNgFyziCejXJx0rzxPiD2rkxTWco4c9wiMF6n37Tb9aQvIF8+t7GyEyIFCwQHZ0VMQaAl+nCZHOYz5I5EKw==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-browser@3.956.0': - resolution: {integrity: sha512-s8KwYR3HqiGNni7a1DN2P3RUog64QoBQ6VCSzJkHBWb6++8KSOpqeeDkfmEz+22y1LOne+bRrpDGKa0aqOc3rQ==} + '@aws-sdk/util-locate-window@3.965.4': + resolution: {integrity: sha512-H1onv5SkgPBK2P6JR2MjGgbOnttoNzSPIRoeZTNPZYyaplwGg50zS3amXvXqF0/qfXpWEC9rLWU564QTB9bSog==} + engines: {node: '>=20.0.0'} - '@aws-sdk/util-user-agent-node@3.956.0': - resolution: {integrity: sha512-H0r6ol3Rr63/3xvrUsLqHps+cA7VkM7uCU5NtuTHnMbv3uYYTKf9M2XFHAdVewmmRgssTzvqemrARc8Ji3SNvg==} - engines: {node: '>=18.0.0'} + '@aws-sdk/util-user-agent-browser@3.972.6': + resolution: {integrity: sha512-Fwr/llD6GOrFgQnKaI2glhohdGuBDfHfora6iG9qsBBBR8xv1SdCSwbtf5CWlUdCw5X7g76G/9Hf0Inh0EmoxA==} + + '@aws-sdk/util-user-agent-node@3.973.0': + resolution: {integrity: sha512-A9J2G4Nf236e9GpaC1JnA8wRn6u6GjnOXiTwBLA6NUJhlBTIGfrTy+K1IazmF8y+4OFdW3O5TZlhyspJMqiqjA==} + engines: {node: '>=20.0.0'} peerDependencies: aws-crt: '>=1.0.0' peerDependenciesMeta: aws-crt: optional: true - '@aws-sdk/xml-builder@3.956.0': - resolution: {integrity: sha512-x/IvXUeQYNUEQojpRIQpFt4X7XGxqzjUlXFRdwaTCtTz3q1droXVJvYOhnX3KiMgzeHGlBJfY4Nmq3oZNEUGFw==} + '@aws-sdk/xml-builder@3.972.8': + resolution: {integrity: sha512-Ql8elcUdYCha83Ol7NznBsgN5GVZnv3vUd86fEc6waU6oUdY0T1O9NODkEEOS/Uaogr87avDrUC6DSeM4oXjZg==} + engines: {node: '>=20.0.0'} + + '@aws/lambda-invoke-store@0.2.3': + resolution: {integrity: sha512-oLvsaPMTBejkkmHhjf09xTgk71mOqyr/409NKhRIL08If7AhVfUsJhVsx386uJaqNd42v9kWamQ9lFbkoC2dYw==} engines: {node: '>=18.0.0'} - '@aws/lambda-invoke-store@0.2.2': - resolution: {integrity: sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg==} - engines: {node: '>=18.0.0'} - - '@babel/code-frame@7.27.1': - resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.5': - resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.2': - resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.5': - resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} + '@babel/helper-create-class-features-plugin@7.28.6': + resolution: {integrity: sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1275,8 +1273,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.5': - resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + '@babel/helper-define-polyfill-provider@0.6.6': + resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -1288,12 +1286,12 @@ packages: resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.27.1': - resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1302,8 +1300,8 @@ packages: resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.27.1': - resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} engines: {node: '>=6.9.0'} '@babel/helper-remap-async-to-generator@7.27.1': @@ -1312,8 +1310,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-replace-supers@7.27.1': - resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + '@babel/helper-replace-supers@7.28.6': + resolution: {integrity: sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1334,16 +1332,16 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.28.3': - resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} + '@babel/helper-wrap-function@7.28.6': + resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.4': - resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.5': - resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} hasBin: true @@ -1371,8 +1369,8 @@ packages: peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': - resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6': + resolution: {integrity: sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1404,14 +1402,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-assertions@7.27.1': - resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + '@babel/plugin-syntax-import-assertions@7.28.6': + resolution: {integrity: sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.27.1': - resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + '@babel/plugin-syntax-import-attributes@7.28.6': + resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1426,8 +1424,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.27.1': - resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + '@babel/plugin-syntax-jsx@7.28.6': + resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1474,8 +1472,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.27.1': - resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + '@babel/plugin-syntax-typescript@7.28.6': + resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1492,14 +1490,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.28.0': - resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.27.1': - resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1510,32 +1508,32 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.5': - resolution: {integrity: sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==} + '@babel/plugin-transform-block-scoping@7.28.6': + resolution: {integrity: sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-properties@7.27.1': - resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + '@babel/plugin-transform-class-properties@7.28.6': + resolution: {integrity: sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.28.3': - resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} + '@babel/plugin-transform-class-static-block@7.28.6': + resolution: {integrity: sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.28.4': - resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} + '@babel/plugin-transform-classes@7.28.6': + resolution: {integrity: sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-computed-properties@7.27.1': - resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + '@babel/plugin-transform-computed-properties@7.28.6': + resolution: {integrity: sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1546,8 +1544,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-dotall-regex@7.27.1': - resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + '@babel/plugin-transform-dotall-regex@7.28.6': + resolution: {integrity: sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1558,8 +1556,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': - resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1570,14 +1568,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-explicit-resource-management@7.28.0': - resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} + '@babel/plugin-transform-explicit-resource-management@7.28.6': + resolution: {integrity: sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.28.5': - resolution: {integrity: sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==} + '@babel/plugin-transform-exponentiation-operator@7.28.6': + resolution: {integrity: sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1600,8 +1598,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-json-strings@7.27.1': - resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + '@babel/plugin-transform-json-strings@7.28.6': + resolution: {integrity: sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1612,8 +1610,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.28.5': - resolution: {integrity: sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==} + '@babel/plugin-transform-logical-assignment-operators@7.28.6': + resolution: {integrity: sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1630,14 +1628,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-commonjs@7.27.1': - resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + '@babel/plugin-transform-modules-commonjs@7.28.6': + resolution: {integrity: sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.28.5': - resolution: {integrity: sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==} + '@babel/plugin-transform-modules-systemjs@7.29.0': + resolution: {integrity: sha512-PrujnVFbOdUpw4UHiVwKvKRLMMic8+eC0CuNlxjsyZUiBjhFdPsewdXCkveh2KqBA9/waD0W1b4hXSOBQJezpQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1648,8 +1646,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': - resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0': + resolution: {integrity: sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1660,20 +1658,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': - resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6': + resolution: {integrity: sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-numeric-separator@7.27.1': - resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + '@babel/plugin-transform-numeric-separator@7.28.6': + resolution: {integrity: sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.28.4': - resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} + '@babel/plugin-transform-object-rest-spread@7.28.6': + resolution: {integrity: sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1684,14 +1682,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-catch-binding@7.27.1': - resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + '@babel/plugin-transform-optional-catch-binding@7.28.6': + resolution: {integrity: sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.28.5': - resolution: {integrity: sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==} + '@babel/plugin-transform-optional-chaining@7.28.6': + resolution: {integrity: sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1702,14 +1700,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-methods@7.27.1': - resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + '@babel/plugin-transform-private-methods@7.28.6': + resolution: {integrity: sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-private-property-in-object@7.27.1': - resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + '@babel/plugin-transform-private-property-in-object@7.28.6': + resolution: {integrity: sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1750,8 +1748,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx@7.27.1': - resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==} + '@babel/plugin-transform-react-jsx@7.28.6': + resolution: {integrity: sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1762,14 +1760,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.28.4': - resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} + '@babel/plugin-transform-regenerator@7.29.0': + resolution: {integrity: sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regexp-modifiers@7.27.1': - resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + '@babel/plugin-transform-regexp-modifiers@7.28.6': + resolution: {integrity: sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1786,8 +1784,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-spread@7.27.1': - resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + '@babel/plugin-transform-spread@7.28.6': + resolution: {integrity: sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1810,8 +1808,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.5': - resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + '@babel/plugin-transform-typescript@7.28.6': + resolution: {integrity: sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1822,8 +1820,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-property-regex@7.27.1': - resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + '@babel/plugin-transform-unicode-property-regex@7.28.6': + resolution: {integrity: sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1834,14 +1832,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-unicode-sets-regex@7.27.1': - resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + '@babel/plugin-transform-unicode-sets-regex@7.28.6': + resolution: {integrity: sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.28.5': - resolution: {integrity: sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==} + '@babel/preset-env@7.29.0': + resolution: {integrity: sha512-fNEdfc0yi16lt6IZo2Qxk3knHVdfMYX33czNb4v8yWhemoBhibCpQK/uYHtSKIiO+p/zd3+8fYVXhQdOVV608w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1863,27 +1861,27 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.2': - resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.5': - resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.5': - resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@blueprintjs/colors@5.1.12': - resolution: {integrity: sha512-7GQWUQ82eLE1te++DC8fRO2B31bsSwia82NLamZfKgjHY9V4zxafMT1DK5gKlmmy0nCjpdcCc+df4aVZMHGLww==} + '@blueprintjs/colors@5.1.14': + resolution: {integrity: sha512-Ak6NpUBc0nFpWxucYe7GgMwdcrlARX7yfSPxt4va7z2IM05peNh8OOZ2jQij5+sIgU6IoIkgILAqlQ8nNRhWww==} '@blueprintjs/core@5.19.0': resolution: {integrity: sha512-wgQUNX92ffT1A358BeAseRe/MsyYW84U5xX6vo6UV45vHj18S/gOvqOZ1qew2xtAWudAFsxB8jqmrgJRwFsDqg==} @@ -1916,11 +1914,11 @@ packages: '@types/react': optional: true - '@borewit/text-codec@0.1.1': - resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} + '@borewit/text-codec@0.2.1': + resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} - '@browserbasehq/sdk@2.6.0': - resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} + '@browserbasehq/sdk@2.7.0': + resolution: {integrity: sha512-1iwuj3fChplMq+S66M9tGb9ZXA4e7Vi8MjqQQ6/T6rzoAWLGfDnEAPbgTOU479o+Mi3of5/6YXk1oIHKTw0NBw==} '@browserbasehq/stagehand@1.14.0': resolution: {integrity: sha512-Hi/EzgMFWz+FKyepxHTrqfTPjpsuBS4zRy3e9sbMpBgLPv+9c0R+YZEvS7Bw4mTS66QtvvURRT6zgDGFotthVQ==} @@ -1931,14 +1929,14 @@ packages: openai: ^4.62.1 zod: ^3.23.8 - '@bufbuild/protobuf@2.10.2': - resolution: {integrity: sha512-uFsRXwIGyu+r6AMdz+XijIIZJYpoWeYzILt5yZ2d3mCjQrWUTVpVD9WL/jZAbvp+Ed04rOhrsk7FiTcEDseB5A==} + '@bufbuild/protobuf@2.11.0': + resolution: {integrity: sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==} - '@cacheable/utils@2.3.2': - resolution: {integrity: sha512-8kGE2P+HjfY8FglaOiW+y8qxcaQAfAhVML+i66XJR3YX5FtyDqn6Txctr3K2FrbxLKixRRYYBWMbuGciOhYNDg==} + '@cacheable/utils@2.4.0': + resolution: {integrity: sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==} - '@casl/ability@6.7.5': - resolution: {integrity: sha512-NaOHPi9JMn8Kesh+GRkjNKAYkl4q8qMFAlqw7w2yrE+cBQZSbV9GkBGKvgzs3CdzEc5Yl1cn3JwDxxbBN5gjog==} + '@casl/ability@6.8.0': + resolution: {integrity: sha512-Ipt4mzI4gSgnomFdaPjaLgY2MWuXqAEZLrU6qqWBB7khGiBBuuEp6ytYDnq09bRXqcjaeeHiaCvCGFbBA2SpvA==} '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} @@ -2000,18 +1998,21 @@ packages: peerDependencies: '@cypress/request': ^3.0.0 - '@cypress/request@3.0.9': - resolution: {integrity: sha512-I3l7FdGRXluAS44/0NguwWlO83J18p0vlr2FYHrJkWdNYhgVoiYo61IXPqaOsL+vNxU1ZqMACzItGK3/KKDsdw==} + '@cypress/request@3.0.10': + resolution: {integrity: sha512-hauBrOdvu08vOsagkZ/Aju5XuiZx6ldsLfByg1htFeldhex+PeMrYauANzFsMJeAA0+dyPLbDoX2OYuvVoLDkQ==} engines: {node: '>= 6'} + '@datastructures-js/deque@1.0.8': + resolution: {integrity: sha512-PSBhJ2/SmeRPRHuBv7i/fHWIdSC3JTyq56qb+Rq0wjOagi0/fdV5/B/3Md5zFZus/W6OkSPMaxMKKMNMrSmubg==} + '@dub/analytics@0.0.32': resolution: {integrity: sha512-jmZrgbArOX08/kz+hi1s9ggt0k64WzRErt6jM6TfuqUHlXQYNNNRcefvKrmd9vwmbvNGR/GxZnYeyco6Zyw4fg==} - '@emnapi/core@1.7.1': - resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + '@emnapi/core@1.8.1': + resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + '@emnapi/runtime@1.8.1': + resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -2073,8 +2074,8 @@ packages: '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - '@envelop/core@5.4.0': - resolution: {integrity: sha512-/1fat63pySE8rw/dZZArEVytLD90JApY85deDJ0/34gm+yhQ3k70CloSUevxoOE4YCGveG3s9SJJfQeeB4NAtQ==} + '@envelop/core@5.5.1': + resolution: {integrity: sha512-3DQg8sFskDo386TkL5j12jyRAdip/8yzK3x7YGbZBgobZ4aKXrvDU0GppU0SnmrpQnNaiTUsxBs9LKkwQ/eyvw==} engines: {node: '>=18.0.0'} '@envelop/instrumentation@1.0.0': @@ -2094,8 +2095,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -2106,8 +2107,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -2118,8 +2119,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -2130,8 +2131,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -2142,8 +2143,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -2154,8 +2155,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -2166,8 +2167,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -2178,8 +2179,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -2190,8 +2191,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -2202,8 +2203,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -2214,8 +2215,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -2226,8 +2227,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -2238,8 +2239,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -2250,8 +2251,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -2262,8 +2263,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -2274,8 +2275,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -2286,8 +2287,8 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] @@ -2298,8 +2299,8 @@ packages: cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -2310,8 +2311,8 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] @@ -2322,8 +2323,8 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -2334,8 +2335,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -2346,8 +2347,8 @@ packages: cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] @@ -2358,8 +2359,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -2370,8 +2371,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -2382,8 +2383,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -2394,14 +2395,14 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.9.0': - resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -2436,11 +2437,16 @@ packages: '@fastify/busboy@3.2.0': resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} - '@floating-ui/core@1.7.3': - resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + '@fastify/otel@0.16.0': + resolution: {integrity: sha512-2304BdM5Q/kUvQC9qJO1KZq3Zn1WWsw+WWkVmFEaj1UE2hEIiuFqrPeglQOwEtw/ftngisqfQ3v70TWMmwhhHA==} + peerDependencies: + '@opentelemetry/api': ^1.9.0 - '@floating-ui/dom@1.7.4': - resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/core@1.7.4': + resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==} + + '@floating-ui/dom@1.7.5': + resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==} '@floating-ui/react-dom@1.3.0': resolution: {integrity: sha512-htwHm67Ji5E/pROEAr7f8IKFShuiCKHwUC/UY4vC3I5jiSvGFAYnSYiZO5MlGmads+QqvUkR9ANHEguGrDv72g==} @@ -2448,8 +2454,8 @@ packages: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/react-dom@2.1.6': - resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + '@floating-ui/react-dom@2.1.7': + resolution: {integrity: sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -2478,20 +2484,20 @@ packages: '@fractalwagmi/solana-wallet-adapter@0.1.1': resolution: {integrity: sha512-oTZLEuD+zLKXyhZC5tDRMPKPj8iaxKLxXiCjqRfOo4xmSbS2izGRWLJbKMYYsJysn/OI3UJ3P6CWP8WUWi0dZg==} - '@graphql-tools/executor@1.5.0': - resolution: {integrity: sha512-3HzAxfexmynEWwRB56t/BT+xYKEYLGPvJudR1jfs+XZX8bpfqujEhqVFoxmkpEE8BbFcKuBNoQyGkTi1eFJ+hA==} + '@graphql-tools/executor@1.5.1': + resolution: {integrity: sha512-n94Qcu875Mji9GQ52n5UbgOTxlgvFJicBPYD+FRks9HKIQpdNPjkkrKZUYNG51XKa+bf03rxNflm4+wXhoHHrA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/merge@9.1.6': - resolution: {integrity: sha512-bTnP+4oom4nDjmkS3Ykbe+ljAp/RIiWP3R35COMmuucS24iQxGLa9Hn8VMkLIoaoPxgz6xk+dbC43jtkNsFoBw==} + '@graphql-tools/merge@9.1.7': + resolution: {integrity: sha512-Y5E1vTbTabvcXbkakdFUt4zUIzB1fyaEnVmIWN0l0GMed2gdD01TpZWLUm4RNAxpturvolrb24oGLQrBbPLSoQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - '@graphql-tools/schema@10.0.30': - resolution: {integrity: sha512-yPXU17uM/LR90t92yYQqn9mAJNOVZJc0nQtYeZyZeQZeQjwIGlTubvvoDL0fFVk+wZzs4YQOgds2NwSA4npodA==} + '@graphql-tools/schema@10.0.31': + resolution: {integrity: sha512-ZewRgWhXef6weZ0WiP7/MV47HXiuFbFpiDUVLQl6mgXsWSsGELKFxQsyUCBos60Qqy1JEFAIu3Ns6GGYjGkqkQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -2502,6 +2508,12 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-tools/utils@11.0.0': + resolution: {integrity: sha512-bM1HeZdXA2C3LSIeLOnH/bcqSgbQgKEDrjxODjqi3y58xai2TkNrtYcQSoWzGbt9VMN1dORGjR7Vem8SPnUFQA==} + engines: {node: '>=16.0.0'} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@graphql-typed-document-node/core@3.2.0': resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -2542,8 +2554,8 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - '@hono/node-server@1.19.7': - resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} + '@hono/node-server@1.19.9': + resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -2566,9 +2578,9 @@ packages: resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead - '@ibm-cloud/watsonx-ai@1.7.5': - resolution: {integrity: sha512-j9iQ7cuMbE3fYF+midMDMG16ziZLVZa0IMO3kd2HUFMjIxJ7JN6O29WSt9KaOeDAWRMb7wkN1jcdMcYWpFtytw==} - engines: {node: '>=18.0.0'} + '@ibm-cloud/watsonx-ai@1.7.8': + resolution: {integrity: sha512-UpU/hTHRrCwzkqV1+H/CGbHIGRKty6SX1Aea2CBbyRonsEPIPQtFfhz8FHhs+o6Ca/TCupHNlcLAQUFektZmEQ==} + engines: {node: '>=20.0.0'} '@icons/material@0.2.4': resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} @@ -2680,25 +2692,20 @@ packages: cpu: [x64] os: [win32] - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + '@inquirer/core@6.0.0': + resolution: {integrity: sha512-fKi63Khkisgda3ohnskNf5uZJj+zXOaBvOllHsOkdsXRA/ubQLJQrZchFFi57NKbZzkTunXiBMdvWOv71alonw==} + engines: {node: '>=14.18.0'} + + '@inquirer/select@1.3.3': + resolution: {integrity: sha512-RzlRISXWqIKEf83FDC9ZtJ3JvuK1l7aGpretf41BCWYrvla2wU8W8MTRNMiPrPJ+1SIqrRC1nZdZ60hD9hRXLg==} + engines: {node: '>=14.18.0'} + + '@inquirer/type@1.5.5': + resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - '@ioredis/commands@1.4.0': - resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} - - '@isaacs/balanced-match@4.0.1': - resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} - engines: {node: 20 || >=22} - - '@isaacs/brace-expansion@5.0.0': - resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} - engines: {node: 20 || >=22} + '@ioredis/commands@1.5.1': + resolution: {integrity: sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw==} '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -2820,36 +2827,120 @@ packages: peerDependencies: tslib: '2' + '@jsonjoy.com/base64@17.67.0': + resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@jsonjoy.com/buffers@1.2.1': resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' + '@jsonjoy.com/buffers@17.67.0': + resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@jsonjoy.com/codegen@1.0.0': resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' + '@jsonjoy.com/codegen@17.67.0': + resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-core@4.56.10': + resolution: {integrity: sha512-PyAEA/3cnHhsGcdY+AmIU+ZPqTuZkDhCXQ2wkXypdLitSpd6d5Ivxhnq4wa2ETRWFVJGabYynBWxIijOswSmOw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-fsa@4.56.10': + resolution: {integrity: sha512-/FVK63ysNzTPOnCCcPoPHt77TOmachdMS422txM4KhxddLdbW1fIbFMYH0AM0ow/YchCyS5gqEjKLNyv71j/5Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-builtins@4.56.10': + resolution: {integrity: sha512-uUnKz8R0YJyKq5jXpZtkGV9U0pJDt8hmYcLRrPjROheIfjMXsz82kXMgAA/qNg0wrZ1Kv+hrg7azqEZx6XZCVw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-to-fsa@4.56.10': + resolution: {integrity: sha512-oH+O6Y4lhn9NyG6aEoFwIBNKZeYy66toP5LJcDOMBgL99BKQMUf/zWJspdRhMdn/3hbzQsZ8EHHsuekbFLGUWw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node-utils@4.56.10': + resolution: {integrity: sha512-8EuPBgVI2aDPwFdaNQeNpHsyqPi3rr+85tMNG/lHvQLiVjzoZsvxA//Xd8aB567LUhy4QS03ptT+unkD/DIsNg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-node@4.56.10': + resolution: {integrity: sha512-7R4Gv3tkUdW3dXfXiOkqxkElxKNVdd8BDOWC0/dbERd0pXpPY+s2s1Mino+aTvkGrFPiY+mmVxA7zhskm4Ue4Q==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-print@4.56.10': + resolution: {integrity: sha512-JW4fp5mAYepzFsSGrQ48ep8FXxpg4niFWHdF78wDrFGof7F3tKDJln72QFDEn/27M1yHd4v7sKHHVPh78aWcEw==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/fs-snapshot@4.56.10': + resolution: {integrity: sha512-DkR6l5fj7+qj0+fVKm/OOXMGfDFCGXLfyHkORH3DF8hxkpDgIHbhf/DwncBMs2igu/ST7OEkexn1gIqoU6Y+9g==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@jsonjoy.com/json-pack@1.21.0': resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' + '@jsonjoy.com/json-pack@17.67.0': + resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@jsonjoy.com/json-pointer@1.0.2': resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' + '@jsonjoy.com/json-pointer@17.67.0': + resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@jsonjoy.com/util@1.9.0': resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==} engines: {node: '>=10.0'} peerDependencies: tslib: '2' + '@jsonjoy.com/util@17.67.0': + resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + '@juggle/resize-observer@3.4.0': resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} @@ -2886,8 +2977,8 @@ packages: peerDependencies: '@langchain/core': '>=0.3.58 <0.4.0' - '@langchain/community@0.3.58': - resolution: {integrity: sha512-jrMbv1Xz2yqcWtlj+wnMmbagIK6J/fbQdP6R+Az+e+er5CeWy2eZHKBGFL5XYbAzYJdYhAbA4gt8kwVT6oaZfw==} + '@langchain/community@0.3.59': + resolution: {integrity: sha512-lYoVFC9wArWMXaixDgIadTE22jk4ZYAvSHHmwaMRagkGr5f4kyqMeJ83UUeW76XPx2cBy2fRSO+acSgqSuWE6A==} engines: {node: '>=18'} peerDependencies: '@arcjet/redact': ^v1.0.0-alpha.23 @@ -3266,8 +3357,8 @@ packages: youtubei.js: optional: true - '@langchain/core@0.3.79': - resolution: {integrity: sha512-ZLAs5YMM5N2UXN3kExMglltJrKKoW7hs3KMZFlXUnD7a5DFKBYxPFMeXA4rT+uvTxuJRZPCYX0JKI5BhyAWx4A==} + '@langchain/core@0.3.80': + resolution: {integrity: sha512-vcJDV2vk1AlCwSh3aBm/urQ1ZrlXFFBocv11bz/NBUfLWD5/UDNMzwPdaAd2dKvNmTWa9FM2lirLU3+JCf4cRA==} engines: {node: '>=18'} '@langchain/google-common@0.1.8': @@ -3361,26 +3452,26 @@ packages: peerDependencies: '@langchain/core': '>=0.2.21 <0.4.0' - '@ledgerhq/devices@8.7.1': - resolution: {integrity: sha512-xUtqLp4t7PhVKkMG/mCrSzxGQ25JA06UAIxxq07EwuJKyWaey1gSdvqr0TvbTw/fsJNya4FIBYTrUkjH9XpdBQ==} + '@ledgerhq/devices@8.10.0': + resolution: {integrity: sha512-ytT66KI8MizFX6dGJKthOzPDw5uNRmmg+RaMta62jbFePKYqfXtYTp6Wc0ErTXaL8nFS3IujHENwKthPmsj6jw==} - '@ledgerhq/errors@6.27.1': - resolution: {integrity: sha512-Q6nXkg63kSjxa8DDBtjSGAuiOP/jQVQM/0f+0R/ovilQKuxMIxEMXPfonvhQ2AIP4bqKOzGV1c9Iqnow22w/Zg==} + '@ledgerhq/errors@6.29.0': + resolution: {integrity: sha512-mmDsGN662zd0XGKyjzSKkg+5o1/l9pvV1HkVHtbzaydvHAtRypghmVoWMY9XAQDLXiUBXGIsLal84NgmGeuKWA==} - '@ledgerhq/hw-transport-webhid@6.30.10': - resolution: {integrity: sha512-vPTHhgCNeymlqjcK3VlMCAzEqUC5o6+NTFq2V6ywbKBkT7Wl8jUR2y+Kfba41Pg+DXTUZ4BzgMM6tNNx48ioiA==} + '@ledgerhq/hw-transport-webhid@6.31.0': + resolution: {integrity: sha512-XtCFOJ1ooaqCefB6WDfV/ie+GaO/7xQgezwRpyLroMBYboSpnEd3Diu3BAoptXKTNklwZEiUVRLVXlltTBzNvw==} - '@ledgerhq/hw-transport@6.31.14': - resolution: {integrity: sha512-aTKItTLDCN2aoZv6nX6weMslgzT0mI8Hp2FEt2iTomQ6V5r1/BRDRbz2WB1989dZsgBajPRuUWhTZ/Bt6tiijw==} + '@ledgerhq/hw-transport@6.32.0': + resolution: {integrity: sha512-bf2nxzDQ21DV/bsmExfWI0tatoVeoqhu/ePWalD/nPgPnTn/ZIDq7VBU+TY5p0JZaE87NQwmRUAjm6C1Exe61A==} - '@ledgerhq/logs@6.13.0': - resolution: {integrity: sha512-4+qRW2Pc8V+btL0QEmdB2X+uyx0kOWMWE1/LWsq5sZy3Q5tpi4eItJS6mB0XL3wGW59RQ+8bchNQQ1OW/va8Og==} + '@ledgerhq/logs@6.14.0': + resolution: {integrity: sha512-kJFu1+asWQmU9XlfR1RM3lYR76wuEoPyZvkI/CNjpft78BQr3+MMf3Nu77ABzcKFnhIcmAkOLlDQ6B8L6hDXHA==} - '@lit-labs/ssr-dom-shim@1.4.0': - resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} + '@lit-labs/ssr-dom-shim@1.5.1': + resolution: {integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==} - '@lit/reactive-element@2.1.1': - resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} + '@lit/reactive-element@2.1.2': + resolution: {integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==} '@lukeed/csprng@1.1.0': resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} @@ -3499,8 +3590,8 @@ packages: '@microsoft/tsdoc@0.15.1': resolution: {integrity: sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==} - '@modelcontextprotocol/sdk@1.25.1': - resolution: {integrity: sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==} + '@modelcontextprotocol/sdk@1.27.1': + resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -3780,8 +3871,8 @@ packages: '@swc/core': optional: true - '@nestjs/common@10.4.20': - resolution: {integrity: sha512-hxJxZF7jcKGuUzM9EYbuES80Z/36piJbiqmPy86mk8qOn5gglFebBTvcx7PWVbRNSb4gngASYnefBj/Y2HAzpQ==} + '@nestjs/common@10.4.22': + resolution: {integrity: sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==} peerDependencies: class-transformer: '*' class-validator: '*' @@ -3793,8 +3884,8 @@ packages: class-validator: optional: true - '@nestjs/common@11.1.9': - resolution: {integrity: sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==} + '@nestjs/common@11.1.14': + resolution: {integrity: sha512-IN/tlqd7Nl9gl6f0jsWEuOrQDaCI9vHzxv0fisHysfBQzfQIkqlv5A7w4Qge02BUQyczXT9HHPgHtWHCxhjRng==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -3806,8 +3897,8 @@ packages: class-validator: optional: true - '@nestjs/core@10.4.20': - resolution: {integrity: sha512-kRdtyKA3+Tu70N3RQ4JgmO1E3LzAMs/eppj7SfjabC7TgqNWoS4RLhWl4BqmsNVmjj6D5jgfPVtHtgYkU3AfpQ==} + '@nestjs/core@10.4.22': + resolution: {integrity: sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/microservices': ^10.0.0 @@ -3823,8 +3914,8 @@ packages: '@nestjs/websockets': optional: true - '@nestjs/core@11.1.9': - resolution: {integrity: sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==} + '@nestjs/core@11.1.14': + resolution: {integrity: sha512-7OXPPMoDr6z+5NkoQKu4hOhfjz/YYqM3bNilPqv1WVFWrzSmuNXxvhbX69YMmNmRYascPXiwESqf5jJdjKXEww==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -3854,8 +3945,8 @@ packages: class-validator: optional: true - '@nestjs/microservices@10.4.20': - resolution: {integrity: sha512-zu/o84Z0uTUClNnGIGfIjcrO3z6T60h/pZPSJK50o4mehbEvJ76fijj6R/WTW0VP+1N16qOv/NsiYLKJA5Cc3w==} + '@nestjs/microservices@10.4.22': + resolution: {integrity: sha512-9Oxc0jQuppGLaQv5yaB2tVS2rAZzZ9NqDS1A4UlDLiYwJB7M6e89G6tmyOQjGjPwgoXPxQS4Vg2voSiKiED2gw==} peerDependencies: '@grpc/grpc-js': '*' '@nestjs/common': ^10.0.0 @@ -3890,8 +3981,8 @@ packages: nats: optional: true - '@nestjs/platform-express@10.4.20': - resolution: {integrity: sha512-rh97mX3rimyf4xLMLHuTOBKe6UD8LOJ14VlJ1F/PTd6C6ZK9Ak6EHuJvdaGcSFQhd3ZMBh3I6CuujKGW9pNdIg==} + '@nestjs/platform-express@10.4.22': + resolution: {integrity: sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -3924,8 +4015,8 @@ packages: class-validator: optional: true - '@nestjs/testing@10.4.20': - resolution: {integrity: sha512-nMkRDukDKskdPruM6EsgMq7yJua+CPZM6I6FrLP8yXw8BiVSPv9Nm0CtcGGwt3kgZF9hfxKjGqLjsvVBsv6Vfw==} + '@nestjs/testing@10.4.22': + resolution: {integrity: sha512-HO9aPus3bAedAC+jKVAA8jTdaj4fs5M9fing4giHrcYV2txe9CvC1l1WAjwQ9RDhEHdugjY4y+FZA/U/YqPZrA==} peerDependencies: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 @@ -4004,8 +4095,8 @@ packages: cpu: [x64] os: [win32] - '@neynar/nodejs-sdk@3.112.0': - resolution: {integrity: sha512-kBCCs9UXw/K0qd7HBmhgIt13QK28lrFHseFX5RjIXbygd+8CGue/1ORTzFgkOHUbcEzImLfD5YTXvbD1OJAfnw==} + '@neynar/nodejs-sdk@3.137.0': + resolution: {integrity: sha512-VAqVg3O5An3E2izz3CYxh7gIz1t9PN3DP9CtDpu4uv8hpO2pjIDMIS55Z5Ef7dV1VwINCcubsQUcmv4un6bpfQ==} engines: {node: '>=19.9.0'} '@neynar/react@0.9.7': @@ -4020,9 +4111,6 @@ packages: '@ngraveio/bc-ur@1.1.13': resolution: {integrity: sha512-j73akJMV4+vLR2yQ4AphPIT5HZmxVjn/LxpL7YHoINnXoH6ccc90Zzck6/n6a3bCXOVZwBxq+YHwbAKRV+P8Zg==} - '@noble/ciphers@0.5.3': - resolution: {integrity: sha512-B0+6IIHiqEs3BPMT0hcRmHvEj2QHOLu+uwt+tqDDeVd0oyVzh7BPrDcPjRnV1PV/5LaknXJJQvOuRGR0zQJz+w==} - '@noble/ciphers@1.2.1': resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==} engines: {node: ^14.21.3 || >=16} @@ -4031,11 +4119,9 @@ packages: resolution: {integrity: sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.1.0': - resolution: {integrity: sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==} - - '@noble/curves@1.2.0': - resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + '@noble/ciphers@2.1.1': + resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} + engines: {node: '>= 20.19.0'} '@noble/curves@1.4.2': resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} @@ -4056,13 +4142,9 @@ packages: resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} engines: {node: ^14.21.3 || >=16} - '@noble/hashes@1.3.1': - resolution: {integrity: sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==} - engines: {node: '>= 16'} - - '@noble/hashes@1.3.2': - resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} - engines: {node: '>= 16'} + '@noble/curves@2.0.1': + resolution: {integrity: sha512-vs1Az2OOTBiP4q0pwjW5aF0xp9n4MxVrmkFBxc6EKZc6ddYx5gaZiAsZoq0uRRXWbi3AT/sBqn05eRPtn1JCPw==} + engines: {node: '>= 20.19.0'} '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} @@ -4080,6 +4162,10 @@ packages: resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@2.0.1': + resolution: {integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==} + engines: {node: '>= 20.19.0'} + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -4109,9 +4195,9 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} - '@openapitools/openapi-generator-cli@2.25.2': - resolution: {integrity: sha512-TXElbW1NXCy0EECXiO5AD2ZzT1dmaCs41Z8t3pBUGaJf8zgF/Lm0P6GRhVEpw29iHBNjZcy8nrgQ1acUfuCdng==} - engines: {node: '>=16'} + '@openapitools/openapi-generator-cli@2.30.0': + resolution: {integrity: sha512-mdALZ7zgfCnSIYoUrKhE/TOQm8P7na+KbvUFFC4mR43YrS1ZKaN++rNRpkdsKtJEmYrxAa/HODiV5SDGHPJqWw==} + engines: {node: '>=20.19.0'} hasBin: true '@openrouter/ai-sdk-provider@1.2.0': @@ -4125,10 +4211,18 @@ packages: resolution: {integrity: sha512-9B9RU0H7Ya1Dx/Rkyc4stuBZSGVQF27WigitInx2QQoj6KUpEFYPKoWjdFTunJYxmXmh17HeBvbMa1EhGyPmqQ==} engines: {node: '>=8.0.0'} + '@opentelemetry/api-logs@0.207.0': + resolution: {integrity: sha512-lAb0jQRVyleQQGiuuvCOTDVspc14nx6XJjP4FspJ1sNARo3Regq4ZZbrc3rN4b1TYSuUCvgH+UXUPug4SLOqEQ==} + engines: {node: '>=8.0.0'} + '@opentelemetry/api-logs@0.208.0': resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} engines: {node: '>=8.0.0'} + '@opentelemetry/api-logs@0.211.0': + resolution: {integrity: sha512-swFdZq8MCdmdR22jTVGQDhwqDzcI4M10nhjXkLr1EsIzXgZBqm4ZlmmcWsg3TSNf+3mzgOiqveXmBLZuDi2Lgg==} + engines: {node: '>=8.0.0'} + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -4146,8 +4240,8 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/context-async-hooks@2.2.0': - resolution: {integrity: sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==} + '@opentelemetry/context-async-hooks@2.5.1': + resolution: {integrity: sha512-MHbu8XxCHcBn6RwvCt2Vpn1WnLMNECfNKYB14LI5XypcgH4IE0/DiVifVR9tAkwPMyLXN8dOoPJfya3IryLQVw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' @@ -4164,6 +4258,18 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/core@2.5.0': + resolution: {integrity: sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.5.1': + resolution: {integrity: sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + '@opentelemetry/exporter-logs-otlp-grpc@0.203.0': resolution: {integrity: sha512-g/2Y2noc/l96zmM+g0LdeuyYKINyBwN6FJySoU15LHPLcMN/1a0wNk2SegwKcxrRdE7Xsm7fkIR5n6XFe3QpPw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4176,6 +4282,12 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-logs-otlp-http@0.208.0': + resolution: {integrity: sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/exporter-logs-otlp-proto@0.203.0': resolution: {integrity: sha512-nl/7S91MXn5R1aIzoWtMKGvqxgJgepB/sH9qW0rZvZtabnsjbf8OQ1uSx3yogtvLr0GzwD596nQKz2fV7q2RBw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4236,8 +4348,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-amqplib@0.55.0': - resolution: {integrity: sha512-5ULoU8p+tWcQw5PDYZn8rySptGSLZHNX/7srqo2TioPnAAcvTy6sQFQXsNPrAnyRRtYGMetXVyZUy5OaX1+IfA==} + '@opentelemetry/instrumentation-amqplib@0.58.0': + resolution: {integrity: sha512-fjpQtH18J6GxzUZ+cwNhWUpb71u+DzT7rFkg5pLssDGaEber91Y2WNGdpVpwGivfEluMlNMZumzjEqfg8DeKXQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4272,8 +4384,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-connect@0.52.0': - resolution: {integrity: sha512-GXPxfNB5szMbV3I9b7kNWSmQBoBzw7MT0ui6iU/p+NIzVx3a06Ri2cdQO7tG9EKb4aKSLmfX9Cw5cKxXqX6Ohg==} + '@opentelemetry/instrumentation-connect@0.54.0': + resolution: {integrity: sha512-43RmbhUhqt3uuPnc16cX6NsxEASEtn8z/cYV8Zpt6EP4p2h9s4FNuJ4Q9BbEQ2C0YlCCB/2crO1ruVz/hWt8fA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4290,8 +4402,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-dataloader@0.26.0': - resolution: {integrity: sha512-P2BgnFfTOarZ5OKPmYfbXfDFjQ4P9WkQ1Jji7yH5/WwB6Wm/knynAoA1rxbjWcDlYupFkyT0M1j6XLzDzy0aCA==} + '@opentelemetry/instrumentation-dataloader@0.28.0': + resolution: {integrity: sha512-ExXGBp0sUj8yhm6Znhf9jmuOaGDsYfDES3gswZnKr4MCqoBWQdEFn6EoDdt5u+RdbxQER+t43FoUihEfTSqsjA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4308,8 +4420,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-express@0.57.0': - resolution: {integrity: sha512-HAdx/o58+8tSR5iW+ru4PHnEejyKrAy9fYFhlEI81o10nYxrGahnMAHWiSjhDC7UQSY3I4gjcPgSKQz4rm/asg==} + '@opentelemetry/instrumentation-express@0.59.0': + resolution: {integrity: sha512-pMKV/qnHiW/Q6pmbKkxt0eIhuNEtvJ7sUAyee192HErlr+a1Jx+FZ3WjfmzhQL1geewyGEiPGkmjjAgNY8TgDA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4326,8 +4438,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-fs@0.28.0': - resolution: {integrity: sha512-FFvg8fq53RRXVBRHZViP+EMxMR03tqzEGpuq55lHNbVPyFklSVfQBN50syPhK5UYYwaStx0eyCtHtbRreusc5g==} + '@opentelemetry/instrumentation-fs@0.30.0': + resolution: {integrity: sha512-n3Cf8YhG7reaj5dncGlRIU7iT40bxPOjsBEA5Bc1a1g6e9Qvb+JFJ7SEiMlPbUw4PBmxE3h40ltE8LZ3zVt6OA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4338,8 +4450,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-generic-pool@0.52.0': - resolution: {integrity: sha512-ISkNcv5CM2IwvsMVL31Tl61/p2Zm2I2NAsYq5SSBgOsOndT0TjnptjufYVScCnD5ZLD1tpl4T3GEYULLYOdIdQ==} + '@opentelemetry/instrumentation-generic-pool@0.54.0': + resolution: {integrity: sha512-8dXMBzzmEdXfH/wjuRvcJnUFeWzZHUnExkmFJ2uPfa31wmpyBCMxO59yr8f/OXXgSogNgi/uPo9KW9H7LMIZ+g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4350,8 +4462,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-graphql@0.56.0': - resolution: {integrity: sha512-IPvNk8AFoVzTAM0Z399t34VDmGDgwT6rIqCUug8P9oAGerl2/PEIYMPOl/rerPGu+q8gSWdmbFSjgg7PDVRd3Q==} + '@opentelemetry/instrumentation-graphql@0.58.0': + resolution: {integrity: sha512-+yWVVY7fxOs3j2RixCbvue8vUuJ1inHxN2q1sduqDB0Wnkr4vOzVKRYl/Zy7B31/dcPS72D9lo/kltdOTBM3bQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4368,8 +4480,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-hapi@0.55.0': - resolution: {integrity: sha512-prqAkRf9e4eEpy4G3UcR32prKE8NLNlA90TdEU1UsghOTg0jUvs40Jz8LQWFEs5NbLbXHYGzB4CYVkCI8eWEVQ==} + '@opentelemetry/instrumentation-hapi@0.57.0': + resolution: {integrity: sha512-Os4THbvls8cTQTVA8ApLfZZztuuqGEeqog0XUnyRW7QVF0d/vOVBEcBCk1pazPFmllXGEdNbbat8e2fYIWdFbw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4380,8 +4492,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-http@0.208.0': - resolution: {integrity: sha512-rhmK46DRWEbQQB77RxmVXGyjs6783crXCnFjYQj+4tDH/Kpv9Rbg3h2kaNyp5Vz2emF1f9HOQQvZoHzwMWOFZQ==} + '@opentelemetry/instrumentation-http@0.211.0': + resolution: {integrity: sha512-n0IaQ6oVll9PP84SjbOCwDjaJasWRHi6BLsbMLiT6tNj7QbVOkuA5sk/EfZczwI0j5uTKl1awQPivO/ldVtsqA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4392,8 +4504,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-ioredis@0.56.0': - resolution: {integrity: sha512-XSWeqsd3rKSsT3WBz/JKJDcZD4QYElZEa0xVdX8f9dh4h4QgXhKRLorVsVkK3uXFbC2sZKAS2Ds+YolGwD83Dg==} + '@opentelemetry/instrumentation-ioredis@0.59.0': + resolution: {integrity: sha512-875UxzBHWkW+P4Y45SoFM2AR8f8TzBMD8eO7QXGCyFSCUMP5s9vtt/BS8b/r2kqLyaRPK6mLbdnZznK3XzQWvw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4404,8 +4516,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-kafkajs@0.18.0': - resolution: {integrity: sha512-KCL/1HnZN5zkUMgPyOxfGjLjbXjpd4odDToy+7c+UsthIzVLFf99LnfIBE8YSSrYE4+uS7OwJMhvhg3tWjqMBg==} + '@opentelemetry/instrumentation-kafkajs@0.20.0': + resolution: {integrity: sha512-yJXOuWZROzj7WmYCUiyT27tIfqBrVtl1/TwVbQyWPz7rL0r1Lu7kWjD0PiVeTCIL6CrIZ7M2s8eBxsTAOxbNvw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4416,8 +4528,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-knex@0.53.0': - resolution: {integrity: sha512-xngn5cH2mVXFmiT1XfQ1aHqq1m4xb5wvU6j9lSgLlihJ1bXzsO543cpDwjrZm2nMrlpddBf55w8+bfS4qDh60g==} + '@opentelemetry/instrumentation-knex@0.55.0': + resolution: {integrity: sha512-FtTL5DUx5Ka/8VK6P1VwnlUXPa3nrb7REvm5ddLUIeXXq4tb9pKd+/ThB1xM/IjefkRSN3z8a5t7epYw1JLBJQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4428,8 +4540,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-koa@0.57.0': - resolution: {integrity: sha512-3JS8PU/D5E3q295mwloU2v7c7/m+DyCqdu62BIzWt+3u9utjxC9QS7v6WmUNuoDN3RM+Q+D1Gpj13ERo+m7CGg==} + '@opentelemetry/instrumentation-koa@0.59.0': + resolution: {integrity: sha512-K9o2skADV20Skdu5tG2bogPKiSpXh4KxfLjz6FuqIVvDJNibwSdu5UvyyBzRVp1rQMV6UmoIk6d3PyPtJbaGSg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.9.0 @@ -4440,8 +4552,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-lru-memoizer@0.53.0': - resolution: {integrity: sha512-LDwWz5cPkWWr0HBIuZUjslyvijljTwmwiItpMTHujaULZCxcYE9eU44Qf/pbVC8TulT0IhZi+RoGvHKXvNhysw==} + '@opentelemetry/instrumentation-lru-memoizer@0.55.0': + resolution: {integrity: sha512-FDBfT7yDGcspN0Cxbu/k8A0Pp1Jhv/m7BMTzXGpcb8ENl3tDj/51U65R5lWzUH15GaZA15HQ5A5wtafklxYj7g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4458,8 +4570,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongodb@0.61.0': - resolution: {integrity: sha512-OV3i2DSoY5M/pmLk+68xr5RvkHU8DRB3DKMzYJdwDdcxeLs62tLbkmRyqJZsYf3Ht7j11rq35pHOWLuLzXL7pQ==} + '@opentelemetry/instrumentation-mongodb@0.64.0': + resolution: {integrity: sha512-pFlCJjweTqVp7B220mCvCld1c1eYKZfQt1p3bxSbcReypKLJTwat+wbL2YZoX9jPi5X2O8tTKFEOahO5ehQGsA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4470,8 +4582,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mongoose@0.55.0': - resolution: {integrity: sha512-5afj0HfF6aM6Nlqgu6/PPHFk8QBfIe3+zF9FGpX76jWPS0/dujoEYn82/XcLSaW5LPUDW8sni+YeK0vTBNri+w==} + '@opentelemetry/instrumentation-mongoose@0.57.0': + resolution: {integrity: sha512-MthiekrU/BAJc5JZoZeJmo0OTX6ycJMiP6sMOSRTkvz5BrPMYDqaJos0OgsLPL/HpcgHP7eo5pduETuLguOqcg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4482,8 +4594,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql2@0.55.0': - resolution: {integrity: sha512-0cs8whQG55aIi20gnK8B7cco6OK6N+enNhW0p5284MvqJ5EPi+I1YlWsWXgzv/V2HFirEejkvKiI4Iw21OqDWg==} + '@opentelemetry/instrumentation-mysql2@0.57.0': + resolution: {integrity: sha512-nHSrYAwF7+aV1E1V9yOOP9TchOodb6fjn4gFvdrdQXiRE7cMuffyLLbCZlZd4wsspBzVwOXX8mpURdRserAhNA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4494,8 +4606,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-mysql@0.54.0': - resolution: {integrity: sha512-bqC1YhnwAeWmRzy1/Xf9cDqxNG2d/JDkaxnqF5N6iJKN1eVWI+vg7NfDkf52/Nggp3tl1jcC++ptC61BD6738A==} + '@opentelemetry/instrumentation-mysql@0.57.0': + resolution: {integrity: sha512-HFS/+FcZ6Q7piM7Il7CzQ4VHhJvGMJWjx7EgCkP5AnTntSN5rb5Xi3TkYJHBKeR27A0QqPlGaCITi93fUDs++Q==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4506,8 +4618,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-nestjs-core@0.55.0': - resolution: {integrity: sha512-JFLNhbbEGnnQrMKOYoXx0nNk5N9cPeghu4xP/oup40a7VaSeYruyOiFbg9nkbS4ZQiI8aMuRqUT3Mo4lQjKEKg==} + '@opentelemetry/instrumentation-nestjs-core@0.57.0': + resolution: {integrity: sha512-mzTjjethjuk70o/vWUeV12QwMG9EAFJpkn13/q8zi++sNosf2hoGXTplIdbs81U8S3PJ4GxHKsBjM0bj1CGZ0g==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4530,8 +4642,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-pg@0.61.0': - resolution: {integrity: sha512-UeV7KeTnRSM7ECHa3YscoklhUtTQPs6V6qYpG283AB7xpnPGCUCUfECFT9jFg6/iZOQTt3FHkB1wGTJCNZEvPw==} + '@opentelemetry/instrumentation-pg@0.63.0': + resolution: {integrity: sha512-dKm/ODNN3GgIQVlbD6ZPxwRc3kleLf95hrRWXM+l8wYo+vSeXtEpQPT53afEf6VFWDVzJK55VGn8KMLtSve/cg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4548,8 +4660,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-redis@0.57.0': - resolution: {integrity: sha512-bCxTHQFXzrU3eU1LZnOZQ3s5LURxQPDlU3/upBzlWY77qOI1GZuGofazj3jtzjctMJeBEJhNwIFEgRPBX1kp/Q==} + '@opentelemetry/instrumentation-redis@0.59.0': + resolution: {integrity: sha512-JKv1KDDYA2chJ1PC3pLP+Q9ISMQk6h5ey+99mB57/ARk0vQPGZTTEb4h4/JlcEpy7AYT8HIGv7X6l+br03Neeg==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4584,8 +4696,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 - '@opentelemetry/instrumentation-tedious@0.27.0': - resolution: {integrity: sha512-jRtyUJNZppPBjPae4ZjIQ2eqJbcRaRfJkr0lQLHFmOU/no5A6e9s1OHLd5XZyZoBJ/ymngZitanyRRA5cniseA==} + '@opentelemetry/instrumentation-tedious@0.30.0': + resolution: {integrity: sha512-bZy9Q8jFdycKQ2pAsyuHYUHNmCxCOGdG6eg1Mn75RvQDccq832sU5OWOBnc12EFUELI6icJkhR7+EQKMBam2GA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 @@ -4596,8 +4708,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.7.0 - '@opentelemetry/instrumentation-undici@0.19.0': - resolution: {integrity: sha512-Pst/RhR61A2OoZQZkn6OLpdVpXp6qn3Y92wXa6umfJe9rV640r4bc6SWvw4pPN6DiQqPu2c8gnSSZPDtC6JlpQ==} + '@opentelemetry/instrumentation-undici@0.21.0': + resolution: {integrity: sha512-gok0LPUOTz2FQ1YJMZzaHcOzDFyT64XJ8M9rNkugk923/p6lDGms/cRW1cqgqp6N6qcd6K6YdVHwPEhnx9BWbw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.7.0 @@ -4614,18 +4726,36 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.207.0': + resolution: {integrity: sha512-y6eeli9+TLKnznrR8AZlQMSJT7wILpXH+6EYq5Vf/4Ao+huI7EedxQHwRgVUOMLFbe7VFDvHJrX9/f4lcwnJsA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.208.0': resolution: {integrity: sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/instrumentation@0.211.0': + resolution: {integrity: sha512-h0nrZEC/zvI994nhg7EgQ8URIHt0uDTwN90r3qQUdZORS455bbx+YebnGeEuFghUT0HlJSrLF4iHw67f+odY+Q==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/otlp-exporter-base@0.203.0': resolution: {integrity: sha512-Wbxf7k+87KyvxFr5D7uOiSq/vHXWommvdnNE7vECO3tAhsA2GfOlpWINCMWUEPdHZ7tCXxw6Epp3vgx3jU7llQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/otlp-exporter-base@0.208.0': + resolution: {integrity: sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/otlp-grpc-exporter-base@0.203.0': resolution: {integrity: sha512-te0Ze1ueJF+N/UOFl5jElJW4U0pZXQ8QklgSfJ2linHN0JJsuaHG8IabEUi2iqxY8ZBDlSiz1Trfv5JcjWWWwQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4638,6 +4768,12 @@ packages: peerDependencies: '@opentelemetry/api': ^1.3.0 + '@opentelemetry/otlp-transformer@0.208.0': + resolution: {integrity: sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/propagator-b3@2.0.1': resolution: {integrity: sha512-Hc09CaQ8Tf5AGLmf449H726uRoBNGPBL4bjr7AnnUpzWMvhdn61F78z9qb6IqB737TffBsokGAK1XykFEZ1igw==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4660,8 +4796,8 @@ packages: peerDependencies: '@opentelemetry/api': ^1.0.0 - '@opentelemetry/resource-detector-aws@2.9.0': - resolution: {integrity: sha512-2dk1TuuImatD8n0OyBgghucluGcj2XtnortmJdLH0OffM7cVtat4h7Dcg8IZvP7WrMjbP4ZQQ2cpD1Fhvx6BsA==} + '@opentelemetry/resource-detector-aws@2.12.0': + resolution: {integrity: sha512-VelueKblsnQEiBVqEYcvM9VEb+B8zN6nftltdO9HAD7qi/OlicP4z/UGJ9EeW2m++WabdMoj0G3QVL8YV0P9tw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': ^1.0.0 @@ -4696,12 +4832,24 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/resources@2.5.1': + resolution: {integrity: sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-logs@0.203.0': resolution: {integrity: sha512-vM2+rPq0Vi3nYA5akQD2f3QwossDnTDLvKbea6u/A2NZ3XDkPxMfo/PNrDoXhDUD/0pPo2CdH5ce/thn9K0kLw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.4.0 <1.10.0' + '@opentelemetry/sdk-logs@0.208.0': + resolution: {integrity: sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + '@opentelemetry/sdk-metrics@2.0.1': resolution: {integrity: sha512-wf8OaJoSnujMAHWR3g+/hGvNcsC16rf9s1So4JlMiFaFHiE4HpIA3oUh+uWZQ7CNuK8gVW/pQSkgoa5HkkOl0g==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4714,6 +4862,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.9.0 <1.10.0' + '@opentelemetry/sdk-metrics@2.5.1': + resolution: {integrity: sha512-RKMn3QKi8nE71ULUo0g/MBvq1N4icEBo7cQSKnL3URZT16/YH3nSVgWegOjwx7FRBTrjOIkMJkCUn/ZFIEfn4A==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + '@opentelemetry/sdk-node@0.203.0': resolution: {integrity: sha512-zRMvrZGhGVMvAbbjiNQW3eKzW/073dlrSiAKPVWmkoQzah9wfynpVPeL55f9fVIm0GaBxTLcPeukWGy0/Wj7KQ==} engines: {node: ^18.19.0 || >=20.6.0} @@ -4732,20 +4886,26 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace-base@2.5.1': + resolution: {integrity: sha512-iZH3Gw8cxQn0gjpOjJMmKLd9GIaNh/E3v3ST67vyzLSxHBs14HsG4dy7jMYyC5WXGdBVEcM7U/XTF5hCQxjDMw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + '@opentelemetry/sdk-trace-node@2.0.1': resolution: {integrity: sha512-UhdbPF19pMpBtCWYP5lHbTogLWx9N0EBxtdagvkn5YtsAnCBZzL7SjktG+ZmupRgifsHMjwUaCCaVmqGfSADmA==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/sdk-trace-node@2.2.0': - resolution: {integrity: sha512-+OaRja3f0IqGG2kptVeYsrZQK9nKRSpfFrKtRBq4uh6nIB8bTBgaGvYQrQoRrQWQMA5dK5yLhDMDc0dvYvCOIQ==} + '@opentelemetry/sdk-trace-node@2.5.1': + resolution: {integrity: sha512-9lopQ6ZoElETOEN0csgmtEV5/9C7BMfA7VtF4Jape3i954b6sTY2k3Xw3CxUTKreDck/vpAuJM+EDo4zheUw+A==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.38.0': - resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==} + '@opentelemetry/semantic-conventions@1.40.0': + resolution: {integrity: sha512-cifvXDhcqMwwTlTK04GBNeIe7yyo28Mfby85QXFe1Yk8nmi36Ab/5UQwptOx84SsoGNRg+EVSjwzfSZMy6pmlw==} engines: {node: '>=14'} '@opentelemetry/sql-common@0.41.2': @@ -4764,86 +4924,86 @@ packages: resolution: {integrity: sha512-9+qMSaDpahC0+vX2ChM46/ls6a5Ankqs6RTLrHSaFpm7o1mFanP82e+jm9/0o5D660ueK8dWJGPCXQrBxBNNWA==} engines: {node: '>= 12'} - '@parcel/watcher-android-arm64@2.5.1': - resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] - '@parcel/watcher-darwin-arm64@2.5.1': - resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] - '@parcel/watcher-darwin-x64@2.5.1': - resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] - '@parcel/watcher-freebsd-x64@2.5.1': - resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] - '@parcel/watcher-linux-arm-glibc@2.5.1': - resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - '@parcel/watcher-linux-arm-musl@2.5.1': - resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - '@parcel/watcher-linux-arm64-glibc@2.5.1': - resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - '@parcel/watcher-linux-arm64-musl@2.5.1': - resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - '@parcel/watcher-linux-x64-glibc@2.5.1': - resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - '@parcel/watcher-linux-x64-musl@2.5.1': - resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - '@parcel/watcher-win32-arm64@2.5.1': - resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] - '@parcel/watcher-win32-ia32@2.5.1': - resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} engines: {node: '>= 10.0.0'} cpu: [ia32] os: [win32] - '@parcel/watcher-win32-x64@2.5.1': - resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] - '@parcel/watcher@2.5.1': - resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} engines: {node: '>= 10.0.0'} '@particle-network/analytics@1.0.2': @@ -4877,8 +5037,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@playwright/test@1.57.0': - resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} + '@playwright/test@1.58.2': + resolution: {integrity: sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==} engines: {node: '>=18'} hasBin: true @@ -4914,8 +5074,11 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@posthog/core@1.8.1': - resolution: {integrity: sha512-jfzBtQIk9auRi/biO+G/gumK5KxqsD5wOr7XpYMROE/I3pazjP4zIziinp21iQuIQJMXrDvwt9Af3njgOGwtew==} + '@posthog/core@1.23.1': + resolution: {integrity: sha512-GViD5mOv/mcbZcyzz3z9CS0R79JzxVaqEz4sP5Dsea178M/j3ZWe6gaHDZB9yuyGfcmIMQ/8K14yv+7QrK4sQQ==} + + '@posthog/types@1.356.1': + resolution: {integrity: sha512-miIUjs4LiBDMOxKkC87HEJLIih0pNGMAjxx+mW4X7jLpN41n0PLMW7swRE6uuxcMV0z3H6MllRSCYmsokkyfuQ==} '@postiz/wallets@0.0.1': resolution: {integrity: sha512-zCkg5ZXHZkyCREvoAtxQAp5IoCYfSQs9xonzyMvV/LoY32KjudV5wc4rb4R7NYNdPTGfcni1R8ETojASnK6oUw==} @@ -4953,8 +5116,8 @@ packages: '@prisma/get-platform@6.5.0': resolution: {integrity: sha512-xYcvyJwNMg2eDptBYFqFLUCfgi+wZLcj6HDMsj0Qw0irvauG4IKmkbywnqwok0B+k+W+p+jThM2DKTSmoPCkzw==} - '@prisma/instrumentation@6.19.0': - resolution: {integrity: sha512-QcuYy25pkXM8BJ37wVFBO7Zh34nyRV1GOb2n3lPkkbRYfl4hWl3PTcImP41P0KrzVXfa/45p6eVCos27x3exIg==} + '@prisma/instrumentation@7.2.0': + resolution: {integrity: sha512-Rh9Z4x5kEj1OdARd7U18AtVrnL6rmLSI0qYShaB4W7Wx5BKbgzndWF+QnuzMb7GLfVdlT5aYCXoPQVYuYtVu0g==} peerDependencies: '@opentelemetry/api': ^1.8 @@ -5326,14 +5489,14 @@ packages: '@types/react': optional: true - '@react-aria/focus@3.21.3': - resolution: {integrity: sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==} + '@react-aria/focus@3.21.4': + resolution: {integrity: sha512-6gz+j9ip0/vFRTKJMl3R30MHopn4i19HqqLfSQfElxJD+r9hBnYG1Q6Wd/kl/WRR1+CALn2F+rn06jUnf5sT8Q==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/interactions@3.26.0': - resolution: {integrity: sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==} + '@react-aria/interactions@3.27.0': + resolution: {integrity: sha512-D27pOy+0jIfHK60BB26AgqjjRFOYdvVSkwC31b2LicIzRCSPOSP06V4gMHuGmkhNTF4+YWDi1HHYjxIvMeiSlA==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -5344,8 +5507,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-aria/utils@3.32.0': - resolution: {integrity: sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg==} + '@react-aria/utils@3.33.0': + resolution: {integrity: sha512-yvz7CMH8d2VjwbSa5nGXqjU031tYhD8ddax95VzJsHSPyqHDEGfxul8RkhGV6oO7bVqZxVs6xY66NIgae+FHjw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -5371,18 +5534,18 @@ packages: peerDependencies: react-native: ^0.0.0-0 || >=0.60 <1.0 - '@react-native/assets-registry@0.83.1': - resolution: {integrity: sha512-AT7/T6UwQqO39bt/4UL5EXvidmrddXrt0yJa7ENXndAv+8yBzMsZn6fyiax6+ERMt9GLzAECikv3lj22cn2wJA==} + '@react-native/assets-registry@0.84.1': + resolution: {integrity: sha512-lAJ6PDZv95FdT9s9uhc9ivhikW1Zwh4j9XdXM7J2l4oUA3t37qfoBmTSDLuPyE3Bi+Xtwa11hJm0BUTT2sc/gg==} engines: {node: '>= 20.19.4'} - '@react-native/codegen@0.83.1': - resolution: {integrity: sha512-FpRxenonwH+c2a5X5DZMKUD7sCudHxB3eSQPgV9R+uxd28QWslyAWrpnJM/Az96AEksHnymDzEmzq2HLX5nb+g==} + '@react-native/codegen@0.84.1': + resolution: {integrity: sha512-n1RIU0QAavgCg1uC5+s53arL7/mpM+16IBhJ3nCFSd/iK5tUmCwxQDcIDC703fuXfpub/ZygeSjVN8bcOWn0gA==} engines: {node: '>= 20.19.4'} peerDependencies: '@babel/core': '*' - '@react-native/community-cli-plugin@0.83.1': - resolution: {integrity: sha512-FqR1ftydr08PYlRbrDF06eRiiiGOK/hNmz5husv19sK6iN5nHj1SMaCIVjkH/a5vryxEddyFhU6PzO/uf4kOHg==} + '@react-native/community-cli-plugin@0.84.1': + resolution: {integrity: sha512-f6a+mJEJ6Joxlt/050TqYUr7uRRbeKnz8lnpL7JajhpsgZLEbkJRjH8HY5QiLcRdUwWFtizml4V+vcO3P4RxoQ==} engines: {node: '>= 20.19.4'} peerDependencies: '@react-native-community/cli': '*' @@ -5393,31 +5556,31 @@ packages: '@react-native/metro-config': optional: true - '@react-native/debugger-frontend@0.83.1': - resolution: {integrity: sha512-01Rn3goubFvPjHXONooLmsW0FLxJDKIUJNOlOS0cPtmmTIx9YIjxhe/DxwHXGk7OnULd7yl3aYy7WlBsEd5Xmg==} + '@react-native/debugger-frontend@0.84.1': + resolution: {integrity: sha512-rUU/Pyh3R5zT0WkVgB+yA6VwOp7HM5Hz4NYE97ajFS07OUIcv8JzBL3MXVdSSjLfldfqOuPEuKUaZcAOwPgabw==} engines: {node: '>= 20.19.4'} - '@react-native/debugger-shell@0.83.1': - resolution: {integrity: sha512-d+0w446Hxth5OP/cBHSSxOEpbj13p2zToUy6e5e3tTERNJ8ueGlW7iGwGTrSymNDgXXFjErX+dY4P4/3WokPIQ==} + '@react-native/debugger-shell@0.84.1': + resolution: {integrity: sha512-LIGhh4q4ette3yW5OzmukNMYwmINYrRGDZqKyTYc/VZyNpblZPw72coXVHXdfpPT6+YlxHqXzn3UjFZpNODGCQ==} engines: {node: '>= 20.19.4'} - '@react-native/dev-middleware@0.83.1': - resolution: {integrity: sha512-QJaSfNRzj3Lp7MmlCRgSBlt1XZ38xaBNXypXAp/3H3OdFifnTZOeYOpFmcpjcXYnDqkxetuwZg8VL65SQhB8dg==} + '@react-native/dev-middleware@0.84.1': + resolution: {integrity: sha512-Z83ra+Gk6ElAhH3XRrv3vwbwCPTb04sPPlNpotxcFZb5LtRQZwT91ZQEXw3GOJCVIFp9EQ/gj8AQbVvtHKOUlQ==} engines: {node: '>= 20.19.4'} - '@react-native/gradle-plugin@0.83.1': - resolution: {integrity: sha512-6ESDnwevp1CdvvxHNgXluil5OkqbjkJAkVy7SlpFsMGmVhrSxNAgD09SSRxMNdKsnLtzIvMsFCzyHLsU/S4PtQ==} + '@react-native/gradle-plugin@0.84.1': + resolution: {integrity: sha512-7uVlPBE3uluRNRX4MW7PUJIO1LDBTpAqStKHU7LHH+GRrdZbHsWtOEAX8PiY4GFfBEvG8hEjiuTOqAxMjV+hDg==} engines: {node: '>= 20.19.4'} - '@react-native/js-polyfills@0.83.1': - resolution: {integrity: sha512-qgPpdWn/c5laA+3WoJ6Fak8uOm7CG50nBsLlPsF8kbT7rUHIVB9WaP6+GPsoKV/H15koW7jKuLRoNVT7c3Ht3w==} + '@react-native/js-polyfills@0.84.1': + resolution: {integrity: sha512-UsTe2AbUugsfyI7XIHMQq4E7xeC8a6GrYwuK+NohMMMJMxmyM3JkzIk+GB9e2il6ScEQNMJNaj+q+i5za8itxQ==} engines: {node: '>= 20.19.4'} - '@react-native/normalize-colors@0.83.1': - resolution: {integrity: sha512-84feABbmeWo1kg81726UOlMKAhcQyFXYz2SjRKYkS78QmfhVDhJ2o/ps1VjhFfBz0i/scDwT1XNv9GwmRIghkg==} + '@react-native/normalize-colors@0.84.1': + resolution: {integrity: sha512-/UPaQ4jl95soXnLDEJ6Cs6lnRXhwbxtT4KbZz+AFDees7prMV2NOLcHfCnzmTabf5Y3oxENMVBL666n4GMLcTA==} - '@react-native/virtualized-lists@0.83.1': - resolution: {integrity: sha512-MdmoAbQUTOdicCocm5XAFDJWsswxk7hxa6ALnm6Y88p01HFML0W593hAn6qOt9q6IM1KbAcebtH6oOd4gcQy8w==} + '@react-native/virtualized-lists@0.84.1': + resolution: {integrity: sha512-sJoDunzhci8ZsqxlUiKoLut4xQeQcmbIgvDHGQKeBz6uEq9HgU+hCWOijMRr6sLP0slQVfBAza34Rq7IbXZZOA==} engines: {node: '>= 20.19.4'} peerDependencies: '@types/react': ^19.2.0 @@ -5435,8 +5598,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - '@react-types/shared@3.32.1': - resolution: {integrity: sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==} + '@react-types/shared@3.33.0': + resolution: {integrity: sha512-xuUpP6MyuPmJtzNOqF5pzFUIHH2YogyOQfUQHag54PRmWB7AbjuGWBUv0l1UDmz6+AbzAYGmDVAzcRDOu2PFpw==} peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 @@ -5445,19 +5608,24 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@redis/bloom@5.10.0': - resolution: {integrity: sha512-doIF37ob+l47n0rkpRNgU8n4iacBlKM9xLiP1LtTZTvz8TloJB8qx/MgvhMhKdYG+CvCY2aPBnN2706izFn/4A==} + '@redis/bloom@5.11.0': + resolution: {integrity: sha512-KYiVilAhAFN3057afUb/tfYJpsEyTkQB+tQcn5gVVA7DgcNOAj8lLxe4j8ov8BF6I9C1Fe/kwlbuAICcTMX8Lw==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.10.0 + '@redis/client': ^5.11.0 '@redis/client@1.6.1': resolution: {integrity: sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==} engines: {node: '>=14'} - '@redis/client@5.10.0': - resolution: {integrity: sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==} + '@redis/client@5.11.0': + resolution: {integrity: sha512-GHoprlNQD51Xq2Ztd94HHV94MdFZQ3CVrpA04Fz8MVoHM0B7SlbmPEVIjwTbcv58z8QyjnrOuikS0rWF03k5dQ==} engines: {node: '>= 18'} + peerDependencies: + '@node-rs/xxhash': ^1.1.0 + peerDependenciesMeta: + '@node-rs/xxhash': + optional: true '@redis/graph@1.1.1': resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} @@ -5469,33 +5637,33 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@redis/json@5.10.0': - resolution: {integrity: sha512-B2G8XlOmTPUuZtD44EMGbtoepQG34RCDXLZbjrtON1Djet0t5Ri7/YPXvL9aomXqP8lLTreaprtyLKF4tmXEEA==} + '@redis/json@5.11.0': + resolution: {integrity: sha512-1iAy9kAtcD0quB21RbPTbUqqy+T2Uu2JxucwE+B4A+VaDbIRvpZR6DMqV8Iqaws2YxJYB3GC5JVNzPYio2ErUg==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.10.0 + '@redis/client': ^5.11.0 '@redis/search@1.2.0': resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} peerDependencies: '@redis/client': ^1.0.0 - '@redis/search@5.10.0': - resolution: {integrity: sha512-3SVcPswoSfp2HnmWbAGUzlbUPn7fOohVu2weUQ0S+EMiQi8jwjL+aN2p6V3TI65eNfVsJ8vyPvqWklm6H6esmg==} + '@redis/search@5.11.0': + resolution: {integrity: sha512-g1l7f3Rnyk/xI99oGHIgWHSKFl45Re5YTIcO8j/JE8olz389yUFyz2+A6nqVy/Zi031VgPDWscbbgOk8hlhZ3g==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.10.0 + '@redis/client': ^5.11.0 '@redis/time-series@1.1.0': resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} peerDependencies: '@redis/client': ^1.0.0 - '@redis/time-series@5.10.0': - resolution: {integrity: sha512-cPkpddXH5kc/SdRhF0YG0qtjL+noqFT0AcHbQ6axhsPsO7iqPi1cjxgdkE9TNeKiBUUdCaU1DbqkR/LzbzPBhg==} + '@redis/time-series@5.11.0': + resolution: {integrity: sha512-TWFeOcU4xkj0DkndnOyhtxvX1KWD+78UHT3XX3x3XRBUGWeQrKo3jqzDsZwxbggUgf9yLJr/akFHXru66X5UQA==} engines: {node: '>= 18'} peerDependencies: - '@redis/client': ^5.10.0 + '@redis/client': ^5.11.0 '@remirror/core-constants@3.0.0': resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==} @@ -5613,8 +5781,8 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm-eabi@4.54.0': - resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} + '@rollup/rollup-android-arm-eabi@4.59.0': + resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} cpu: [arm] os: [android] @@ -5623,8 +5791,8 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-android-arm64@4.54.0': - resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} + '@rollup/rollup-android-arm64@4.59.0': + resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} cpu: [arm64] os: [android] @@ -5633,8 +5801,8 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-arm64@4.54.0': - resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} + '@rollup/rollup-darwin-arm64@4.59.0': + resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} cpu: [arm64] os: [darwin] @@ -5643,8 +5811,8 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-darwin-x64@4.54.0': - resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} + '@rollup/rollup-darwin-x64@4.59.0': + resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} cpu: [x64] os: [darwin] @@ -5653,8 +5821,8 @@ packages: cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.54.0': - resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} + '@rollup/rollup-freebsd-arm64@4.59.0': + resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} cpu: [arm64] os: [freebsd] @@ -5663,8 +5831,8 @@ packages: cpu: [x64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.54.0': - resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} + '@rollup/rollup-freebsd-x64@4.59.0': + resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} cpu: [x64] os: [freebsd] @@ -5673,8 +5841,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.54.0': - resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': + resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} cpu: [arm] os: [linux] @@ -5683,8 +5851,8 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.54.0': - resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} + '@rollup/rollup-linux-arm-musleabihf@4.59.0': + resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} cpu: [arm] os: [linux] @@ -5693,8 +5861,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.54.0': - resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} + '@rollup/rollup-linux-arm64-gnu@4.59.0': + resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} cpu: [arm64] os: [linux] @@ -5703,8 +5871,8 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.54.0': - resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} + '@rollup/rollup-linux-arm64-musl@4.59.0': + resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} cpu: [arm64] os: [linux] @@ -5713,8 +5881,13 @@ packages: cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.54.0': - resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} + '@rollup/rollup-linux-loong64-gnu@4.59.0': + resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.59.0': + resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} cpu: [loong64] os: [linux] @@ -5723,8 +5896,13 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.54.0': - resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.59.0': + resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} cpu: [ppc64] os: [linux] @@ -5733,8 +5911,8 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.54.0': - resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} + '@rollup/rollup-linux-riscv64-gnu@4.59.0': + resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} cpu: [riscv64] os: [linux] @@ -5743,8 +5921,8 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.54.0': - resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} + '@rollup/rollup-linux-riscv64-musl@4.59.0': + resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} cpu: [riscv64] os: [linux] @@ -5753,8 +5931,8 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.54.0': - resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} + '@rollup/rollup-linux-s390x-gnu@4.59.0': + resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} cpu: [s390x] os: [linux] @@ -5763,8 +5941,8 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.54.0': - resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} + '@rollup/rollup-linux-x64-gnu@4.59.0': + resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} cpu: [x64] os: [linux] @@ -5773,18 +5951,23 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.54.0': - resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} + '@rollup/rollup-linux-x64-musl@4.59.0': + resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} cpu: [x64] os: [linux] + '@rollup/rollup-openbsd-x64@4.59.0': + resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + cpu: [x64] + os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.50.2': resolution: {integrity: sha512-nMKvq6FRHSzYfKLHZ+cChowlEkR2lj/V0jYj9JnGUVPL2/mIeFGmVM2mLaFeNa5Jev7W7TovXqXIG2d39y1KYA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.54.0': - resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} + '@rollup/rollup-openharmony-arm64@4.59.0': + resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} cpu: [arm64] os: [openharmony] @@ -5793,8 +5976,8 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.54.0': - resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} + '@rollup/rollup-win32-arm64-msvc@4.59.0': + resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} cpu: [arm64] os: [win32] @@ -5803,13 +5986,13 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.54.0': - resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} + '@rollup/rollup-win32-ia32-msvc@4.59.0': + resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.54.0': - resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} + '@rollup/rollup-win32-x64-gnu@4.59.0': + resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} cpu: [x64] os: [win32] @@ -5818,31 +6001,28 @@ packages: cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.54.0': - resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} + '@rollup/rollup-win32-x64-msvc@4.59.0': + resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} cpu: [x64] os: [win32] '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@rushstack/eslint-patch@1.15.0': - resolution: {integrity: sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==} + '@rushstack/eslint-patch@1.16.1': + resolution: {integrity: sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==} '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - '@scure/base@1.1.1': - resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} - '@scure/base@1.1.9': resolution: {integrity: sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==} '@scure/base@1.2.6': resolution: {integrity: sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==} - '@scure/bip32@1.3.1': - resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} + '@scure/base@2.0.0': + resolution: {integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==} '@scure/bip32@1.4.0': resolution: {integrity: sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==} @@ -5853,8 +6033,8 @@ packages: '@scure/bip32@1.7.0': resolution: {integrity: sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==} - '@scure/bip39@1.2.1': - resolution: {integrity: sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==} + '@scure/bip32@2.0.1': + resolution: {integrity: sha512-4Md1NI5BzoVP+bhyJaY3K6yMesEFzNS1sE/cP+9nuvE7p/b0kx9XbpDHHFl8dHtufcbdHRUUQdRqLIPHN/s7yA==} '@scure/bip39@1.3.0': resolution: {integrity: sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==} @@ -5865,6 +6045,9 @@ packages: '@scure/bip39@1.6.0': resolution: {integrity: sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==} + '@scure/bip39@2.0.1': + resolution: {integrity: sha512-PsxdFj/d2AcJcZDX1FXN3dDgitDDTmwf78rKZq1a6c1P1Nan1X/Sxc7667zU3U+AN60g7SxxP0YCVw2H/hBycg==} + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -5881,156 +6064,171 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} - '@sentry-internal/browser-utils@10.32.1': - resolution: {integrity: sha512-sjLLep1es3rTkbtAdTtdpc/a6g7v7bK5YJiZJsUigoJ4NTiFeMI5uIDCxbH/tjJ1q23YE1LzVn7T96I+qBRjHA==} + '@sentry-internal/browser-utils@10.40.0': + resolution: {integrity: sha512-3CDeVNBXYOIvBVdT0SOdMZx5LzYDLuhGK/z7A14sYZz4Cd2+f4mSeFDaEOoH/g2SaY2CKR5KGkAADy8IyjZ21w==} engines: {node: '>=18'} - '@sentry-internal/feedback@10.32.1': - resolution: {integrity: sha512-O24G8jxbfBY1RE/v2qFikPJISVMOrd/zk8FKyl+oUVYdOxU2Ucjk2cR3EQruBFlc7irnL6rT3GPfRZ/kBgLkmQ==} + '@sentry-internal/feedback@10.40.0': + resolution: {integrity: sha512-V/ixkcdCNMo04KgsCEeNEu966xUUTD6czKT2LOAO5siZACqFjT/Rp9VR1n7QQrVo3sL7P3QNiTHtX0jaeWbwzg==} engines: {node: '>=18'} '@sentry-internal/node-cpu-profiler@2.2.0': resolution: {integrity: sha512-oLHVYurqZfADPh5hvmQYS5qx8t0UZzT2u6+/68VXsFruQEOnYJTODKgU3BVLmemRs3WE6kCJjPeFdHVYOQGSzQ==} engines: {node: '>=18'} - '@sentry-internal/replay-canvas@10.32.1': - resolution: {integrity: sha512-/XGTzWNWVc+B691fIVekV2KeoHFEDA5KftrLFAhEAW7uWOwk/xy3aQX4TYM0LcPm2PBKvoumlAD+Sd/aXk63oA==} + '@sentry-internal/replay-canvas@10.40.0': + resolution: {integrity: sha512-wzQwilFHO2baeCt0dTMf0eW+rgK8O+mkisf9sQzPXzG3Krr/iVtFg1T5T1Th3YsCsEdn6yQ3hcBPLEXjMSvccg==} engines: {node: '>=18'} - '@sentry-internal/replay@10.32.1': - resolution: {integrity: sha512-KKmLUgIaLRM0VjrMA1ByQTawZyRDYSkG2evvEOVpEtR9F0sumidAQdi7UY71QEKE1RYe/Jcp/3WoaqsMh8tbnQ==} + '@sentry-internal/replay@10.40.0': + resolution: {integrity: sha512-vsH2Ut0KIIQIHNdS3zzEGLJ2C9btbpvJIWAVk7l7oft66JzlUNC89qNaQ5SAypjLQx4Ln2V/ZTqfEoNzXOAsoQ==} engines: {node: '>=18'} - '@sentry/babel-plugin-component-annotate@4.6.1': - resolution: {integrity: sha512-aSIk0vgBqv7PhX6/Eov+vlI4puCE0bRXzUG5HdCsHBpAfeMkI8Hva6kSOusnzKqs8bf04hU7s3Sf0XxGTj/1AA==} - engines: {node: '>= 14'} + '@sentry/babel-plugin-component-annotate@5.1.1': + resolution: {integrity: sha512-x2wEpBHwsTyTF2rWsLKJlzrRF1TTIGOfX+ngdE+Yd5DBkoS58HwQv824QOviPGQRla4/ypISqAXzjdDPL/zalg==} + engines: {node: '>= 18'} - '@sentry/browser@10.32.1': - resolution: {integrity: sha512-NPNCXTZ05ZGTFyJdKNqjykpFm+urem0ebosILQiw3C4BxNVNGH4vfYZexyl6prRhmg91oB6GjVNiVDuJiap1gg==} + '@sentry/browser@10.40.0': + resolution: {integrity: sha512-nCt3FKUMFad0C6xl5wCK0Jz+qT4Vev4fv6HJRn0YoNRRDQCfsUVxAz7pNyyiPNGM/WCDp9wJpGJsRvbBRd2anw==} engines: {node: '>=18'} - '@sentry/bundler-plugin-core@4.6.1': - resolution: {integrity: sha512-WPeRbnMXm927m4Kr69NTArPfI+p5/34FHftdCRI3LFPMyhZDzz6J3wLy4hzaVUgmMf10eLzmq2HGEMvpQmdynA==} - engines: {node: '>= 14'} + '@sentry/bundler-plugin-core@5.1.1': + resolution: {integrity: sha512-F+itpwR9DyQR7gEkrXd2tigREPTvtF5lC8qu6e4anxXYRTui1+dVR0fXNwjpyAZMhIesLfXRN7WY7ggdj7hi0Q==} + engines: {node: '>= 18'} - '@sentry/cli-darwin@2.58.4': - resolution: {integrity: sha512-kbTD+P4X8O+nsNwPxCywtj3q22ecyRHWff98rdcmtRrvwz8CKi/T4Jxn/fnn2i4VEchy08OWBuZAqaA5Kh2hRQ==} + '@sentry/cli-darwin@2.58.5': + resolution: {integrity: sha512-lYrNzenZFJftfwSya7gwrHGxtE+Kob/e1sr9lmHMFOd4utDlmq0XFDllmdZAMf21fxcPRI1GL28ejZ3bId01fQ==} engines: {node: '>=10'} os: [darwin] - '@sentry/cli-linux-arm64@2.58.4': - resolution: {integrity: sha512-0g0KwsOozkLtzN8/0+oMZoOuQ0o7W6O+hx+ydVU1bktaMGKEJLMAWxOQNjsh1TcBbNIXVOKM/I8l0ROhaAb8Ig==} + '@sentry/cli-linux-arm64@2.58.5': + resolution: {integrity: sha512-/4gywFeBqRB6tR/iGMRAJ3HRqY6Z7Yp4l8ZCbl0TDLAfHNxu7schEw4tSnm2/Hh9eNMiOVy4z58uzAWlZXAYBQ==} engines: {node: '>=10'} cpu: [arm64] os: [linux, freebsd, android] - '@sentry/cli-linux-arm@2.58.4': - resolution: {integrity: sha512-rdQ8beTwnN48hv7iV7e7ZKucPec5NJkRdrrycMJMZlzGBPi56LqnclgsHySJ6Kfq506A2MNuQnKGaf/sBC9REA==} + '@sentry/cli-linux-arm@2.58.5': + resolution: {integrity: sha512-KtHweSIomYL4WVDrBrYSYJricKAAzxUgX86kc6OnlikbyOhoK6Fy8Vs6vwd52P6dvWPjgrMpUYjW2M5pYXQDUw==} engines: {node: '>=10'} cpu: [arm] os: [linux, freebsd, android] - '@sentry/cli-linux-i686@2.58.4': - resolution: {integrity: sha512-NseoIQAFtkziHyjZNPTu1Gm1opeQHt7Wm1LbLrGWVIRvUOzlslO9/8i6wETUZ6TjlQxBVRgd3Q0lRBG2A8rFYA==} + '@sentry/cli-linux-i686@2.58.5': + resolution: {integrity: sha512-G7261dkmyxqlMdyvyP06b+RTIVzp1gZNgglj5UksxSouSUqRd/46W/2pQeOMPhloDYo9yLtCN2YFb3Mw4aUsWw==} engines: {node: '>=10'} cpu: [x86, ia32] os: [linux, freebsd, android] - '@sentry/cli-linux-x64@2.58.4': - resolution: {integrity: sha512-d3Arz+OO/wJYTqCYlSN3Ktm+W8rynQ/IMtSZLK8nu0ryh5mJOh+9XlXY6oDXw4YlsM8qCRrNquR8iEI1Y/IH+Q==} + '@sentry/cli-linux-x64@2.58.5': + resolution: {integrity: sha512-rP04494RSmt86xChkQ+ecBNRYSPbyXc4u0IA7R7N1pSLCyO74e5w5Al+LnAq35cMfVbZgz5Sm0iGLjyiUu4I1g==} engines: {node: '>=10'} cpu: [x64] os: [linux, freebsd, android] - '@sentry/cli-win32-arm64@2.58.4': - resolution: {integrity: sha512-bqYrF43+jXdDBh0f8HIJU3tbvlOFtGyRjHB8AoRuMQv9TEDUfENZyCelhdjA+KwDKYl48R1Yasb4EHNzsoO83w==} + '@sentry/cli-win32-arm64@2.58.5': + resolution: {integrity: sha512-AOJ2nCXlQL1KBaCzv38m3i2VmSHNurUpm7xVKd6yAHX+ZoVBI8VT0EgvwmtJR2TY2N2hNCC7UrgRmdUsQ152bA==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@sentry/cli-win32-i686@2.58.4': - resolution: {integrity: sha512-3triFD6jyvhVcXOmGyttf+deKZcC1tURdhnmDUIBkiDPJKGT/N5xa4qAtHJlAB/h8L9jgYih9bvJnvvFVM7yug==} + '@sentry/cli-win32-i686@2.58.5': + resolution: {integrity: sha512-EsuboLSOnlrN7MMPJ1eFvfMDm+BnzOaSWl8eYhNo8W/BIrmNgpRUdBwnWn9Q2UOjJj5ZopukmsiMYtU/D7ml9g==} engines: {node: '>=10'} cpu: [x86, ia32] os: [win32] - '@sentry/cli-win32-x64@2.58.4': - resolution: {integrity: sha512-cSzN4PjM1RsCZ4pxMjI0VI7yNCkxiJ5jmWncyiwHXGiXrV1eXYdQ3n1LhUYLZ91CafyprR0OhDcE+RVZ26Qb5w==} + '@sentry/cli-win32-x64@2.58.5': + resolution: {integrity: sha512-IZf+XIMiQwj+5NzqbOQfywlOitmCV424Vtf9c+ep61AaVScUFD1TSrQbOcJJv5xGxhlxNOMNgMeZhdexdzrKZg==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@sentry/cli@2.58.4': - resolution: {integrity: sha512-ArDrpuS8JtDYEvwGleVE+FgR+qHaOp77IgdGSacz6SZy6Lv90uX0Nu4UrHCQJz8/xwIcNxSqnN22lq0dH4IqTg==} + '@sentry/cli@2.58.5': + resolution: {integrity: sha512-tavJ7yGUZV+z3Ct2/ZB6mg339i08sAk6HDkgqmSRuQEu2iLS5sl9HIvuXfM6xjv8fwlgFOSy++WNABNAcGHUbg==} engines: {node: '>= 10'} hasBin: true - '@sentry/core@10.32.1': - resolution: {integrity: sha512-PH2ldpSJlhqsMj2vCTyU0BI2Fx1oIDhm7Izo5xFALvjVCS0gmlqHt1udu6YlKn8BtpGH6bGzssvv5APrk+OdPQ==} + '@sentry/core@10.40.0': + resolution: {integrity: sha512-/wrcHPp9Avmgl6WBimPjS4gj810a1wU5oX9fF1bzJfeIIbF3jTsAbv0oMbgDp0cSDnkwv2+NvcPnn3+c5J6pBA==} engines: {node: '>=18'} - '@sentry/nestjs@10.32.1': - resolution: {integrity: sha512-StgRg8AojiCbH+Q7uhO/9DOhfpjw6SxtsTWwNoioLfHIx968btdQPhALrHji0xXR8DYDBf+bk99P1KdqgDDh/w==} + '@sentry/nestjs@10.40.0': + resolution: {integrity: sha512-JhrgWqhQyiQSPqmWu2eEzoxh+2TIaeFNKg9jcmkE1kHsTuE2wM/UNtebHX5VXg8qqreEXknjXaE9/zW+/UsuxA==} engines: {node: '>=18'} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 '@nestjs/core': ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0 - '@sentry/nextjs@10.32.1': - resolution: {integrity: sha512-MlgQiKg9P2clKeyH+ZLdmNiMNfTMs/2DBK9V/enLZvYJd1sy5hmrkAV/NiLxVP0uXAeMEVtrgFMIb64cH7ZcXQ==} + '@sentry/nextjs@10.40.0': + resolution: {integrity: sha512-0aID+iQ/8oEfmB2j8RRnQqio0AQcxTMiuEV+ev8K64UqJOb64cXNGBYP7fAankd0/jQOvIOuHvZhoZi9pwiRbg==} engines: {node: '>=18'} peerDependencies: next: 14.2.35 - '@sentry/node-core@10.32.1': - resolution: {integrity: sha512-w56rxdBanBKc832zuwnE+zNzUQ19fPxfHEtOhK8JGPu3aSwQYcIxwz9z52lOx3HN7k/8Fj5694qlT3x/PokhRw==} + '@sentry/node-core@10.40.0': + resolution: {integrity: sha512-ciZGOF54rJH9Fkg7V3v4gmWVufnJRqQQOrn0KStuo49vfPQAJLGePDx+crQv0iNVoLc6Hmrr6E7ebNHSb4NSAw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0 - '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 '@opentelemetry/instrumentation': '>=0.57.1 <1' - '@opentelemetry/resources': ^1.30.1 || ^2.1.0 || ^2.2.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0 - '@opentelemetry/semantic-conventions': ^1.37.0 + '@opentelemetry/resources': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@opentelemetry/context-async-hooks': + optional: true + '@opentelemetry/core': + optional: true + '@opentelemetry/instrumentation': + optional: true + '@opentelemetry/resources': + optional: true + '@opentelemetry/sdk-trace-base': + optional: true + '@opentelemetry/semantic-conventions': + optional: true - '@sentry/node@10.32.1': - resolution: {integrity: sha512-oxlybzt8QW0lx/QaEj1DcvZDRXkgouewFelu/10dyUwv5So3YvipfvWInda+yMLmn25OggbloDQ0gyScA2jU3g==} + '@sentry/node@10.40.0': + resolution: {integrity: sha512-HQETLoNZTUUM8PBxFPT4X0qepzk5NcyWg3jyKUmF7Hh/19KSJItBXXZXxx+8l3PC2eASXUn70utXi65PoXEHWA==} engines: {node: '>=18'} - '@sentry/opentelemetry@10.32.1': - resolution: {integrity: sha512-YLssSz5Y+qPvufrh2cDaTXDoXU8aceOhB+YTjT8/DLF6SOj7Tzen52aAcjNaifawaxEsLCC8O+B+A2iA+BllvA==} + '@sentry/opentelemetry@10.40.0': + resolution: {integrity: sha512-Zx6T258qlEhQfdghIlazSTbK7uRO0pXWw4/4/VPR8pMOiRPh8dAoJg8AB0L55PYPMpVdXxNf7L9X0EZoDYibJw==} engines: {node: '>=18'} peerDependencies: '@opentelemetry/api': ^1.9.0 - '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 || ^2.2.0 - '@opentelemetry/core': ^1.30.1 || ^2.1.0 || ^2.2.0 - '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 || ^2.2.0 - '@opentelemetry/semantic-conventions': ^1.37.0 + '@opentelemetry/context-async-hooks': ^1.30.1 || ^2.1.0 + '@opentelemetry/core': ^1.30.1 || ^2.1.0 + '@opentelemetry/sdk-trace-base': ^1.30.1 || ^2.1.0 + '@opentelemetry/semantic-conventions': ^1.39.0 - '@sentry/profiling-node@10.32.1': - resolution: {integrity: sha512-UDSZayQw4K5wv/XNHoB+i+KrnlCStLb0H2lsypF4dgQFCrHXmbwhMh9ieofVGk5bxdmXoL3lSE+3W9cJbpqy2A==} + '@sentry/profiling-node@10.40.0': + resolution: {integrity: sha512-/F9PVVsHrAJsU8biqHcskcbOMJPIeebl1+UNIh4q3O1WE0yfzmCc/lwBJjno0ipbksX0J3cOd4tbNDq7nNxRjg==} engines: {node: '>=18'} hasBin: true - '@sentry/react@10.32.1': - resolution: {integrity: sha512-/tX0HeACbAmVP57x8txTrGk/U3fa9pDBaoAtlOrnPv5VS/aC5SGkehXWeTGSAa+ahlOWwp3IF8ILVXRiOoG/Vg==} + '@sentry/react@10.40.0': + resolution: {integrity: sha512-3T5W/e3QJMimXRIOx8xMEZbxeIuFiKlXvHLcMTLGygGBYnxQGeb8Oz/8heov+3zF1JoCIxeVQNFW0woySApfyA==} engines: {node: '>=18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/vercel-edge@10.32.1': - resolution: {integrity: sha512-3hrc7TVs4ZeYSCOZdgmv9D1Bke2osnImfupceW8THecNv3uEUjYbrC2UkS/TFMiVHc9qpYzUnKbsGezMp3Bcaw==} + '@sentry/vercel-edge@10.40.0': + resolution: {integrity: sha512-DdW8F5NE69Wm1CdKTaElFBtTsEzZZlYWs6tkHPY6GapQ97XY+71zu73cx7jFJgCGG/W4l0Em/BQlzNcw4U0V9A==} engines: {node: '>=18'} - '@sentry/webpack-plugin@4.6.1': - resolution: {integrity: sha512-CJgT/t2pQWsPsMx9VJ86goU/orCQhL2HhDj5ZYBol6fPPoEGeTqKOPCnv/xsbCAfGSp1uHpyRLTA/Gx96u7VVA==} - engines: {node: '>= 14'} + '@sentry/webpack-plugin@5.1.1': + resolution: {integrity: sha512-XgQg+t2aVrlQDfIiAEizqR/bsy6GtBygwgR+Kw11P/cYczj4W9PZ2IYqQEStBzHqnRTh5DbpyMcUNW2CujdA9A==} + engines: {node: '>= 18'} peerDependencies: - webpack: '>=4.40.0' + webpack: '>=5.0.0' - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} @@ -6054,220 +6252,220 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@smithy/abort-controller@4.2.7': - resolution: {integrity: sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==} + '@smithy/abort-controller@4.2.10': + resolution: {integrity: sha512-qocxM/X4XGATqQtUkbE9SPUB6wekBi+FyJOMbPj0AhvyvFGYEmOlz6VB22iMePCQsFmMIvFSeViDvA7mZJG47g==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader-native@4.2.1': - resolution: {integrity: sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==} + '@smithy/chunked-blob-reader-native@4.2.2': + resolution: {integrity: sha512-QzzYIlf4yg0w5TQaC9VId3B3ugSk1MI/wb7tgcHtd7CBV9gNRKZrhc2EPSxSZuDy10zUZ0lomNMgkc6/VVe8xg==} engines: {node: '>=18.0.0'} - '@smithy/chunked-blob-reader@5.2.0': - resolution: {integrity: sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==} + '@smithy/chunked-blob-reader@5.2.1': + resolution: {integrity: sha512-y5d4xRiD6TzeP5BWlb+Ig/VFqF+t9oANNhGeMqyzU7obw7FYgTgVi50i5JqBTeKp+TABeDIeeXFZdz65RipNtA==} engines: {node: '>=18.0.0'} - '@smithy/config-resolver@4.4.5': - resolution: {integrity: sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==} + '@smithy/config-resolver@4.4.9': + resolution: {integrity: sha512-ejQvXqlcU30h7liR9fXtj7PIAau1t/sFbJpgWPfiYDs7zd16jpH0IsSXKcba2jF6ChTXvIjACs27kNMc5xxE2Q==} engines: {node: '>=18.0.0'} - '@smithy/core@3.20.0': - resolution: {integrity: sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==} + '@smithy/core@3.23.6': + resolution: {integrity: sha512-4xE+0L2NrsFKpEVFlFELkIHQddBvMbQ41LRIP74dGCXnY1zQ9DgksrBcRBDJT+iOzGy4VEJIeU3hkUK5mn06kg==} engines: {node: '>=18.0.0'} - '@smithy/credential-provider-imds@4.2.7': - resolution: {integrity: sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==} + '@smithy/credential-provider-imds@4.2.10': + resolution: {integrity: sha512-3bsMLJJLTZGZqVGGeBVFfLzuRulVsGTj12BzRKODTHqUABpIr0jMN1vN3+u6r2OfyhAQ2pXaMZWX/swBK5I6PQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-codec@4.2.7': - resolution: {integrity: sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ==} + '@smithy/eventstream-codec@4.2.10': + resolution: {integrity: sha512-A4ynrsFFfSXUHicfTcRehytppFBcY3HQxEGYiyGktPIOye3Ot7fxpiy4VR42WmtGI4Wfo6OXt/c1Ky1nUFxYYQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-browser@4.2.7': - resolution: {integrity: sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g==} + '@smithy/eventstream-serde-browser@4.2.10': + resolution: {integrity: sha512-0xupsu9yj9oDVuQ50YCTS9nuSYhGlrwqdaKQel9y2Fz7LU9fNErVlw9N0o4pm4qqvWEGbSTI4HKc6XJfB30MVw==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-config-resolver@4.3.7': - resolution: {integrity: sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ==} + '@smithy/eventstream-serde-config-resolver@4.3.10': + resolution: {integrity: sha512-8kn6sinrduk0yaYHMJDsNuiFpXwQwibR7n/4CDUqn4UgaG+SeBHu5jHGFdU9BLFAM7Q4/gvr9RYxBHz9/jKrhA==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-node@4.2.7': - resolution: {integrity: sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A==} + '@smithy/eventstream-serde-node@4.2.10': + resolution: {integrity: sha512-uUrxPGgIffnYfvIOUmBM5i+USdEBRTdh7mLPttjphgtooxQ8CtdO1p6K5+Q4BBAZvKlvtJ9jWyrWpBJYzBKsyQ==} engines: {node: '>=18.0.0'} - '@smithy/eventstream-serde-universal@4.2.7': - resolution: {integrity: sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g==} + '@smithy/eventstream-serde-universal@4.2.10': + resolution: {integrity: sha512-aArqzOEvcs2dK+xQVCgLbpJQGfZihw8SD4ymhkwNTtwKbnrzdhJsFDKuMQnam2kF69WzgJYOU5eJlCx+CA32bw==} engines: {node: '>=18.0.0'} - '@smithy/fetch-http-handler@5.3.8': - resolution: {integrity: sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==} + '@smithy/fetch-http-handler@5.3.11': + resolution: {integrity: sha512-wbTRjOxdFuyEg0CpumjZO0hkUl+fetJFqxNROepuLIoijQh51aMBmzFLfoQdwRjxsuuS2jizzIUTjPWgd8pd7g==} engines: {node: '>=18.0.0'} - '@smithy/hash-blob-browser@4.2.8': - resolution: {integrity: sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw==} + '@smithy/hash-blob-browser@4.2.11': + resolution: {integrity: sha512-DrcAx3PM6AEbWZxsKl6CWAGnVwiz28Wp1ZhNu+Hi4uI/6C1PIZBIaPM2VoqBDAsOWbM6ZVzOEQMxFLLdmb4eBQ==} engines: {node: '>=18.0.0'} - '@smithy/hash-node@4.2.7': - resolution: {integrity: sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==} + '@smithy/hash-node@4.2.10': + resolution: {integrity: sha512-1VzIOI5CcsvMDvP3iv1vG/RfLJVVVc67dCRyLSB2Hn9SWCZrDO3zvcIzj3BfEtqRW5kcMg5KAeVf1K3dR6nD3w==} engines: {node: '>=18.0.0'} - '@smithy/hash-stream-node@4.2.7': - resolution: {integrity: sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ==} + '@smithy/hash-stream-node@4.2.10': + resolution: {integrity: sha512-w78xsYrOlwXKwN5tv1GnKIRbHb1HygSpeZMP6xDxCPGf1U/xDHjCpJu64c5T35UKyEPwa0bPeIcvU69VY3khUA==} engines: {node: '>=18.0.0'} - '@smithy/invalid-dependency@4.2.7': - resolution: {integrity: sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==} + '@smithy/invalid-dependency@4.2.10': + resolution: {integrity: sha512-vy9KPNSFUU0ajFYk0sDZIYiUlAWGEAhRfehIr5ZkdFrRFTAuXEPUd41USuqHU6vvLX4r6Q9X7MKBco5+Il0Org==} engines: {node: '>=18.0.0'} '@smithy/is-array-buffer@2.2.0': resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} engines: {node: '>=14.0.0'} - '@smithy/is-array-buffer@4.2.0': - resolution: {integrity: sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ==} + '@smithy/is-array-buffer@4.2.1': + resolution: {integrity: sha512-Yfu664Qbf1B4IYIsYgKoABt010daZjkaCRvdU/sPnZG6TtHOB0md0RjNdLGzxe5UIdn9js4ftPICzmkRa9RJ4Q==} engines: {node: '>=18.0.0'} - '@smithy/md5-js@4.2.7': - resolution: {integrity: sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw==} + '@smithy/md5-js@4.2.10': + resolution: {integrity: sha512-Op+Dh6dPLWTjWITChFayDllIaCXRofOed8ecpggTC5fkh8yXes0vAEX7gRUfjGK+TlyxoCAA05gHbZW/zB9JwQ==} engines: {node: '>=18.0.0'} - '@smithy/middleware-content-length@4.2.7': - resolution: {integrity: sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==} + '@smithy/middleware-content-length@4.2.10': + resolution: {integrity: sha512-TQZ9kX5c6XbjhaEBpvhSvMEZ0klBs1CFtOdPFwATZSbC9UeQfKHPLPN9Y+I6wZGMOavlYTOlHEPDrt42PMSH9w==} engines: {node: '>=18.0.0'} - '@smithy/middleware-endpoint@4.4.1': - resolution: {integrity: sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==} + '@smithy/middleware-endpoint@4.4.20': + resolution: {integrity: sha512-9W6Np4ceBP3XCYAGLoMCmn8t2RRVzuD1ndWPLBbv7H9CrwM9Bprf6Up6BM9ZA/3alodg0b7Kf6ftBK9R1N04vw==} engines: {node: '>=18.0.0'} - '@smithy/middleware-retry@4.4.17': - resolution: {integrity: sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==} + '@smithy/middleware-retry@4.4.37': + resolution: {integrity: sha512-/1psZZllBBSQ7+qo5+hhLz7AEPGLx3Z0+e3ramMBEuPK2PfvLK4SrncDB9VegX5mBn+oP/UTDrM6IHrFjvX1ZA==} engines: {node: '>=18.0.0'} - '@smithy/middleware-serde@4.2.8': - resolution: {integrity: sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==} + '@smithy/middleware-serde@4.2.11': + resolution: {integrity: sha512-STQdONGPwbbC7cusL60s7vOa6He6A9w2jWhoapL0mgVjmR19pr26slV+yoSP76SIssMTX/95e5nOZ6UQv6jolg==} engines: {node: '>=18.0.0'} - '@smithy/middleware-stack@4.2.7': - resolution: {integrity: sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==} + '@smithy/middleware-stack@4.2.10': + resolution: {integrity: sha512-pmts/WovNcE/tlyHa8z/groPeOtqtEpp61q3W0nW1nDJuMq/x+hWa/OVQBtgU0tBqupeXq0VBOLA4UZwE8I0YA==} engines: {node: '>=18.0.0'} - '@smithy/node-config-provider@4.3.7': - resolution: {integrity: sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==} + '@smithy/node-config-provider@4.3.10': + resolution: {integrity: sha512-UALRbJtVX34AdP2VECKVlnNgidLHA2A7YgcJzwSBg1hzmnO/bZBHl/LDQQyYifzUwp1UOODnl9JJ3KNawpUJ9w==} engines: {node: '>=18.0.0'} - '@smithy/node-http-handler@4.4.7': - resolution: {integrity: sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==} + '@smithy/node-http-handler@4.4.12': + resolution: {integrity: sha512-zo1+WKJkR9x7ZtMeMDAAsq2PufwiLDmkhcjpWPRRkmeIuOm6nq1qjFICSZbnjBvD09ei8KMo26BWxsu2BUU+5w==} engines: {node: '>=18.0.0'} - '@smithy/property-provider@4.2.7': - resolution: {integrity: sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==} + '@smithy/property-provider@4.2.10': + resolution: {integrity: sha512-5jm60P0CU7tom0eNrZ7YrkgBaoLFXzmqB0wVS+4uK8PPGmosSrLNf6rRd50UBvukztawZ7zyA8TxlrKpF5z9jw==} engines: {node: '>=18.0.0'} - '@smithy/protocol-http@5.3.7': - resolution: {integrity: sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==} + '@smithy/protocol-http@5.3.10': + resolution: {integrity: sha512-2NzVWpYY0tRdfeCJLsgrR89KE3NTWT2wGulhNUxYlRmtRmPwLQwKzhrfVaiNlA9ZpJvbW7cjTVChYKgnkqXj1A==} engines: {node: '>=18.0.0'} - '@smithy/querystring-builder@4.2.7': - resolution: {integrity: sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==} + '@smithy/querystring-builder@4.2.10': + resolution: {integrity: sha512-HeN7kEvuzO2DmAzLukE9UryiUvejD3tMp9a1D1NJETerIfKobBUCLfviP6QEk500166eD2IATaXM59qgUI+YDA==} engines: {node: '>=18.0.0'} - '@smithy/querystring-parser@4.2.7': - resolution: {integrity: sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==} + '@smithy/querystring-parser@4.2.10': + resolution: {integrity: sha512-4Mh18J26+ao1oX5wXJfWlTT+Q1OpDR8ssiC9PDOuEgVBGloqg18Fw7h5Ct8DyT9NBYwJgtJ2nLjKKFU6RP1G1Q==} engines: {node: '>=18.0.0'} - '@smithy/service-error-classification@4.2.7': - resolution: {integrity: sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==} + '@smithy/service-error-classification@4.2.10': + resolution: {integrity: sha512-0R/+/Il5y8nB/By90o8hy/bWVYptbIfvoTYad0igYQO5RefhNCDmNzqxaMx7K1t/QWo0d6UynqpqN5cCQt1MCg==} engines: {node: '>=18.0.0'} - '@smithy/shared-ini-file-loader@4.4.2': - resolution: {integrity: sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==} + '@smithy/shared-ini-file-loader@4.4.5': + resolution: {integrity: sha512-pHgASxl50rrtOztgQCPmOXFjRW+mCd7ALr/3uXNzRrRoGV5G2+78GOsQ3HlQuBVHCh9o6xqMNvlIKZjWn4Euug==} engines: {node: '>=18.0.0'} - '@smithy/signature-v4@5.3.7': - resolution: {integrity: sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==} + '@smithy/signature-v4@5.3.10': + resolution: {integrity: sha512-Wab3wW8468WqTKIxI+aZe3JYO52/RYT/8sDOdzkUhjnLakLe9qoQqIcfih/qxcF4qWEFoWBszY0mj5uxffaVXA==} engines: {node: '>=18.0.0'} - '@smithy/smithy-client@4.10.2': - resolution: {integrity: sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==} + '@smithy/smithy-client@4.12.0': + resolution: {integrity: sha512-R8bQ9K3lCcXyZmBnQqUZJF4ChZmtWT5NLi6x5kgWx5D+/j0KorXcA0YcFg/X5TOgnTCy1tbKc6z2g2y4amFupQ==} engines: {node: '>=18.0.0'} - '@smithy/types@4.11.0': - resolution: {integrity: sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==} + '@smithy/types@4.13.0': + resolution: {integrity: sha512-COuLsZILbbQsdrwKQpkkpyep7lCsByxwj7m0Mg5v66/ZTyenlfBc40/QFQ5chO0YN/PNEH1Bi3fGtfXPnYNeDw==} engines: {node: '>=18.0.0'} - '@smithy/url-parser@4.2.7': - resolution: {integrity: sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==} + '@smithy/url-parser@4.2.10': + resolution: {integrity: sha512-uypjF7fCDsRk26u3qHmFI/ePL7bxxB9vKkE+2WKEciHhz+4QtbzWiHRVNRJwU3cKhrYDYQE3b0MRFtqfLYdA4A==} engines: {node: '>=18.0.0'} - '@smithy/util-base64@4.3.0': - resolution: {integrity: sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ==} + '@smithy/util-base64@4.3.1': + resolution: {integrity: sha512-BKGuawX4Doq/bI/uEmg+Zyc36rJKWuin3py89PquXBIBqmbnJwBBsmKhdHfNEp0+A4TDgLmT/3MSKZ1SxHcR6w==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-browser@4.2.0': - resolution: {integrity: sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg==} + '@smithy/util-body-length-browser@4.2.1': + resolution: {integrity: sha512-SiJeLiozrAoCrgDBUgsVbmqHmMgg/2bA15AzcbcW+zan7SuyAVHN4xTSbq0GlebAIwlcaX32xacnrG488/J/6g==} engines: {node: '>=18.0.0'} - '@smithy/util-body-length-node@4.2.1': - resolution: {integrity: sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA==} + '@smithy/util-body-length-node@4.2.2': + resolution: {integrity: sha512-4rHqBvxtJEBvsZcFQSPQqXP2b/yy/YlB66KlcEgcH2WNoOKCKB03DSLzXmOsXjbl8dJ4OEYTn31knhdznwk7zw==} engines: {node: '>=18.0.0'} '@smithy/util-buffer-from@2.2.0': resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} engines: {node: '>=14.0.0'} - '@smithy/util-buffer-from@4.2.0': - resolution: {integrity: sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew==} + '@smithy/util-buffer-from@4.2.1': + resolution: {integrity: sha512-/swhmt1qTiVkaejlmMPPDgZhEaWb/HWMGRBheaxwuVkusp/z+ErJyQxO6kaXumOciZSWlmq6Z5mNylCd33X7Ig==} engines: {node: '>=18.0.0'} - '@smithy/util-config-provider@4.2.0': - resolution: {integrity: sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q==} + '@smithy/util-config-provider@4.2.1': + resolution: {integrity: sha512-462id/00U8JWFw6qBuTSWfN5TxOHvDu4WliI97qOIOnuC/g+NDAknTU8eoGXEPlLkRVgWEr03jJBLV4o2FL8+A==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-browser@4.3.16': - resolution: {integrity: sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==} + '@smithy/util-defaults-mode-browser@4.3.36': + resolution: {integrity: sha512-R0smq7EHQXRVMxkAxtH5akJ/FvgAmNF6bUy/GwY/N20T4GrwjT633NFm0VuRpC+8Bbv8R9A0DoJ9OiZL/M3xew==} engines: {node: '>=18.0.0'} - '@smithy/util-defaults-mode-node@4.2.19': - resolution: {integrity: sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==} + '@smithy/util-defaults-mode-node@4.2.39': + resolution: {integrity: sha512-otWuoDm35btJV1L8MyHrPl462B07QCdMTktKc7/yM+Psv6KbED/ziXiHnmr7yPHUjfIwE9S8Max0LO24Mo3ZVg==} engines: {node: '>=18.0.0'} - '@smithy/util-endpoints@3.2.7': - resolution: {integrity: sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==} + '@smithy/util-endpoints@3.3.1': + resolution: {integrity: sha512-xyctc4klmjmieQiF9I1wssBWleRV0RhJ2DpO8+8yzi2LO1Z+4IWOZNGZGNj4+hq9kdo+nyfrRLmQTzc16Op2Vg==} engines: {node: '>=18.0.0'} - '@smithy/util-hex-encoding@4.2.0': - resolution: {integrity: sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw==} + '@smithy/util-hex-encoding@4.2.1': + resolution: {integrity: sha512-c1hHtkgAWmE35/50gmdKajgGAKV3ePJ7t6UtEmpfCWJmQE9BQAQPz0URUVI89eSkcDqCtzqllxzG28IQoZPvwA==} engines: {node: '>=18.0.0'} - '@smithy/util-middleware@4.2.7': - resolution: {integrity: sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==} + '@smithy/util-middleware@4.2.10': + resolution: {integrity: sha512-LxaQIWLp4y0r72eA8mwPNQ9va4h5KeLM0I3M/HV9klmFaY2kN766wf5vsTzmaOpNNb7GgXAd9a25P3h8T49PSA==} engines: {node: '>=18.0.0'} - '@smithy/util-retry@4.2.7': - resolution: {integrity: sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==} + '@smithy/util-retry@4.2.10': + resolution: {integrity: sha512-HrBzistfpyE5uqTwiyLsFHscgnwB0kgv8vySp7q5kZ0Eltn/tjosaSGGDj/jJ9ys7pWzIP/icE2d+7vMKXLv7A==} engines: {node: '>=18.0.0'} - '@smithy/util-stream@4.5.8': - resolution: {integrity: sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==} + '@smithy/util-stream@4.5.15': + resolution: {integrity: sha512-OlOKnaqnkU9X+6wEkd7mN+WB7orPbCVDauXOj22Q7VtiTkvy7ZdSsOg4QiNAZMgI4OkvNf+/VLUC3VXkxuWJZw==} engines: {node: '>=18.0.0'} - '@smithy/util-uri-escape@4.2.0': - resolution: {integrity: sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA==} + '@smithy/util-uri-escape@4.2.1': + resolution: {integrity: sha512-YmiUDn2eo2IOiWYYvGQkgX5ZkBSiTQu4FlDo5jNPpAxng2t6Sjb6WutnZV9l6VR4eJul1ABmCrnWBC9hKHQa6Q==} engines: {node: '>=18.0.0'} '@smithy/util-utf8@2.3.0': resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} engines: {node: '>=14.0.0'} - '@smithy/util-utf8@4.2.0': - resolution: {integrity: sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw==} + '@smithy/util-utf8@4.2.1': + resolution: {integrity: sha512-DSIwNaWtmzrNQHv8g7DBGR9mulSit65KSj5ymGEIAknmIN8IpbZefEep10LaMG/P/xquwbmJ1h9ectz8z6mV6g==} engines: {node: '>=18.0.0'} - '@smithy/util-waiter@4.2.7': - resolution: {integrity: sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw==} + '@smithy/util-waiter@4.2.10': + resolution: {integrity: sha512-4eTWph/Lkg1wZEDAyObwme0kmhEb7J/JjibY2znJdrYRgKbKqB7YoEhhJVJ4R1g/SYih4zuwX7LpJaM8RsnTVg==} engines: {node: '>=18.0.0'} - '@smithy/uuid@1.1.0': - resolution: {integrity: sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw==} + '@smithy/uuid@1.1.1': + resolution: {integrity: sha512-dSfDCeihDmZlV2oyr0yWPTUfh07suS+R5OB+FZGiv/hHyK3hrFBW5rR1UYjfa57vBsrP9lciFkRPzebaV1Qujw==} engines: {node: '>=18.0.0'} '@socket.io/component-emitter@3.1.2': @@ -6638,20 +6836,20 @@ packages: '@storybook/channels@7.6.17': resolution: {integrity: sha512-GFG40pzaSxk1hUr/J/TMqW5AFDDPUSu+HkeE/oqSWJbOodBOLJzHN6CReJS6y1DjYSZLNFt1jftPWZZInG/XUA==} - '@storybook/channels@7.6.21': - resolution: {integrity: sha512-899XbW60IXIkWDo90bS5ovjxnFUDgD8B2ZwUEJUmuhIXqQeSg2iJ8uYI699Csei+DoDn5gZYJD+BHbSUuc4g+Q==} + '@storybook/channels@7.6.23': + resolution: {integrity: sha512-19BaHUOnylMNlvKzEoTrvm1MQa5RLJIUItq01iaqlwwUE00oTdzA2D+g745TpAb9hBI6cAo1c0uie5SeHRyMFg==} '@storybook/client-logger@7.6.17': resolution: {integrity: sha512-6WBYqixAXNAXlSaBWwgljWpAu10tPRBJrcFvx2gPUne58EeMM20Gi/iHYBz2kMCY+JLAgeIH7ZxInqwO8vDwiQ==} - '@storybook/client-logger@7.6.21': - resolution: {integrity: sha512-NWh32K+N6htmmPfqSPOlA6gy80vFQZLnusK8+/7Hp0sSG//OV5ahlnlSveLUOub2e97CU5EvYUL1xNmSuqk2jQ==} + '@storybook/client-logger@7.6.23': + resolution: {integrity: sha512-p1Z5pRWSsF/FCtkJ5/99ysuw82Raon8JmnlxxpyIVDJ5KD3VutBX9pZL8XPa8C1IKvc0oBfqSgBUdTRRo0lqYQ==} '@storybook/core-events@7.6.17': resolution: {integrity: sha512-AriWMCm/k1cxlv10f+jZ1wavThTRpLaN3kY019kHWbYT9XgaSuLU67G7GPr3cGnJ6HuA6uhbzu8qtqVCd6OfXA==} - '@storybook/core-events@7.6.21': - resolution: {integrity: sha512-Ez6bhYuXbEkHVCmnNB/oqN0sQwphsmtPmjYdPMlTtEpVEIXHAw2qOlaDiGakoDHkgrTaxiYvdJrPH0UcEJcWDQ==} + '@storybook/core-events@7.6.23': + resolution: {integrity: sha512-r/OtLZdPAPxl8SSgJmxt4xeBrC46dBPyxzB68SuA9C581jlpbV1tAhV6Yj7FADo10JlZJrO3Qw9s2FfHII5vag==} '@storybook/csf@0.1.13': resolution: {integrity: sha512-7xOOwCLGB3ebM87eemep89MYRFTko+D8qE7EdAAq74lgdqRR5cOUtYWJLjO2dLtP94nqoOdHJo6MdLLKzg412Q==} @@ -6665,8 +6863,8 @@ packages: '@storybook/preview-api@7.6.17': resolution: {integrity: sha512-wLfDdI9RWo1f2zzFe54yRhg+2YWyxLZvqdZnSQ45mTs4/7xXV5Wfbv3QNTtcdw8tT3U5KRTrN1mTfTCiRJc0Kw==} - '@storybook/preview-api@7.6.21': - resolution: {integrity: sha512-L5e6VjphfsnJk/kkOIRJzDaTfX5sNpiusocqEbHKTM7c9ZDAuaLPZKluP87AJ0u16UdWMuCu6YaQ6eAakDa9gg==} + '@storybook/preview-api@7.6.23': + resolution: {integrity: sha512-Z7gG5eFf0RymKuiUoOu/GIF/TD5uMrLZcvlsTgcXQszJc7jJRR6pYzKmahrgoHH+MyZghU7oFNYGpfSltGUihw==} '@storybook/router@7.6.17': resolution: {integrity: sha512-GnyC0j6Wi5hT4qRhSyT8NPtJfGmf82uZw97LQRWeyYu5gWEshUdM7aj40XlNiScd5cZDp0owO1idduVF2k2l2A==} @@ -6680,18 +6878,18 @@ packages: '@storybook/types@7.6.17': resolution: {integrity: sha512-GRY0xEJQ0PrL7DY2qCNUdIfUOE0Gsue6N+GBJw9ku1IUDFLJRDOF+4Dx2BvYcVCPI5XPqdWKlEyZdMdKjiQN7Q==} - '@storybook/types@7.6.21': - resolution: {integrity: sha512-rJaBMxzXZOsJpqZGhebFJxOguZQBw5j+MVpqbFBA6vLZPx9wEbDBeVsPUxCxj+V1XkVcrNXf9qfThyJ8ETmLBw==} + '@storybook/types@7.6.23': + resolution: {integrity: sha512-b1QRH5AH9Xpzz0VAOgtbMSXimM2yJL2xKZOZshb0/IRohN5Q0zYOM1hve1jbwm8gGLRKIag1unnTV8UoXwbELQ==} - '@stripe/react-stripe-js@5.4.1': - resolution: {integrity: sha512-ipeYcAHa4EPmjwfv0lFE+YDVkOQ0TMKkFWamW+BqmnSkEln/hO8rmxGPPWcd9WjqABx6Ro8Xg4pAS7evCcR9cw==} + '@stripe/react-stripe-js@5.6.0': + resolution: {integrity: sha512-tucu/vTGc+5NXbo2pUiaVjA4ENdRBET8qGS00BM4BAU8J4Pi3eY6BHollsP2+VSuzzlvXwMg0it3ZLhbCj2fPg==} peerDependencies: '@stripe/stripe-js': '>=8.0.0 <9.0.0' react: '>=16.8.0 <20.0.0' react-dom: '>=16.8.0 <20.0.0' - '@stripe/stripe-js@8.6.0': - resolution: {integrity: sha512-EB0/GGgs4hfezzkiMkinlRgWtjz8fSdwVQhwYS7Sg/RQrSvuNOz+ssPjD+lAzqaYTCB0zlbrt0fcqVziLJrufQ==} + '@stripe/stripe-js@8.8.0': + resolution: {integrity: sha512-NNYuyW8qmLjyHnpyFgs/23wUrjB8k0xN9YIZFOMLewCa/pIkIji9e9aY/EgdNryEDDRptc6TcPIHRvG1R0ClFw==} engines: {node: '>=12.16'} '@supercharge/request-ip@1.2.0': @@ -6894,65 +7092,65 @@ packages: resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} engines: {node: '>=10'} - '@tailwindcss/node@4.1.18': - resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + '@tailwindcss/node@4.2.1': + resolution: {integrity: sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg==} - '@tailwindcss/oxide-android-arm64@4.1.18': - resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-android-arm64@4.2.1': + resolution: {integrity: sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg==} + engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.1.18': - resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-darwin-arm64@4.2.1': + resolution: {integrity: sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw==} + engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.1.18': - resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-darwin-x64@4.2.1': + resolution: {integrity: sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw==} + engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.1.18': - resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-freebsd-x64@4.2.1': + resolution: {integrity: sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA==} + engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': - resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': + resolution: {integrity: sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw==} + engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': - resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': + resolution: {integrity: sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-arm64-musl@4.1.18': - resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': + resolution: {integrity: sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ==} + engines: {node: '>= 20'} cpu: [arm64] os: [linux] - '@tailwindcss/oxide-linux-x64-gnu@4.1.18': - resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': + resolution: {integrity: sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-linux-x64-musl@4.1.18': - resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-linux-x64-musl@4.2.1': + resolution: {integrity: sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g==} + engines: {node: '>= 20'} cpu: [x64] os: [linux] - '@tailwindcss/oxide-wasm32-wasi@4.1.18': - resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} + '@tailwindcss/oxide-wasm32-wasi@4.2.1': + resolution: {integrity: sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -6963,70 +7161,70 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': - resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': + resolution: {integrity: sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA==} + engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.1.18': - resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} - engines: {node: '>= 10'} + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': + resolution: {integrity: sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ==} + engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.1.18': - resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} - engines: {node: '>= 10'} + '@tailwindcss/oxide@4.2.1': + resolution: {integrity: sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw==} + engines: {node: '>= 20'} - '@tailwindcss/postcss@4.1.18': - resolution: {integrity: sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==} + '@tailwindcss/postcss@4.2.1': + resolution: {integrity: sha512-OEwGIBnXnj7zJeonOh6ZG9woofIjGrd2BORfvE5p9USYKDCZoQmfqLcfNiRWoJlRWLdNPn2IgVZuWAOM4iTYMw==} - '@tailwindcss/vite@4.1.18': - resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} + '@tailwindcss/vite@4.2.1': + resolution: {integrity: sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 - '@tanstack/react-virtual@3.13.13': - resolution: {integrity: sha512-4o6oPMDvQv+9gMi8rE6gWmsOjtUZUYIJHv7EB+GblyYdi8U6OqLl8rhHWIUZSL1dUU2dPwTdTgybCKf9EjIrQg==} + '@tanstack/react-virtual@3.13.19': + resolution: {integrity: sha512-KzwmU1IbE0IvCZSm6OXkS+kRdrgW2c2P3Ho3NC+zZXWK6oObv/L+lcV/2VuJ+snVESRlMJ+w/fg4WXI/JzoNGQ==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tanstack/virtual-core@3.13.13': - resolution: {integrity: sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA==} + '@tanstack/virtual-core@3.13.19': + resolution: {integrity: sha512-/BMP7kNhzKOd7wnDeB8NrIRNLwkf5AhCYCvtfZV2GXWbBieFm/el0n6LOAXlTi6ZwHICSNnQcIxRCWHrLzDY+g==} - '@temporalio/activity@1.14.0': - resolution: {integrity: sha512-ayGqfjqW8R1nhow54Y3A5ezoVwFr4SbB8VHaQA3seDFOB+6TyOVSlulYqGgFMxl/FXBkRa/VEswEDqS/xQq7aQ==} - engines: {node: '>= 18.0.0'} + '@temporalio/activity@1.15.0': + resolution: {integrity: sha512-kKEIrHMTsANiEpDVZd+v3OT6kU20UtcvAK7iibJNzQnoZUGC7XnqdKQlBuGYBxgpJzD9ppGgskXRczW+XU5Kng==} + engines: {node: '>= 20.0.0'} - '@temporalio/client@1.14.0': - resolution: {integrity: sha512-kjzJ+7M2kHj32cTTSQT5WOjEIOxY0TNV5g6Sw9PzWmKWdtIZig+d7qUIA3VjDe/TieNozxjR2wNAX5sKzYFANA==} - engines: {node: '>= 18.0.0'} + '@temporalio/client@1.15.0': + resolution: {integrity: sha512-SxTGqRIa2+Vy4P9+06ZpUf4u7ZZmOXfx/kr9XvNqAApLxTMKjTQIg5OH5Wt4JLUtIR7dFkuHIyhewdRyG+hSsQ==} + engines: {node: '>= 20.0.0'} - '@temporalio/common@1.14.0': - resolution: {integrity: sha512-jVmurBdFHdqw/wIehzVJikS8MhavL630p88TJ64P5PH0nP8S5V8R5vhkmHZ7n0sMRO+A0QFyWYyvnccu6MQZvw==} - engines: {node: '>= 18.0.0'} + '@temporalio/common@1.15.0': + resolution: {integrity: sha512-tBfC3fdOExsNoS5krkMUXnaMtCRKj05Jts4+TH+cgHpbys68nslFvUQLqwPIw2x6155Divb9MF219a/75itbTg==} + engines: {node: '>= 20.0.0'} - '@temporalio/core-bridge@1.14.0': - resolution: {integrity: sha512-62WRbESKVtCx1FafbikQB90EwKNF+mEAaOJKifUIU4lQnk9wlZPRfrf6pwyqr+Uqi7uZhD2YqHXWUNVYbmQU7w==} - engines: {node: '>= 18.0.0'} + '@temporalio/core-bridge@1.15.0': + resolution: {integrity: sha512-Qdrs5zju5MiOwmERCWzQ6uHZwn1JaQk/ppS2UHbxqZndjbAEFPDU9KQqFLkxWAicHMy7+LGPnA4DpVOANGlTZA==} + engines: {node: '>= 20.0.0'} - '@temporalio/nexus@1.14.0': - resolution: {integrity: sha512-0tgf+EBuz5vgYUukaYUzVHKr27XNQejXXO1i0x8+4sjR5zN6euNKraHfRzrDWRSm3nTZ6199rCTbR+CPrqaC/g==} - engines: {node: '>= 18.0.0'} + '@temporalio/nexus@1.15.0': + resolution: {integrity: sha512-E6CdIjskkbK2aObxcb76Z4V3o1D3QDxEtsxmuHX5D7HEABuYGdV+oeOzDyxMlfeY9GyIM9Nvky4XCiSz2h2XRA==} + engines: {node: '>= 20.0.0'} - '@temporalio/proto@1.14.0': - resolution: {integrity: sha512-duYVjt3x6SkuFzJr+5NlklEgookPqW065qdcvogmdfVjrgiwz4W/07AN3+fL4ufmqt1//0SyF6nyqv9RNADYNA==} - engines: {node: '>= 18.0.0'} + '@temporalio/proto@1.15.0': + resolution: {integrity: sha512-Awy4Fjzyba7Pg/CVZjjQ3x2CWkDL1qELyTZWcLlyjXq8bX694JVfBsmiMmF6tHn5/ySOIxDTcc0MSScZ0oKX5A==} + engines: {node: '>= 20.0.0'} - '@temporalio/worker@1.14.0': - resolution: {integrity: sha512-wo5rgPSt83aT1hLYmh/0X4yOx/6uRbIvBa9LXqGo7s9s1GJkUyJpAahRt8aMoLm4qPsiZtu1gtU5KcASOmgqtg==} - engines: {node: '>= 18.0.0'} + '@temporalio/worker@1.15.0': + resolution: {integrity: sha512-9e0AWP2OxYFgeztMdkoWYbDVqmNubreRkG7/frVKFT3xHdIOrFQ2W6Yomv61q3oKMXTIrpvjClHtiTjAUr70uA==} + engines: {node: '>= 20.0.0'} - '@temporalio/workflow@1.14.0': - resolution: {integrity: sha512-hxUqCZTkdSwgy5nc/O1DIpYH0Z77cM57RfJvhK4ELmkkb1jh/Q4dshDannH1qQ1zYT0IKRBHSW7m1aMy1+dgDA==} - engines: {node: '>= 18.0.0'} + '@temporalio/workflow@1.15.0': + resolution: {integrity: sha512-VaMhVtlA0hLLM/pna26vFSdn5W1Arq2+ccgItdbdRdZUa0X8eW2B7sl/PcUYQOWc4aMUGnvPATKUGUoFKyCxSg==} + engines: {node: '>= 20.0.0'} '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} @@ -7043,179 +7241,179 @@ packages: '@types/react': optional: true - '@tiptap/core@3.14.0': - resolution: {integrity: sha512-nm0VWVA1Vq/jaKY3wyRXViL/kf78yMdH7qETpv4qZXDQLU+pdWV3IGoRTQTKESc7d8L1wL/2uCeByLNUJfrSIw==} + '@tiptap/core@3.20.0': + resolution: {integrity: sha512-aC9aROgia/SpJqhsXFiX9TsligL8d+oeoI8W3u00WI45s0VfsqjgeKQLDLF7Tu7hC+7F02teC84SAHuup003VQ==} peerDependencies: - '@tiptap/pm': ^3.14.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-blockquote@3.14.0': - resolution: {integrity: sha512-I7aOqcVLHBgCeRtMaMHA+ILSS8Sli46fjFq8477stOpQ79TPiBd6e4SDuFCAu58M94mVLMvlPKF2Eh5IvbIMyQ==} + '@tiptap/extension-blockquote@3.20.0': + resolution: {integrity: sha512-LQzn6aGtL4WXz2+rYshl/7/VnP2qJTpD7fWL96GXAzhqviPEY1bJES7poqJb3MU/gzl8VJUVzVzU1VoVfUKlbA==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-bold@3.14.0': - resolution: {integrity: sha512-T4ma6VLoHm9JupglidD3CfZXm89A3HMv99gLplXNizvy1mlr4R3uC3aBqKw6lAP+NoqCqbIgjwc4YYsqZClNwA==} + '@tiptap/extension-bold@3.20.0': + resolution: {integrity: sha512-sQklEWiyf58yDjiHtm5vmkVjfIc/cBuSusmCsQ0q9vGYnEF1iOHKhGpvnCeEXNeqF3fiJQRlquzt/6ymle3Iwg==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-bubble-menu@3.14.0': - resolution: {integrity: sha512-nraHy+5jumT67J7hWrCuVwVTS2vNj4FpV5kO8epVySBmgEBr/7Pyi4w7mQA1VRVOMdjeN9iypbgQ2rKhpfaoTw==} + '@tiptap/extension-bubble-menu@3.20.0': + resolution: {integrity: sha512-MDosUfs8Tj+nwg8RC+wTMWGkLJORXmbR6YZgbiX4hrc7G90Gopdd6kj6ht5/T8t7dLLaX7N0+DEHdUEPGED7dw==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-bullet-list@3.14.0': - resolution: {integrity: sha512-luqPX4u52hiOAHJ95mYsNE+x+9dZxsM461Xny9d/eTXLjAcnwS7MghjrnpljvyYsSXNiwQtxUyEr4uEZZJ5gIQ==} + '@tiptap/extension-bullet-list@3.20.0': + resolution: {integrity: sha512-OcKMeopBbqWzhSi6o8nNz0aayogg1sfOAhto3NxJu3Ya32dwBFqmHXSYM6uW4jOphNvVPyjiq9aNRh3qTdd1dw==} peerDependencies: - '@tiptap/extension-list': ^3.14.0 + '@tiptap/extension-list': ^3.20.0 - '@tiptap/extension-code-block@3.14.0': - resolution: {integrity: sha512-hRSdIhhm3Q9JBMQdKaifRVFnAa4sG+M7l1QcTKR3VSYVy2/oR0U+aiOifi5OvMRBUwhaR71Ro+cMT9FH9s26Kg==} + '@tiptap/extension-code-block@3.20.0': + resolution: {integrity: sha512-lBbmNek14aCjrHcBcq3PRqWfNLvC6bcRa2Osc6e/LtmXlcpype4f6n+Yx+WZ+f2uUh0UmDRCz7BEyUETEsDmlQ==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-code@3.14.0': - resolution: {integrity: sha512-Sx9yLorzS+oqNmXID4jt0G5tDnsEgU0HtEXPLD3KNt/ltVxWJU0AXwCsp1/Dg0HIDL868vWpJ2jC1t/4oaf9kA==} + '@tiptap/extension-code@3.20.0': + resolution: {integrity: sha512-TYDWFeSQ9umiyrqsT6VecbuhL8XIHkUhO+gEk0sVvH67ZLwjFDhAIIgWIr1/dbIGPcvMZM19E7xUUhAdIaXaOQ==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-document@3.14.0': - resolution: {integrity: sha512-O3D7/GPB3XrWGy0y/b4LMHiY0eTd+dyIbSdiFtmUnbC/E9lqQLw43GiqvD9Gm6AyKhBA+Z45dKMbaOe1c6eTwQ==} + '@tiptap/extension-document@3.20.0': + resolution: {integrity: sha512-oJfLIG3vAtZo/wg29WiBcyWt22KUgddpP8wqtCE+kY5Dw8znLR9ehNmVWlSWJA5OJUMO0ntAHx4bBT+I2MBd5w==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-dropcursor@3.14.0': - resolution: {integrity: sha512-IwHyiZKLjV9WSBlQFS+afMjucIML8wFAKkG8UKCu+CVOe/Qd1ImDGyv6rzPlCmefJkDHIUWS+c2STapJlUD1VQ==} + '@tiptap/extension-dropcursor@3.20.0': + resolution: {integrity: sha512-d+cxplRlktVgZPwatnc34IArlppM0IFKS1J5wLk+ba1jidizsbMVh45tP/BTK2flhyfRqcNoB5R0TArhUpbkNQ==} peerDependencies: - '@tiptap/extensions': ^3.14.0 + '@tiptap/extensions': ^3.20.0 - '@tiptap/extension-floating-menu@3.14.0': - resolution: {integrity: sha512-+ErwDF74NzX4JV0nXMSIUT9V8FDdo85r0SaBZ8lb2NLmElaA3LDklcNV7SsoKlRcwsAXtFkqQbDwXLNGQLYSPQ==} + '@tiptap/extension-floating-menu@3.20.0': + resolution: {integrity: sha512-rYs4Bv5pVjqZ/2vvR6oe7ammZapkAwN51As/WDbemvYDjfOGRqK58qGauUjYZiDzPOEIzI2mxGwsZ4eJhPW4Ig==} peerDependencies: '@floating-ui/dom': ^1.0.0 - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-gapcursor@3.14.0': - resolution: {integrity: sha512-hMg2U59+c9FreYtTvzxx5GWKejdZLRITMLEu4OTfrgQok6uF4qkzGEEqmYqPiHk08TBqAg18Y5bbpyqTsuit9A==} + '@tiptap/extension-gapcursor@3.20.0': + resolution: {integrity: sha512-P/LasfvG9/qFq43ZAlNbAnPnXC+/RJf49buTrhtFvI9Zg0+Lbpjx1oh6oMHB19T88Y28KtrckfFZ8aTSUWDq6w==} peerDependencies: - '@tiptap/extensions': ^3.14.0 + '@tiptap/extensions': ^3.20.0 - '@tiptap/extension-hard-break@3.14.0': - resolution: {integrity: sha512-XKxr8usQp+kFevhDK6Ccmnq1CIkLmPClhKwbt7AClGLKLBtEVAS1qUgcmKudkw8cD8Q2/69twI37LXa23sfuLA==} + '@tiptap/extension-hard-break@3.20.0': + resolution: {integrity: sha512-rqvhMOw4f+XQmEthncbvDjgLH6fz8L9splnKZC7OeS0eX8b0qd7+xI1u5kyxF3KA2Z0BnigES++jjWuecqV6mA==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-heading@3.14.0': - resolution: {integrity: sha512-4xpahSo3b1dN2nwA0XKXLQVz9nZ/vE443a/Y5QLWeXiu3v9wkcMs/5kQ5ysFeDZRBTfVUWBqhngI7zhvDUx2zQ==} + '@tiptap/extension-heading@3.20.0': + resolution: {integrity: sha512-JgJhurnCe3eN6a0lEsNQM/46R1bcwzwWWZEFDSb1P9dR8+t1/5v7cMZWsSInpD7R4/74iJn0+M5hcXLwCmBmYA==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-history@3.14.0': - resolution: {integrity: sha512-wHDCVXTHcrbOuA6sUFxAZ980/TQa8F6cytP6HfP8J/nDlNDJ/hiov3RIw7kkkHrlHZYG+Qu8EprqFZU6OHAzNQ==} + '@tiptap/extension-history@3.20.0': + resolution: {integrity: sha512-NA3dFanmnPTmz72yEVwOjs/3Y4ze9UObFo94yzzn6OBfhOfE7HDqHw/FS+abqy2OrZK5kI2+leOUDjH+qGqdeA==} peerDependencies: - '@tiptap/extensions': ^3.14.0 + '@tiptap/extensions': ^3.20.0 - '@tiptap/extension-horizontal-rule@3.14.0': - resolution: {integrity: sha512-65O4T9vPKLUKO1fLowh5jqtfQlH5eaIL7qb/uj5sXMMg8O7TCvBIRkwNuYsFTkJmTk4vBy+fjZ0uwSY3DFkO1g==} + '@tiptap/extension-horizontal-rule@3.20.0': + resolution: {integrity: sha512-6uvcutFMv+9wPZgptDkbRDjAm3YVxlibmkhWD5GuaWwS9L/yUtobpI3GycujRSUZ8D3q6Q9J7LqpmQtQRTalWA==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-italic@3.14.0': - resolution: {integrity: sha512-Arl5EaG4wdyipwvKjsI7Krlk3OkmqvLfF0YfGwsd5AVDxTiYuiDGgz7RF8J2kttbBeiUTqwME5xpkryQK3F+fg==} + '@tiptap/extension-italic@3.20.0': + resolution: {integrity: sha512-/DhnKQF8yN8RxtuL8abZ28wd5281EaGoE2Oha35zXSOF1vNYnbyt8Ymkv/7u1BcWEWTvRPgaju0YCGXisPRLYw==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-link@3.14.0': - resolution: {integrity: sha512-xaeJIktD42rJ4t9fbQpKe+yYNZ+YFIK96cp1Kdm0hZHv/8MPMNRiF85TRY+9U1aoyh5uRcspgCj7EKQb2Hs7qg==} + '@tiptap/extension-link@3.20.0': + resolution: {integrity: sha512-qI/5A+R0ZWBxo/8HxSn1uOyr7odr3xHBZ/gzOR1GUJaZqjlJxkWFX0RtXMbLKEGEvT25o345cF7b0wFznEh8qA==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-list-item@3.14.0': - resolution: {integrity: sha512-19Dcp8HCFdhINmRy0KQLFfz9ZEuVwFWGAAjYG7BvMvkd9k4sJ5vCv5fej59G99rhsc+tCmik77w+SLksOcxwKQ==} + '@tiptap/extension-list-item@3.20.0': + resolution: {integrity: sha512-qEtjaaGPuqaFB4VpLrGDoIe9RHnckxPfu6d3rc22ap6TAHCDyRv05CEyJogqccnFceG/v5WN4znUBER8RWnWHA==} peerDependencies: - '@tiptap/extension-list': ^3.14.0 + '@tiptap/extension-list': ^3.20.0 - '@tiptap/extension-list-keymap@3.14.0': - resolution: {integrity: sha512-1oPbvNnQjeOxkHZcUbWPx/IY9o4fT3QGk/9A9cIjFrJRD2AHzbYfPDHNHINtg7Bj0jWz74cHvAHcaxP+M27jkA==} + '@tiptap/extension-list-keymap@3.20.0': + resolution: {integrity: sha512-Z4GvKy04Ms4cLFN+CY6wXswd36xYsT2p/YL0V89LYFMZTerOeTjFYlndzn6svqL8NV1PRT5Diw4WTTxJSmcJPA==} peerDependencies: - '@tiptap/extension-list': ^3.14.0 + '@tiptap/extension-list': ^3.20.0 - '@tiptap/extension-list@3.14.0': - resolution: {integrity: sha512-rsjFH0Vd/4UbDsjwMLay7oz72VVu1r35t8ofAzy5587jn5JAjflaZs05XbRRMD2imUTK41dyajVSh8CqSnDEJw==} + '@tiptap/extension-list@3.20.0': + resolution: {integrity: sha512-+V0/gsVWAv+7vcY0MAe6D52LYTIicMSHw00wz3ISZgprSb2yQhJ4+4gurOnUrQ4Du3AnRQvxPROaofwxIQ66WQ==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/extension-mention@3.14.0': - resolution: {integrity: sha512-PGSAKke1pUuGEadqthvSyJLHLh4eVwRoOMsAAmxr1P0UqATpEgVMHAeyEXrRf5FORN0Wu1LzgiuXPFLkhZhfzg==} + '@tiptap/extension-mention@3.20.0': + resolution: {integrity: sha512-wUjsq7Za0JJdJzrGNG+g8nrCpek/85GQ0Rm9bka3PynIVRwus+xQqW6IyWVPBdl1BSkrbgMAUqtrfoh1ymznbg==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 - '@tiptap/suggestion': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 + '@tiptap/suggestion': ^3.20.0 - '@tiptap/extension-ordered-list@3.14.0': - resolution: {integrity: sha512-/fXjVL4JajkJQoc213iiput0bCXC4ztUPUpvNuI62VcgFKHcTvX4eYxED1VflotCx0OdkyY9yYD8PtvyO5lkmA==} + '@tiptap/extension-ordered-list@3.20.0': + resolution: {integrity: sha512-jVKnJvrizLk7etwBMfyoj6H2GE4M+PD4k7Bwp6Bh1ohBWtfIA1TlngdS842Mx5i1VB2e3UWIwr8ZH46gl6cwMA==} peerDependencies: - '@tiptap/extension-list': ^3.14.0 + '@tiptap/extension-list': ^3.20.0 - '@tiptap/extension-paragraph@3.14.0': - resolution: {integrity: sha512-NFxk2yNo3Cvh9g8evea+yTLNV48se7MbMcVizTnVhobqtBKv793qsb5FM5Hu30Y72FQPNfH+LRoap4XZyBPfVw==} + '@tiptap/extension-paragraph@3.20.0': + resolution: {integrity: sha512-mM99zK4+RnEXIMCv6akfNATAs0Iija6FgyFA9J9NZ6N4o8y9QiNLLa6HjLpAC+W+VoCgQIekyoF/Q9ftxmAYDQ==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-strike@3.14.0': - resolution: {integrity: sha512-R8BbAhnWpisBml6okMKl98hY4tJjedTTgyTkx8tPabIJ92nS9IURKEk3foWB9uHxdTOBUqTvVT+2ScDf9r6QHg==} + '@tiptap/extension-strike@3.20.0': + resolution: {integrity: sha512-0vcTZRRAiDfon3VM1mHBr9EFmTkkUXMhm0Xtdtn0bGe+sIqufyi+hUYTEw93EQOD9XNsPkrud6jzQNYpX2H3AQ==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-text@3.14.0': - resolution: {integrity: sha512-XlpnD87LQ7lLcDcBenHgzxv3uivQzPdVHM16CY4lXR4aKDIp2mxjPZr4twHT+cOnRQHc8VYpRgkEo6LLX6VylA==} + '@tiptap/extension-text@3.20.0': + resolution: {integrity: sha512-tf8bE8tSaOEWabCzPm71xwiUhyMFKqY9jkP5af3Kr1/F45jzZFIQAYZooHI/+zCHRrgJ99MQHKHe1ZNvODrKHQ==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extension-underline@3.14.0': - resolution: {integrity: sha512-zmnWlsi2g/tMlThHby0Je9O+v24j4d+qcXF3nuzLUUaDsGCEtOyC9RzwITft59ViK+Nc2PD2W/J14rsB0j+qoQ==} + '@tiptap/extension-underline@3.20.0': + resolution: {integrity: sha512-LzNXuy2jwR/y+ymoUqC72TiGzbOCjioIjsDu0MNYpHuHqTWPK5aV9Mh0nbZcYFy/7fPlV1q0W139EbJeYBZEAQ==} peerDependencies: - '@tiptap/core': ^3.14.0 + '@tiptap/core': ^3.20.0 - '@tiptap/extensions@3.14.0': - resolution: {integrity: sha512-qQBVKqzU4ZVjRn8W0UbdfE4LaaIgcIWHOMrNnJ+PutrRzQ6ZzhmD/kRONvRWBfG9z3DU7pSKGwVYSR2hztsGuQ==} + '@tiptap/extensions@3.20.0': + resolution: {integrity: sha512-HIsXX942w3nbxEQBlMAAR/aa6qiMBEP7CsSMxaxmTIVAmW35p6yUASw6GdV1u0o3lCZjXq2OSRMTskzIqi5uLg==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 - '@tiptap/pm@3.14.0': - resolution: {integrity: sha512-xrZmqI5jl4yMeAsu8p8gVP9S3An5h2MBi8BQHNnZmpyzkUrlpd40vlT6u13SWIqVi5ZWhBZ6U3rL7mkVLZuRKg==} + '@tiptap/pm@3.20.0': + resolution: {integrity: sha512-jn+2KnQZn+b+VXr8EFOJKsnjVNaA4diAEr6FOazupMt8W8ro1hfpYtZ25JL87Kao/WbMze55sd8M8BDXLUKu1A==} - '@tiptap/react@3.14.0': - resolution: {integrity: sha512-Eo/nLyKxHvnLIF4gI2WFhGJiVrqfA6XL9kismVG9NwBNF/NblMDmZZu6Z2SH/ONJQz2Egn7UBPNp3BMq/qZDcg==} + '@tiptap/react@3.20.0': + resolution: {integrity: sha512-jFLNzkmn18zqefJwPje0PPd9VhZ7Oy28YHiSvSc7YpBnQIbuN/HIxZ2lrOsKyEHta0WjRZjfU5X1pGxlbcGwOA==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^17.0.0 || ^18.0.0 || ^19.0.0 react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - '@tiptap/starter-kit@3.14.0': - resolution: {integrity: sha512-fHsC4oDVzvMU9btg+IUmu/eqPquapjJ341qaNI7cCeSCKjjE6XJEN6WcONLAVId2OZUwML0IX1Jgl+6gJxU9Jw==} + '@tiptap/starter-kit@3.20.0': + resolution: {integrity: sha512-W4+1re35pDNY/7rpXVg+OKo/Fa4Gfrn08Bq3E3fzlJw6gjE3tYU8dY9x9vC2rK9pd9NOp7Af11qCFDaWpohXkw==} - '@tiptap/suggestion@3.14.0': - resolution: {integrity: sha512-B9BQ9Tck8HsISDc6jZmtaSpl8KK69JbKHfU0ntILxgj8aBASElewO+W8WE49JSTxuyJGRgnhGSgaGpM4LhbLAg==} + '@tiptap/suggestion@3.20.0': + resolution: {integrity: sha512-OA9Fe+1Q/Ex0ivTcpRcVFiLnNsVdIBmiEoctt/gu4H2ayCYmZ906veioXNdc1m/3MtVVUIuEnvwwsrOZXlfDEw==} peerDependencies: - '@tiptap/core': ^3.14.0 - '@tiptap/pm': ^3.14.0 + '@tiptap/core': ^3.20.0 + '@tiptap/pm': ^3.20.0 '@tokenizer/inflate@0.2.7': resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} engines: {node: '>=18'} - '@tokenizer/inflate@0.3.1': - resolution: {integrity: sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==} + '@tokenizer/inflate@0.4.1': + resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} engines: {node: '>=18'} '@tokenizer/token@0.3.0': @@ -7371,6 +7569,12 @@ packages: '@types/diff-match-patch@1.0.36': resolution: {integrity: sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==} + '@types/dom-mediacapture-transform@0.1.11': + resolution: {integrity: sha512-Y2p+nGf1bF2XMttBnsVPHUWzRRZzqUoJAKmiP10b5umnO6DDrWI0BrGDJy1pOHoOULVmGSfFNkQrAlC5dcj6nQ==} + + '@types/dom-webcodecs@0.1.13': + resolution: {integrity: sha512-O5hkiFIcjjszPIYyUSyvScyvrBoV3NOEEZx/pMlsu44TKzWNkLVBBxnxJz42in5n3QIolYOcBYFCPZZ0h8SkwQ==} + '@types/eslint-scope@3.7.7': resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} @@ -7383,11 +7587,11 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/express-serve-static-core@4.19.7': - resolution: {integrity: sha512-FvPtiIf1LfhzsaIXhv/PHan/2FeQBbtBDtfX2QfvPxdUelMDEckK08SM6nqo1MIZY3RUlfA+HV8+hFUSio78qg==} + '@types/express-serve-static-core@4.19.8': + resolution: {integrity: sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==} - '@types/express-serve-static-core@5.1.0': - resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} + '@types/express-serve-static-core@5.1.1': + resolution: {integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==} '@types/express@4.17.25': resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} @@ -7416,8 +7620,8 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + '@types/http-cache-semantics@4.2.0': + resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} @@ -7449,8 +7653,8 @@ packages: '@types/jsonwebtoken@9.0.10': resolution: {integrity: sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==} - '@types/katex@0.16.7': - resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + '@types/katex@0.16.8': + resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} '@types/keyv@3.1.4': resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} @@ -7458,8 +7662,8 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} - '@types/lodash@4.17.21': - resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} '@types/luxon@3.4.2': resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==} @@ -7500,14 +7704,17 @@ packages: '@types/multipipe@3.0.5': resolution: {integrity: sha512-mHBbV67bsmUtLtio0gj/GPzGsjv+Y6K1ff/48iR6YAfFfLkBtRIR0M5lZPbkMCyHGrCZM9p3VNnfY1QCws4t4w==} + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + '@types/mysql@2.15.27': resolution: {integrity: sha512-YfWiV16IY0OeBfBCk8+hXKmdTKrKlwKN1MNKAPBu5JYxLwBEZl7QzeEpGnlZb3VMGJrrGmB84gXiH+ofs/TezA==} '@types/node-fetch@2.6.13': resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - '@types/node-telegram-bot-api@0.64.13': - resolution: {integrity: sha512-/d3sYpZq4sDwKWAm9XsIsb8MclclJ1yPXZC7uAzZRrJIFY8yg/63oNrLf9CBTlaS/XGR1N66BwibfJQGiWVsqg==} + '@types/node-telegram-bot-api@0.64.14': + resolution: {integrity: sha512-Nq1LAAw4PGpR8Vii5F7uGlAaAFmaT3cPIt7mUZv6VrftPsrt9xjmnhXD9hoKFstAzn/a5ZCr+ksbkmK+QBhZiA==} '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -7518,8 +7725,11 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/nodemailer@6.4.21': - resolution: {integrity: sha512-Eix+sb/Nj28MNnWvO2X1OLrk5vuD4C9SMnb2Vf4itWnxphYeSceqkFX7IdmxTzn+dvmnNz7paMbg4Uc60wSfJg==} + '@types/node@20.19.35': + resolution: {integrity: sha512-Uarfe6J91b9HAUXxjvSOdiO2UPOKLm07Q1oh0JHxoZ1y8HoqxDAu3gVrsrOHeiio0kSsoVBt4wFrKOm0dKxVPQ==} + + '@types/nodemailer@6.4.23': + resolution: {integrity: sha512-aFV3/NsYFLSx9mbb5gtirBSXJnAlrusoKNuPbxsASWc7vrKLmIrTQRpdcxNcSFL3VW2A2XpeLEavwb2qMi6nlQ==} '@types/oracledb@6.5.2': resolution: {integrity: sha512-kK1eBS/Adeyis+3OlBDMeQQuasIDLUYXsi2T15ccNJ0iyUpQ4xDF7svFu3+bGVrI0CMBUclPciz+lsQR3JX3TQ==} @@ -7533,14 +7743,17 @@ packages: '@types/pg-pool@2.0.6': resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} + '@types/pg-pool@2.0.7': + resolution: {integrity: sha512-U4CwmGVQcbEuqpyju8/ptOKg6gEC+Tqsvj2xS9o1g71bUh8twxnC6ZL5rZKCsGN0iyH0CwgUyc9VR5owNQF9Ng==} + '@types/pg@8.15.5': resolution: {integrity: sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==} '@types/pg@8.15.6': resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} - '@types/prismjs@1.26.5': - resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} + '@types/prismjs@1.26.6': + resolution: {integrity: sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw==} '@types/prop-types@15.7.15': resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} @@ -7655,8 +7868,11 @@ packages: '@types/validator@13.15.10': resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} - '@types/webextension-polyfill@0.12.4': - resolution: {integrity: sha512-wK8YdSI0pDiaehSLDIvtvonYmLwUUivg4Z6JCJO8rkyssMAG82cFJgwPK/V7NO61mJBLg/tXeoXQL8AFzpXZmQ==} + '@types/webextension-polyfill@0.12.5': + resolution: {integrity: sha512-uKSAv6LgcVdINmxXMKBuVIcg/2m5JZugoZO8x20g7j2bXJkPIl/lVGQcDlbV+aXAiTyXT2RA5U5mI4IGCDMQeg==} + + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} '@types/ws@7.4.7': resolution: {integrity: sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==} @@ -7735,11 +7951,11 @@ packages: '@ucast/core@1.10.2': resolution: {integrity: sha512-ons5CwXZ/51wrUPfoduC+cO7AS1/wRb0ybpQJ9RrssossDxVy4t49QxWoWgfBDvVKsz9VXzBk9z0wqTdZ+Cq8g==} - '@ucast/js@3.0.4': - resolution: {integrity: sha512-TgG1aIaCMdcaEyckOZKQozn1hazE0w90SVdlpIJ/er8xVumE11gYAtSbw/LBeUnA4fFnFWTcw3t6reqseeH/4Q==} + '@ucast/js@3.1.0': + resolution: {integrity: sha512-eJ7yQeYtMK85UZjxoxBEbTWx6UMxEXKbjVyp+NlzrT5oMKV5Gpo/9bjTl3r7msaXTVC8iD9NJacqJ8yp7joX+Q==} - '@ucast/mongo2js@1.4.0': - resolution: {integrity: sha512-vR9RJ3BHlkI3RfKJIZFdVktxWvBCQRiSTeJSWN9NPxP5YJkpfXvcBWAMLwvyJx4HbB+qib5/AlSDEmQiuQyx2w==} + '@ucast/mongo2js@1.4.1': + resolution: {integrity: sha512-9aeg5cmqwRQnKCXHN6I17wk83Rcm487bHelaG8T4vfpWneAI469wSI3Srnbu+PuZ5znWRbnwtVq9RgPL+bN6CA==} '@ucast/mongo@2.4.3': resolution: {integrity: sha512-XcI8LclrHWP83H+7H2anGCEeDq0n+12FU2mXCTz6/Tva9/9ddK/iacvvhCyW6cijAAOILmt0tWplRyRhVyZLsA==} @@ -7751,8 +7967,8 @@ packages: react: '>=18.0.0' react-dom: '>=18.0.0' - '@uiw/copy-to-clipboard@1.0.19': - resolution: {integrity: sha512-AYxzFUBkZrhtExb2QC0C4lFH2+BSx6JVId9iqeGHakBuosqiQHUQaNZCvIBeM97Ucp+nJ22flOh8FBT2pKRRAA==} + '@uiw/copy-to-clipboard@1.0.20': + resolution: {integrity: sha512-IFQhS62CLNon1YgYJTEzXR2N3WVXg7V1FaBRDLMlzU6JY5X6Hr3OPAcw4WNoKcz2XcFD6XCgwEjlsmj+JA0mWA==} '@uiw/react-markdown-preview@5.1.5': resolution: {integrity: sha512-DNOqx1a6gJR7Btt57zpGEKTfHRlb7rWbtctMRO2f82wWcuoJsxPBrM+JWebDdOD0LfD8oe2CQvW2ICQJKHQhZg==} @@ -8045,14 +8261,14 @@ packages: peerDependencies: '@uppy/core': ^4.5.2 - '@upstash/redis@1.35.8': - resolution: {integrity: sha512-QqLpVCD9PCPE6hlRzOkz864nfijSdazxtyJLIy9ZeTh6kU2nBIKKfjT5HMHjIRD4BCm6TK1dbUT9pxhFjcvpng==} + '@upstash/redis@1.36.3': + resolution: {integrity: sha512-wxo1ei4OHDHm4UGMgrNVz9QUEela9N/Iwi4p1JlHNSowQiPi+eljlGnfbZVkV0V4PIrjGtGFJt5GjWM5k28enA==} '@urql/core@5.2.0': resolution: {integrity: sha512-/n0ieD0mvvDnVAXEQgX/7qJiVcvYvNkOHeBvkwtylfjydar123caCXcl58PXFY11oU1oquJocVXHxLAbtv4x1A==} - '@vercel/oidc@3.0.5': - resolution: {integrity: sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw==} + '@vercel/oidc@3.2.0': + resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} engines: {node: '>= 20'} '@vitejs/plugin-react@4.7.0': @@ -8285,16 +8501,16 @@ packages: resolution: {integrity: sha512-b4PhJ+zYj4357zwk4TTuF2nEe0vVtOrwdsrNo5hL+u1ojXNhh1FgJ6pg1jzDlwlT4oBdzfSwaBwMCtFCsIWg8Q==} engines: {node: '>=18.0.0'} - '@whatwg-node/node-fetch@0.8.4': - resolution: {integrity: sha512-AlKLc57loGoyYlrzDbejB9EeR+pfdJdGzbYnkEuZaGekFboBwzfVYVMsy88PMriqPI1ORpiGYGgSSWpx7a2sDA==} + '@whatwg-node/node-fetch@0.8.5': + resolution: {integrity: sha512-4xzCl/zphPqlp9tASLVeUhB5+WJHbuWGYpfoC2q1qh5dw0AqZBW7L27V5roxYWijPxj4sspRAAoOH3d2ztaHUQ==} engines: {node: '>=18.0.0'} '@whatwg-node/promise-helpers@1.3.2': resolution: {integrity: sha512-Nst5JdK47VIl9UcGwtv2Rcgyn5lWtZ0/mhRQ4G8NN2isxpq2TO30iqHzmwoJycjWuyUfg3GFXqP/gFHXeV57IA==} engines: {node: '>=16.0.0'} - '@whatwg-node/server@0.10.17': - resolution: {integrity: sha512-QxI+HQfJeI/UscFNCTcSri6nrHP25mtyAMbhEri7W2ctdb3EsorPuJz7IovSgNjvKVs73dg9Fmayewx1O2xOxA==} + '@whatwg-node/server@0.10.18': + resolution: {integrity: sha512-kMwLlxUbduttIgaPdSkmEarFpP+mSY8FEm+QWMBRJwxOHWkri+cxd8KZHO9EMrB9vgUuz+5WEaCawaL5wGVoXg==} engines: {node: '>=18.0.0'} '@wyw-in-js/processor-utils@0.5.5': @@ -8394,12 +8610,12 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} engines: {node: '>=0.4.0'} - acorn@8.15.0: - resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true @@ -8461,14 +8677,14 @@ packages: peerDependencies: ajv: ^8.8.2 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} ajv@8.12.0: resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} anser@1.4.10: resolution: {integrity: sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==} @@ -8656,8 +8872,8 @@ packages: resolution: {integrity: sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ==} engines: {node: '>=4'} - autoprefixer@10.4.23: - resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} + autoprefixer@10.4.27: + resolution: {integrity: sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -8676,12 +8892,15 @@ packages: aws4@1.13.2: resolution: {integrity: sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==} - axe-core@4.11.0: - resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + axe-core@4.11.1: + resolution: {integrity: sha512-BASOg+YwO2C+346x3LZOeoovTIoTrRqEsqMa6fmfAV0P+U9mFr9NsyOEpiYvFjbc64NMrSswhV50WdXzdb/Z5A==} engines: {node: '>=4'} - axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + axios@1.13.5: + resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==} + + axios@1.13.6: + resolution: {integrity: sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==} axobject-query@3.2.4: resolution: {integrity: sha512-aPTElBrbifBU1krmZxGZOlBkslORe7Ll7+BDnI50Wy4LgOt69luMgevkDfTq1O/ZgprooPCtWpjCwKSZw/iZ4A==} @@ -8715,18 +8934,18 @@ packages: resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} engines: {node: '>=10', npm: '>=6'} - babel-plugin-polyfill-corejs2@0.4.14: - resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + babel-plugin-polyfill-corejs2@0.4.15: + resolution: {integrity: sha512-hR3GwrRwHUfYwGfrisXPIDP3JcYfBrW7wKE7+Au6wDYl7fm/ka1NEII6kORzxNU556JjfidZeBsO10kYvtV1aw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-corejs3@0.13.0: - resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + babel-plugin-polyfill-corejs3@0.14.0: + resolution: {integrity: sha512-AvDcMxJ34W4Wgy4KBIIePQTAOP1Ie2WFwkQp3dB7FQ/f0lI5+nM96zUnYEOE1P9sEg0es5VCP0HxiWu5fUHZAQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.5: - resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + babel-plugin-polyfill-regenerator@0.6.6: + resolution: {integrity: sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -8750,6 +8969,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} @@ -8770,12 +8993,13 @@ packages: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} - baseline-browser-mapping@2.9.11: - resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} hasBin: true - basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} + basic-ftp@5.2.0: + resolution: {integrity: sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==} engines: {node: '>=10.0.0'} bcp-47-match@2.0.3: @@ -8829,22 +9053,18 @@ packages: blueimp-canvas-to-blob@3.29.0: resolution: {integrity: sha512-0pcSSGxC0QxT+yVkivxIqW0Y4VlO2XSDPofBAqoJ1qJxgH9eiUDLv50Rixij2cDuEfx4M6DpD9UGZpRhT5Q8qg==} - bn.js@4.12.2: - resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==} + bn.js@4.12.3: + resolution: {integrity: sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==} - bn.js@5.2.2: - resolution: {integrity: sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==} - - body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + bn.js@5.2.3: + resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} body-parser@1.20.4: resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.1: - resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} boolbase@1.0.0: @@ -8856,8 +9076,8 @@ packages: bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} - bowser@2.13.1: - resolution: {integrity: sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw==} + bowser@2.14.1: + resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -8865,6 +9085,10 @@ packages: brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.4: + resolution: {integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -8960,8 +9184,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cache-manager@7.2.7: - resolution: {integrity: sha512-TKeeb9nSybk1e9E5yAiPVJ6YKdX9FYhwqqy8fBfVKAFVTJYZUNmeIvwjURW6+UikNsO6l2ta27thYgo/oumDsw==} + cache-manager@7.2.8: + resolution: {integrity: sha512-0HDaDLBBY/maa/LmUVAr70XUOwsiQD+jyzCBjmUErYZUKdMS9dT59PqW59PpVqfGM7ve6H0J6307JTpkCYefHQ==} cacheable-lookup@5.0.4: resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} @@ -9002,8 +9226,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001761: - resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} + caniuse-lite@1.0.30001775: + resolution: {integrity: sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==} canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} @@ -9012,10 +9236,6 @@ packages: capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} - cargo-cp-artifact@0.1.9: - resolution: {integrity: sha512-6F+UYzTaGB+awsTXg0uSJA1/b/B3DDJzpKVRu0UmyI7DmNeaAl2RFHuTGIN6fEgpadRxoXGb7gbC1xo4C3IdyA==} - hasBin: true - caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -9068,9 +9288,6 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - chardet@2.1.1: - resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - charenc@0.0.2: resolution: {integrity: sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==} @@ -9078,15 +9295,15 @@ packages: resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} engines: {pnpm: '>=8'} - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} engines: {node: '>= 16'} cheerio-select@2.1.0: resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} - cheerio@1.1.2: - resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + cheerio@1.2.0: + resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} engines: {node: '>=20.18.1'} chokidar@3.5.3: @@ -9101,6 +9318,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chownr@2.0.0: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} @@ -9131,6 +9352,9 @@ packages: cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.0: + resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} @@ -9140,8 +9364,8 @@ packages: class-transformer: ^0.4.0 || ^0.5.0 class-validator: ^0.14.0 - class-validator@0.14.3: - resolution: {integrity: sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==} + class-validator@0.14.4: + resolution: {integrity: sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==} class-variance-authority@0.6.1: resolution: {integrity: sha512-eurOEGc7YVx3majOrOb099PNKgO3KnKSApOprXI4BTq6bcfbqbQXPN2u+rPPmIJ2di23bMwhk0SxCCthBmszEQ==} @@ -9172,6 +9396,10 @@ packages: resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} engines: {node: '>= 10'} + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -9272,8 +9500,8 @@ packages: resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} engines: {node: '>=20'} - commander@14.0.2: - resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} commander@2.20.3: @@ -9338,8 +9566,8 @@ packages: confbox@0.1.8: resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} - confbox@0.2.2: - resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} @@ -9409,10 +9637,6 @@ packages: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} - cookie@0.7.1: - resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} - engines: {node: '>= 0.6'} - cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} @@ -9420,18 +9644,18 @@ packages: copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} - core-js-compat@3.47.0: - resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} + core-js-compat@3.48.0: + resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} - core-js-pure@3.47.0: - resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} + core-js-pure@3.48.0: + resolution: {integrity: sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==} core-js@2.6.12: resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. - core-js@3.47.0: - resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} + core-js@3.48.0: + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -9443,6 +9667,10 @@ packages: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -9656,15 +9884,6 @@ packages: supports-color: optional: true - debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -9681,8 +9900,8 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} - decode-named-character-reference@1.2.0: - resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} @@ -9731,8 +9950,8 @@ packages: resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} engines: {node: '>=18'} - default-browser@5.4.0: - resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} defaults@1.0.4: @@ -9802,11 +10021,6 @@ packages: detect-browser@5.3.0: resolution: {integrity: sha512-53rsFbGdwMwlF7qvCt0ypLM5V5/Mbl0szB7GPN8y9NCcbknYOeVVXdrXEq+90IwAfrrzt6Hd+u2E2ntakICU8w==} - detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -9837,12 +10051,12 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + diff@5.2.2: + resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} diffie-hellman@5.0.3: @@ -9898,6 +10112,9 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.3.1: + resolution: {integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -9945,16 +10162,16 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - editorconfig@1.0.4: - resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + editorconfig@1.0.7: + resolution: {integrity: sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==} engines: {node: '>=14'} hasBin: true ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + electron-to-chromium@1.5.302: + resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -9963,8 +10180,8 @@ packages: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} engines: {node: '>=12'} - emoji-picker-react@4.16.1: - resolution: {integrity: sha512-MrPX0tOCfRL3uYI4of/2GRZ7S6qS7YlacKiF78uFH84/C62vcuHE2DZyv5b4ZJMk0e06es1jjB4e31Bb+YSM8w==} + emoji-picker-react@4.18.0: + resolution: {integrity: sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q==} engines: {node: '>=10'} peerDependencies: react: '>=16' @@ -10000,15 +10217,15 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - engine.io-client@6.6.3: - resolution: {integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==} + engine.io-client@6.6.4: + resolution: {integrity: sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==} engine.io-parser@5.2.3: resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} engines: {node: '>=10.0.0'} - enhanced-resolve@5.18.4: - resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + enhanced-resolve@5.20.0: + resolution: {integrity: sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==} engines: {node: '>=10.13.0'} entities@2.2.0: @@ -10022,6 +10239,10 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} @@ -10091,8 +10312,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} engines: {node: '>=18'} hasBin: true @@ -10259,8 +10480,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -10317,6 +10538,9 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} @@ -10377,16 +10601,12 @@ packages: exponential-backoff@3.1.3: resolution: {integrity: sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==} - express-rate-limit@7.5.1: - resolution: {integrity: sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==} + express-rate-limit@8.2.1: + resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' - express@4.21.2: - resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} - engines: {node: '>= 0.10.0'} - express@4.22.1: resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==} engines: {node: '>= 0.10.0'} @@ -10476,19 +10696,19 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - fast-xml-parser@4.5.3: - resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} + fast-xml-parser@4.5.4: + resolution: {integrity: sha512-jE8ugADnYOBsu1uaoayVl1tVKAMNOXyjwvv2U6udEA2ORBhDooJDWoGxTkhd4Qn4yh59JVVt/pKXtjPwx9OguQ==} hasBin: true - fast-xml-parser@5.2.5: - resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + fast-xml-parser@5.3.6: + resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==} hasBin: true fastestsmallesttextencoderdecoder@1.0.22: resolution: {integrity: sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fault@1.0.4: resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} @@ -10551,8 +10771,8 @@ packages: resolution: {integrity: sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==} engines: {node: '>=18'} - file-type@21.1.0: - resolution: {integrity: sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==} + file-type@21.3.0: + resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} engines: {node: '>=20'} file-type@3.9.0: @@ -10579,10 +10799,6 @@ packages: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} - finalhandler@1.3.1: - resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} - engines: {node: '>= 0.8'} - finalhandler@1.3.2: resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} engines: {node: '>= 0.8'} @@ -10722,10 +10938,6 @@ packages: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} - fs-extra@11.3.2: - resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} - engines: {node: '>=14.14'} - fs-extra@11.3.3: resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} engines: {node: '>=14.14'} @@ -10843,8 +11055,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.13.0: - resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + get-tsconfig@4.13.6: + resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==} get-uri@6.0.5: resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} @@ -10881,9 +11093,9 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - glob@13.0.0: - resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} - engines: {node: 20 || >=22} + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -10946,8 +11158,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - gradient-parser@1.1.1: - resolution: {integrity: sha512-Hu0YfNU+38EsTmnUfLXUKFMXq9yz7htGYpF4x+dlbBhUCvIvzLt0yVLT/gJRmvLKFJdqNFrz4eKkIUjIXSr7Tw==} + gradient-parser@1.2.0: + resolution: {integrity: sha512-6ABGa9CR7WR/0pAJicBy5SJkiikbFM6kf/JjykwX7x+t+s8ORWVnlbi6FkHeFFb36yWsjUpHqSYrygd7ofEUqA==} engines: {node: '>=0.10.0'} graphemer@1.4.0: @@ -10975,8 +11187,8 @@ packages: peerDependencies: graphql: ^15.2.0 || ^16.0.0 - graphql@16.12.0: - resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} + graphql@16.13.0: + resolution: {integrity: sha512-uSisMYERbaB9bkA9M4/4dnqyktaEkf1kMHNKq/7DHyxVeWqHQ2mBmVqm5u6/FVHwF3iCNalKcg82Zfl+tffWoA==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} groq-sdk@0.5.0: @@ -10990,8 +11202,8 @@ packages: resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} engines: {node: '>=14.0.0'} - h3@1.15.4: - resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + h3@1.15.5: + resolution: {integrity: sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==} handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} @@ -11060,8 +11272,8 @@ packages: hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} - hashery@1.3.0: - resolution: {integrity: sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==} + hashery@1.5.0: + resolution: {integrity: sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==} engines: {node: '>=20'} hasown@2.0.2: @@ -11135,15 +11347,21 @@ packages: help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - hermes-compiler@0.14.0: - resolution: {integrity: sha512-clxa193o+GYYwykWVFfpHduCATz8fR5jvU7ngXpfKHj+E9hr9vjLNtdLSEe8MUbObvVexV3wcyxQ00xTPIrB1Q==} + hermes-compiler@250829098.0.9: + resolution: {integrity: sha512-hZ5O7PDz1vQ99TS7HD3FJ9zVynfU1y+VWId6U1Pldvd8hmAYrNec/XLPYJKD3dLOW6NXak6aAQAuMuSo3ji0tQ==} hermes-estree@0.32.0: resolution: {integrity: sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==} + hermes-estree@0.33.3: + resolution: {integrity: sha512-6kzYZHCk8Fy1Uc+t3HGYyJn3OL4aeqKLTyina4UFtWl8I0kSL7OmKThaiX+Uh2f8nGw3mo4Ifxg0M5Zk3/Oeqg==} + hermes-parser@0.32.0: resolution: {integrity: sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==} + hermes-parser@0.33.3: + resolution: {integrity: sha512-Yg3HgaG4CqgyowtYjX/FsnPAuZdHOqSMtnbpylbptsQ9nwwSKsy6uRWcGO5RK0EqiX12q8HvDWKgeAVajRO5DA==} + highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} @@ -11204,12 +11422,12 @@ packages: zod-openapi: optional: true - hono@4.11.1: - resolution: {integrity: sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==} + hono@4.12.3: + resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} engines: {node: '>=16.9.0'} - hookified@1.14.0: - resolution: {integrity: sha512-pi1ynXIMFx/uIIwpWJ/5CEtOHLGtnUB0WhGeeYT+fKcQ+WCQbm3/rrkAXnpfph++PgepNqPdTC2WTj8A6k6zoQ==} + hookified@1.15.1: + resolution: {integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==} hot-reload-extension-vite@1.1.0: resolution: {integrity: sha512-DSuJHbAZ56pbJ7u5iZ2Y0qMBKdgsGYRVm3EL07+3NqA2AhIW8+/o7xgyjEuwT4WA0gxV2wL7EMzabbEzoOBUAw==} @@ -11237,8 +11455,8 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} - htmlparser2@10.0.0: - resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -11246,10 +11464,6 @@ packages: http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -11305,22 +11519,22 @@ packages: resolution: {integrity: sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==} engines: {node: '>= 12'} - i18next-browser-languagedetector@8.2.0: - resolution: {integrity: sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==} + i18next-browser-languagedetector@8.2.1: + resolution: {integrity: sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==} i18next-resources-to-backend@1.2.1: resolution: {integrity: sha512-okHbVA+HZ7n1/76MsfhPqDou0fptl2dAlhRDu2ideXloRRduzHsqDOznJBef+R3DFZnbvWoBW+KxJ7fnFjd6Yw==} - i18next@25.7.3: - resolution: {integrity: sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==} + i18next@25.8.13: + resolution: {integrity: sha512-E0vzjBY1yM+nsFrtgkjLhST2NBkirkvOVoQa0MSldhsuZ3jUge7ZNpuwG0Cfc74zwo5ZwRzg3uOgT+McBn32iA==} peerDependencies: typescript: ^5 peerDependenciesMeta: typescript: optional: true - ibm-cloud-sdk-core@5.4.5: - resolution: {integrity: sha512-7ClYtr/Xob83hypKUa1D9N8/ViH71giKQ0kqjHcoyKum6yvwsWAeFA6zf6WTWb+DdZ1XSBrMPhgCCoy0bqReLg==} + ibm-cloud-sdk-core@5.4.8: + resolution: {integrity: sha512-tLMlZv13cV6S1UPj/bhv8XfV9Z1BDDs/4DxHKWnCw7QlJMzmGdHLPX386x9nrFMQMPZ48eAH+Thsa06tzUZkaA==} engines: {node: '>=20'} iconv-lite@0.4.24: @@ -11331,8 +11545,8 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - iconv-lite@0.7.1: - resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} engines: {node: '>=0.10.0'} idb-keyval@6.2.2: @@ -11369,8 +11583,8 @@ packages: import-in-the-middle@1.15.0: resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} - import-in-the-middle@2.0.1: - resolution: {integrity: sha512-bruMpJ7xz+9jwGzrwEhWgvRrlKRYCRDBrfU+ur3FcasYXLJDxTruJ//8g2Noj+QFyRBeqbpj8Bhn4Fbw6HjvhA==} + import-in-the-middle@2.0.6: + resolution: {integrity: sha512-3vZV3jX0XRFW3EJDTwzWoZa+RH1b8eTTx6YOCjglrLyPuepwoBti1k3L2dKwdCUrnVEfc5CuRuGstaC/uQJJaw==} import-local@3.2.0: resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} @@ -11401,8 +11615,8 @@ packages: inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} - inlineresources@1.1.0: - resolution: {integrity: sha512-hmb6QWAg6zx1yxj8znuSiiON4w2LlebCnYvIrE8fzDBKD7Nb4UKg7gaqp22pe2aTn4fcjgHYlqu5VzLVrSqvEw==} + inlineresources@1.2.0: + resolution: {integrity: sha512-4nqegG4MexBhm9w1a14rueyd3SKfXroAJfFYaxIEkz+GKRchWzPzKnh8c3mCCIz7t7rtkmtGkLD8MqCpmcNqZg==} inquirer@8.2.4: resolution: {integrity: sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==} @@ -11412,10 +11626,6 @@ packages: resolution: {integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==} engines: {node: '>=12.0.0'} - inquirer@8.2.7: - resolution: {integrity: sha512-UjOaSel/iddGZJ5xP/Eixh6dY1XghiBw4XK13rCCIJcJfyhhoul/7KhLLUGtebEj6GDYM6Vnx/mVsjx2L/mFIA==} - engines: {node: '>=12.0.0'} - internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} @@ -11431,10 +11641,14 @@ packages: invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} - ioredis@5.8.2: - resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==} + ioredis@5.10.0: + resolution: {integrity: sha512-HVBe9OFuqs+Z6n64q09PQvP1/R4Bm+30PAyyD4wIEqssh3v9L21QjCVk4kRLucMBcDokJTcLjsGeVRlq/nH6DA==} engines: {node: '>=12.22.0'} + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} @@ -11602,8 +11816,8 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} - is-network-error@1.3.0: - resolution: {integrity: sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==} + is-network-error@1.3.1: + resolution: {integrity: sha512-6QCxa49rQbmUWLfk0nuGqzql9U8uaV2H6279bRErPBHe/109hCzsLUBUHfbEtvLIHBd6hyXbgedBSHevm43Edw==} engines: {node: '>=16'} is-number-object@1.1.1: @@ -11713,8 +11927,8 @@ packages: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} is2@2.0.9: @@ -11730,10 +11944,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isexe@3.1.1: - resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} - engines: {node: '>=16'} - iso-3166-1@2.1.1: resolution: {integrity: sha512-RZxXf8cw5Y8LyHZIwIRvKw8sWTIHh2/txBT+ehO0QroesVfnz3JNFFX4i/OC/Yuv2bDIVYrHna5PMvjtpefq5w==} @@ -11984,6 +12194,7 @@ packages: jpeg-exif@1.1.4: resolution: {integrity: sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. js-base64@2.6.4: resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} @@ -12163,15 +12374,15 @@ packages: resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} engines: {node: '>=18'} - katex@0.16.27: - resolution: {integrity: sha512-aeQoDkuRWSqQN6nSvVCEFvfXdqo1OQiCmmW1kc9xSdjutPv7BGO7pqY9sQRJpMOGrEdfDgF2TfRXe5eUAD2Waw==} + katex@0.16.33: + resolution: {integrity: sha512-q3N5u+1sY9Bu7T4nlXoiRBXWfwSefNGoKeOwekV+gw0cAXQlz2Ww6BLcmBxVDeXBMUDQv6fK5bcNaJLxob3ZQA==} hasBin: true keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - keyv@5.5.5: - resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==} + keyv@5.6.0: + resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} keyvaluestorage-interface@1.0.0: resolution: {integrity: sha512-8t6Q3TclQ4uZynJY9IGr2+SsIGwK9JHcO6ootkHCGA0CrQCRy+VkouYNO2xicET6b9al7QKzpebNow+gkpCL8g==} @@ -12184,11 +12395,11 @@ packages: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} - konva@10.0.12: - resolution: {integrity: sha512-DHmkeG5FbW6tLCkbMQTi1ihWycfzljrn0V7umUUuewxx7aoINcI71ksgBX9fTPNXhlsK4/JoMgKwI/iCde+BRw==} + konva@10.2.0: + resolution: {integrity: sha512-JBoz0Xjbf49UPxCZegZ4WseqOzJ+C4AUDOtJ9eBve5RS5Fcq/u8TdBD5fDl/uPFInpC3a9uycm0sRyZpF4hheg==} - langchain@0.3.36: - resolution: {integrity: sha512-PqC19KChFF0QlTtYDFgfEbIg+SCnCXox29G8tY62QWfj9bOW7ew2kgWmPw5qoHLOTKOdQPvXET20/1Pdq8vAtQ==} + langchain@0.3.37: + resolution: {integrity: sha512-1jPsZ6xsxkcQPUvqRjvfuOLwZLLyt49hzcOK7OYAJovIkkOxd5gzK4Yw6giPUQ8g4XHyvULNlWBz+subdkcokw==} engines: {node: '>=18'} peerDependencies: '@langchain/anthropic': '*' @@ -12283,80 +12494,80 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libphonenumber-js@1.12.33: - resolution: {integrity: sha512-r9kw4OA6oDO4dPXkOrXTkArQAafIKAU71hChInV4FxZ69dxCfbwQGDPzqR5/vea94wU705/3AZroEbSoeVWrQw==} + libphonenumber-js@1.12.38: + resolution: {integrity: sha512-vwzxmasAy9hZigxtqTbFEwp8ZdZ975TiqVDwj5bKx5sR+zi5ucUQy9mbVTkKM9GzqdLdxux/hTw2nmN5J7POMA==} lighthouse-logger@1.4.2: resolution: {integrity: sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==} - lightningcss-android-arm64@1.30.2: - resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + lightningcss-android-arm64@1.31.1: + resolution: {integrity: sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] - lightningcss-darwin-arm64@1.30.2: - resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + lightningcss-darwin-arm64@1.31.1: + resolution: {integrity: sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] - lightningcss-darwin-x64@1.30.2: - resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + lightningcss-darwin-x64@1.31.1: + resolution: {integrity: sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] - lightningcss-freebsd-x64@1.30.2: - resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + lightningcss-freebsd-x64@1.31.1: + resolution: {integrity: sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] - lightningcss-linux-arm-gnueabihf@1.30.2: - resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + lightningcss-linux-arm-gnueabihf@1.31.1: + resolution: {integrity: sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] - lightningcss-linux-arm64-gnu@1.30.2: - resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + lightningcss-linux-arm64-gnu@1.31.1: + resolution: {integrity: sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-arm64-musl@1.30.2: - resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + lightningcss-linux-arm64-musl@1.31.1: + resolution: {integrity: sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - lightningcss-linux-x64-gnu@1.30.2: - resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + lightningcss-linux-x64-gnu@1.31.1: + resolution: {integrity: sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-linux-x64-musl@1.30.2: - resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + lightningcss-linux-x64-musl@1.31.1: + resolution: {integrity: sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - lightningcss-win32-arm64-msvc@1.30.2: - resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + lightningcss-win32-arm64-msvc@1.31.1: + resolution: {integrity: sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] - lightningcss-win32-x64-msvc@1.30.2: - resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + lightningcss-win32-x64-msvc@1.31.1: + resolution: {integrity: sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] - lightningcss@1.30.2: - resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + lightningcss@1.31.1: + resolution: {integrity: sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ==} engines: {node: '>= 12.0.0'} lilconfig@3.1.3: @@ -12375,11 +12586,11 @@ packages: linkifyjs@4.3.2: resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==} - lit-element@4.2.1: - resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} + lit-element@4.2.2: + resolution: {integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==} - lit-html@3.3.1: - resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} + lit-html@3.3.2: + resolution: {integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==} lit@3.1.0: resolution: {integrity: sha512-rzo/hmUqX8zmOdamDAeydfjsGXbbdtAFqMhmocnh2j9aDYqbu0fjXygjCa0T99Od9VQ/2itwaGrjZz/ZELVl7w==} @@ -12412,8 +12623,8 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash-es@4.17.22: - resolution: {integrity: sha512-XEawp1t0gxSi9x01glktRZ5HDy0HXqrM0x5pXQM98EaI0NxO6jVM7omDOxsuEo5UIASAnm2bRp1Jt/e0a2XU8Q==} + lodash-es@4.17.23: + resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} lodash._baseiteratee@4.7.0: resolution: {integrity: sha512-nqB9M+wITz0BX/Q2xg6fQ8mLkyfF7MU7eE+MNBNjTHFKeKaZAPEzEg+E8LWxKWf1DQVflNEn9N49yAuqKh2mWQ==} @@ -12498,6 +12709,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.23: + resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} @@ -12535,8 +12749,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.4: - resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + lru-cache@11.2.6: + resolution: {integrity: sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==} engines: {node: 20 || >=22} lru-cache@4.1.5: @@ -12601,8 +12815,8 @@ packages: map-or-similar@1.5.0: resolution: {integrity: sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==} - markdown-it@14.1.0: - resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + markdown-it@14.1.1: + resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==} hasBin: true markdown-table@3.0.4: @@ -12646,8 +12860,8 @@ packages: mdast-util-from-markdown@1.3.1: resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} - mdast-util-from-markdown@2.0.2: - resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} mdast-util-gfm-autolink-literal@2.0.1: resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} @@ -12714,12 +12928,17 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + mediabunny@1.35.1: + resolution: {integrity: sha512-VrprpjkLTZyIyhzBAc9D3HqgXarAE+le7+6x0Sdu9WN2SD86L8bUy0hz06Xwf14dVPqS7OwpY2KOhlUyqmI2eQ==} + memfs@3.5.3: resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} engines: {node: '>= 4.0.0'} - memfs@4.51.1: - resolution: {integrity: sha512-Eyt3XrufitN2ZL9c/uIRMyDwXanLI88h/L3MoWqNY747ha3dMR9dWqp8cRT5ntjZ0U1TNuq4U91ZXK0sMBjYOQ==} + memfs@4.56.10: + resolution: {integrity: sha512-eLvzyrwqLHnLYalJP7YZ3wBe79MXktMdfQbvMrVD80K+NhrIukCVBvgP30zTJYEEDh9hZ/ep9z0KOdD7FSHo7w==} + peerDependencies: + tslib: '2' memoize-one@5.2.1: resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} @@ -12752,61 +12971,61 @@ packages: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} engines: {node: '>= 0.6'} - metro-babel-transformer@0.83.3: - resolution: {integrity: sha512-1vxlvj2yY24ES1O5RsSIvg4a4WeL7PFXgKOHvXTXiW0deLvQr28ExXj6LjwCCDZ4YZLhq6HddLpZnX4dEdSq5g==} + metro-babel-transformer@0.83.4: + resolution: {integrity: sha512-xfNtsYIigybqm9xVL3ygTYYNFyYTMf2lGg/Wt+znVGtwcjXoRPG80WlL5SS09ZjYVei3MoE920i7MNr7ukSULA==} engines: {node: '>=20.19.4'} - metro-cache-key@0.83.3: - resolution: {integrity: sha512-59ZO049jKzSmvBmG/B5bZ6/dztP0ilp0o988nc6dpaDsU05Cl1c/lRf+yx8m9WW/JVgbmfO5MziBU559XjI5Zw==} + metro-cache-key@0.83.4: + resolution: {integrity: sha512-Y8E6mm1alkYIRzmfkOdrwXMzJ4HKANYiZE7J2d3iYTwmnLIQG+aoIpvla+bo6LRxH1Gm3qjEiOl+LbxvPCzIug==} engines: {node: '>=20.19.4'} - metro-cache@0.83.3: - resolution: {integrity: sha512-3jo65X515mQJvKqK3vWRblxDEcgY55Sk3w4xa6LlfEXgQ9g1WgMh9m4qVZVwgcHoLy0a2HENTPCCX4Pk6s8c8Q==} + metro-cache@0.83.4: + resolution: {integrity: sha512-Pm6CiksVms0cZNDDe/nFzYr1xpXzJLOSwvOjl4b3cYtXxEFllEjD6EeBgoQK5C8yk7U54PcuRaUAFSvJ+eCKbg==} engines: {node: '>=20.19.4'} - metro-config@0.83.3: - resolution: {integrity: sha512-mTel7ipT0yNjKILIan04bkJkuCzUUkm2SeEaTads8VfEecCh+ltXchdq6DovXJqzQAXuR2P9cxZB47Lg4klriA==} + metro-config@0.83.4: + resolution: {integrity: sha512-ydOgMNI9aT8l2LOTOugt1FvC7getPKG9uJo9Vclg9/RWJxbwkBF/FMBm6w5gH8NwJokSmQrbNkojXPn7nm0kGw==} engines: {node: '>=20.19.4'} - metro-core@0.83.3: - resolution: {integrity: sha512-M+X59lm7oBmJZamc96usuF1kusd5YimqG/q97g4Ac7slnJ3YiGglW5CsOlicTR5EWf8MQFxxjDoB6ytTqRe8Hw==} + metro-core@0.83.4: + resolution: {integrity: sha512-EE+j/imryd3og/6Ly9usku9vcTLQr2o4IDax/izsr6b0HRqZK9k6f5SZkGkOPqnsACLq6csPCx+2JsgF9DkVbw==} engines: {node: '>=20.19.4'} - metro-file-map@0.83.3: - resolution: {integrity: sha512-jg5AcyE0Q9Xbbu/4NAwwZkmQn7doJCKGW0SLeSJmzNB9Z24jBe0AL2PHNMy4eu0JiKtNWHz9IiONGZWq7hjVTA==} + metro-file-map@0.83.4: + resolution: {integrity: sha512-RSZLpGQhW9topefjJ9dp77Ff7BP88b17sb/YjxLHC1/H0lJVYYC9Cgqua21Vxe4RUJK2z64hw72g+ySLGTCawA==} engines: {node: '>=20.19.4'} - metro-minify-terser@0.83.3: - resolution: {integrity: sha512-O2BmfWj6FSfzBLrNCXt/rr2VYZdX5i6444QJU0fFoc7Ljg+Q+iqebwE3K0eTvkI6TRjELsXk1cjU+fXwAR4OjQ==} + metro-minify-terser@0.83.4: + resolution: {integrity: sha512-KmZnpxfj0nPIRkbBNTc6xul5f5GPvWL5kQ1UkisB7qFkgh6+UiJG+L4ukJ2sK7St6+8Za/Cb68MUEYkUouIYcQ==} engines: {node: '>=20.19.4'} - metro-resolver@0.83.3: - resolution: {integrity: sha512-0js+zwI5flFxb1ktmR///bxHYg7OLpRpWZlBBruYG8OKYxeMP7SV0xQ/o/hUelrEMdK4LJzqVtHAhBm25LVfAQ==} + metro-resolver@0.83.4: + resolution: {integrity: sha512-drWdylyNqgdaJufz0GjU/ielv2hjcc6piegjjJwKn8l7A/72aLQpUpOHtP+GMR+kOqhSsD4MchhJ6PSANvlSEw==} engines: {node: '>=20.19.4'} - metro-runtime@0.83.3: - resolution: {integrity: sha512-JHCJb9ebr9rfJ+LcssFYA2x1qPYuSD/bbePupIGhpMrsla7RCwC/VL3yJ9cSU+nUhU4c9Ixxy8tBta+JbDeZWw==} + metro-runtime@0.83.4: + resolution: {integrity: sha512-sWj9KN311yG22Zv0kVbAp9dorB9HtTThvQKsAn6PLxrVrz+1UBsLrQSxjE/s4PtzDi1HABC648jo4K9Euz/5jw==} engines: {node: '>=20.19.4'} - metro-source-map@0.83.3: - resolution: {integrity: sha512-xkC3qwUBh2psVZgVavo8+r2C9Igkk3DibiOXSAht1aYRRcztEZNFtAMtfSB7sdO2iFMx2Mlyu++cBxz/fhdzQg==} + metro-source-map@0.83.4: + resolution: {integrity: sha512-pPbmQwS0zgU+/0u5KPkuvlsQP0V+WYQ9qNshqupIL720QRH0vS3QR25IVVtbunofEDJchI11Q4QtIbmUyhpOBw==} engines: {node: '>=20.19.4'} - metro-symbolicate@0.83.3: - resolution: {integrity: sha512-F/YChgKd6KbFK3eUR5HdUsfBqVsanf5lNTwFd4Ca7uuxnHgBC3kR/Hba/RGkenR3pZaGNp5Bu9ZqqP52Wyhomw==} + metro-symbolicate@0.83.4: + resolution: {integrity: sha512-clyWAXDgkDHPwvldl95pcLTrJIqUj9GbZayL8tfeUs69ilsIUBpVym2lRd/8l3/8PIHCInxL868NvD2Y7OqKXg==} engines: {node: '>=20.19.4'} hasBin: true - metro-transform-plugins@0.83.3: - resolution: {integrity: sha512-eRGoKJU6jmqOakBMH5kUB7VitEWiNrDzBHpYbkBXW7C5fUGeOd2CyqrosEzbMK5VMiZYyOcNFEphvxk3OXey2A==} + metro-transform-plugins@0.83.4: + resolution: {integrity: sha512-c0ROVcyvdaGPUFIg2N5nEQF4xbsqB2p1PPPhVvK1d/Y7ZhBAFiwQ75so0SJok32q+I++lc/hq7IdPCp2frPGQg==} engines: {node: '>=20.19.4'} - metro-transform-worker@0.83.3: - resolution: {integrity: sha512-Ztekew9t/gOIMZX1tvJOgX7KlSLL5kWykl0Iwu2cL2vKMKVALRl1hysyhUw0vjpAvLFx+Kfq9VLjnHIkW32fPA==} + metro-transform-worker@0.83.4: + resolution: {integrity: sha512-6I81IZLeU/0ww7OBgCPALFl0OE0FQwvIuKCtuViSiKufmislF7kVr7IHH9GYtQuZcnualQ82gYeQ11KzZQTouw==} engines: {node: '>=20.19.4'} - metro@0.83.3: - resolution: {integrity: sha512-+rP+/GieOzkt97hSJ0MrPOuAH/jpaS21ZDvL9DJ35QYRDlQcwzcvUlGUf79AnQxq/2NPiS/AULhhM4TKutIt8Q==} + metro@0.83.4: + resolution: {integrity: sha512-eBkAtcob+YmvSLL+/rsFiK8dHNfDbQA2/pi0lnxg3E6LLtUpwDfdGJ9WBWXkj0PVeOhoWQyj9Rt7s/+6k/GXuA==} engines: {node: '>=20.19.4'} hasBin: true @@ -13019,23 +13238,19 @@ packages: minimalistic-crypto-utils@1.0.1: resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - minimatch@10.1.1: - resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} - engines: {node: 20 || >=22} + minimatch@10.2.4: + resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} + engines: {node: 18 || 20 || >=22} - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@8.0.4: - resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} + minimatch@8.0.7: + resolution: {integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==} engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.1: - resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} - engines: {node: '>=16 || 14 >=14.17'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + minimatch@9.0.9: + resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -13053,8 +13268,8 @@ packages: resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} engines: {node: '>=8'} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} minizlib@2.1.2: @@ -13147,14 +13362,18 @@ packages: mute-stream@0.0.8: resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} namespace-emitter@2.0.1: resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} - nan@2.24.0: - resolution: {integrity: sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg==} + nan@2.25.0: + resolution: {integrity: sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==} nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} @@ -13206,8 +13425,8 @@ packages: peerDependencies: '@nestjs/common': '>=8' - nestjs-temporal-core@3.2.0: - resolution: {integrity: sha512-EIf1PZKBj9YR2ln8eDqBliGqo9IpWmetPbSbsO+X00s1qwewWNGGw7VX2Fq4Bw8/WPkbWeHgUHXwphkuOIIyaQ==} + nestjs-temporal-core@3.2.3: + resolution: {integrity: sha512-x2gULa5Rb4bhwrh8Xnic1jkg3Y1fdwKLFYK940PWPLx/2DDvadnGHgV0HT6BuhEd/3dDG3tit8GVgDe6vmkLgA==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} peerDependencies: '@nestjs/common': ^9.0.0 || ^10.0.0 || ^11.0.0 @@ -13264,8 +13483,8 @@ packages: no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} - node-abi@3.85.0: - resolution: {integrity: sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==} + node-abi@3.87.0: + resolution: {integrity: sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==} engines: {node: '>=10'} node-abort-controller@3.1.1: @@ -13285,6 +13504,10 @@ packages: node-emoji@1.11.0: resolution: {integrity: sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==} + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + node-fetch-native@1.6.7: resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} @@ -13322,12 +13545,12 @@ packages: resolution: {integrity: sha512-s4Hrg5q+VPl4/tJVG++pImxF6eb8tNJNj4KnDqAOKL6zGU34lo9RXmyAN158njwGN+v8hdNf8s9fWIYW9hPb5A==} engines: {node: '>=0.12'} - nodemailer@7.0.11: - resolution: {integrity: sha512-gnXhNRE0FNhD7wPSCGhdNh46Hs6nm+uTyg+Kq0cZukNQiYdnCsoQjodNP9BQVG9XrcK/v6/MgpAPBUFyzh9pvw==} + nodemailer@7.0.13: + resolution: {integrity: sha512-PNDFSJdP+KFgdsG3ZzMXCgquO7I6McjY2vlqILjtJd0hy8wEvtugS9xKRF2NWlPNGxvLCXlTNIae4serI7dinw==} engines: {node: '>=6.0.0'} - nodemon@3.1.11: - resolution: {integrity: sha512-is96t8F/1//UHAjNPHpbsNY46ELPpftGUoSVNXwUfMk/qdjSylYrWSu1XavVTBOn526kFiOR733ATgNBCQyH0g==} + nodemon@3.1.14: + resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==} engines: {node: '>=10'} hasBin: true @@ -13352,8 +13575,8 @@ packages: normalize.css@8.0.1: resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} - nostr-tools@2.19.4: - resolution: {integrity: sha512-qVLfoTpZegNYRJo5j+Oi6RPu0AwLP6jcvzcB3ySMnIT5DrAGNXfs5HNBspB/2HiGfH3GY+v6yXkTtcKSBQZwSg==} + nostr-tools@2.23.3: + resolution: {integrity: sha512-AALyt9k8xPdF4UV2mlLJ2mgCn4kpTB0DZ8t2r6wjdUh6anfx2cTVBsHUlo9U0EY/cKC5wcNyiMAmRJV5OVEalA==} peerDependencies: typescript: '>=5.0.0' peerDependenciesMeta: @@ -13394,8 +13617,8 @@ packages: oauth-sign@0.9.0: resolution: {integrity: sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==} - ob1@0.83.3: - resolution: {integrity: sha512-egUxXCDwoWG06NGCS5s5AdcpnumHKJlfd3HH06P3m9TEMwwScfcY35wpQxbm9oHof+dM/lVH9Rfyu1elTVelSA==} + ob1@0.83.4: + resolution: {integrity: sha512-9JiflaRKCkxKzH8uuZlax72cHzZ8iFLsNIORFOAKDgZUOfvfwYWOVS0ezGLzPp/yEhVktD+PTTImC0AAehSOBw==} engines: {node: '>=20.19.4'} object-assign@4.1.1: @@ -13511,8 +13734,8 @@ packages: zod: optional: true - openai@6.15.0: - resolution: {integrity: sha512-F1Lvs5BoVvmZtzkUEVyh8mDQPPFolq4F+xdsx/DO8Hee8YF3IGAlZqUIsF+DVGhqf4aU0a3bTghsxB6OIsRy1g==} + openai@6.25.0: + resolution: {integrity: sha512-mEh6VZ2ds2AGGokWARo18aPISI1OhlgdEIC1ewhkZr8pSIT31dec0ecr9Nhxx0JlybyOgoAT1sWeKtwPZzJyww==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -13556,8 +13779,8 @@ packages: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} - ox@0.11.1: - resolution: {integrity: sha512-1l1gOLAqg0S0xiN1dH5nkPna8PucrZgrIJOfS49MLNiMevxu07Iz4ZjuJS9N+xifvT+PsZyIptS7WHM8nC+0+A==} + ox@0.12.4: + resolution: {integrity: sha512-+P+C7QzuwPV8lu79dOwjBKfB2CbnbEXe/hfyyrff1drrO1nOOj3Hc87svHfcW1yneRr3WXaKr6nz11nq+/DF9Q==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -13736,9 +13959,9 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.1: - resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} - engines: {node: 20 || >=22} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} path-to-regexp@0.1.12: resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} @@ -13784,14 +14007,14 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - pg-cloudflare@1.2.7: - resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} + pg-cloudflare@1.3.0: + resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} - pg-connection-string@2.9.1: - resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} + pg-connection-string@2.11.0: + resolution: {integrity: sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==} - pg-cursor@2.15.3: - resolution: {integrity: sha512-eHw63TsiGtFEfAd7tOTZ+TLy+i/2ePKS20H84qCQ+aQ60pve05Okon9tKMC+YN3j6XyeFoHnaim7Lt9WVafQsA==} + pg-cursor@2.18.0: + resolution: {integrity: sha512-WkMubzXP+FWDIC6XfA9pZwJHO0rmUwbNXUNFfBshp9amnCraQslVLYqEuWA+7qemtyz+v3zybcvcX//Dq5WpxQ==} peerDependencies: pg: ^8 @@ -13803,8 +14026,8 @@ packages: resolution: {integrity: sha512-jO/oJOununpx8DzKgvSsWm61P8JjwXlaxSlbbfTBo1nvSWoo/+I6qZYaSN96jm/KDwa5d+JMQwPGgcP6HXDRow==} engines: {node: '>=16.0.0'} - pg-pool@3.10.1: - resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} + pg-pool@3.12.0: + resolution: {integrity: sha512-eIJ0DES8BLaziFHW7VgJEBPi5hg3Nyng5iKpYtj3wbcAUV9A1wLgWiY7ajf/f/oO1wfxt83phXPY8Emztg7ITg==} peerDependencies: pg: '>=8.0' @@ -13814,8 +14037,8 @@ packages: peerDependencies: pg-query-stream: 4.10.3 - pg-protocol@1.10.3: - resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} + pg-protocol@1.12.0: + resolution: {integrity: sha512-uOANXNRACNdElMXJ0tPz6RBM0XQ61nONGAwlt8da5zs/iUOOCLBQOHSXnrC6fMsvtjxbOJrZZl5IScGv+7mpbg==} pg-query-stream@4.10.3: resolution: {integrity: sha512-h2utrzpOIzeT9JfaqfvBbVuvCfBjH86jNfVrGGTbyepKAIOyTfDew0lAt8bbJjs9n/I5bGDl7S2sx6h5hPyJxw==} @@ -13835,6 +14058,15 @@ packages: pg-native: optional: true + pg@8.19.0: + resolution: {integrity: sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==} + engines: {node: '>= 16.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} @@ -13877,8 +14109,8 @@ packages: pino-std-serializers@4.0.0: resolution: {integrity: sha512-cK0pekc1Kjy5w9V2/n+8MkZwusa6EyyxfeQCB799CQRhRt/CqYKiWs5adeu8Shve2ZNffvfC/7J64A2PJo1W/Q==} - pino-std-serializers@7.0.0: - resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} + pino-std-serializers@7.1.0: + resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} pino@7.11.0: resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} @@ -13909,13 +14141,13 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} - playwright-core@1.57.0: - resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + playwright-core@1.58.2: + resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} engines: {node: '>=18'} hasBin: true - playwright@1.57.0: - resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + playwright@1.58.2: + resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} engines: {node: '>=18'} hasBin: true @@ -13930,8 +14162,8 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - polotno@2.34.1: - resolution: {integrity: sha512-na8ulXfzIQJonOem6TzH1Knz+nalQr10zAQlAdKfzg8ba/ihKaX6qKWHJdRhCLn1AUzoUtXnp/Eqv8GDTJL5DQ==} + polotno@2.36.10: + resolution: {integrity: sha512-Kpfpw/76cKD6hj/vpnYFP5dFcFryKyLFh4zWTj06F0hbPCV8XscRd1FsfJJbJqIHRWgzFkf4jY0OOqGNxfZdiA==} peerDependencies: react: ^18.2.0 react-dom: ^18.2.0 @@ -14023,19 +14255,19 @@ packages: resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} engines: {node: '>=0.10.0'} - postgres@3.4.7: - resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==} + postgres@3.4.8: + resolution: {integrity: sha512-d+JFcLM17njZaOLkv6SCev7uoLaBtfK86vMUXhW1Z4glPWh4jozno9APvW/XKFJ3CCxVoC7OL38BqRydtu5nGg==} engines: {node: '>=12'} - posthog-js@1.309.1: - resolution: {integrity: sha512-JUJcQhYzNNKO0cgnSbowCsVi2RTu75XGZ2EmnTQti4tMGRCTOv/HCnZasdFniBGZ0rLugQkaScYca/84Ta2u5Q==} + posthog-js@1.356.1: + resolution: {integrity: sha512-4EQliSyTp3j/xOaWpZmu7fk1b4S+J3qy4JOu5Xy3/MYFxv1SlAylgifRdCbXZxCQWb6PViaNvwRf4EmburgfWA==} posthog-node@4.18.0: resolution: {integrity: sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==} engines: {node: '>=15.0.0'} - preact@10.28.0: - resolution: {integrity: sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==} + preact@10.28.4: + resolution: {integrity: sha512-uKFfOHWuSNpRFVTnljsCluEFq57OKT+0QdOiQo8XWnQ/pSvg7OpX5eNOejELXJMWy+BwM2nobz0FkvzmnpCNsQ==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -14046,8 +14278,8 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - prettier@3.7.4: - resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} hasBin: true @@ -14135,8 +14367,8 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} - prosemirror-changeset@2.3.1: - resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==} + prosemirror-changeset@2.4.0: + resolution: {integrity: sha512-LvqH2v7Q2SF6yxatuPP2e8vSUKS/L+xAU7dPDC4RMyHMhZoGDfBC74mYuyYF4gLqOEG758wajtyhNnsTkuhvng==} prosemirror-collab@1.3.1: resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==} @@ -14159,11 +14391,11 @@ packages: prosemirror-keymap@1.2.3: resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==} - prosemirror-markdown@1.13.2: - resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==} + prosemirror-markdown@1.13.4: + resolution: {integrity: sha512-D98dm4cQ3Hs6EmjK500TdAOew4Z03EV71ajEFiWra3Upr7diytJsjF4mPV2dW+eK5uNectiRj0xFxYI9NLXDbw==} - prosemirror-menu@1.2.5: - resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==} + prosemirror-menu@1.3.0: + resolution: {integrity: sha512-TImyPXCHPcDsSka2/lwJ6WjTASr4re/qWq1yoTTuLOqfXucwF6VcRa2LWCkM/EyTD1UO3CUwiH8qURJoWJRxwg==} prosemirror-model@1.25.4: resolution: {integrity: sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==} @@ -14177,8 +14409,8 @@ packages: prosemirror-state@1.4.4: resolution: {integrity: sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==} - prosemirror-tables@1.8.4: - resolution: {integrity: sha512-CGr2BK5sLdZx+ARbeLO4HBZYa3qSG3FmwOVmzYs0Zp7n5SkrGqj+1CeNuubFNZEr64yMAQ20SanbFyIyHWZc8w==} + prosemirror-tables@1.8.5: + resolution: {integrity: sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==} prosemirror-trailing-node@3.0.0: resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==} @@ -14187,11 +14419,11 @@ packages: prosemirror-state: ^1.4.2 prosemirror-view: ^1.33.8 - prosemirror-transform@1.10.5: - resolution: {integrity: sha512-RPDQCxIDhIBb1o36xxwsaeAvivO8VLJcgBtzmOwQ64bMtsVFh5SSuJ6dWSxO1UsHTiTXPCgQm3PDJt7p6IOLbw==} + prosemirror-transform@1.11.0: + resolution: {integrity: sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==} - prosemirror-view@1.41.4: - resolution: {integrity: sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==} + prosemirror-view@1.41.6: + resolution: {integrity: sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==} proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} @@ -14233,8 +14465,8 @@ packages: pump@2.0.1: resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} @@ -14268,21 +14500,24 @@ packages: engines: {node: '>=10.13.0'} hasBin: true - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + qs@6.14.2: + resolution: {integrity: sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==} engines: {node: '>=0.6'} - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} engines: {node: '>=0.6'} - qs@6.5.3: - resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} + qs@6.5.5: + resolution: {integrity: sha512-mzR4sElr1bfCaPJe7m8ilJ6ZXdDaGoObcYR0ZHSsktM/Lt21MVHj5De30GQH2eiZ1qGRTO7LCAzQsUeXTNexWQ==} engines: {node: '>=0.6'} quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + query-selector-shadow-dom@1.0.1: + resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} + query-string@7.1.3: resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} engines: {node: '>=6'} @@ -14330,12 +14565,8 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - rasterizehtml@1.4.0: - resolution: {integrity: sha512-rQhCo4A0CTl9V/Nm01RN3jEl//Qfhs29d1Vu+oES+xNrRX9y3cPjYyhcAOFPfExgg0j4BV6DPbzj7zfMpT1RLw==} - - raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} + rasterizehtml@1.4.1: + resolution: {integrity: sha512-mcE6b5cKhQO0Qka9vduSQW/F/crQ7EUjrTpXDPAdcj9uNtYijBiNmUOxOrIuMMq7GO/X3CR6UckHTgh5ZVqqBA==} raw-body@2.5.3: resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} @@ -14388,8 +14619,8 @@ packages: peerDependencies: react: ^18.3.1 - react-dropzone@14.3.8: - resolution: {integrity: sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug==} + react-dropzone@14.4.1: + resolution: {integrity: sha512-QDuV76v3uKbHiH34SpwifZ+gOLi1+RdsCO1kl5vxMT4wW8R82+sthjvBw4th3NHF/XX6FBsqDYZVNN+pnhaw0g==} engines: {node: '>= 10.13'} peerDependencies: react: '>= 16.8 || 18.0.0' @@ -14402,14 +14633,14 @@ packages: react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - react-hook-form@7.69.0: - resolution: {integrity: sha512-yt6ZGME9f4F6WHwevrvpAjh42HMvocuSnSIHUGycBqXIJdhqGSPQzTpGF+1NLREk/58IdPxEMfPcFCjlMhclGw==} + react-hook-form@7.71.2: + resolution: {integrity: sha512-1CHvcDYzuRUNOflt4MOq3ZM46AronNJtQ1S7tnX6YN4y72qhgiUItpacZUAQ0TyWYci3yz1X+rXaSxiuEm86PA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - react-hotkeys-hook@5.2.1: - resolution: {integrity: sha512-xbKh6zJxd/vJHT4Bw4+0pBD662Fk20V+VFhLqciCg+manTVO4qlqRqiwFOYelfHN9dBvWj9vxaPkSS26ZSIJGg==} + react-hotkeys-hook@5.2.4: + resolution: {integrity: sha512-BgKg+A1+TawkYluh5Bo4cTmcgMN5L29uhJbDUQdHwPX+qgXRjIPYU5kIDHyxnAwCkCBiu9V5OpB2mpyeluVF2A==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' @@ -14439,8 +14670,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.3: - resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} + react-is@19.2.4: + resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} react-konva-utils@2.0.0: resolution: {integrity: sha512-pOb+TF13gFAjfPmUqsE42J4GJ+xhUS97qS32p0NRTqSeqtamWyKJikGa1XeVvV5yItu9SWDo7onL79GGPG96HQ==} @@ -14490,13 +14721,13 @@ packages: react: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 react-dom: ^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18 || ^19 - react-native@0.83.1: - resolution: {integrity: sha512-mL1q5HPq5cWseVhWRLl+Fwvi5z1UO+3vGOpjr+sHFwcUletPRZ5Kv+d0tUfqHmvi73/53NjlQqX1Pyn4GguUfA==} + react-native@0.84.1: + resolution: {integrity: sha512-0PjxOyXRu3tZ8EobabxSukvhKje2HJbsZikR0U+pvS0pYZza2hXKjcSBiBdFN4h9D0S3v6a8kkrDK6WTRKMwzg==} engines: {node: '>= 20.19.4'} hasBin: true peerDependencies: '@types/react': ^19.1.1 - react: ^19.2.0 + react: ^19.2.3 peerDependenciesMeta: '@types/react': optional: true @@ -14679,6 +14910,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + real-require@0.1.0: resolution: {integrity: sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==} engines: {node: '>= 12.13.0'} @@ -14702,8 +14937,8 @@ packages: redis@4.7.1: resolution: {integrity: sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==} - redis@5.10.0: - resolution: {integrity: sha512-0/Y+7IEiTgVGPrLFKy8oAEArSyEJkU0zvgV5xyi9NzNQ+SLZmyFbUsWIbgPcd4UdUh00opXGKlXJwMmsis5Byw==} + redis@5.11.0: + resolution: {integrity: sha512-YwXjATVDT+AuxcyfOwZn046aml9jMlQPvU1VXIlLDVAExe0u93aTfPYSeRgG4p9Q/Jlkj+LXJ1XEoFV+j2JKcQ==} engines: {node: '>= 18'} redux@4.2.1: @@ -14725,6 +14960,9 @@ packages: refractor@4.9.0: resolution: {integrity: sha512-nEG1SPXFoGGx+dcjftjv8cAjEusIh6ED1xhf5DG3C0x/k+rmZ2duKnc3QLpt6qeHv5fPb8uwN3VWN2BT7fr3Og==} + refractor@5.0.0: + resolution: {integrity: sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw==} + regenerate-unicode-properties@10.2.2: resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} engines: {node: '>=4'} @@ -14767,8 +15005,8 @@ packages: rehype-prism-plus@2.0.0: resolution: {integrity: sha512-FeM/9V2N7EvDZVdR2dqhAzlw5YI49m9Tgn7ZrYJeYHIahM6gcXpH0K1y2gNnKanZCydOMluJvX2cB9z3lhY8XQ==} - rehype-prism-plus@2.0.1: - resolution: {integrity: sha512-Wglct0OW12tksTUseAPyWPo3srjBOY7xKlql/DPKi7HbsdZTyaLCAoO58QBKSczFQxElTsQlOY3JDOFzB/K++Q==} + rehype-prism-plus@2.0.2: + resolution: {integrity: sha512-jTHb8ZtQHd2VWAAKeCINgv/8zNEF0+LesmwJak69GemoPVN9/8fGEARTvqOpKqmN57HwaM9z8UKBVNVJe8zggw==} rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} @@ -14882,12 +15120,9 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} hasBin: true responselike@2.0.1: @@ -14958,8 +15193,8 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rollup@4.54.0: - resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} + rollup@4.59.0: + resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -14970,8 +15205,8 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - rpc-websockets@9.3.2: - resolution: {integrity: sha512-VuW2xJDnl1k8n8kjbdRSWawPRkwaVqUQNjE1TdeTawf0y0abGhtVJFTXCLfgpgGDBkO/Fj6kny8Dc/nvOW78MA==} + rpc-websockets@9.3.5: + resolution: {integrity: sha512-4mAmr+AEhPYJ9TmDtxF3r3ZcbWy7W8kvZ4PoZYw/Xgp2J7WixjwTgiQZsoTDvch5nimmg3Ay6/0Kuh9oIvVs9A==} rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} @@ -14991,6 +15226,10 @@ packages: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -15044,13 +15283,14 @@ packages: sane-domparser-error@0.2.0: resolution: {integrity: sha512-wxjDV5jty95tNv8N/4WA15UNGqqaor/xX7rnNYY961hifN3bheYoKqtXN+V/M6EUgmUAs6pMul3klwUPMEiVXA==} - sass@1.97.1: - resolution: {integrity: sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==} + sass@1.97.3: + resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} engines: {node: '>=14.0.0'} hasBin: true - sax@1.4.3: - resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + sax@1.4.4: + resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} + engines: {node: '>=11.0.0'} saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} @@ -15102,9 +15342,10 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} @@ -15124,10 +15365,6 @@ packages: serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} - serve-static@1.16.2: - resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} - engines: {node: '>= 0.8.0'} - serve-static@1.16.3: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} @@ -15282,12 +15519,12 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} - socket.io-client@4.8.1: - resolution: {integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==} + socket.io-client@4.8.3: + resolution: {integrity: sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==} engines: {node: '>=10.0.0'} - socket.io-parser@4.2.4: - resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + socket.io-parser@4.2.5: + resolution: {integrity: sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==} engines: {node: '>=10.0.0'} socks-proxy-agent@8.0.5: @@ -15301,8 +15538,8 @@ packages: sonic-boom@2.8.0: resolution: {integrity: sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg==} - sonic-boom@4.2.0: - resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} + sonic-boom@4.2.1: + resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} sort-keys-length@1.0.1: resolution: {integrity: sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==} @@ -15312,8 +15549,8 @@ packages: resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} engines: {node: '>=0.10.0'} - sortablejs@1.15.6: - resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} + sortablejs@1.15.7: + resolution: {integrity: sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==} source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} @@ -15400,10 +15637,6 @@ packages: resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} engines: {node: '>= 0.6'} - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - statuses@2.0.2: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} @@ -15502,8 +15735,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} strip-bom@3.0.0: @@ -15551,8 +15784,8 @@ packages: strnum@1.1.2: resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} - strnum@2.1.2: - resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==} + strnum@2.2.0: + resolution: {integrity: sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==} strtok3@10.3.4: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} @@ -15642,8 +15875,8 @@ packages: swagger-ui-dist@5.17.14: resolution: {integrity: sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==} - swc-loader@0.2.6: - resolution: {integrity: sha512-9Zi9UP2YmDpgmQVbyOPJClY0dwf58JDyDMQ7uRc4krmc72twNI2fvlBWHLqVekBpPc7h5NJkGVT1zNDxFrqhvg==} + swc-loader@0.2.7: + resolution: {integrity: sha512-nwYWw3Fh9ame3Rtm7StS9SBLpHRRnYcK7bnpF3UKZmesAK0gw2/ADvlURFAINmPvKtDLzp+GBiP9yLoEjg6S9w==} peerDependencies: '@swc/core': ^1.2.147 webpack: '>=2' @@ -15651,8 +15884,8 @@ packages: sweetalert2@11.4.8: resolution: {integrity: sha512-BDS/+E8RwaekGSxCPUbPnsRAyQ439gtXkTF/s98vY2l9DaVEOMjGj1FaQSorfGREKsbbxGSP7UXboibL5vgTMA==} - swr@2.3.8: - resolution: {integrity: sha512-gaCPRVoMq8WGDcWj9p4YWzCMPHzE0WNl6W8ADIx9c3JBEIdMkJGMzW+uzXvxHMltwcYACr9jP+32H8/hgwMR7w==} + swr@2.4.1: + resolution: {integrity: sha512-2CC6CiKQtEwaEeNiqWTAw9PGykW8SR5zZX8MZk6TeAvEAnVS7Visz8WzphqgtQ8v2xz/4Q5K+j+SeMaKXeeQIA==} peerDependencies: react: ^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -15666,8 +15899,8 @@ packages: synchronous-promise@2.0.17: resolution: {integrity: sha512-AsS729u2RHUfEra9xJrE39peJcc2stq2+poBXX8bcM08Y6g9j/i/PUzwNQqkaJde7Ntg1TO7bSREbR5sdosQ+g==} - tabbable@6.3.0: - resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + tabbable@6.4.0: + resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} tailwind-merge@1.14.0: resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==} @@ -15686,8 +15919,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tailwindcss@4.1.18: - resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + tailwindcss@4.2.1: + resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} tapable@2.3.0: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} @@ -15696,6 +15929,7 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me tcp-port-used@1.0.2: resolution: {integrity: sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA==} @@ -15719,8 +15953,8 @@ packages: uglify-js: optional: true - terser@5.44.1: - resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} + terser@5.46.0: + resolution: {integrity: sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==} engines: {node: '>=10'} hasBin: true @@ -15853,8 +16087,8 @@ packages: resolution: {integrity: sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==} engines: {node: '>=14.16'} - token-types@6.1.1: - resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} + token-types@6.1.2: + resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} engines: {node: '>=14.16'} toposort@2.0.2: @@ -16060,8 +16294,8 @@ packages: twemoji-parser@11.0.2: resolution: {integrity: sha512-5kO2XCcpAql6zjdLwRwJjYvAZyDy3+Uj7v1ipBzLthQmDL7Ce19bEqHr3ImSNeoSW2OA8u02XmARbXHaNO8GhA==} - twitter-api-v2@1.28.0: - resolution: {integrity: sha512-VBmiAMylCEr94OChaHJ+0TBrOZNrduwWUe7QLoa/KdOdv1fNiToJ0xZGOrNKFd2B7jrAdAkfUW6yA5LuXYOYLQ==} + twitter-api-v2@1.29.0: + resolution: {integrity: sha512-v473q5bwme4N+DWSg6qY+JCvfg1nSJRWwui3HUALafxfqCvVkKiYmS/5x/pVeJwTmyeBxexMbzHwnzrH4h6oYQ==} twitter-text@3.1.0: resolution: {integrity: sha512-nulfUi3FN6z0LUjYipJid+eiwXvOLb8Ass7Jy/6zsXmZK3URte043m8fL3FyDzrK+WLpyqhHuR/TcARTN/iuGQ==} @@ -16150,8 +16384,8 @@ packages: uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} - ufo@1.6.1: - resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + ufo@1.6.3: + resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} @@ -16185,8 +16419,11 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici@7.16.0: - resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + undici@7.22.0: + resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} unicode-canonical-property-names-ecmascript@2.0.1: @@ -16208,8 +16445,8 @@ packages: resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} engines: {node: '>=4'} - unicode-segmenter@0.14.4: - resolution: {integrity: sha512-pR5VCiCrLrKOL6FRW61jnk9+wyMtKKowq+jyFY9oc6uHbWKhDL4yVRiI4YZPksGMK72Pahh8m0cn/0JvbDDyJg==} + unicode-segmenter@0.14.5: + resolution: {integrity: sha512-jHGmj2LUuqDcX3hqY12Ql+uhUTn8huuxNZGq7GvtF6bSybzH3aFgedYu/KTzQStEgt1Ra2F3HxadNXsNjb3m3g==} unicode-trie@2.0.0: resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} @@ -16269,6 +16506,9 @@ packages: unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + universalify@0.2.0: resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} engines: {node: '>= 4.0.0'} @@ -16288,14 +16528,11 @@ packages: resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==} engines: {node: '>=18.12.0'} - unplugin@1.0.1: - resolution: {integrity: sha512-aqrHaVBWW1JVKBHmGo33T5TxeL0qWzfvjWokObHA9bYmN7eNDkwOxmLjhioHl9878qDFMAaT51XNroRyuz7WxA==} - unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - unstorage@1.17.3: - resolution: {integrity: sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q==} + unstorage@1.17.4: + resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==} peerDependencies: '@azure/app-configuration': ^1.8.0 '@azure/cosmos': ^4.2.0 @@ -16303,14 +16540,14 @@ packages: '@azure/identity': ^4.6.0 '@azure/keyvault-secrets': ^4.9.0 '@azure/storage-blob': ^12.26.0 - '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@capacitor/preferences': ^6 || ^7 || ^8 '@deno/kv': '>=0.9.0' '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 '@planetscale/database': ^1.19.0 '@upstash/redis': ^1.34.3 '@vercel/blob': '>=0.27.1' '@vercel/functions': ^2.2.12 || ^3.0.0 - '@vercel/kv': ^1.0.1 + '@vercel/kv': ^1 || ^2 || ^3 aws4fetch: ^1.0.20 db0: '>=0.2.1' idb-keyval: ^6.2.1 @@ -16412,8 +16649,8 @@ packages: '@types/react': optional: true - use-debounce@10.0.6: - resolution: {integrity: sha512-C5OtPyhAZgVoteO9heXMTdW7v/IbFI+8bSVKYCJrSmiWWCLsbUxiBSp4t9v0hNBTGY97bT72ydDIDyGSFWfwXg==} + use-debounce@10.1.0: + resolution: {integrity: sha512-lu87Za35V3n/MyMoEpD5zJv0k7hCn0p+V/fK2kWD+3k2u3kOCwO593UArbczg1fhfs2rqPEnHpULJ3KmGdDzvg==} engines: {node: '>= 16.0.0'} peerDependencies: react: '*' @@ -16466,6 +16703,10 @@ packages: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} + utf-8-validate@6.0.6: + resolution: {integrity: sha512-q3l3P9UtEEiAHcsgsqTgf9PPjctrDWoIXW3NpOHFdRDbLvu4DLIcxHangJ4RLrWkBcKjmcs/6NkerI8T/rE4LA==} + engines: {node: '>=6.14.2'} + util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} @@ -16564,8 +16805,8 @@ packages: typescript: optional: true - viem@2.43.3: - resolution: {integrity: sha512-zM251fspfSjENCtfmT7cauuD+AA/YAlkFU7cksdEQJxj7wDuO0XFRWRH+RMvfmTFza88B9kug5cKU+Wk2nAjJg==} + viem@2.46.3: + resolution: {integrity: sha512-2LJS+Hyh2sYjHXQtzfv1kU9pZx9dxFzvoU/ZKIcn0FNtOU0HQuIICuYdWtUDFHaGXbAdVo8J1eCvmjkL9JVGwg==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -16673,15 +16914,15 @@ packages: warning@4.0.3: resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} - watchpack@2.4.4: - resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} + watchpack@2.5.1: + resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} engines: {node: '>=10.13.0'} wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - weaviate-client@3.10.0: - resolution: {integrity: sha512-PB338DjIwUus1Mq1dxhCc6fEp+yA+aY4H4sSFDS0No/GguEufd6SDhHHVLOYMy2cPgX35dWgEx5jUbG5o3aPZA==} + weaviate-client@3.11.0: + resolution: {integrity: sha512-pEO+V8OZ84KUKz9ftQnSuooCT4Fdh3SjkDj6FPfxI3Iy6qc+PTTAMFFO0wL2prnf71DIs0tdNw/1RFX4kJkE/w==} engines: {node: '>=20.0.0'} web-namespaces@2.0.1: @@ -16695,8 +16936,8 @@ packages: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} - web-vitals@4.2.4: - resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} + web-vitals@5.1.0: + resolution: {integrity: sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==} webextension-polyfill@0.12.0: resolution: {integrity: sha512-97TBmpoWJEE+3nFBQ4VocyCdLKfw54rFaJ6EVQYLBCXqCIpLSZkwGgASpv4oPt9gdKCJ80RJlcmNzNn008Ag6Q==} @@ -16712,15 +16953,12 @@ packages: resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} engines: {node: '>=6'} - webpack-sources@3.3.3: - resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + webpack-sources@3.3.4: + resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} - webpack-virtual-modules@0.5.0: - resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} - - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + webpack@5.105.3: + resolution: {integrity: sha512-LLBBA4oLmT7sZdHiYE/PeVuifOxYyE2uL/V+9VQP7YSYdJU7bSf7H8bZRRxW8kEPMkmVjnrXmoR3oejIdX0xbg==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -16790,8 +17028,8 @@ packages: which-module@2.0.1: resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} - which-typed-array@1.1.19: - resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} engines: {node: '>= 0.4'} which@1.3.1: @@ -16803,11 +17041,6 @@ packages: engines: {node: '>= 8'} hasBin: true - which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} - hasBin: true - why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -16864,18 +17097,6 @@ packages: utf-8-validate: optional: true - ws@8.17.1: - resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -16900,6 +17121,18 @@ packages: utf-8-validate: optional: true + ws@8.19.0: + resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + wsl-utils@0.1.0: resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} engines: {node: '>=18'} @@ -16929,8 +17162,8 @@ packages: xmlserializer@0.6.1: resolution: {integrity: sha512-FNb0eEqqUUbnuvxuHqNuKH8qCGKqxu+558Zi8UzOoQk8Z9LdvpONK+v7m3gpKVHrk5Aq+0nNLsKxu/6OYh7Umw==} - xstate@5.25.0: - resolution: {integrity: sha512-yyWzfhVRoTHNLjLoMmdwZGagAYfmnzpm9gPjlX2MhJZsDojXGqRxODDOi4BsgGRKD46NZRAdcLp6CKOyvQS0Bw==} + xstate@5.28.0: + resolution: {integrity: sha512-Iaqq6ZrUzqeUtA3hC5LQKZfR8ZLzEFTImMHJM3jWEdVvXWdKvvVLXZEiNQWm3SCA9ZbEou/n5rcsna1wb9t28A==} xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} @@ -17005,8 +17238,8 @@ packages: zod-from-json-schema@0.5.2: resolution: {integrity: sha512-/dNaicfdhJTOuUd4RImbLUE2g5yrSzzDjI/S6C2vO2ecAGZzn9UcRVgtyLSnENSmAOBRiSpUdzDS6fDWX3Z35g==} - zod-to-json-schema@3.25.0: - resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: zod: ^3.25 || ^4 @@ -17016,11 +17249,11 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.2.1: - resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} - zustand@5.0.9: - resolution: {integrity: sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==} + zustand@5.0.11: + resolution: {integrity: sha512-fdZY+dk7zn/vbWNCYmzZULHRrss0jx5pPFiOuMZ/5HJN6Yv3u+1Wswy/4MpZEkEGhtNH+pwxZB8OKgUBPzYAGg==} engines: {node: '>=12.20.0'} peerDependencies: '@types/react': '>=18.0.0' @@ -17042,16 +17275,16 @@ packages: snapshots: - '@0no-co/graphql.web@1.2.0(graphql@16.12.0)': + '@0no-co/graphql.web@1.2.0(graphql@16.13.0)': optionalDependencies: - graphql: 16.12.0 + graphql: 16.13.0 '@a2a-js/sdk@0.2.5': dependencies: '@types/cors': 2.8.19 '@types/express': 4.17.25 - body-parser: 2.2.1 - cors: 2.8.5 + body-parser: 2.2.2 + cors: 2.8.6 express: 4.22.1 uuid: 11.1.0 transitivePeerDependencies: @@ -17059,11 +17292,11 @@ snapshots: '@adraffy/ens-normalize@1.11.1': {} - '@ag-ui/client@0.0.42': + '@ag-ui/client@0.0.46': dependencies: - '@ag-ui/core': 0.0.42 - '@ag-ui/encoder': 0.0.42 - '@ag-ui/proto': 0.0.42 + '@ag-ui/core': 0.0.46 + '@ag-ui/encoder': 0.0.46 + '@ag-ui/proto': 0.0.46 '@types/uuid': 10.0.0 compare-versions: 6.1.1 fast-json-patch: 3.1.1 @@ -17077,22 +17310,22 @@ snapshots: rxjs: 7.8.1 zod: 3.25.76 - '@ag-ui/core@0.0.42': + '@ag-ui/core@0.0.46': dependencies: rxjs: 7.8.1 zod: 3.25.76 - '@ag-ui/encoder@0.0.42': + '@ag-ui/encoder@0.0.46': dependencies: - '@ag-ui/core': 0.0.42 - '@ag-ui/proto': 0.0.42 + '@ag-ui/core': 0.0.46 + '@ag-ui/proto': 0.0.46 - '@ag-ui/langgraph@0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ag-ui/langgraph@0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@ag-ui/client': 0.0.42 - '@ag-ui/core': 0.0.42 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ag-ui/client': 0.0.46 + '@ag-ui/core': 0.0.37 + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) partial-json: 0.1.7 rxjs: 7.8.1 transitivePeerDependencies: @@ -17103,12 +17336,12 @@ snapshots: - react - react-dom - '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@copilotkit/runtime@1.10.6(c9e743140f0883eda50bf44e0628c06c))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': + '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(9413ba786a25fdda1f6d87c0f5568c1b))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': dependencies: - '@ag-ui/client': 0.0.42 - '@ag-ui/core': 0.0.42 + '@ag-ui/client': 0.0.46 + '@ag-ui/core': 0.0.37 '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) - '@copilotkit/runtime': 1.10.6(c9e743140f0883eda50bf44e0628c06c) + '@copilotkit/runtime': 1.10.6(9413ba786a25fdda1f6d87c0f5568c1b) '@mastra/client-js': 0.15.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) rxjs: 7.8.1 @@ -17130,10 +17363,10 @@ snapshots: - valibot - zod-openapi - '@ag-ui/proto@0.0.42': + '@ag-ui/proto@0.0.46': dependencies: - '@ag-ui/core': 0.0.42 - '@bufbuild/protobuf': 2.10.2 + '@ag-ui/core': 0.0.46 + '@bufbuild/protobuf': 2.11.0 '@protobuf-ts/protoc': 2.11.1 '@ai-sdk/anthropic@2.0.23(zod@3.25.76)': @@ -17146,7 +17379,7 @@ snapshots: dependencies: '@ai-sdk/provider': 2.0.0 '@ai-sdk/provider-utils': 3.0.10(zod@3.25.76) - '@vercel/oidc': 3.0.5 + '@vercel/oidc': 3.2.0 zod: 3.25.76 '@ai-sdk/google@2.0.17(zod@3.25.76)': @@ -17167,10 +17400,10 @@ snapshots: '@ai-sdk/provider-utils': 3.0.10(zod@3.25.76) zod: 3.25.76 - '@ai-sdk/openai@2.0.88(zod@3.25.76)': + '@ai-sdk/openai@2.0.95(zod@3.25.76)': dependencies: - '@ai-sdk/provider': 2.0.0 - '@ai-sdk/provider-utils': 3.0.19(zod@3.25.76) + '@ai-sdk/provider': 2.0.1 + '@ai-sdk/provider-utils': 3.0.21(zod@3.25.76) zod: 3.25.76 '@ai-sdk/provider-utils@2.2.8(zod@3.25.76)': @@ -17187,9 +17420,9 @@ snapshots: eventsource-parser: 3.0.6 zod: 3.25.76 - '@ai-sdk/provider-utils@3.0.19(zod@3.25.76)': + '@ai-sdk/provider-utils@3.0.21(zod@3.25.76)': dependencies: - '@ai-sdk/provider': 2.0.0 + '@ai-sdk/provider': 2.0.1 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.0.6 zod: 3.25.76 @@ -17202,12 +17435,16 @@ snapshots: dependencies: json-schema: 0.4.0 + '@ai-sdk/provider@2.0.1': + dependencies: + json-schema: 0.4.0 + '@ai-sdk/react@1.2.12(react@18.3.1)(zod@3.25.76)': dependencies: '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) react: 18.3.1 - swr: 2.3.8(react@18.3.1) + swr: 2.4.1(react@18.3.1) throttleit: 2.1.0 optionalDependencies: zod: 3.25.76 @@ -17217,7 +17454,7 @@ snapshots: '@ai-sdk/provider': 1.1.3 '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) '@ai-sdk/xai@2.0.23(zod@3.25.76)': dependencies: @@ -17310,87 +17547,79 @@ snapshots: '@types/json-schema': 7.0.15 js-yaml: 4.1.1 - '@apm-js-collab/code-transformer@0.8.2': {} - - '@apm-js-collab/tracing-hooks@0.3.1': - dependencies: - '@apm-js-collab/code-transformer': 0.8.2 - debug: 4.4.3(supports-color@5.5.0) - module-details-from-path: 1.0.4 - transitivePeerDependencies: - - supports-color - '@atproto/api@0.15.27': dependencies: - '@atproto/common-web': 0.4.7 + '@atproto/common-web': 0.4.17 '@atproto/lexicon': 0.4.14 - '@atproto/syntax': 0.4.2 + '@atproto/syntax': 0.4.3 '@atproto/xrpc': 0.7.7 await-lock: 2.2.2 multiformats: 9.9.0 tlds: 1.261.0 zod: 3.25.76 - '@atproto/common-web@0.4.7': + '@atproto/common-web@0.4.17': dependencies: - '@atproto/lex-data': 0.0.3 - '@atproto/lex-json': 0.0.3 + '@atproto/lex-data': 0.0.12 + '@atproto/lex-json': 0.0.12 + '@atproto/syntax': 0.4.3 zod: 3.25.76 - '@atproto/lex-data@0.0.3': + '@atproto/lex-data@0.0.12': dependencies: - '@atproto/syntax': 0.4.2 multiformats: 9.9.0 tslib: 2.8.1 uint8arrays: 3.0.0 - unicode-segmenter: 0.14.4 + unicode-segmenter: 0.14.5 - '@atproto/lex-json@0.0.3': + '@atproto/lex-json@0.0.12': dependencies: - '@atproto/lex-data': 0.0.3 + '@atproto/lex-data': 0.0.12 tslib: 2.8.1 '@atproto/lexicon@0.4.14': dependencies: - '@atproto/common-web': 0.4.7 - '@atproto/syntax': 0.4.2 + '@atproto/common-web': 0.4.17 + '@atproto/syntax': 0.4.3 iso-datestring-validator: 2.2.2 multiformats: 9.9.0 zod: 3.25.76 - '@atproto/lexicon@0.6.0': + '@atproto/lexicon@0.6.1': dependencies: - '@atproto/common-web': 0.4.7 - '@atproto/syntax': 0.4.2 + '@atproto/common-web': 0.4.17 + '@atproto/syntax': 0.4.3 iso-datestring-validator: 2.2.2 multiformats: 9.9.0 zod: 3.25.76 - '@atproto/syntax@0.4.2': {} + '@atproto/syntax@0.4.3': + dependencies: + tslib: 2.8.1 '@atproto/xrpc@0.7.7': dependencies: - '@atproto/lexicon': 0.6.0 + '@atproto/lexicon': 0.6.1 zod: 3.25.76 '@aws-crypto/crc32@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.956.0 + '@aws-sdk/types': 3.973.4 tslib: 2.8.1 '@aws-crypto/crc32c@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.956.0 + '@aws-sdk/types': 3.973.4 tslib: 2.8.1 '@aws-crypto/sha1-browser@5.2.0': dependencies: '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-locate-window': 3.953.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-locate-window': 3.965.4 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 @@ -17399,15 +17628,15 @@ snapshots: '@aws-crypto/sha256-js': 5.2.0 '@aws-crypto/supports-web-crypto': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-locate-window': 3.953.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-locate-window': 3.965.4 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 '@aws-crypto/sha256-js@5.2.0': dependencies: '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.956.0 + '@aws-sdk/types': 3.973.4 tslib: 2.8.1 '@aws-crypto/supports-web-crypto@5.2.0': @@ -17416,694 +17645,626 @@ snapshots: '@aws-crypto/util@5.2.0': dependencies: - '@aws-sdk/types': 3.956.0 + '@aws-sdk/types': 3.973.4 '@smithy/util-utf8': 2.3.0 tslib: 2.8.1 - '@aws-sdk/client-bedrock-agent-runtime@3.956.0': + '@aws-sdk/client-bedrock-agent-runtime@3.1000.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/eventstream-serde-browser': 4.2.7 - '@smithy/eventstream-serde-config-resolver': 4.3.7 - '@smithy/eventstream-serde-node': 4.2.7 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/credential-provider-node': 3.972.14 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.0 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/eventstream-serde-browser': 4.2.10 + '@smithy/eventstream-serde-config-resolver': 4.3.10 + '@smithy/eventstream-serde-node': 4.2.10 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-bedrock-runtime@3.956.0': + '@aws-sdk/client-bedrock-runtime@3.1000.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@aws-sdk/eventstream-handler-node': 3.956.0 - '@aws-sdk/middleware-eventstream': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/middleware-websocket': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/token-providers': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/eventstream-serde-browser': 4.2.7 - '@smithy/eventstream-serde-config-resolver': 4.3.7 - '@smithy/eventstream-serde-node': 4.2.7 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/credential-provider-node': 3.972.14 + '@aws-sdk/eventstream-handler-node': 3.972.9 + '@aws-sdk/middleware-eventstream': 3.972.6 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/middleware-websocket': 3.972.10 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/token-providers': 3.1000.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.0 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/eventstream-serde-browser': 4.2.10 + '@smithy/eventstream-serde-config-resolver': 4.3.10 + '@smithy/eventstream-serde-node': 4.2.10 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-kendra@3.956.0': + '@aws-sdk/client-kendra@3.1000.0': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/credential-provider-node': 3.972.14 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.0 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-s3@3.956.0': + '@aws-sdk/client-s3@3.1000.0': 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.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@aws-sdk/middleware-bucket-endpoint': 3.956.0 - '@aws-sdk/middleware-expect-continue': 3.956.0 - '@aws-sdk/middleware-flexible-checksums': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-location-constraint': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-sdk-s3': 3.956.0 - '@aws-sdk/middleware-ssec': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/signature-v4-multi-region': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/eventstream-serde-browser': 4.2.7 - '@smithy/eventstream-serde-config-resolver': 4.3.7 - '@smithy/eventstream-serde-node': 4.2.7 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-blob-browser': 4.2.8 - '@smithy/hash-node': 4.2.7 - '@smithy/hash-stream-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/md5-js': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/credential-provider-node': 3.972.14 + '@aws-sdk/middleware-bucket-endpoint': 3.972.6 + '@aws-sdk/middleware-expect-continue': 3.972.6 + '@aws-sdk/middleware-flexible-checksums': 3.973.1 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-location-constraint': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-sdk-s3': 3.972.15 + '@aws-sdk/middleware-ssec': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/signature-v4-multi-region': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.0 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/eventstream-serde-browser': 4.2.10 + '@smithy/eventstream-serde-config-resolver': 4.3.10 + '@smithy/eventstream-serde-node': 4.2.10 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-blob-browser': 4.2.11 + '@smithy/hash-node': 4.2.10 + '@smithy/hash-stream-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/md5-js': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 + '@smithy/util-waiter': 4.2.10 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-ses@3.956.0': + '@aws-sdk/core@3.973.15': dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 - '@smithy/util-waiter': 4.2.7 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/xml-builder': 3.972.8 + '@smithy/core': 3.23.6 + '@smithy/node-config-provider': 4.3.10 + '@smithy/property-provider': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-utf8': 4.2.1 + tslib: 2.8.1 + + '@aws-sdk/crc64-nvme@3.972.3': + dependencies: + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.15 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.972.15': + dependencies: + '@aws-sdk/core': 3.973.15 + '@aws-sdk/types': 3.973.4 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/node-http-handler': 4.4.12 + '@smithy/property-provider': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.15 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.15 + '@aws-sdk/credential-provider-env': 3.972.13 + '@aws-sdk/credential-provider-http': 3.972.15 + '@aws-sdk/credential-provider-login': 3.972.13 + '@aws-sdk/credential-provider-process': 3.972.13 + '@aws-sdk/credential-provider-sso': 3.972.13 + '@aws-sdk/credential-provider-web-identity': 3.972.13 + '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@smithy/credential-provider-imds': 4.2.10 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/client-sso@3.956.0': + '@aws-sdk/credential-provider-login@3.972.13': dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/core@3.956.0': + '@aws-sdk/credential-provider-node@3.972.14': dependencies: - '@aws-sdk/types': 3.956.0 - '@aws-sdk/xml-builder': 3.956.0 - '@smithy/core': 3.20.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-utf8': 4.2.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-env@3.956.0': - dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/property-provider': 4.2.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-http@3.956.0': - dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/node-http-handler': 4.4.7 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/util-stream': 4.5.8 - tslib: 2.8.1 - - '@aws-sdk/credential-provider-ini@3.956.0': - dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/credential-provider-env': 3.956.0 - '@aws-sdk/credential-provider-http': 3.956.0 - '@aws-sdk/credential-provider-login': 3.956.0 - '@aws-sdk/credential-provider-process': 3.956.0 - '@aws-sdk/credential-provider-sso': 3.956.0 - '@aws-sdk/credential-provider-web-identity': 3.956.0 - '@aws-sdk/nested-clients': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 + '@aws-sdk/credential-provider-env': 3.972.13 + '@aws-sdk/credential-provider-http': 3.972.15 + '@aws-sdk/credential-provider-ini': 3.972.13 + '@aws-sdk/credential-provider-process': 3.972.13 + '@aws-sdk/credential-provider-sso': 3.972.13 + '@aws-sdk/credential-provider-web-identity': 3.972.13 + '@aws-sdk/types': 3.973.4 + '@smithy/credential-provider-imds': 4.2.10 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-login@3.956.0': + '@aws-sdk/credential-provider-process@3.972.13': dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/nested-clients': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/property-provider': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.972.13': + dependencies: + '@aws-sdk/core': 3.973.15 + '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/token-providers': 3.999.0 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-node@3.956.0': + '@aws-sdk/credential-provider-web-identity@3.972.13': dependencies: - '@aws-sdk/credential-provider-env': 3.956.0 - '@aws-sdk/credential-provider-http': 3.956.0 - '@aws-sdk/credential-provider-ini': 3.956.0 - '@aws-sdk/credential-provider-process': 3.956.0 - '@aws-sdk/credential-provider-sso': 3.956.0 - '@aws-sdk/credential-provider-web-identity': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/credential-provider-process@3.956.0': + '@aws-sdk/eventstream-handler-node@3.972.9': dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/eventstream-codec': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.956.0': + '@aws-sdk/middleware-bucket-endpoint@3.972.6': dependencies: - '@aws-sdk/client-sso': 3.956.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/token-providers': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/credential-provider-web-identity@3.956.0': - dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/nested-clients': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - transitivePeerDependencies: - - aws-crt - - '@aws-sdk/eventstream-handler-node@3.956.0': - dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/eventstream-codec': 4.2.7 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-arn-parser': 3.972.2 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.1 tslib: 2.8.1 - '@aws-sdk/middleware-bucket-endpoint@3.956.0': + '@aws-sdk/middleware-eventstream@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-arn-parser': 3.953.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-config-provider': 4.2.0 + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-eventstream@3.956.0': + '@aws-sdk/middleware-expect-continue@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-expect-continue@3.956.0': - dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - tslib: 2.8.1 - - '@aws-sdk/middleware-flexible-checksums@3.956.0': + '@aws-sdk/middleware-flexible-checksums@3.973.1': dependencies: '@aws-crypto/crc32': 5.2.0 '@aws-crypto/crc32c': 5.2.0 '@aws-crypto/util': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/is-array-buffer': 4.2.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/crc64-nvme': 3.972.3 + '@aws-sdk/types': 3.973.4 + '@smithy/is-array-buffer': 4.2.1 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@aws-sdk/middleware-host-header@3.956.0': + '@aws-sdk/middleware-host-header@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-location-constraint@3.956.0': + '@aws-sdk/middleware-location-constraint@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-logger@3.956.0': + '@aws-sdk/middleware-logger@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-recursion-detection@3.956.0': + '@aws-sdk/middleware-recursion-detection@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@aws/lambda-invoke-store': 0.2.2 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@aws/lambda-invoke-store': 0.2.3 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-sdk-s3@3.956.0': + '@aws-sdk/middleware-sdk-s3@3.972.15': dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-arn-parser': 3.953.0 - '@smithy/core': 3.20.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-arn-parser': 3.972.2 + '@smithy/core': 3.23.6 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@aws-sdk/middleware-ssec@3.956.0': + '@aws-sdk/middleware-ssec@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-user-agent@3.956.0': + '@aws-sdk/middleware-user-agent@3.972.15': dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@smithy/core': 3.20.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@smithy/core': 3.23.6 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.956.0': + '@aws-sdk/middleware-websocket@3.972.10': dependencies: - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-format-url': 3.956.0 - '@smithy/eventstream-codec': 4.2.7 - '@smithy/eventstream-serde-browser': 4.2.7 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-hex-encoding': 4.2.0 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-format-url': 3.972.6 + '@smithy/eventstream-codec': 4.2.10 + '@smithy/eventstream-serde-browser': 4.2.10 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.956.0': + '@aws-sdk/nested-clients@3.996.3': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.956.0 - '@aws-sdk/middleware-host-header': 3.956.0 - '@aws-sdk/middleware-logger': 3.956.0 - '@aws-sdk/middleware-recursion-detection': 3.956.0 - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/region-config-resolver': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-endpoints': 3.956.0 - '@aws-sdk/util-user-agent-browser': 3.956.0 - '@aws-sdk/util-user-agent-node': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/core': 3.20.0 - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/hash-node': 4.2.7 - '@smithy/invalid-dependency': 4.2.7 - '@smithy/middleware-content-length': 4.2.7 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-retry': 4.4.17 - '@smithy/middleware-serde': 4.2.8 - '@smithy/middleware-stack': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/node-http-handler': 4.4.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-body-length-node': 4.2.1 - '@smithy/util-defaults-mode-browser': 4.3.16 - '@smithy/util-defaults-mode-node': 4.2.19 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/util-utf8': 4.2.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/middleware-host-header': 3.972.6 + '@aws-sdk/middleware-logger': 3.972.6 + '@aws-sdk/middleware-recursion-detection': 3.972.6 + '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/region-config-resolver': 3.972.6 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-endpoints': 3.996.3 + '@aws-sdk/util-user-agent-browser': 3.972.6 + '@aws-sdk/util-user-agent-node': 3.973.0 + '@smithy/config-resolver': 4.4.9 + '@smithy/core': 3.23.6 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/hash-node': 4.2.10 + '@smithy/invalid-dependency': 4.2.10 + '@smithy/middleware-content-length': 4.2.10 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-retry': 4.4.37 + '@smithy/middleware-serde': 4.2.11 + '@smithy/middleware-stack': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/node-http-handler': 4.4.12 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-body-length-node': 4.2.2 + '@smithy/util-defaults-mode-browser': 4.3.36 + '@smithy/util-defaults-mode-node': 4.2.39 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/region-config-resolver@3.956.0': + '@aws-sdk/region-config-resolver@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/config-resolver': 4.4.5 - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/config-resolver': 4.4.9 + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/s3-request-presigner@3.956.0': + '@aws-sdk/s3-request-presigner@3.1000.0': dependencies: - '@aws-sdk/signature-v4-multi-region': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@aws-sdk/util-format-url': 3.956.0 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/protocol-http': 5.3.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 + '@aws-sdk/signature-v4-multi-region': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@aws-sdk/util-format-url': 3.972.6 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/protocol-http': 5.3.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/signature-v4-multi-region@3.956.0': + '@aws-sdk/signature-v4-multi-region@3.996.3': dependencies: - '@aws-sdk/middleware-sdk-s3': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/signature-v4': 5.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/middleware-sdk-s3': 3.972.15 + '@aws-sdk/types': 3.973.4 + '@smithy/protocol-http': 5.3.10 + '@smithy/signature-v4': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/token-providers@3.956.0': + '@aws-sdk/token-providers@3.1000.0': dependencies: - '@aws-sdk/core': 3.956.0 - '@aws-sdk/nested-clients': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 transitivePeerDependencies: - aws-crt - '@aws-sdk/types@3.956.0': + '@aws-sdk/token-providers@3.999.0': dependencies: - '@smithy/types': 4.11.0 + '@aws-sdk/core': 3.973.15 + '@aws-sdk/nested-clients': 3.996.3 + '@aws-sdk/types': 3.973.4 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/types@3.973.4': + dependencies: + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/util-arn-parser@3.953.0': + '@aws-sdk/util-arn-parser@3.972.2': dependencies: tslib: 2.8.1 - '@aws-sdk/util-endpoints@3.956.0': + '@aws-sdk/util-endpoints@3.996.3': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-endpoints': 3.2.7 + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-endpoints': 3.3.1 tslib: 2.8.1 - '@aws-sdk/util-format-url@3.956.0': + '@aws-sdk/util-format-url@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/querystring-builder': 4.2.7 - '@smithy/types': 4.11.0 + '@aws-sdk/types': 3.973.4 + '@smithy/querystring-builder': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/util-locate-window@3.953.0': + '@aws-sdk/util-locate-window@3.965.4': dependencies: tslib: 2.8.1 - '@aws-sdk/util-user-agent-browser@3.956.0': + '@aws-sdk/util-user-agent-browser@3.972.6': dependencies: - '@aws-sdk/types': 3.956.0 - '@smithy/types': 4.11.0 - bowser: 2.13.1 + '@aws-sdk/types': 3.973.4 + '@smithy/types': 4.13.0 + bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/util-user-agent-node@3.956.0': + '@aws-sdk/util-user-agent-node@3.973.0': dependencies: - '@aws-sdk/middleware-user-agent': 3.956.0 - '@aws-sdk/types': 3.956.0 - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 + '@aws-sdk/middleware-user-agent': 3.972.15 + '@aws-sdk/types': 3.973.4 + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@aws-sdk/xml-builder@3.956.0': + '@aws-sdk/xml-builder@3.972.8': dependencies: - '@smithy/types': 4.11.0 - fast-xml-parser: 5.2.5 + '@smithy/types': 4.13.0 + fast-xml-parser: 5.3.6 tslib: 2.8.1 - '@aws/lambda-invoke-store@0.2.2': {} + '@aws/lambda-invoke-store@0.2.3': {} - '@babel/code-frame@7.27.1': + '@babel/code-frame@7.29.0': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.5': {} + '@babel/compat-data@7.29.0': {} - '@babel/core@7.28.5': + '@babel/core@7.29.0': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@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 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@5.5.0) @@ -18113,51 +18274,51 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.5': + '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 - '@babel/helper-compilation-targets@7.27.2': + '@babel/helper-compilation-targets@7.28.6': dependencies: - '@babel/compat-data': 7.28.5 + '@babel/compat-data': 7.29.0 '@babel/helper-validator-option': 7.27.1 browserslist: 4.28.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.5)': + '@babel/helper-define-polyfill-provider@0.6.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 debug: 4.4.3(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.11 @@ -18168,55 +18329,55 @@ snapshots: '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.27.1': + '@babel/helper-module-imports@7.28.6': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 - '@babel/helper-plugin-utils@7.27.1': {} + '@babel/helper-plugin-utils@7.28.6': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.5 + '@babel/helper-wrap-function': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color @@ -18226,686 +18387,686 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-wrap-function@7.28.3': + '@babel/helper-wrap-function@7.28.6': dependencies: - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/helpers@7.28.4': + '@babel/helpers@7.28.6': dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 - '@babel/parser@7.28.5': + '@babel/parser@7.29.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.5)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.5)': + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-block-scoping@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.5)': + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.5)': + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-globals': 7.28.0 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) - '@babel/traverse': 7.28.5 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/template': 7.27.2 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/template': 7.28.6 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.5)': + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-logical-assignment-operators@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-systemjs@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.5 + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.5)': + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/traverse': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-optional-chaining@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.5)': + '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-jsx@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/types': 7.28.5 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.5)': + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-typescript@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.28.6 - '@babel/preset-env@7.28.5(@babel/core@7.28.5)': + '@babel/preset-env@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/compat-data': 7.28.5 - '@babel/core': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.5) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-block-scoping': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.5) - '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) - '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-exponentiation-operator': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-logical-assignment-operators': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-systemjs': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.5) - '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.5) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) - babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) - babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) - core-js-compat: 3.47.0 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.0(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.29.0) + core-js-compat: 3.48.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/types': 7.29.0 esutils: 2.0.3 - '@babel/preset-react@7.28.5(@babel/core@7.28.5)': + '@babel/preset-react@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': + '@babel/preset-typescript@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.28.6 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-typescript': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/runtime@7.28.4': {} + '@babel/runtime@7.28.6': {} - '@babel/template@7.27.2': + '@babel/template@7.28.6': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 - '@babel/traverse@7.28.5': + '@babel/traverse@7.29.0': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/types@7.28.5': + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 '@bcoe/v8-coverage@0.2.3': {} - '@blueprintjs/colors@5.1.12': + '@blueprintjs/colors@5.1.14': dependencies: tslib: 2.6.3 '@blueprintjs/core@5.19.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@blueprintjs/colors': 5.1.12 + '@blueprintjs/colors': 5.1.14 '@blueprintjs/icons': 5.23.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@popperjs/core': 2.11.8 classnames: 2.5.1 @@ -18941,9 +19102,9 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@borewit/text-codec@0.1.1': {} + '@borewit/text-codec@0.2.1': {} - '@browserbasehq/sdk@2.6.0': + '@browserbasehq/sdk@2.7.0': dependencies: '@types/node': 18.16.9 '@types/node-fetch': 2.6.13 @@ -18955,32 +19116,32 @@ snapshots: transitivePeerDependencies: - encoding - '@browserbasehq/stagehand@1.14.0(@playwright/test@1.57.0)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@browserbasehq/stagehand@1.14.0(@playwright/test@1.58.2)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@anthropic-ai/sdk': 0.27.3 - '@browserbasehq/sdk': 2.6.0 - '@playwright/test': 1.57.0 + '@browserbasehq/sdk': 2.7.0 + '@playwright/test': 1.58.2 deepmerge: 4.3.1 dotenv: 16.6.1 - openai: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + openai: 6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - bufferutil - encoding - utf-8-validate - '@bufbuild/protobuf@2.10.2': {} + '@bufbuild/protobuf@2.11.0': {} - '@cacheable/utils@2.3.2': + '@cacheable/utils@2.4.0': dependencies: - hashery: 1.3.0 - keyv: 5.5.5 + hashery: 1.5.0 + keyv: 5.6.0 - '@casl/ability@6.7.5': + '@casl/ability@6.8.0': dependencies: - '@ucast/mongo2js': 1.4.0 + '@ucast/mongo2js': 1.4.1 '@cfworker/json-schema@4.1.1': {} @@ -18998,9 +19159,9 @@ snapshots: '@colors/colors@1.5.0': optional: true - '@copilotkit/react-core@1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@copilotkit/react-core@1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@copilotkit/runtime-client-gql': 1.10.6(graphql@16.12.0)(react@18.3.1) + '@copilotkit/runtime-client-gql': 1.10.6(graphql@16.13.0)(react@18.3.1) '@copilotkit/shared': 1.10.6 '@scarf/scarf': 1.4.0 react: 18.3.1 @@ -19013,10 +19174,10 @@ snapshots: - graphql - supports-color - '@copilotkit/react-textarea@1.10.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@copilotkit/react-textarea@1.10.6(@types/react-dom@18.3.0)(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@copilotkit/react-core': 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@copilotkit/runtime-client-gql': 1.10.6(graphql@16.12.0)(react@18.3.1) + '@copilotkit/react-core': 1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@copilotkit/runtime-client-gql': 1.10.6(graphql@16.13.0)(react@18.3.1) '@copilotkit/shared': 1.10.6 '@emotion/css': 11.13.5 '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) @@ -19045,10 +19206,10 @@ snapshots: - graphql - supports-color - '@copilotkit/react-ui@1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@copilotkit/react-ui@1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@copilotkit/react-core': 1.10.6(@types/react@18.3.1)(graphql@16.12.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@copilotkit/runtime-client-gql': 1.10.6(graphql@16.12.0)(react@18.3.1) + '@copilotkit/react-core': 1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@copilotkit/runtime-client-gql': 1.10.6(graphql@16.13.0)(react@18.3.1) '@copilotkit/shared': 1.10.6 '@headlessui/react': 2.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 @@ -19064,49 +19225,49 @@ snapshots: - react-dom - supports-color - '@copilotkit/runtime-client-gql@1.10.6(graphql@16.12.0)(react@18.3.1)': + '@copilotkit/runtime-client-gql@1.10.6(graphql@16.13.0)(react@18.3.1)': dependencies: '@copilotkit/shared': 1.10.6 - '@urql/core': 5.2.0(graphql@16.12.0) + '@urql/core': 5.2.0(graphql@16.13.0) react: 18.3.1 untruncate-json: 0.0.1 - urql: 4.2.2(@urql/core@5.2.0(graphql@16.12.0))(react@18.3.1) + urql: 4.2.2(@urql/core@5.2.0(graphql@16.13.0))(react@18.3.1) transitivePeerDependencies: - encoding - graphql - '@copilotkit/runtime@1.10.6(c9e743140f0883eda50bf44e0628c06c)': + '@copilotkit/runtime@1.10.6(9413ba786a25fdda1f6d87c0f5568c1b)': dependencies: - '@ag-ui/client': 0.0.42 - '@ag-ui/core': 0.0.42 - '@ag-ui/encoder': 0.0.42 - '@ag-ui/langgraph': 0.0.21(@ag-ui/client@0.0.42)(@ag-ui/core@0.0.42)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@ag-ui/proto': 0.0.42 + '@ag-ui/client': 0.0.46 + '@ag-ui/core': 0.0.37 + '@ag-ui/encoder': 0.0.46 + '@ag-ui/langgraph': 0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ag-ui/proto': 0.0.46 '@anthropic-ai/sdk': 0.57.0 '@copilotkit/shared': 1.10.6 - '@graphql-yoga/plugin-defer-stream': 3.18.0(graphql-yoga@5.18.0(graphql@16.12.0))(graphql@16.12.0) - '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) - '@langchain/community': 0.3.58(839bee139a23dbd5124ee69527468f5f) - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) - '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1) - '@langchain/openai': 0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@graphql-yoga/plugin-defer-stream': 3.18.0(graphql-yoga@5.18.0(graphql@16.13.0))(graphql@16.13.0) + '@langchain/aws': 0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/community': 0.3.59(4eb30e6535dbeef0da685ab48a24609d) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) + '@langchain/langgraph-sdk': 0.0.70(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1) + '@langchain/openai': 0.4.9(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) '@scarf/scarf': 1.4.0 class-transformer: 0.5.1 - class-validator: 0.14.3 + class-validator: 0.14.4 express: 4.22.1 - graphql: 16.12.0 - graphql-scalars: 1.25.0(graphql@16.12.0) - graphql-yoga: 5.18.0(graphql@16.12.0) + graphql: 16.13.0 + graphql-scalars: 1.25.0(graphql@16.13.0) + graphql-yoga: 5.18.0(graphql@16.13.0) groq-sdk: 0.5.0 - langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + langchain: 0.3.37(@langchain/aws@0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(axios@1.13.6)(cheerio@1.2.0)(handlebars@4.7.8)(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + openai: 4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) partial-json: 0.1.7 pino: 9.14.0 pino-pretty: 11.3.0 reflect-metadata: 0.2.2 rxjs: 7.8.1 - type-graphql: 2.0.0-rc.1(class-validator@0.14.3)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0) + type-graphql: 2.0.0-rc.1(class-validator@0.14.4)(graphql-scalars@1.25.0(graphql@16.13.0))(graphql@16.13.0) zod: 3.25.76 transitivePeerDependencies: - '@arcjet/redact' @@ -19262,10 +19423,10 @@ snapshots: '@ag-ui/core': 0.0.37 '@segment/analytics-node': 2.3.0 chalk: 4.1.2 - graphql: 16.12.0 + graphql: 16.13.0 uuid: 10.0.0 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - encoding @@ -19273,8 +19434,8 @@ snapshots: dependencies: '@rollup/pluginutils': 4.2.1 '@webcomponents/custom-elements': 1.6.0 - acorn-walk: 8.3.4 - cheerio: 1.1.2 + acorn-walk: 8.3.5 + cheerio: 1.2.0 convert-source-map: 1.9.0 debug: 4.4.3(supports-color@5.5.0) es-module-lexer: 0.10.5 @@ -19294,9 +19455,9 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@cypress/request-promise@5.0.0(@cypress/request@3.0.9)(request@2.88.2)': + '@cypress/request-promise@5.0.0(@cypress/request@3.0.10)(request@2.88.2)': dependencies: - '@cypress/request': 3.0.9 + '@cypress/request': 3.0.10 bluebird: 3.7.2 request-promise-core: 1.1.3(request@2.88.2) stealthy-require: 1.1.1 @@ -19304,7 +19465,7 @@ snapshots: transitivePeerDependencies: - request - '@cypress/request@3.0.9': + '@cypress/request@3.0.10': dependencies: aws-sign2: 0.7.0 aws4: 1.13.2 @@ -19319,23 +19480,25 @@ snapshots: json-stringify-safe: 5.0.1 mime-types: 2.1.35 performance-now: 2.1.0 - qs: 6.14.0 + qs: 6.14.2 safe-buffer: 5.2.1 tough-cookie: 5.1.2 tunnel-agent: 0.6.0 uuid: 8.3.2 + '@datastructures-js/deque@1.0.8': {} + '@dub/analytics@0.0.32': dependencies: server-only: 0.0.1 - '@emnapi/core@1.7.1': + '@emnapi/core@1.8.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.7.1': + '@emnapi/runtime@1.8.1': dependencies: tslib: 2.8.1 optional: true @@ -19347,8 +19510,8 @@ snapshots: '@emotion/babel-plugin@11.13.5': dependencies: - '@babel/helper-module-imports': 7.27.1 - '@babel/runtime': 7.28.4 + '@babel/helper-module-imports': 7.28.6 + '@babel/runtime': 7.28.6 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -19389,7 +19552,7 @@ snapshots: '@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 @@ -19415,7 +19578,7 @@ snapshots: '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) @@ -19438,7 +19601,7 @@ snapshots: '@emotion/weak-memoize@0.4.0': {} - '@envelop/core@5.4.0': + '@envelop/core@5.5.1': dependencies: '@envelop/instrumentation': 1.0.0 '@envelop/types': 5.2.1 @@ -19460,160 +19623,160 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true - '@esbuild/aix-ppc64@0.27.2': + '@esbuild/aix-ppc64@0.27.3': optional: true '@esbuild/android-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.27.2': + '@esbuild/android-arm64@0.27.3': optional: true '@esbuild/android-arm@0.25.12': optional: true - '@esbuild/android-arm@0.27.2': + '@esbuild/android-arm@0.27.3': optional: true '@esbuild/android-x64@0.25.12': optional: true - '@esbuild/android-x64@0.27.2': + '@esbuild/android-x64@0.27.3': optional: true '@esbuild/darwin-arm64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.27.2': + '@esbuild/darwin-arm64@0.27.3': optional: true '@esbuild/darwin-x64@0.25.12': optional: true - '@esbuild/darwin-x64@0.27.2': + '@esbuild/darwin-x64@0.27.3': optional: true '@esbuild/freebsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-arm64@0.27.2': + '@esbuild/freebsd-arm64@0.27.3': optional: true '@esbuild/freebsd-x64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.27.2': + '@esbuild/freebsd-x64@0.27.3': optional: true '@esbuild/linux-arm64@0.25.12': optional: true - '@esbuild/linux-arm64@0.27.2': + '@esbuild/linux-arm64@0.27.3': optional: true '@esbuild/linux-arm@0.25.12': optional: true - '@esbuild/linux-arm@0.27.2': + '@esbuild/linux-arm@0.27.3': optional: true '@esbuild/linux-ia32@0.25.12': optional: true - '@esbuild/linux-ia32@0.27.2': + '@esbuild/linux-ia32@0.27.3': optional: true '@esbuild/linux-loong64@0.25.12': optional: true - '@esbuild/linux-loong64@0.27.2': + '@esbuild/linux-loong64@0.27.3': optional: true '@esbuild/linux-mips64el@0.25.12': optional: true - '@esbuild/linux-mips64el@0.27.2': + '@esbuild/linux-mips64el@0.27.3': optional: true '@esbuild/linux-ppc64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.27.2': + '@esbuild/linux-ppc64@0.27.3': optional: true '@esbuild/linux-riscv64@0.25.12': optional: true - '@esbuild/linux-riscv64@0.27.2': + '@esbuild/linux-riscv64@0.27.3': optional: true '@esbuild/linux-s390x@0.25.12': optional: true - '@esbuild/linux-s390x@0.27.2': + '@esbuild/linux-s390x@0.27.3': optional: true '@esbuild/linux-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.27.2': + '@esbuild/linux-x64@0.27.3': optional: true '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/netbsd-arm64@0.27.2': + '@esbuild/netbsd-arm64@0.27.3': optional: true '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/netbsd-x64@0.27.2': + '@esbuild/netbsd-x64@0.27.3': optional: true '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.27.2': + '@esbuild/openbsd-arm64@0.27.3': optional: true '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/openbsd-x64@0.27.2': + '@esbuild/openbsd-x64@0.27.3': optional: true '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/openharmony-arm64@0.27.2': + '@esbuild/openharmony-arm64@0.27.3': optional: true '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/sunos-x64@0.27.2': + '@esbuild/sunos-x64@0.27.3': optional: true '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/win32-arm64@0.27.2': + '@esbuild/win32-arm64@0.27.3': optional: true '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/win32-ia32@0.27.2': + '@esbuild/win32-ia32@0.27.3': optional: true '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.27.2': + '@esbuild/win32-x64@0.27.3': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@8.57.0)': + '@eslint-community/eslint-utils@4.9.1(eslint@8.57.0)': dependencies: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 @@ -19622,14 +19785,14 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: - ajv: 6.12.6 + ajv: 6.14.0 debug: 4.4.3(supports-color@5.5.0) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 3.1.2 + minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -19654,24 +19817,34 @@ snapshots: '@fastify/busboy@3.2.0': {} - '@floating-ui/core@1.7.3': + '@fastify/otel@0.16.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + minimatch: 10.2.4 + transitivePeerDependencies: + - supports-color + + '@floating-ui/core@1.7.4': dependencies: '@floating-ui/utils': 0.2.10 - '@floating-ui/dom@1.7.4': + '@floating-ui/dom@1.7.5': dependencies: - '@floating-ui/core': 1.7.3 + '@floating-ui/core': 1.7.4 '@floating-ui/utils': 0.2.10 '@floating-ui/react-dom@1.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.7.4 + '@floating-ui/dom': 1.7.5 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@floating-ui/react-dom@2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@floating-ui/react-dom@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/dom': 1.7.4 + '@floating-ui/dom': 1.7.5 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -19681,15 +19854,15 @@ snapshots: aria-hidden: 1.2.6 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tabbable: 6.3.0 + tabbable: 6.4.0 '@floating-ui/react@0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@floating-ui/react-dom': 2.1.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@floating-ui/react-dom': 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@floating-ui/utils': 0.2.10 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - tabbable: 6.3.0 + tabbable: 6.4.0 '@floating-ui/utils@0.2.10': {} @@ -19708,50 +19881,58 @@ snapshots: - react - react-dom - '@graphql-tools/executor@1.5.0(graphql@16.12.0)': + '@graphql-tools/executor@1.5.1(graphql@16.13.0)': dependencies: - '@graphql-tools/utils': 10.11.0(graphql@16.12.0) - '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) '@repeaterjs/repeater': 3.0.6 '@whatwg-node/disposablestack': 0.0.6 '@whatwg-node/promise-helpers': 1.3.2 - graphql: 16.12.0 + graphql: 16.13.0 tslib: 2.8.1 - '@graphql-tools/merge@9.1.6(graphql@16.12.0)': + '@graphql-tools/merge@9.1.7(graphql@16.13.0)': dependencies: - '@graphql-tools/utils': 10.11.0(graphql@16.12.0) - graphql: 16.12.0 + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + graphql: 16.13.0 tslib: 2.8.1 - '@graphql-tools/schema@10.0.30(graphql@16.12.0)': + '@graphql-tools/schema@10.0.31(graphql@16.13.0)': dependencies: - '@graphql-tools/merge': 9.1.6(graphql@16.12.0) - '@graphql-tools/utils': 10.11.0(graphql@16.12.0) - graphql: 16.12.0 + '@graphql-tools/merge': 9.1.7(graphql@16.13.0) + '@graphql-tools/utils': 11.0.0(graphql@16.13.0) + graphql: 16.13.0 tslib: 2.8.1 - '@graphql-tools/utils@10.11.0(graphql@16.12.0)': + '@graphql-tools/utils@10.11.0(graphql@16.13.0)': dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) '@whatwg-node/promise-helpers': 1.3.2 cross-inspect: 1.0.1 - graphql: 16.12.0 + graphql: 16.13.0 tslib: 2.8.1 - '@graphql-typed-document-node/core@3.2.0(graphql@16.12.0)': + '@graphql-tools/utils@11.0.0(graphql@16.13.0)': dependencies: - graphql: 16.12.0 + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) + '@whatwg-node/promise-helpers': 1.3.2 + cross-inspect: 1.0.1 + graphql: 16.13.0 + tslib: 2.8.1 + + '@graphql-typed-document-node/core@3.2.0(graphql@16.13.0)': + dependencies: + graphql: 16.13.0 '@graphql-yoga/logger@2.0.1': dependencies: tslib: 2.8.1 - '@graphql-yoga/plugin-defer-stream@3.18.0(graphql-yoga@5.18.0(graphql@16.12.0))(graphql@16.12.0)': + '@graphql-yoga/plugin-defer-stream@3.18.0(graphql-yoga@5.18.0(graphql@16.13.0))(graphql@16.13.0)': dependencies: - '@graphql-tools/utils': 10.11.0(graphql@16.12.0) - graphql: 16.12.0 - graphql-yoga: 5.18.0(graphql@16.12.0) + '@graphql-tools/utils': 10.11.0(graphql@16.13.0) + graphql: 16.13.0 + graphql-yoga: 5.18.0(graphql@16.13.0) '@graphql-yoga/subscription@5.0.5': dependencies: @@ -19780,26 +19961,26 @@ snapshots: '@headlessui/react@2.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react': 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/focus': 3.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/interactions': 3.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@tanstack/react-virtual': 3.13.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/focus': 3.21.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/interactions': 3.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@tanstack/react-virtual': 3.13.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) - '@hono/node-server@1.19.7(hono@4.11.1)': + '@hono/node-server@1.19.9(hono@4.12.3)': dependencies: - hono: 4.11.1 + hono: 4.12.3 - '@hookform/resolvers@3.10.0(react-hook-form@7.69.0(react@18.3.1))': + '@hookform/resolvers@3.10.0(react-hook-form@7.71.2(react@18.3.1))': dependencies: - react-hook-form: 7.69.0(react@18.3.1) + react-hook-form: 7.71.2(react@18.3.1) '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.4.3(supports-color@5.5.0) - minimatch: 3.1.2 + minimatch: 3.1.5 transitivePeerDependencies: - supports-color @@ -19807,12 +19988,12 @@ snapshots: '@humanwhocodes/object-schema@2.0.3': {} - '@ibm-cloud/watsonx-ai@1.7.5': + '@ibm-cloud/watsonx-ai@1.7.8': dependencies: '@types/node': 18.16.9 extend: 3.0.2 form-data: 4.0.5 - ibm-cloud-sdk-core: 5.4.5 + ibm-cloud-sdk-core: 5.4.8 transitivePeerDependencies: - supports-color @@ -19886,7 +20067,7 @@ snapshots: '@img/sharp-wasm32@0.33.5': dependencies: - '@emnapi/runtime': 1.7.1 + '@emnapi/runtime': 1.8.1 optional: true '@img/sharp-win32-ia32@0.33.5': @@ -19895,26 +20076,42 @@ snapshots: '@img/sharp-win32-x64@0.33.5': optional: true - '@inquirer/external-editor@1.0.3(@types/node@18.16.9)': + '@inquirer/core@6.0.0': dependencies: - chardet: 2.1.1 - iconv-lite: 0.7.1 - optionalDependencies: - '@types/node': 18.16.9 + '@inquirer/type': 1.5.5 + '@types/mute-stream': 0.0.4 + '@types/node': 20.19.35 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-spinners: 2.9.2 + cli-width: 4.1.0 + figures: 3.2.0 + mute-stream: 1.0.0 + run-async: 3.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 - '@ioredis/commands@1.4.0': {} - - '@isaacs/balanced-match@4.0.1': {} - - '@isaacs/brace-expansion@5.0.0': + '@inquirer/select@1.3.3': dependencies: - '@isaacs/balanced-match': 4.0.1 + '@inquirer/core': 6.0.0 + '@inquirer/type': 1.5.5 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + figures: 3.2.0 + + '@inquirer/type@1.5.5': + dependencies: + mute-stream: 1.0.0 + + '@ioredis/commands@1.5.1': {} '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -20046,7 +20243,7 @@ snapshots: '@jest/schemas@29.6.3': dependencies: - '@sinclair/typebox': 0.27.8 + '@sinclair/typebox': 0.27.10 '@jest/source-map@29.6.3': dependencies: @@ -20070,7 +20267,7 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 6.1.1 @@ -20134,14 +20331,82 @@ snapshots: dependencies: tslib: 2.8.1 + '@jsonjoy.com/base64@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + '@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)': dependencies: tslib: 2.8.1 + '@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + '@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)': dependencies: tslib: 2.8.1 + '@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-core@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-fsa@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-builtins@4.56.10(tslib@2.8.1)': + dependencies: + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-to-fsa@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-fsa': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node-utils@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-node@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-core': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.56.10(tslib@2.8.1) + glob-to-regex.js: 1.2.0(tslib@2.8.1) + thingies: 2.5.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-print@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + + '@jsonjoy.com/fs-snapshot@4.56.10(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + '@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)': dependencies: '@jsonjoy.com/base64': 1.1.2(tslib@2.8.1) @@ -20154,18 +20419,41 @@ snapshots: tree-dump: 1.1.0(tslib@2.8.1) tslib: 2.8.1 + '@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/base64': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + hyperdyperid: 1.2.0 + thingies: 2.5.0(tslib@2.8.1) + tree-dump: 1.1.0(tslib@2.8.1) + tslib: 2.8.1 + '@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)': dependencies: '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) tslib: 2.8.1 + '@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/util': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + '@jsonjoy.com/util@1.9.0(tslib@2.8.1)': dependencies: '@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1) '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) tslib: 2.8.1 + '@jsonjoy.com/util@17.67.0(tslib@2.8.1)': + dependencies: + '@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1) + '@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1) + tslib: 2.8.1 + '@juggle/resize-observer@3.4.0': {} '@keystonehq/alias-sampling@0.1.2': {} @@ -20218,59 +20506,59 @@ snapshots: '@kurkle/color@0.3.4': {} - '@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/aws@0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@aws-sdk/client-bedrock-agent-runtime': 3.956.0 - '@aws-sdk/client-bedrock-runtime': 3.956.0 - '@aws-sdk/client-kendra': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@aws-sdk/client-bedrock-agent-runtime': 3.1000.0 + '@aws-sdk/client-bedrock-runtime': 3.1000.0 + '@aws-sdk/client-kendra': 3.1000.0 + '@aws-sdk/credential-provider-node': 3.972.14 + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) transitivePeerDependencies: - aws-crt - '@langchain/community@0.3.58(839bee139a23dbd5124ee69527468f5f)': + '@langchain/community@0.3.59(4eb30e6535dbeef0da685ab48a24609d)': dependencies: - '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.57.0)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) - '@ibm-cloud/watsonx-ai': 1.7.5 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@langchain/weaviate': 0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.58.2)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) + '@ibm-cloud/watsonx-ai': 1.7.8 + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/weaviate': 0.2.3(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) binary-extensions: 2.3.0 flat: 5.0.2 - ibm-cloud-sdk-core: 5.4.5 + ibm-cloud-sdk-core: 5.4.8 js-yaml: 4.1.1 - langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + langchain: 0.3.37(@langchain/aws@0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(axios@1.13.6)(cheerio@1.2.0)(handlebars@4.7.8)(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) math-expression-evaluator: 2.0.7 - openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-bedrock-agent-runtime': 3.956.0 - '@aws-sdk/client-bedrock-runtime': 3.956.0 - '@aws-sdk/client-kendra': 3.956.0 - '@aws-sdk/client-s3': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@browserbasehq/sdk': 2.6.0 + '@aws-sdk/client-bedrock-agent-runtime': 3.1000.0 + '@aws-sdk/client-bedrock-runtime': 3.1000.0 + '@aws-sdk/client-kendra': 3.1000.0 + '@aws-sdk/client-s3': 3.1000.0 + '@aws-sdk/credential-provider-node': 3.972.14 + '@browserbasehq/sdk': 2.7.0 '@smithy/util-utf8': 2.3.0 - '@upstash/redis': 1.35.8 - cheerio: 1.1.2 + '@upstash/redis': 1.36.3 + cheerio: 1.2.0 crypto-js: 4.2.0 - fast-xml-parser: 4.5.3 + fast-xml-parser: 4.5.4 google-auth-library: 9.15.1 googleapis: 137.1.0 html-to-text: 9.0.5 ignore: 5.3.2 - ioredis: 5.8.2 + ioredis: 5.10.0 jsdom: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) jsonwebtoken: 9.0.3 - lodash: 4.17.21 + lodash: 4.17.23 pg: 8.16.3 - playwright: 1.57.0 + playwright: 1.58.2 redis: 4.7.1 - weaviate-client: 3.10.0 - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + weaviate-client: 3.11.0 + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@langchain/anthropic' - '@langchain/aws' @@ -20292,49 +20580,49 @@ snapshots: - handlebars - peggy - '@langchain/community@0.3.58(d743da2b976a18eaa6701515aaffafa9)': + '@langchain/community@0.3.59(5e7fc56f823c1fb62b8d797ade7587fc)': dependencies: - '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.57.0)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) - '@ibm-cloud/watsonx-ai': 1.7.5 - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@langchain/weaviate': 0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.58.2)(bufferutil@4.1.0)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(utf-8-validate@5.0.10)(zod@3.25.76) + '@ibm-cloud/watsonx-ai': 1.7.8 + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/weaviate': 0.2.3(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) binary-extensions: 2.3.0 flat: 5.0.2 - ibm-cloud-sdk-core: 5.4.5 + ibm-cloud-sdk-core: 5.4.8 js-yaml: 4.1.1 - langchain: 0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + langchain: 0.3.37(@langchain/aws@0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(axios@1.13.6)(cheerio@1.2.0)(handlebars@4.7.8)(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) math-expression-evaluator: 2.0.7 - openai: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-bedrock-agent-runtime': 3.956.0 - '@aws-sdk/client-bedrock-runtime': 3.956.0 - '@aws-sdk/client-kendra': 3.956.0 - '@aws-sdk/client-s3': 3.956.0 - '@aws-sdk/credential-provider-node': 3.956.0 - '@browserbasehq/sdk': 2.6.0 + '@aws-sdk/client-bedrock-agent-runtime': 3.1000.0 + '@aws-sdk/client-bedrock-runtime': 3.1000.0 + '@aws-sdk/client-kendra': 3.1000.0 + '@aws-sdk/client-s3': 3.1000.0 + '@aws-sdk/credential-provider-node': 3.972.14 + '@browserbasehq/sdk': 2.7.0 '@smithy/util-utf8': 2.3.0 - '@upstash/redis': 1.35.8 - cheerio: 1.1.2 + '@upstash/redis': 1.36.3 + cheerio: 1.2.0 crypto-js: 4.2.0 - fast-xml-parser: 4.5.3 + fast-xml-parser: 4.5.4 google-auth-library: 9.15.1 googleapis: 137.1.0 html-to-text: 9.0.5 ignore: 5.3.2 - ioredis: 5.8.2 + ioredis: 5.10.0 jsdom: 22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10) jsonwebtoken: 9.0.3 - lodash: 4.17.21 + lodash: 4.17.23 pg: 8.16.3 - playwright: 1.57.0 + playwright: 1.58.2 redis: 4.7.1 - weaviate-client: 3.10.0 - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + weaviate-client: 3.11.0 + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@langchain/anthropic' - '@langchain/aws' @@ -20356,177 +20644,177 @@ snapshots: - handlebars - peggy - '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))': + '@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 10.0.0 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/exporter-trace-otlp-proto' - '@opentelemetry/sdk-trace-base' - openai - '@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))': + '@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 10.0.0 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/exporter-trace-otlp-proto' - '@opentelemetry/sdk-trace-base' - openai - '@langchain/google-common@0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': + '@langchain/google-common@0.1.8(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - zod - '@langchain/google-gauth@0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': + '@langchain/google-gauth@0.1.8(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76)': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/google-common': 0.1.8(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/google-common': 0.1.8(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(zod@3.25.76) google-auth-library: 8.9.0 transitivePeerDependencies: - encoding - supports-color - zod - '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 - '@langchain/langgraph-sdk@0.0.112(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@langchain/langgraph-sdk@0.0.112(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1)': + '@langchain/langgraph-sdk@0.0.70(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 18.3.1 - '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@langchain/langgraph-sdk@0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@langchain/langgraph@0.2.74(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.0(zod@3.25.76))': + '@langchain/langgraph@0.2.74(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(zod-to-json-schema@3.25.1(zod@3.25.76))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) - '@langchain/langgraph-sdk': 0.0.112(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/langgraph-sdk': 0.0.112(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - react - react-dom - '@langchain/openai@0.4.9(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@langchain/openai@0.4.9(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - encoding - ws - '@langchain/openai@0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))': + '@langchain/openai@0.5.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - openai: 5.23.2(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 5.23.2(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - ws - '@langchain/textsplitters@0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/textsplitters@0.1.0(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - '@langchain/weaviate@0.2.3(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/weaviate@0.2.3(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 - weaviate-client: 3.10.0 + weaviate-client: 3.11.0 transitivePeerDependencies: - encoding - '@ledgerhq/devices@8.7.1': + '@ledgerhq/devices@8.10.0': dependencies: - '@ledgerhq/errors': 6.27.1 - '@ledgerhq/logs': 6.13.0 + '@ledgerhq/errors': 6.29.0 + '@ledgerhq/logs': 6.14.0 rxjs: 7.8.2 semver: 7.7.3 - '@ledgerhq/errors@6.27.1': {} + '@ledgerhq/errors@6.29.0': {} - '@ledgerhq/hw-transport-webhid@6.30.10': + '@ledgerhq/hw-transport-webhid@6.31.0': dependencies: - '@ledgerhq/devices': 8.7.1 - '@ledgerhq/errors': 6.27.1 - '@ledgerhq/hw-transport': 6.31.14 - '@ledgerhq/logs': 6.13.0 + '@ledgerhq/devices': 8.10.0 + '@ledgerhq/errors': 6.29.0 + '@ledgerhq/hw-transport': 6.32.0 + '@ledgerhq/logs': 6.14.0 - '@ledgerhq/hw-transport@6.31.14': + '@ledgerhq/hw-transport@6.32.0': dependencies: - '@ledgerhq/devices': 8.7.1 - '@ledgerhq/errors': 6.27.1 - '@ledgerhq/logs': 6.13.0 + '@ledgerhq/devices': 8.10.0 + '@ledgerhq/errors': 6.29.0 + '@ledgerhq/logs': 6.14.0 events: 3.3.0 - '@ledgerhq/logs@6.13.0': {} + '@ledgerhq/logs@6.14.0': {} - '@lit-labs/ssr-dom-shim@1.4.0': {} + '@lit-labs/ssr-dom-shim@1.5.1': {} - '@lit/reactive-element@2.1.1': + '@lit/reactive-element@2.1.2': dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit-labs/ssr-dom-shim': 1.5.1 '@lukeed/csprng@1.1.0': {} @@ -20589,7 +20877,7 @@ snapshots: nopt: 5.0.0 npmlog: 5.0.1 rimraf: 3.0.2 - semver: 7.7.3 + semver: 7.7.4 tar: 6.2.1 transitivePeerDependencies: - encoding @@ -20603,7 +20891,7 @@ snapshots: json-schema: 0.4.0 rxjs: 7.8.1 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - '@hono/arktype-validator' - '@hono/effect-validator' @@ -20638,25 +20926,25 @@ snapshots: '@mastra/schema-compat': 0.11.4(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76) '@openrouter/ai-sdk-provider-v5': '@openrouter/ai-sdk-provider@1.2.0(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76)' '@opentelemetry/api': 1.9.0 - '@opentelemetry/auto-instrumentations-node': 0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)) - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/auto-instrumentations-node': 0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-grpc': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-exporter-base': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-transformer': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-node': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@sindresorhus/slugify': 2.2.1 ai: 4.3.19(react@18.3.1)(zod@3.25.76) ai-v5: ai@5.0.60(zod@3.25.76) date-fns: 3.6.0 dotenv: 16.6.1 - hono: 4.11.1 - hono-openapi: 0.4.8(hono@4.11.1)(openapi-types@12.1.3)(zod@3.25.76) + hono: 4.12.3 + hono-openapi: 0.4.8(hono@4.12.3)(openapi-types@12.1.3)(zod@3.25.76) js-tiktoken: 1.0.21 json-schema: 0.4.0 json-schema-to-zod: 2.7.0 @@ -20665,9 +20953,9 @@ snapshots: pino-pretty: 13.1.3 radash: 12.1.1 sift: 17.1.3 - xstate: 5.25.0 + xstate: 5.28.0 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - '@hono/arktype-validator' - '@hono/effect-validator' @@ -20687,9 +20975,9 @@ snapshots: '@mastra/deployer@0.19.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(typescript@5.5.4)(zod@3.25.76)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/server': 0.19.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(zod@3.25.76) '@neon-rs/load': 0.1.82 @@ -20708,7 +20996,7 @@ snapshots: esbuild: 0.25.12 find-workspaces: 0.3.1 fs-extra: 11.3.3 - hono: 4.11.1 + hono: 4.12.3 local-pkg: 1.1.2 resolve-from: 5.0.0 rollup: 4.50.2 @@ -20727,11 +21015,11 @@ snapshots: pino: 9.14.0 pino-pretty: 13.1.3 - '@mastra/mcp@0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(hono@4.11.1)(zod@3.25.76)': + '@mastra/mcp@0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(zod@3.25.76)': dependencies: '@apidevtools/json-schema-ref-parser': 14.2.1(@types/json-schema@7.0.15) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) - '@modelcontextprotocol/sdk': 1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76) + '@modelcontextprotocol/sdk': 1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76) date-fns: 4.1.0 exit-hook: 4.0.0 fast-deep-equal: 3.1.3 @@ -20742,27 +21030,27 @@ snapshots: transitivePeerDependencies: - '@cfworker/json-schema' - '@types/json-schema' - - hono - supports-color '@mastra/memory@0.15.13(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(react@18.3.1)(zod@3.25.76)': dependencies: '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/schema-compat': 0.11.9(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76) - '@upstash/redis': 1.35.8 + '@upstash/redis': 1.36.3 ai: 4.3.19(react@18.3.1)(zod@3.25.76) ai-v5: ai@5.0.60(zod@3.25.76) async-mutex: 0.5.0 js-tiktoken: 1.0.21 json-schema: 0.4.0 - pg: 8.16.3 - pg-pool: 3.10.1(pg@8.16.3) - postgres: 3.4.7 - redis: 5.10.0 + pg: 8.19.0 + pg-pool: 3.12.0(pg@8.19.0) + postgres: 3.4.8 + redis: 5.11.0 xxhash-wasm: 1.1.0 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: + - '@node-rs/xxhash' - pg-native - react @@ -20770,7 +21058,7 @@ snapshots: dependencies: '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) async-mutex: 0.5.0 - pg: 8.16.3 + pg: 8.19.0 pg-promise: 11.15.0(pg-query-stream@4.10.3(pg@8.16.3)) xxhash-wasm: 1.1.0 transitivePeerDependencies: @@ -20784,7 +21072,7 @@ snapshots: zod: 3.25.76 zod-from-json-schema: 0.5.2 zod-from-json-schema-v3: zod-from-json-schema@0.0.5 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) '@mastra/schema-compat@0.11.9(ai@4.3.19(react@18.3.1)(zod@3.25.76))(zod@3.25.76)': dependencies: @@ -20794,7 +21082,7 @@ snapshots: zod: 3.25.76 zod-from-json-schema: 0.5.2 zod-from-json-schema-v3: zod-from-json-schema@0.0.5 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) '@mastra/server@0.19.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(zod@3.25.76)': dependencies: @@ -20809,28 +21097,28 @@ snapshots: '@microsoft/tsdoc@0.15.1': {} - '@modelcontextprotocol/sdk@1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.27.1(@cfworker/json-schema@4.1.1)(zod@3.25.76)': dependencies: - '@hono/node-server': 1.19.7(hono@4.11.1) - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) + '@hono/node-server': 1.19.9(hono@4.12.3) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 - cors: 2.8.5 + cors: 2.8.6 cross-spawn: 7.0.6 eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 7.5.1(express@5.2.1) + express-rate-limit: 8.2.1(express@5.2.1) + hono: 4.12.3 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) optionalDependencies: '@cfworker/json-schema': 4.1.1 transitivePeerDependencies: - - hono - supports-color '@mole-inc/bin-wrapper@8.0.1': @@ -20848,7 +21136,7 @@ snapshots: '@mui/material@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/core-downloads-tracker': 5.18.0 '@mui/system': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1) '@mui/types': 7.2.24(@types/react@18.3.1) @@ -20860,7 +21148,7 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-is: 19.2.3 + react-is: 19.2.4 react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) optionalDependencies: '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) @@ -20869,7 +21157,7 @@ snapshots: '@mui/private-theming@5.17.1(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/utils': 5.17.1(@types/react@18.3.1)(react@18.3.1) prop-types: 15.8.1 react: 18.3.1 @@ -20878,7 +21166,7 @@ snapshots: '@mui/private-theming@6.4.9(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/utils': 6.4.9(@types/react@18.3.1)(react@18.3.1) prop-types: 15.8.1 react: 18.3.1 @@ -20887,7 +21175,7 @@ snapshots: '@mui/styled-engine@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 csstype: 3.2.3 @@ -20899,7 +21187,7 @@ snapshots: '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 '@emotion/sheet': 1.4.0 @@ -20912,7 +21200,7 @@ snapshots: '@mui/system@5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/private-theming': 5.17.1(@types/react@18.3.1)(react@18.3.1) '@mui/styled-engine': 5.18.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1) '@mui/types': 7.2.24(@types/react@18.3.1) @@ -20928,7 +21216,7 @@ snapshots: '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/private-theming': 6.4.9(@types/react@18.3.1)(react@18.3.1) '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1) '@mui/types': 7.2.24(@types/react@18.3.1) @@ -20948,25 +21236,25 @@ snapshots: '@mui/utils@5.17.1(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/types': 7.2.24(@types/react@18.3.1) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 - react-is: 19.2.3 + react-is: 19.2.4 optionalDependencies: '@types/react': 18.3.1 '@mui/utils@6.4.9(@types/react@18.3.1)(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@mui/types': 7.2.24(@types/react@18.3.1) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 - react-is: 19.2.3 + react-is: 19.2.4 optionalDependencies: '@types/react': 18.3.1 @@ -21044,26 +21332,26 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.7.1 - '@emnapi/runtime': 1.7.1 + '@emnapi/core': 1.8.1 + '@emnapi/runtime': 1.8.1 '@tybys/wasm-util': 0.10.1 optional: true '@neon-rs/load@0.1.82': {} - '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/throttler@6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14))(ioredis@5.8.2)(reflect-metadata@0.1.14)': + '@nest-lab/throttler-storage-redis@1.2.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(@nestjs/throttler@6.5.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(reflect-metadata@0.1.14))(ioredis@5.10.0)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/throttler': 6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14) - ioredis: 5.8.2 + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/throttler': 6.5.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(reflect-metadata@0.1.14) + ioredis: 5.10.0 reflect-metadata: 0.1.14 tslib: 2.8.1 - '@nestjs/axios@4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': + '@nestjs/axios@4.0.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.5)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - axios: 1.13.2(debug@4.4.3) + '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + axios: 1.13.5 rxjs: 7.8.2 '@nestjs/cli@10.0.2(@swc/cli@0.3.14(@swc/core@1.5.7(@swc/helpers@0.5.13))(chokidar@3.5.3))(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)': @@ -21098,7 +21386,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: file-type: 20.4.1 iterare: 1.2.1 @@ -21108,13 +21396,13 @@ snapshots: uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.3 + class-validator: 0.14.4 transitivePeerDependencies: - supports-color - '@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - file-type: 21.1.0 + file-type: 21.3.0 iterare: 1.2.1 load-esm: 1.0.3 reflect-metadata: 0.2.2 @@ -21123,13 +21411,13 @@ snapshots: uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.3 + class-validator: 0.14.4 transitivePeerDependencies: - supports-color - '@nestjs/core@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/core@10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -21139,14 +21427,14 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/microservices': 10.4.22(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(cache-manager@7.2.8)(ioredis@5.10.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22) transitivePeerDependencies: - encoding - '@nestjs/core@11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -21156,46 +21444,46 @@ snapshots: tslib: 2.8.1 uid: 2.0.2 optionalDependencies: - '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/microservices': 10.4.22(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(cache-manager@7.2.8)(ioredis@5.10.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22) - '@nestjs/mapped-types@2.0.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)': + '@nestjs/mapped-types@2.0.5(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.3 + class-validator: 0.14.4 - '@nestjs/microservices@10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2)': + '@nestjs/microservices@10.4.22(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(cache-manager@7.2.8)(ioredis@5.10.0)(reflect-metadata@0.1.14)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) iterare: 1.2.1 reflect-metadata: 0.1.14 rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: '@grpc/grpc-js': 1.14.3 - cache-manager: 7.2.7 - ioredis: 5.8.2 + cache-manager: 7.2.8 + ioredis: 5.10.0 - '@nestjs/platform-express@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@nestjs/platform-express@10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) - body-parser: 1.20.3 + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) + body-parser: 1.20.4 cors: 2.8.5 - express: 4.21.2 + express: 4.22.1 multer: 2.0.2 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@nestjs/schedule@4.1.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@nestjs/schedule@4.1.2(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) cron: 3.2.1 uuid: 11.0.3 @@ -21221,12 +21509,12 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@7.4.2(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)': + '@nestjs/swagger@7.4.2(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)': dependencies: '@microsoft/tsdoc': 0.15.1 - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14) js-yaml: 4.1.0 lodash: 4.17.21 path-to-regexp: 3.3.0 @@ -21234,21 +21522,21 @@ snapshots: swagger-ui-dist: 5.17.14 optionalDependencies: class-transformer: 0.5.1 - class-validator: 0.14.3 + class-validator: 0.14.4 - '@nestjs/testing@10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)': + '@nestjs/testing@10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) tslib: 2.8.1 optionalDependencies: - '@nestjs/microservices': 10.4.20(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(cache-manager@7.2.7)(ioredis@5.8.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/platform-express': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20) + '@nestjs/microservices': 10.4.22(@grpc/grpc-js@1.14.3)(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(cache-manager@7.2.8)(ioredis@5.10.0)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/platform-express': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22) - '@nestjs/throttler@6.5.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(reflect-metadata@0.1.14)': + '@nestjs/throttler@6.5.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 '@next/env@14.2.35': {} @@ -21284,17 +21572,16 @@ snapshots: '@next/swc-win32-x64-msvc@14.2.33': optional: true - '@neynar/nodejs-sdk@3.112.0(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.3)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@neynar/nodejs-sdk@3.137.0(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(bufferutil@4.1.0)(class-transformer@0.5.1)(class-validator@0.14.4)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@openapitools/openapi-generator-cli': 2.25.2(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.3) - axios: 1.13.2(debug@4.4.3) - semver: 7.7.3 - viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@openapitools/openapi-generator-cli': 2.30.0(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(class-transformer@0.5.1)(class-validator@0.14.4) + axios: 1.13.6(debug@4.4.3) + semver: 7.7.4 + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@nestjs/microservices' - '@nestjs/platform-express' - '@nestjs/websockets' - - '@types/node' - bufferutil - class-transformer - class-validator @@ -21305,15 +21592,15 @@ snapshots: - utf-8-validate - zod - '@neynar/react@0.9.7(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.57.0)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1)(swr@2.3.8(react@18.3.1))': + '@neynar/react@0.9.7(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4))(@playwright/test@1.58.2)(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(babel-plugin-macros@3.1.0)(hls.js@1.6.15)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3)(swr@2.4.1(react@18.3.1))': dependencies: '@pigment-css/react': 0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4) hls.js: 1.6.15 - next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) + next: 14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) storybook-source-link: 4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - swr: 2.3.8(react@18.3.1) + swr: 2.4.1(react@18.3.1) transitivePeerDependencies: - '@babel/core' - '@opentelemetry/api' @@ -21332,19 +21619,11 @@ snapshots: jsbi: 3.2.5 sha.js: 2.4.12 - '@noble/ciphers@0.5.3': {} - '@noble/ciphers@1.2.1': {} '@noble/ciphers@1.3.0': {} - '@noble/curves@1.1.0': - dependencies: - '@noble/hashes': 1.3.1 - - '@noble/curves@1.2.0': - dependencies: - '@noble/hashes': 1.3.2 + '@noble/ciphers@2.1.1': {} '@noble/curves@1.4.2': dependencies: @@ -21366,9 +21645,9 @@ snapshots: dependencies: '@noble/hashes': 1.8.0 - '@noble/hashes@1.3.1': {} - - '@noble/hashes@1.3.2': {} + '@noble/curves@2.0.1': + dependencies: + '@noble/hashes': 2.0.1 '@noble/hashes@1.4.0': {} @@ -21378,6 +21657,8 @@ snapshots: '@noble/hashes@1.8.0': {} + '@noble/hashes@2.0.1': {} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -21388,7 +21669,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@nolyfill/is-core-module@1.0.39': {} @@ -21406,21 +21687,21 @@ snapshots: '@one-ini/wasm@0.1.1': {} - '@openapitools/openapi-generator-cli@2.25.2(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(@types/node@18.16.9)(class-transformer@0.5.1)(class-validator@0.14.3)': + '@openapitools/openapi-generator-cli@2.30.0(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(class-transformer@0.5.1)(class-validator@0.14.4)': dependencies: - '@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) - '@nestjs/common': 11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@inquirer/select': 1.3.3 + '@nestjs/axios': 4.0.1(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.5)(rxjs@7.8.2) + '@nestjs/common': 11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.14(@nestjs/common@11.1.14(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxtjs/opencollective': 0.3.2 - axios: 1.13.2(debug@4.4.3) + axios: 1.13.5 chalk: 4.1.2 commander: 8.3.0 compare-versions: 6.1.1 concurrently: 9.2.1 console.table: 0.10.0 - fs-extra: 11.3.2 - glob: 13.0.0 - inquirer: 8.2.7(@types/node@18.16.9) + fs-extra: 11.3.3 + glob: 13.0.6 proxy-agent: 6.5.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -21429,7 +21710,6 @@ snapshots: - '@nestjs/microservices' - '@nestjs/platform-express' - '@nestjs/websockets' - - '@types/node' - class-transformer - class-validator - debug @@ -21445,16 +21725,24 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs@0.207.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs@0.208.0': dependencies: '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs@0.211.0': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api@1.9.0': {} - '@opentelemetry/auto-instrumentations-node@0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))': + '@opentelemetry/auto-instrumentations-node@0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-amqplib': 0.50.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-aws-lambda': 0.54.1(@opentelemetry/api@1.9.0) @@ -21497,11 +21785,11 @@ snapshots: '@opentelemetry/instrumentation-undici': 0.14.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation-winston': 0.48.1(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-alibaba-cloud': 0.31.11(@opentelemetry/api@1.9.0) - '@opentelemetry/resource-detector-aws': 2.9.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resource-detector-aws': 2.12.0(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-azure': 0.10.0(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-container': 0.7.11(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-gcp': 0.37.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': 0.203.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - encoding @@ -21511,19 +21799,29 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core@2.0.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/core@2.5.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/exporter-logs-otlp-grpc@0.203.0(@opentelemetry/api@1.9.0)': dependencies: @@ -21544,6 +21842,15 @@ snapshots: '@opentelemetry/otlp-transformer': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-logs': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-http@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/exporter-logs-otlp-proto@0.203.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -21628,22 +21935,23 @@ snapshots: '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/instrumentation-amqplib@0.50.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-amqplib@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-amqplib@0.58.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21651,7 +21959,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/aws-lambda': 8.10.152 transitivePeerDependencies: - supports-color @@ -21659,9 +21967,9 @@ snapshots: '@opentelemetry/instrumentation-aws-sdk@0.58.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21678,26 +21986,26 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-connect@0.47.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-connect@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-connect@0.54.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@types/connect': 3.4.38 transitivePeerDependencies: - supports-color @@ -21706,7 +22014,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21717,10 +22025,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-dataloader@0.26.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-dataloader@0.28.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21734,43 +22042,43 @@ snapshots: '@opentelemetry/instrumentation-express@0.52.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-express@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-express@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-fastify@0.48.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-fs@0.23.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-fs@0.28.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-fs@0.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21781,10 +22089,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-generic-pool@0.52.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-generic-pool@0.54.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21795,10 +22103,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-graphql@0.56.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-graphql@0.58.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21806,25 +22114,25 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-hapi@0.50.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-hapi@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-hapi@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21833,17 +22141,17 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-http@0.208.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-http@0.211.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 forwarded-parse: 2.1.2 transitivePeerDependencies: - supports-color @@ -21853,15 +22161,16 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-ioredis@0.56.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-ioredis@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21869,15 +22178,15 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-kafkajs@0.18.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-kafkajs@0.20.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21885,33 +22194,33 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-knex@0.53.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-knex@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-koa@0.51.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-koa@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-koa@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21922,10 +22231,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-lru-memoizer@0.53.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-lru-memoizer@0.55.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21933,7 +22242,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/memcached': 2.2.10 transitivePeerDependencies: - supports-color @@ -21942,31 +22251,33 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongodb@0.61.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongodb@0.64.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-mongoose@0.50.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mongoose@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mongoose@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -21974,16 +22285,16 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql2@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql2@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -21992,15 +22303,16 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/mysql': 2.15.27 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-mysql@0.54.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-mysql@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@types/mysql': 2.15.27 transitivePeerDependencies: - supports-color @@ -22009,15 +22321,15 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-nestjs-core@0.55.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-nestjs-core@0.57.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22025,7 +22337,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22033,7 +22345,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/oracledb': 6.5.2 transitivePeerDependencies: - supports-color @@ -22041,24 +22353,24 @@ snapshots: '@opentelemetry/instrumentation-pg@0.56.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) '@types/pg': 8.15.5 '@types/pg-pool': 2.0.6 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-pg@0.61.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-pg@0.63.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sql-common': 0.41.2(@opentelemetry/api@1.9.0) '@types/pg': 8.15.6 - '@types/pg-pool': 2.0.6 + '@types/pg-pool': 2.0.7 transitivePeerDependencies: - supports-color @@ -22066,7 +22378,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.203.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -22076,25 +22388,25 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-redis@0.57.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-redis@0.59.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.38.2 - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color '@opentelemetry/instrumentation-restify@0.49.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22102,7 +22414,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22117,7 +22429,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22125,15 +22437,16 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-tedious@0.27.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-tedious@0.30.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@types/tedious': 4.0.14 transitivePeerDependencies: - supports-color @@ -22141,17 +22454,17 @@ snapshots: '@opentelemetry/instrumentation-undici@0.14.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color - '@opentelemetry/instrumentation-undici@0.19.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/instrumentation-undici@0.21.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22172,11 +22485,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@opentelemetry/instrumentation@0.207.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.207.0 + import-in-the-middle: 2.0.6 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + '@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/api-logs': 0.208.0 - import-in-the-middle: 2.0.1 + import-in-the-middle: 2.0.6 + require-in-the-middle: 8.0.1 + transitivePeerDependencies: + - supports-color + + '@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.211.0 + import-in-the-middle: 2.0.6 require-in-the-middle: 8.0.1 transitivePeerDependencies: - supports-color @@ -22187,6 +22518,12 @@ snapshots: '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/otlp-transformer': 0.203.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-exporter-base@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-transformer': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/otlp-grpc-exporter-base@0.203.0(@opentelemetry/api@1.9.0)': dependencies: '@grpc/grpc-js': 1.14.3 @@ -22206,6 +22543,17 @@ snapshots: '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) protobufjs: 7.5.4 + '@opentelemetry/otlp-transformer@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + protobufjs: 7.5.4 + '@opentelemetry/propagator-b3@2.0.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -22221,35 +22569,35 @@ snapshots: '@opentelemetry/resource-detector-alibaba-cloud@0.31.11(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) - '@opentelemetry/resource-detector-aws@2.9.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/resource-detector-aws@2.12.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/resource-detector-azure@0.10.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/resource-detector-container@0.7.11(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/resource-detector-gcp@0.37.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 gcp-metadata: 6.1.1 transitivePeerDependencies: - encoding @@ -22259,13 +22607,19 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-logs@0.203.0(@opentelemetry/api@1.9.0)': dependencies: @@ -22274,6 +22628,13 @@ snapshots: '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs@0.208.0(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics@2.0.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -22286,6 +22647,12 @@ snapshots: '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-metrics@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-node@0.203.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 @@ -22310,7 +22677,7 @@ snapshots: '@opentelemetry/sdk-metrics': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-node': 2.0.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 transitivePeerDependencies: - supports-color @@ -22319,14 +22686,21 @@ snapshots: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.0.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/semantic-conventions': 1.40.0 + + '@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0)': + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@opentelemetry/sdk-trace-node@2.0.1(@opentelemetry/api@1.9.0)': dependencies: @@ -22335,19 +22709,19 @@ snapshots: '@opentelemetry/core': 2.0.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-node@2.2.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/sdk-trace-node@2.5.1(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions@1.38.0': {} + '@opentelemetry/semantic-conventions@1.40.0': {} '@opentelemetry/sql-common@0.41.2(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@optimize-lodash/rollup-plugin@5.1.0(rollup@4.50.2)': dependencies: @@ -22360,65 +22734,65 @@ snapshots: estree-walker: 2.0.2 magic-string: 0.30.21 - '@parcel/watcher-android-arm64@2.5.1': + '@parcel/watcher-android-arm64@2.5.6': optional: true - '@parcel/watcher-darwin-arm64@2.5.1': + '@parcel/watcher-darwin-arm64@2.5.6': optional: true - '@parcel/watcher-darwin-x64@2.5.1': + '@parcel/watcher-darwin-x64@2.5.6': optional: true - '@parcel/watcher-freebsd-x64@2.5.1': + '@parcel/watcher-freebsd-x64@2.5.6': optional: true - '@parcel/watcher-linux-arm-glibc@2.5.1': + '@parcel/watcher-linux-arm-glibc@2.5.6': optional: true - '@parcel/watcher-linux-arm-musl@2.5.1': + '@parcel/watcher-linux-arm-musl@2.5.6': optional: true - '@parcel/watcher-linux-arm64-glibc@2.5.1': + '@parcel/watcher-linux-arm64-glibc@2.5.6': optional: true - '@parcel/watcher-linux-arm64-musl@2.5.1': + '@parcel/watcher-linux-arm64-musl@2.5.6': optional: true - '@parcel/watcher-linux-x64-glibc@2.5.1': + '@parcel/watcher-linux-x64-glibc@2.5.6': optional: true - '@parcel/watcher-linux-x64-musl@2.5.1': + '@parcel/watcher-linux-x64-musl@2.5.6': optional: true - '@parcel/watcher-win32-arm64@2.5.1': + '@parcel/watcher-win32-arm64@2.5.6': optional: true - '@parcel/watcher-win32-ia32@2.5.1': + '@parcel/watcher-win32-ia32@2.5.6': optional: true - '@parcel/watcher-win32-x64@2.5.1': + '@parcel/watcher-win32-x64@2.5.6': optional: true - '@parcel/watcher@2.5.1': + '@parcel/watcher@2.5.6': dependencies: - detect-libc: 1.0.3 + detect-libc: 2.1.2 is-glob: 4.0.3 - micromatch: 4.0.8 node-addon-api: 7.1.1 + picomatch: 4.0.3 optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.1 - '@parcel/watcher-darwin-arm64': 2.5.1 - '@parcel/watcher-darwin-x64': 2.5.1 - '@parcel/watcher-freebsd-x64': 2.5.1 - '@parcel/watcher-linux-arm-glibc': 2.5.1 - '@parcel/watcher-linux-arm-musl': 2.5.1 - '@parcel/watcher-linux-arm64-glibc': 2.5.1 - '@parcel/watcher-linux-arm64-musl': 2.5.1 - '@parcel/watcher-linux-x64-glibc': 2.5.1 - '@parcel/watcher-linux-x64-musl': 2.5.1 - '@parcel/watcher-win32-arm64': 2.5.1 - '@parcel/watcher-win32-ia32': 2.5.1 - '@parcel/watcher-win32-x64': 2.5.1 + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 optional: true '@particle-network/analytics@1.0.2': @@ -22449,11 +22823,11 @@ snapshots: '@pigment-css/react@0.0.30(@types/react@18.3.1)(react@18.3.1)(typescript@5.5.4)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-plugin-utils': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@emotion/css': 11.13.5 '@emotion/is-prop-valid': 1.4.0 '@emotion/react': 11.14.0(@types/react@18.3.1)(react@18.3.1) @@ -22467,7 +22841,7 @@ snapshots: clsx: 2.1.1 cssesc: 3.0.0 csstype: 3.2.3 - lodash: 4.17.21 + lodash: 4.17.23 prop-types: 15.8.1 react: 18.3.1 stylis: 4.3.6 @@ -22482,21 +22856,21 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@playwright/test@1.57.0': + '@playwright/test@1.58.2': dependencies: - playwright: 1.57.0 + playwright: 1.58.2 - '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.10.0)(type-fest@4.41.0)(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: ansi-html: 0.0.9 - core-js-pure: 3.47.0 + core-js-pure: 3.48.0 error-stack-parser: 2.1.4 html-entities: 2.6.0 loader-utils: 2.0.4 react-refresh: 0.10.0 schema-utils: 4.3.3 source-map: 0.7.6 - webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) optionalDependencies: type-fest: 4.41.0 @@ -22504,11 +22878,13 @@ snapshots: '@popperjs/core@2.11.8': {} - '@posthog/core@1.8.1': + '@posthog/core@1.23.1': dependencies: cross-spawn: 7.0.6 - '@postiz/wallets@0.0.1(@babel/runtime@7.28.4)(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.8.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@posthog/types@1.356.1': {} + + '@postiz/wallets@0.0.1(@babel/runtime@7.28.6)(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bs58@6.0.0)(bufferutil@4.1.0)(ioredis@5.10.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@solana/wallet-adapter-alpha': 0.1.14(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-avana': 0.1.17(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) @@ -22540,10 +22916,10 @@ snapshots: '@solana/wallet-adapter-spot': 0.1.19(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-tokenary': 0.1.16(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-tokenpocket': 0.4.23(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-torus': 0.11.32(@babel/runtime@7.28.4)(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@solana/wallet-adapter-torus': 0.11.32(@babel/runtime@7.28.6)(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@solana/wallet-adapter-trust': 0.1.17(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-adapter-unsafe-burner': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-walletconnect': 0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@solana/wallet-adapter-walletconnect': 0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@solana/wallet-adapter-xdefi': 0.1.11(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) transitivePeerDependencies: @@ -22586,8 +22962,8 @@ snapshots: '@prisma/config@6.5.0': dependencies: - esbuild: 0.27.2 - esbuild-register: 3.6.0(esbuild@0.27.2) + esbuild: 0.27.3 + esbuild-register: 3.6.0(esbuild@0.27.3) transitivePeerDependencies: - supports-color @@ -22612,10 +22988,10 @@ snapshots: dependencies: '@prisma/debug': 6.5.0 - '@prisma/instrumentation@6.19.0(@opentelemetry/api@1.9.0)': + '@prisma/instrumentation@7.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.207.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -22652,17 +23028,17 @@ snapshots: '@radix-ui/number@1.0.0': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/primitive@1.0.0': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/primitive@1.1.3': {} '@radix-ui/react-compose-refs@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.1)(react@18.3.1)': @@ -22673,7 +23049,7 @@ snapshots: '@radix-ui/react-context@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 '@radix-ui/react-context@1.1.2(@types/react@18.3.1)(react@18.3.1)': @@ -22684,7 +23060,7 @@ snapshots: '@radix-ui/react-dialog@1.0.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-context': 1.0.0(react@18.3.1) @@ -22728,12 +23104,12 @@ snapshots: '@radix-ui/react-direction@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 '@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-primitive': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -22757,7 +23133,7 @@ snapshots: '@radix-ui/react-focus-guards@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 '@radix-ui/react-focus-guards@1.1.3(@types/react@18.3.1)(react@18.3.1)': @@ -22768,7 +23144,7 @@ snapshots: '@radix-ui/react-focus-scope@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-primitive': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) @@ -22788,7 +23164,7 @@ snapshots: '@radix-ui/react-id@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) react: 18.3.1 @@ -22810,7 +23186,7 @@ snapshots: '@radix-ui/react-portal@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-primitive': 1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -22827,7 +23203,7 @@ snapshots: '@radix-ui/react-presence@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.0(react@18.3.1) react: 18.3.1 @@ -22845,14 +23221,14 @@ snapshots: '@radix-ui/react-primitive@1.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-slot': 1.0.0(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) '@radix-ui/react-primitive@1.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-slot': 1.0.1(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -22877,7 +23253,7 @@ snapshots: '@radix-ui/react-scroll-area@1.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/number': 1.0.0 '@radix-ui/primitive': 1.0.0 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) @@ -22901,13 +23277,13 @@ snapshots: '@radix-ui/react-slot@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 '@radix-ui/react-slot@1.0.1(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-compose-refs': 1.0.0(react@18.3.1) react: 18.3.1 @@ -22927,7 +23303,7 @@ snapshots: '@radix-ui/react-use-callback-ref@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.1)(react@18.3.1)': @@ -22938,7 +23314,7 @@ snapshots: '@radix-ui/react-use-controllable-state@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 @@ -22959,7 +23335,7 @@ snapshots: '@radix-ui/react-use-escape-keydown@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@radix-ui/react-use-callback-ref': 1.0.0(react@18.3.1) react: 18.3.1 @@ -22972,7 +23348,7 @@ snapshots: '@radix-ui/react-use-layout-effect@1.0.0(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.1)(react@18.3.1)': @@ -22981,22 +23357,22 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@react-aria/focus@3.21.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-aria/focus@3.21.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@react-aria/interactions': 3.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-aria/utils': 3.32.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@react-types/shared': 3.32.1(react@18.3.1) + '@react-aria/interactions': 3.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/utils': 3.33.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-types/shared': 3.33.0(react@18.3.1) '@swc/helpers': 0.5.13 clsx: 2.1.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@react-aria/interactions@3.26.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-aria/interactions@3.27.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@react-aria/ssr': 3.9.10(react@18.3.1) - '@react-aria/utils': 3.32.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@react-aria/utils': 3.33.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@react-stately/flags': 3.1.2 - '@react-types/shared': 3.32.1(react@18.3.1) + '@react-types/shared': 3.33.0(react@18.3.1) '@swc/helpers': 0.5.13 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -23006,12 +23382,12 @@ snapshots: '@swc/helpers': 0.5.13 react: 18.3.1 - '@react-aria/utils@3.32.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@react-aria/utils@3.33.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@react-aria/ssr': 3.9.10(react@18.3.1) '@react-stately/flags': 3.1.2 '@react-stately/utils': 3.11.0(react@18.3.1) - '@react-types/shared': 3.32.1(react@18.3.1) + '@react-types/shared': 3.33.0(react@18.3.1) '@swc/helpers': 0.5.13 clsx: 2.1.1 react: 18.3.1 @@ -23031,50 +23407,53 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-promise-suspense: 0.3.4 - '@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))': + '@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))': dependencies: merge-options: 3.0.4 - react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) optional: true - '@react-native/assets-registry@0.83.1': {} + '@react-native/assets-registry@0.84.1': {} - '@react-native/codegen@0.83.1(@babel/core@7.28.5)': + '@react-native/codegen@0.84.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 - glob: 7.2.3 + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 hermes-parser: 0.32.0 invariant: 2.2.4 nullthrows: 1.1.1 + tinyglobby: 0.2.15 yargs: 17.7.2 - '@react-native/community-cli-plugin@0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@react-native/community-cli-plugin@0.84.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: - '@react-native/dev-middleware': 0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@react-native/dev-middleware': 0.84.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) debug: 4.4.3(supports-color@5.5.0) invariant: 2.2.4 - metro: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - metro-config: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - metro-core: 0.83.3 - semver: 7.7.3 + metro: 0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + metro-config: 0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + metro-core: 0.83.4 + semver: 7.7.4 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@react-native/debugger-frontend@0.83.1': {} + '@react-native/debugger-frontend@0.84.1': {} - '@react-native/debugger-shell@0.83.1': + '@react-native/debugger-shell@0.84.1': dependencies: cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@5.5.0) fb-dotslash: 0.5.8 + transitivePeerDependencies: + - supports-color - '@react-native/dev-middleware@0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@react-native/dev-middleware@0.84.1(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: '@isaacs/ttlcache': 1.4.1 - '@react-native/debugger-frontend': 0.83.1 - '@react-native/debugger-shell': 0.83.1 + '@react-native/debugger-frontend': 0.84.1 + '@react-native/debugger-shell': 0.84.1 chrome-launcher: 0.15.2 chromium-edge-launcher: 0.2.0 connect: 3.7.0 @@ -23089,18 +23468,18 @@ snapshots: - supports-color - utf-8-validate - '@react-native/gradle-plugin@0.83.1': {} + '@react-native/gradle-plugin@0.84.1': {} - '@react-native/js-polyfills@0.83.1': {} + '@react-native/js-polyfills@0.84.1': {} - '@react-native/normalize-colors@0.83.1': {} + '@react-native/normalize-colors@0.84.1': {} - '@react-native/virtualized-lists@0.83.1(@types/react@18.3.1)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)': + '@react-native/virtualized-lists@0.84.1(@types/react@18.3.1)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)': dependencies: invariant: 2.2.4 nullthrows: 1.1.1 react: 18.3.1 - react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) optionalDependencies: '@types/react': 18.3.1 @@ -23113,7 +23492,7 @@ snapshots: '@swc/helpers': 0.5.13 react: 18.3.1 - '@react-types/shared@3.32.1(react@18.3.1)': + '@react-types/shared@3.33.0(react@18.3.1)': dependencies: react: 18.3.1 @@ -23121,9 +23500,9 @@ snapshots: dependencies: '@redis/client': 1.6.1 - '@redis/bloom@5.10.0(@redis/client@5.10.0)': + '@redis/bloom@5.11.0(@redis/client@5.11.0)': dependencies: - '@redis/client': 5.10.0 + '@redis/client': 5.11.0 '@redis/client@1.6.1': dependencies: @@ -23131,7 +23510,7 @@ snapshots: generic-pool: 3.9.0 yallist: 4.0.0 - '@redis/client@5.10.0': + '@redis/client@5.11.0': dependencies: cluster-key-slot: 1.1.2 @@ -23143,25 +23522,25 @@ snapshots: dependencies: '@redis/client': 1.6.1 - '@redis/json@5.10.0(@redis/client@5.10.0)': + '@redis/json@5.11.0(@redis/client@5.11.0)': dependencies: - '@redis/client': 5.10.0 + '@redis/client': 5.11.0 '@redis/search@1.2.0(@redis/client@1.6.1)': dependencies: '@redis/client': 1.6.1 - '@redis/search@5.10.0(@redis/client@5.10.0)': + '@redis/search@5.11.0(@redis/client@5.11.0)': dependencies: - '@redis/client': 5.10.0 + '@redis/client': 5.11.0 '@redis/time-series@1.1.0(@redis/client@1.6.1)': dependencies: '@redis/client': 1.6.1 - '@redis/time-series@5.10.0(@redis/client@5.10.0)': + '@redis/time-series@5.11.0(@redis/client@5.11.0)': dependencies: - '@redis/client': 5.10.0 + '@redis/client': 5.11.0 '@remirror/core-constants@3.0.0': {} @@ -23169,7 +23548,7 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript @@ -23180,20 +23559,20 @@ snapshots: dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@18.3.1)(react@18.3.1) - viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23226,12 +23605,12 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) lit: 3.1.0 transitivePeerDependencies: @@ -23263,10 +23642,10 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-ui@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) lit: 3.1.0 qrcode: 1.5.3 @@ -23298,16 +23677,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': + '@reown/appkit-utils@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.2 '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(@types/react@18.3.1)(react@18.3.1) - viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23347,20 +23726,20 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.2 - '@reown/appkit-scaffold-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) - '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) + '@reown/appkit-ui': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-utils': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@18.3.1)(react@18.3.1))(zod@3.25.76) '@reown/appkit-wallet': 1.7.2(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/universal-provider': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@18.3.1)(react@18.3.1) - viem: 2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -23397,9 +23776,9 @@ snapshots: optionalDependencies: rollup: 4.50.2 - '@rollup/plugin-commonjs@28.0.1(rollup@4.54.0)': + '@rollup/plugin-commonjs@28.0.1(rollup@4.59.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.54.0) + '@rollup/pluginutils': 5.3.0(rollup@4.59.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -23407,7 +23786,7 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.54.0 + rollup: 4.59.0 '@rollup/plugin-commonjs@28.0.9(rollup@4.50.2)': dependencies: @@ -23461,160 +23840,163 @@ snapshots: optionalDependencies: rollup: 4.50.2 - '@rollup/pluginutils@5.3.0(rollup@4.54.0)': + '@rollup/pluginutils@5.3.0(rollup@4.59.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.54.0 + rollup: 4.59.0 '@rollup/rollup-android-arm-eabi@4.50.2': optional: true - '@rollup/rollup-android-arm-eabi@4.54.0': + '@rollup/rollup-android-arm-eabi@4.59.0': optional: true '@rollup/rollup-android-arm64@4.50.2': optional: true - '@rollup/rollup-android-arm64@4.54.0': + '@rollup/rollup-android-arm64@4.59.0': optional: true '@rollup/rollup-darwin-arm64@4.50.2': optional: true - '@rollup/rollup-darwin-arm64@4.54.0': + '@rollup/rollup-darwin-arm64@4.59.0': optional: true '@rollup/rollup-darwin-x64@4.50.2': optional: true - '@rollup/rollup-darwin-x64@4.54.0': + '@rollup/rollup-darwin-x64@4.59.0': optional: true '@rollup/rollup-freebsd-arm64@4.50.2': optional: true - '@rollup/rollup-freebsd-arm64@4.54.0': + '@rollup/rollup-freebsd-arm64@4.59.0': optional: true '@rollup/rollup-freebsd-x64@4.50.2': optional: true - '@rollup/rollup-freebsd-x64@4.54.0': + '@rollup/rollup-freebsd-x64@4.59.0': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.50.2': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + '@rollup/rollup-linux-arm-gnueabihf@4.59.0': optional: true '@rollup/rollup-linux-arm-musleabihf@4.50.2': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.54.0': + '@rollup/rollup-linux-arm-musleabihf@4.59.0': optional: true '@rollup/rollup-linux-arm64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-arm64-gnu@4.54.0': + '@rollup/rollup-linux-arm64-gnu@4.59.0': optional: true '@rollup/rollup-linux-arm64-musl@4.50.2': optional: true - '@rollup/rollup-linux-arm64-musl@4.54.0': + '@rollup/rollup-linux-arm64-musl@4.59.0': optional: true '@rollup/rollup-linux-loong64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-loong64-gnu@4.54.0': + '@rollup/rollup-linux-loong64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.59.0': optional: true '@rollup/rollup-linux-ppc64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.54.0': + '@rollup/rollup-linux-ppc64-gnu@4.59.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.59.0': optional: true '@rollup/rollup-linux-riscv64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.54.0': + '@rollup/rollup-linux-riscv64-gnu@4.59.0': optional: true '@rollup/rollup-linux-riscv64-musl@4.50.2': optional: true - '@rollup/rollup-linux-riscv64-musl@4.54.0': + '@rollup/rollup-linux-riscv64-musl@4.59.0': optional: true '@rollup/rollup-linux-s390x-gnu@4.50.2': optional: true - '@rollup/rollup-linux-s390x-gnu@4.54.0': + '@rollup/rollup-linux-s390x-gnu@4.59.0': optional: true '@rollup/rollup-linux-x64-gnu@4.50.2': optional: true - '@rollup/rollup-linux-x64-gnu@4.54.0': + '@rollup/rollup-linux-x64-gnu@4.59.0': optional: true '@rollup/rollup-linux-x64-musl@4.50.2': optional: true - '@rollup/rollup-linux-x64-musl@4.54.0': + '@rollup/rollup-linux-x64-musl@4.59.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.59.0': optional: true '@rollup/rollup-openharmony-arm64@4.50.2': optional: true - '@rollup/rollup-openharmony-arm64@4.54.0': + '@rollup/rollup-openharmony-arm64@4.59.0': optional: true '@rollup/rollup-win32-arm64-msvc@4.50.2': optional: true - '@rollup/rollup-win32-arm64-msvc@4.54.0': + '@rollup/rollup-win32-arm64-msvc@4.59.0': optional: true '@rollup/rollup-win32-ia32-msvc@4.50.2': optional: true - '@rollup/rollup-win32-ia32-msvc@4.54.0': + '@rollup/rollup-win32-ia32-msvc@4.59.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.54.0': + '@rollup/rollup-win32-x64-gnu@4.59.0': optional: true '@rollup/rollup-win32-x64-msvc@4.50.2': optional: true - '@rollup/rollup-win32-x64-msvc@4.54.0': + '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true '@rtsao/scc@1.1.0': {} - '@rushstack/eslint-patch@1.15.0': {} + '@rushstack/eslint-patch@1.16.1': {} '@scarf/scarf@1.4.0': {} - '@scure/base@1.1.1': {} - '@scure/base@1.1.9': {} '@scure/base@1.2.6': {} - '@scure/bip32@1.3.1': - dependencies: - '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.1 - '@scure/base': 1.1.1 + '@scure/base@2.0.0': {} '@scure/bip32@1.4.0': dependencies: @@ -23634,10 +24016,11 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 - '@scure/bip39@1.2.1': + '@scure/bip32@2.0.1': dependencies: - '@noble/hashes': 1.3.1 - '@scure/base': 1.1.1 + '@noble/curves': 2.0.1 + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 '@scure/bip39@1.3.0': dependencies: @@ -23654,6 +24037,11 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 + '@scure/bip39@2.0.1': + dependencies: + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 + '@sec-ant/readable-stream@0.4.1': {} '@segment/analytics-core@1.8.2': @@ -23684,78 +24072,77 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 - '@sentry-internal/browser-utils@10.32.1': + '@sentry-internal/browser-utils@10.40.0': dependencies: - '@sentry/core': 10.32.1 + '@sentry/core': 10.40.0 - '@sentry-internal/feedback@10.32.1': + '@sentry-internal/feedback@10.40.0': dependencies: - '@sentry/core': 10.32.1 + '@sentry/core': 10.40.0 '@sentry-internal/node-cpu-profiler@2.2.0': dependencies: detect-libc: 2.1.2 - node-abi: 3.85.0 + node-abi: 3.87.0 - '@sentry-internal/replay-canvas@10.32.1': + '@sentry-internal/replay-canvas@10.40.0': dependencies: - '@sentry-internal/replay': 10.32.1 - '@sentry/core': 10.32.1 + '@sentry-internal/replay': 10.40.0 + '@sentry/core': 10.40.0 - '@sentry-internal/replay@10.32.1': + '@sentry-internal/replay@10.40.0': dependencies: - '@sentry-internal/browser-utils': 10.32.1 - '@sentry/core': 10.32.1 + '@sentry-internal/browser-utils': 10.40.0 + '@sentry/core': 10.40.0 - '@sentry/babel-plugin-component-annotate@4.6.1': {} + '@sentry/babel-plugin-component-annotate@5.1.1': {} - '@sentry/browser@10.32.1': + '@sentry/browser@10.40.0': dependencies: - '@sentry-internal/browser-utils': 10.32.1 - '@sentry-internal/feedback': 10.32.1 - '@sentry-internal/replay': 10.32.1 - '@sentry-internal/replay-canvas': 10.32.1 - '@sentry/core': 10.32.1 + '@sentry-internal/browser-utils': 10.40.0 + '@sentry-internal/feedback': 10.40.0 + '@sentry-internal/replay': 10.40.0 + '@sentry-internal/replay-canvas': 10.40.0 + '@sentry/core': 10.40.0 - '@sentry/bundler-plugin-core@4.6.1': + '@sentry/bundler-plugin-core@5.1.1': dependencies: - '@babel/core': 7.28.5 - '@sentry/babel-plugin-component-annotate': 4.6.1 - '@sentry/cli': 2.58.4 + '@babel/core': 7.29.0 + '@sentry/babel-plugin-component-annotate': 5.1.1 + '@sentry/cli': 2.58.5 dotenv: 16.6.1 find-up: 5.0.0 - glob: 10.5.0 - magic-string: 0.30.8 - unplugin: 1.0.1 + glob: 13.0.6 + magic-string: 0.30.21 transitivePeerDependencies: - encoding - supports-color - '@sentry/cli-darwin@2.58.4': + '@sentry/cli-darwin@2.58.5': optional: true - '@sentry/cli-linux-arm64@2.58.4': + '@sentry/cli-linux-arm64@2.58.5': optional: true - '@sentry/cli-linux-arm@2.58.4': + '@sentry/cli-linux-arm@2.58.5': optional: true - '@sentry/cli-linux-i686@2.58.4': + '@sentry/cli-linux-i686@2.58.5': optional: true - '@sentry/cli-linux-x64@2.58.4': + '@sentry/cli-linux-x64@2.58.5': optional: true - '@sentry/cli-win32-arm64@2.58.4': + '@sentry/cli-win32-arm64@2.58.5': optional: true - '@sentry/cli-win32-i686@2.58.4': + '@sentry/cli-win32-i686@2.58.5': optional: true - '@sentry/cli-win32-x64@2.58.4': + '@sentry/cli-win32-x64@2.58.5': optional: true - '@sentry/cli@2.58.4': + '@sentry/cli@2.58.5': dependencies: https-proxy-agent: 5.0.1 node-fetch: 2.7.0 @@ -23763,50 +24150,49 @@ snapshots: proxy-from-env: 1.1.0 which: 2.0.2 optionalDependencies: - '@sentry/cli-darwin': 2.58.4 - '@sentry/cli-linux-arm': 2.58.4 - '@sentry/cli-linux-arm64': 2.58.4 - '@sentry/cli-linux-i686': 2.58.4 - '@sentry/cli-linux-x64': 2.58.4 - '@sentry/cli-win32-arm64': 2.58.4 - '@sentry/cli-win32-i686': 2.58.4 - '@sentry/cli-win32-x64': 2.58.4 + '@sentry/cli-darwin': 2.58.5 + '@sentry/cli-linux-arm': 2.58.5 + '@sentry/cli-linux-arm64': 2.58.5 + '@sentry/cli-linux-i686': 2.58.5 + '@sentry/cli-linux-x64': 2.58.5 + '@sentry/cli-win32-arm64': 2.58.5 + '@sentry/cli-win32-i686': 2.58.5 + '@sentry/cli-win32-x64': 2.58.5 transitivePeerDependencies: - encoding - supports-color - '@sentry/core@10.32.1': {} + '@sentry/core@10.40.0': {} - '@sentry/nestjs@10.32.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)': + '@sentry/nestjs@10.40.0(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)': dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 - '@sentry/core': 10.32.1 - '@sentry/node': 10.32.1 + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + '@sentry/core': 10.40.0 + '@sentry/node': 10.40.0 transitivePeerDependencies: - supports-color - '@sentry/nextjs@10.32.1(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react@18.3.1)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/nextjs@10.40.0(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(next@14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3))(react@18.3.1)(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.38.0 - '@rollup/plugin-commonjs': 28.0.1(rollup@4.54.0) - '@sentry-internal/browser-utils': 10.32.1 - '@sentry/bundler-plugin-core': 4.6.1 - '@sentry/core': 10.32.1 - '@sentry/node': 10.32.1 - '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - '@sentry/react': 10.32.1(react@18.3.1) - '@sentry/vercel-edge': 10.32.1 - '@sentry/webpack-plugin': 4.6.1(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) - next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) - resolve: 1.22.8 - rollup: 4.54.0 + '@opentelemetry/semantic-conventions': 1.40.0 + '@rollup/plugin-commonjs': 28.0.1(rollup@4.59.0) + '@sentry-internal/browser-utils': 10.40.0 + '@sentry/bundler-plugin-core': 5.1.1 + '@sentry/core': 10.40.0 + '@sentry/node': 10.40.0 + '@sentry/opentelemetry': 10.40.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/react': 10.40.0(react@18.3.1) + '@sentry/vercel-edge': 10.40.0 + '@sentry/webpack-plugin': 5.1.1(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + next: 14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3) + rollup: 4.59.0 stacktrace-parser: 0.1.11 transitivePeerDependencies: - '@opentelemetry/context-async-hooks' @@ -23817,103 +24203,99 @@ snapshots: - supports-color - webpack - '@sentry/node-core@10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': + '@sentry/node-core@10.40.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: - '@apm-js-collab/tracing-hooks': 0.3.1 + '@sentry/core': 10.40.0 + '@sentry/opentelemetry': 10.40.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + import-in-the-middle: 2.0.6 + optionalDependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 - '@sentry/core': 10.32.1 - '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - import-in-the-middle: 2.0.1 + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + + '@sentry/node@10.40.0': + dependencies: + '@fastify/otel': 0.16.0(@opentelemetry/api@1.9.0) + '@opentelemetry/api': 1.9.0 + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.28.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.54.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.58.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.211.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.20.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-knex': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.55.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.64.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.57.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.63.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis': 0.59.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-tedious': 0.30.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.21.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + '@prisma/instrumentation': 7.2.0(@opentelemetry/api@1.9.0) + '@sentry/core': 10.40.0 + '@sentry/node-core': 10.40.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.211.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + '@sentry/opentelemetry': 10.40.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0) + import-in-the-middle: 2.0.6 transitivePeerDependencies: - supports-color - '@sentry/node@10.32.1': + '@sentry/opentelemetry@10.40.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.40.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-amqplib': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-dataloader': 0.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fs': 0.28.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-generic-pool': 0.52.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.56.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.208.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.56.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-kafkajs': 0.18.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-knex': 0.53.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-lru-memoizer': 0.53.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.61.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.54.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.55.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.61.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis': 0.57.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-tedious': 0.27.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-undici': 0.19.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 - '@prisma/instrumentation': 6.19.0(@opentelemetry/api@1.9.0) - '@sentry/core': 10.32.1 - '@sentry/node-core': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.208.0(@opentelemetry/api@1.9.0))(@opentelemetry/resources@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - '@sentry/opentelemetry': 10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0) - import-in-the-middle: 2.0.1 - minimatch: 9.0.5 - transitivePeerDependencies: - - supports-color + '@opentelemetry/context-async-hooks': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 + '@sentry/core': 10.40.0 - '@sentry/opentelemetry@10.32.1(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.38.0)': - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 - '@sentry/core': 10.32.1 - - '@sentry/profiling-node@10.32.1': + '@sentry/profiling-node@10.40.0': dependencies: '@sentry-internal/node-cpu-profiler': 2.2.0 - '@sentry/core': 10.32.1 - '@sentry/node': 10.32.1 + '@sentry/core': 10.40.0 + '@sentry/node': 10.40.0 transitivePeerDependencies: - supports-color - '@sentry/react@10.32.1(react@18.3.1)': + '@sentry/react@10.40.0(react@18.3.1)': dependencies: - '@sentry/browser': 10.32.1 - '@sentry/core': 10.32.1 - hoist-non-react-statics: 3.3.2 + '@sentry/browser': 10.40.0 + '@sentry/core': 10.40.0 react: 18.3.1 - '@sentry/vercel-edge@10.32.1': + '@sentry/vercel-edge@10.40.0': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) - '@sentry/core': 10.32.1 + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@sentry/core': 10.40.0 - '@sentry/webpack-plugin@4.6.1(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': + '@sentry/webpack-plugin@5.1.1(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12))': dependencies: - '@sentry/bundler-plugin-core': 4.6.1 - unplugin: 1.0.1 + '@sentry/bundler-plugin-core': 5.1.1 uuid: 9.0.1 - webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) transitivePeerDependencies: - encoding - supports-color - '@sinclair/typebox@0.27.8': {} + '@sinclair/typebox@0.27.10': {} '@sindresorhus/is@4.6.0': {} @@ -23936,254 +24318,254 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 - '@smithy/abort-controller@4.2.7': + '@smithy/abort-controller@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/chunked-blob-reader-native@4.2.1': + '@smithy/chunked-blob-reader-native@4.2.2': dependencies: - '@smithy/util-base64': 4.3.0 + '@smithy/util-base64': 4.3.1 tslib: 2.8.1 - '@smithy/chunked-blob-reader@5.2.0': + '@smithy/chunked-blob-reader@5.2.1': dependencies: tslib: 2.8.1 - '@smithy/config-resolver@4.4.5': + '@smithy/config-resolver@4.4.9': dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-config-provider': 4.2.0 - '@smithy/util-endpoints': 3.2.7 - '@smithy/util-middleware': 4.2.7 + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-config-provider': 4.2.1 + '@smithy/util-endpoints': 3.3.1 + '@smithy/util-middleware': 4.2.10 tslib: 2.8.1 - '@smithy/core@3.20.0': + '@smithy/core@3.23.6': dependencies: - '@smithy/middleware-serde': 4.2.8 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-body-length-browser': 4.2.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-stream': 4.5.8 - '@smithy/util-utf8': 4.2.0 - '@smithy/uuid': 1.1.0 + '@smithy/middleware-serde': 4.2.11 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-body-length-browser': 4.2.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-stream': 4.5.15 + '@smithy/util-utf8': 4.2.1 + '@smithy/uuid': 1.1.1 tslib: 2.8.1 - '@smithy/credential-provider-imds@4.2.7': + '@smithy/credential-provider-imds@4.2.10': dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 + '@smithy/node-config-provider': 4.3.10 + '@smithy/property-provider': 4.2.10 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 tslib: 2.8.1 - '@smithy/eventstream-codec@4.2.7': + '@smithy/eventstream-codec@4.2.10': dependencies: '@aws-crypto/crc32': 5.2.0 - '@smithy/types': 4.11.0 - '@smithy/util-hex-encoding': 4.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.1 tslib: 2.8.1 - '@smithy/eventstream-serde-browser@4.2.7': + '@smithy/eventstream-serde-browser@4.2.10': dependencies: - '@smithy/eventstream-serde-universal': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/eventstream-serde-universal': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/eventstream-serde-config-resolver@4.3.7': + '@smithy/eventstream-serde-config-resolver@4.3.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/eventstream-serde-node@4.2.7': + '@smithy/eventstream-serde-node@4.2.10': dependencies: - '@smithy/eventstream-serde-universal': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/eventstream-serde-universal': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/eventstream-serde-universal@4.2.7': + '@smithy/eventstream-serde-universal@4.2.10': dependencies: - '@smithy/eventstream-codec': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/eventstream-codec': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/fetch-http-handler@5.3.8': + '@smithy/fetch-http-handler@5.3.11': dependencies: - '@smithy/protocol-http': 5.3.7 - '@smithy/querystring-builder': 4.2.7 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 + '@smithy/protocol-http': 5.3.10 + '@smithy/querystring-builder': 4.2.10 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 tslib: 2.8.1 - '@smithy/hash-blob-browser@4.2.8': + '@smithy/hash-blob-browser@4.2.11': dependencies: - '@smithy/chunked-blob-reader': 5.2.0 - '@smithy/chunked-blob-reader-native': 4.2.1 - '@smithy/types': 4.11.0 + '@smithy/chunked-blob-reader': 5.2.1 + '@smithy/chunked-blob-reader-native': 4.2.2 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/hash-node@4.2.7': + '@smithy/hash-node@4.2.10': dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/hash-stream-node@4.2.7': + '@smithy/hash-stream-node@4.2.10': dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/invalid-dependency@4.2.7': + '@smithy/invalid-dependency@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 '@smithy/is-array-buffer@2.2.0': dependencies: tslib: 2.8.1 - '@smithy/is-array-buffer@4.2.0': + '@smithy/is-array-buffer@4.2.1': dependencies: tslib: 2.8.1 - '@smithy/md5-js@4.2.7': + '@smithy/md5-js@4.2.10': dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/middleware-content-length@4.2.7': + '@smithy/middleware-content-length@4.2.10': dependencies: - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/middleware-endpoint@4.4.1': + '@smithy/middleware-endpoint@4.4.20': dependencies: - '@smithy/core': 3.20.0 - '@smithy/middleware-serde': 4.2.8 - '@smithy/node-config-provider': 4.3.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 - '@smithy/url-parser': 4.2.7 - '@smithy/util-middleware': 4.2.7 + '@smithy/core': 3.23.6 + '@smithy/middleware-serde': 4.2.11 + '@smithy/node-config-provider': 4.3.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 + '@smithy/url-parser': 4.2.10 + '@smithy/util-middleware': 4.2.10 tslib: 2.8.1 - '@smithy/middleware-retry@4.4.17': + '@smithy/middleware-retry@4.4.37': dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/service-error-classification': 4.2.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-retry': 4.2.7 - '@smithy/uuid': 1.1.0 + '@smithy/node-config-provider': 4.3.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/service-error-classification': 4.2.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-retry': 4.2.10 + '@smithy/uuid': 1.1.1 tslib: 2.8.1 - '@smithy/middleware-serde@4.2.8': + '@smithy/middleware-serde@4.2.11': dependencies: - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/middleware-stack@4.2.7': + '@smithy/middleware-stack@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/node-config-provider@4.3.7': + '@smithy/node-config-provider@4.3.10': dependencies: - '@smithy/property-provider': 4.2.7 - '@smithy/shared-ini-file-loader': 4.4.2 - '@smithy/types': 4.11.0 + '@smithy/property-provider': 4.2.10 + '@smithy/shared-ini-file-loader': 4.4.5 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/node-http-handler@4.4.7': + '@smithy/node-http-handler@4.4.12': dependencies: - '@smithy/abort-controller': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/querystring-builder': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/abort-controller': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/querystring-builder': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/property-provider@4.2.7': + '@smithy/property-provider@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/protocol-http@5.3.7': + '@smithy/protocol-http@5.3.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/querystring-builder@4.2.7': + '@smithy/querystring-builder@4.2.10': dependencies: - '@smithy/types': 4.11.0 - '@smithy/util-uri-escape': 4.2.0 + '@smithy/types': 4.13.0 + '@smithy/util-uri-escape': 4.2.1 tslib: 2.8.1 - '@smithy/querystring-parser@4.2.7': + '@smithy/querystring-parser@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/service-error-classification@4.2.7': + '@smithy/service-error-classification@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 - '@smithy/shared-ini-file-loader@4.4.2': + '@smithy/shared-ini-file-loader@4.4.5': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/signature-v4@5.3.7': + '@smithy/signature-v4@5.3.10': dependencies: - '@smithy/is-array-buffer': 4.2.0 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-middleware': 4.2.7 - '@smithy/util-uri-escape': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/is-array-buffer': 4.2.1 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-middleware': 4.2.10 + '@smithy/util-uri-escape': 4.2.1 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/smithy-client@4.10.2': + '@smithy/smithy-client@4.12.0': dependencies: - '@smithy/core': 3.20.0 - '@smithy/middleware-endpoint': 4.4.1 - '@smithy/middleware-stack': 4.2.7 - '@smithy/protocol-http': 5.3.7 - '@smithy/types': 4.11.0 - '@smithy/util-stream': 4.5.8 + '@smithy/core': 3.23.6 + '@smithy/middleware-endpoint': 4.4.20 + '@smithy/middleware-stack': 4.2.10 + '@smithy/protocol-http': 5.3.10 + '@smithy/types': 4.13.0 + '@smithy/util-stream': 4.5.15 tslib: 2.8.1 - '@smithy/types@4.11.0': + '@smithy/types@4.13.0': dependencies: tslib: 2.8.1 - '@smithy/url-parser@4.2.7': + '@smithy/url-parser@4.2.10': dependencies: - '@smithy/querystring-parser': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/querystring-parser': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-base64@4.3.0': + '@smithy/util-base64@4.3.1': dependencies: - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/util-body-length-browser@4.2.0': + '@smithy/util-body-length-browser@4.2.1': dependencies: tslib: 2.8.1 - '@smithy/util-body-length-node@4.2.1': + '@smithy/util-body-length-node@4.2.2': dependencies: tslib: 2.8.1 @@ -24192,65 +24574,65 @@ snapshots: '@smithy/is-array-buffer': 2.2.0 tslib: 2.8.1 - '@smithy/util-buffer-from@4.2.0': + '@smithy/util-buffer-from@4.2.1': dependencies: - '@smithy/is-array-buffer': 4.2.0 + '@smithy/is-array-buffer': 4.2.1 tslib: 2.8.1 - '@smithy/util-config-provider@4.2.0': + '@smithy/util-config-provider@4.2.1': dependencies: tslib: 2.8.1 - '@smithy/util-defaults-mode-browser@4.3.16': + '@smithy/util-defaults-mode-browser@4.3.36': dependencies: - '@smithy/property-provider': 4.2.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 + '@smithy/property-provider': 4.2.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-defaults-mode-node@4.2.19': + '@smithy/util-defaults-mode-node@4.2.39': dependencies: - '@smithy/config-resolver': 4.4.5 - '@smithy/credential-provider-imds': 4.2.7 - '@smithy/node-config-provider': 4.3.7 - '@smithy/property-provider': 4.2.7 - '@smithy/smithy-client': 4.10.2 - '@smithy/types': 4.11.0 + '@smithy/config-resolver': 4.4.9 + '@smithy/credential-provider-imds': 4.2.10 + '@smithy/node-config-provider': 4.3.10 + '@smithy/property-provider': 4.2.10 + '@smithy/smithy-client': 4.12.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-endpoints@3.2.7': + '@smithy/util-endpoints@3.3.1': dependencies: - '@smithy/node-config-provider': 4.3.7 - '@smithy/types': 4.11.0 + '@smithy/node-config-provider': 4.3.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-hex-encoding@4.2.0': + '@smithy/util-hex-encoding@4.2.1': dependencies: tslib: 2.8.1 - '@smithy/util-middleware@4.2.7': + '@smithy/util-middleware@4.2.10': dependencies: - '@smithy/types': 4.11.0 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-retry@4.2.7': + '@smithy/util-retry@4.2.10': dependencies: - '@smithy/service-error-classification': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/service-error-classification': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/util-stream@4.5.8': + '@smithy/util-stream@4.5.15': dependencies: - '@smithy/fetch-http-handler': 5.3.8 - '@smithy/node-http-handler': 4.4.7 - '@smithy/types': 4.11.0 - '@smithy/util-base64': 4.3.0 - '@smithy/util-buffer-from': 4.2.0 - '@smithy/util-hex-encoding': 4.2.0 - '@smithy/util-utf8': 4.2.0 + '@smithy/fetch-http-handler': 5.3.11 + '@smithy/node-http-handler': 4.4.12 + '@smithy/types': 4.13.0 + '@smithy/util-base64': 4.3.1 + '@smithy/util-buffer-from': 4.2.1 + '@smithy/util-hex-encoding': 4.2.1 + '@smithy/util-utf8': 4.2.1 tslib: 2.8.1 - '@smithy/util-uri-escape@4.2.0': + '@smithy/util-uri-escape@4.2.1': dependencies: tslib: 2.8.1 @@ -24259,26 +24641,26 @@ snapshots: '@smithy/util-buffer-from': 2.2.0 tslib: 2.8.1 - '@smithy/util-utf8@4.2.0': + '@smithy/util-utf8@4.2.1': dependencies: - '@smithy/util-buffer-from': 4.2.0 + '@smithy/util-buffer-from': 4.2.1 tslib: 2.8.1 - '@smithy/util-waiter@4.2.7': + '@smithy/util-waiter@4.2.10': dependencies: - '@smithy/abort-controller': 4.2.7 - '@smithy/types': 4.11.0 + '@smithy/abort-controller': 4.2.10 + '@smithy/types': 4.13.0 tslib: 2.8.1 - '@smithy/uuid@1.1.0': + '@smithy/uuid@1.1.1': dependencies: tslib: 2.8.1 '@socket.io/component-emitter@3.1.2': {} - '@solana-mobile/mobile-wallet-adapter-protocol-web3js@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/mobile-wallet-adapter-protocol-web3js@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 5.0.0 js-base64: 3.7.8 @@ -24289,14 +24671,14 @@ snapshots: - react-native - typescript - '@solana-mobile/mobile-wallet-adapter-protocol@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/mobile-wallet-adapter-protocol@2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: '@solana/codecs-strings': 4.0.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.5.4) '@solana/wallet-standard': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(react@18.3.1) '@solana/wallet-standard-util': 1.1.2 '@wallet-standard/core': 1.1.1 js-base64: 3.7.8 - react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) transitivePeerDependencies: - '@solana/wallet-adapter-base' - '@solana/web3.js' @@ -24305,25 +24687,25 @@ snapshots: - react - typescript - '@solana-mobile/wallet-adapter-mobile@2.2.5(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/wallet-adapter-mobile@2.2.5(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/mobile-wallet-adapter-protocol-web3js': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana-mobile/wallet-standard-mobile': 0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/mobile-wallet-adapter-protocol-web3js': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/wallet-standard-mobile': 0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': 1.3.0 '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) js-base64: 3.7.8 optionalDependencies: - '@react-native-async-storage/async-storage': 1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)) + '@react-native-async-storage/async-storage': 1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - react - react-native - typescript - '@solana-mobile/wallet-standard-mobile@0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana-mobile/wallet-standard-mobile@0.4.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/mobile-wallet-adapter-protocol': 2.2.5(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@5.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/wallet-standard-chains': 1.1.1 '@solana/wallet-standard-features': 1.3.0 '@wallet-standard/base': 1.1.0 @@ -24376,7 +24758,7 @@ snapshots: '@solana/errors@2.3.0(typescript@5.5.4)': dependencies: chalk: 5.6.2 - commander: 14.0.2 + commander: 14.0.3 typescript: 5.5.4 '@solana/errors@4.0.0(typescript@5.5.4)': @@ -24395,9 +24777,9 @@ snapshots: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-base-ui@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana/wallet-adapter-base-ui@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) react: 18.3.1 transitivePeerDependencies: @@ -24412,7 +24794,7 @@ snapshots: '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 - eventemitter3: 5.0.1 + eventemitter3: 5.0.4 '@solana/wallet-adapter-bitkeep@0.3.24(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: @@ -24486,9 +24868,9 @@ snapshots: '@solana/wallet-adapter-ledger@0.9.29(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: - '@ledgerhq/devices': 8.7.1 - '@ledgerhq/hw-transport': 6.31.14 - '@ledgerhq/hw-transport-webhid': 6.30.10 + '@ledgerhq/devices': 8.10.0 + '@ledgerhq/hw-transport': 6.32.0 + '@ledgerhq/hw-transport-webhid': 6.31.0 '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) buffer: 6.0.3 @@ -24531,11 +24913,11 @@ snapshots: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-react-ui@0.9.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana/wallet-adapter-react-ui@0.9.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) - '@solana/wallet-adapter-base-ui': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) - '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/wallet-adapter-base-ui': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana/wallet-adapter-react': 0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -24545,9 +24927,9 @@ snapshots: - react-native - typescript - '@solana/wallet-adapter-react@0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': + '@solana/wallet-adapter-react@0.15.39(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)': dependencies: - '@solana-mobile/wallet-adapter-mobile': 2.2.5(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) + '@solana-mobile/wallet-adapter-mobile': 2.2.5(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/wallet-standard-wallet-adapter-react': 1.1.4(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bs58@6.0.0)(react@18.3.1) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) @@ -24608,11 +24990,11 @@ snapshots: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-torus@0.11.32(@babel/runtime@7.28.4)(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@solana/wallet-adapter-torus@0.11.32(@babel/runtime@7.28.6)(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@toruslabs/solana-embed': 2.1.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) + '@toruslabs/solana-embed': 2.1.0(@babel/runtime@7.28.6)(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) assert: 2.1.0 crypto-browserify: 3.12.1 process: 0.11.10 @@ -24639,11 +25021,11 @@ snapshots: '@solana/wallet-standard-util': 1.1.2 '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@solana/wallet-adapter-walletconnect@0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@solana/wallet-adapter-walletconnect@0.1.21(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/solana-adapter': 0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/solana-adapter': 0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -24768,20 +25150,20 @@ snapshots: '@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@solana/buffer-layout': 4.0.1 '@solana/codecs-numbers': 2.3.0(typescript@5.5.4) agentkeepalive: 4.6.0 - bn.js: 5.2.2 + bn.js: 5.2.3 borsh: 0.7.0 bs58: 4.0.1 buffer: 6.0.3 fast-stable-stringify: 1.0.0 jayson: 4.3.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 9.3.2 + rpc-websockets: 9.3.5 superstruct: 2.0.2 transitivePeerDependencies: - bufferutil @@ -24795,14 +25177,14 @@ snapshots: '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) '@wallet-standard/base': 1.1.0 bs58: 5.0.0 - eventemitter3: 5.0.1 + eventemitter3: 5.0.4 uuid: 9.0.1 '@solflare-wallet/sdk@1.4.2(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))': dependencies: '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) bs58: 5.0.0 - eventemitter3: 5.0.1 + eventemitter3: 5.0.4 uuid: 9.0.1 '@standard-schema/spec@1.1.0': {} @@ -24821,16 +25203,16 @@ snapshots: '@storybook/client-logger': 7.6.17 '@storybook/core-events': 7.6.17 '@storybook/global': 5.0.0 - qs: 6.14.0 + qs: 6.15.0 telejson: 7.2.0 tiny-invariant: 1.3.3 - '@storybook/channels@7.6.21': + '@storybook/channels@7.6.23': dependencies: - '@storybook/client-logger': 7.6.21 - '@storybook/core-events': 7.6.21 + '@storybook/client-logger': 7.6.23 + '@storybook/core-events': 7.6.23 '@storybook/global': 5.0.0 - qs: 6.14.0 + qs: 6.15.0 telejson: 7.2.0 tiny-invariant: 1.3.3 @@ -24838,7 +25220,7 @@ snapshots: dependencies: '@storybook/global': 5.0.0 - '@storybook/client-logger@7.6.21': + '@storybook/client-logger@7.6.23': dependencies: '@storybook/global': 5.0.0 @@ -24846,7 +25228,7 @@ snapshots: dependencies: ts-dedent: 2.2.0 - '@storybook/core-events@7.6.21': + '@storybook/core-events@7.6.23': dependencies: ts-dedent: 2.2.0 @@ -24867,7 +25249,7 @@ snapshots: '@storybook/theming': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/types': 7.6.17 dequal: 2.0.3 - lodash: 4.17.21 + lodash: 4.17.23 memoizerific: 1.11.3 store2: 2.14.4 telejson: 7.2.0 @@ -24886,26 +25268,26 @@ snapshots: '@storybook/types': 7.6.17 '@types/qs': 6.14.0 dequal: 2.0.3 - lodash: 4.17.21 + lodash: 4.17.23 memoizerific: 1.11.3 - qs: 6.14.0 + qs: 6.15.0 synchronous-promise: 2.0.17 ts-dedent: 2.2.0 util-deprecate: 1.0.2 - '@storybook/preview-api@7.6.21': + '@storybook/preview-api@7.6.23': dependencies: - '@storybook/channels': 7.6.21 - '@storybook/client-logger': 7.6.21 - '@storybook/core-events': 7.6.21 + '@storybook/channels': 7.6.23 + '@storybook/client-logger': 7.6.23 + '@storybook/core-events': 7.6.23 '@storybook/csf': 0.1.13 '@storybook/global': 5.0.0 - '@storybook/types': 7.6.21 + '@storybook/types': 7.6.23 '@types/qs': 6.14.0 dequal: 2.0.3 - lodash: 4.17.21 + lodash: 4.17.23 memoizerific: 1.11.3 - qs: 6.14.0 + qs: 6.15.0 synchronous-promise: 2.0.17 ts-dedent: 2.2.0 util-deprecate: 1.0.2 @@ -24914,7 +25296,7 @@ snapshots: dependencies: '@storybook/client-logger': 7.6.17 memoizerific: 1.11.3 - qs: 6.14.0 + qs: 6.15.0 '@storybook/theming@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -24932,72 +25314,72 @@ snapshots: '@types/express': 4.17.25 file-system-cache: 2.3.0 - '@storybook/types@7.6.21': + '@storybook/types@7.6.23': dependencies: - '@storybook/channels': 7.6.21 + '@storybook/channels': 7.6.23 '@types/babel__core': 7.20.5 '@types/express': 4.17.25 file-system-cache: 2.3.0 - '@stripe/react-stripe-js@5.4.1(@stripe/stripe-js@8.6.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@stripe/react-stripe-js@5.6.0(@stripe/stripe-js@8.8.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@stripe/stripe-js': 8.6.0 + '@stripe/stripe-js': 8.8.0 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@stripe/stripe-js@8.6.0': {} + '@stripe/stripe-js@8.8.0': {} '@supercharge/request-ip@1.2.0': {} - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.5)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 - '@svgr/babel-preset@8.1.0(@babel/core@7.28.5)': + '@svgr/babel-preset@8.1.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.5) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.5) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.5) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.5) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.5) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.5) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.5) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.29.0) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.29.0) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.29.0) '@svgr/core@8.1.0(typescript@5.5.4)': dependencies: - '@babel/core': 7.28.5 - '@svgr/babel-preset': 8.1.0(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) camelcase: 6.3.0 cosmiconfig: 8.3.6(typescript@5.5.4) snake-case: 3.0.4 @@ -25007,13 +25389,13 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 entities: 4.5.0 '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.5.4))': dependencies: - '@babel/core': 7.28.5 - '@svgr/babel-preset': 8.1.0(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@svgr/babel-preset': 8.1.0(@babel/core@7.29.0) '@svgr/core': 8.1.0(typescript@5.5.4) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 @@ -25031,11 +25413,11 @@ snapshots: '@svgr/webpack@8.1.0(typescript@5.5.4)': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.28.5) - '@babel/preset-env': 7.28.5(@babel/core@7.28.5) - '@babel/preset-react': 7.28.5(@babel/core@7.28.5) - '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.29.0) + '@babel/preset-env': 7.29.0(@babel/core@7.29.0) + '@babel/preset-react': 7.28.5(@babel/core@7.29.0) + '@babel/preset-typescript': 7.28.5(@babel/core@7.29.0) '@svgr/core': 8.1.0(typescript@5.5.4) '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4)) '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.5.4))(typescript@5.5.4) @@ -25074,9 +25456,9 @@ snapshots: '@swc/counter': 0.1.3 commander: 8.3.0 fast-glob: 3.3.3 - minimatch: 9.0.5 + minimatch: 9.0.9 piscina: 4.9.2 - semver: 7.7.3 + semver: 7.7.4 slash: 3.0.0 source-map: 0.7.6 optionalDependencies: @@ -25154,174 +25536,172 @@ snapshots: dependencies: defer-to-connect: 2.0.1 - '@tailwindcss/node@4.1.18': + '@tailwindcss/node@4.2.1': dependencies: '@jridgewell/remapping': 2.3.5 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.20.0 jiti: 2.6.1 - lightningcss: 1.30.2 + lightningcss: 1.31.1 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.1.18 + tailwindcss: 4.2.1 - '@tailwindcss/oxide-android-arm64@4.1.18': + '@tailwindcss/oxide-android-arm64@4.2.1': optional: true - '@tailwindcss/oxide-darwin-arm64@4.1.18': + '@tailwindcss/oxide-darwin-arm64@4.2.1': optional: true - '@tailwindcss/oxide-darwin-x64@4.1.18': + '@tailwindcss/oxide-darwin-x64@4.2.1': optional: true - '@tailwindcss/oxide-freebsd-x64@4.1.18': + '@tailwindcss/oxide-freebsd-x64@4.2.1': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + '@tailwindcss/oxide-linux-arm64-gnu@4.2.1': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + '@tailwindcss/oxide-linux-arm64-musl@4.2.1': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + '@tailwindcss/oxide-linux-x64-gnu@4.2.1': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.1.18': + '@tailwindcss/oxide-linux-x64-musl@4.2.1': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.1.18': + '@tailwindcss/oxide-wasm32-wasi@4.2.1': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + '@tailwindcss/oxide-win32-arm64-msvc@4.2.1': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + '@tailwindcss/oxide-win32-x64-msvc@4.2.1': optional: true - '@tailwindcss/oxide@4.1.18': + '@tailwindcss/oxide@4.2.1': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.1.18 - '@tailwindcss/oxide-darwin-arm64': 4.1.18 - '@tailwindcss/oxide-darwin-x64': 4.1.18 - '@tailwindcss/oxide-freebsd-x64': 4.1.18 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 - '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 - '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 - '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 - '@tailwindcss/oxide-linux-x64-musl': 4.1.18 - '@tailwindcss/oxide-wasm32-wasi': 4.1.18 - '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 - '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + '@tailwindcss/oxide-android-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-arm64': 4.2.1 + '@tailwindcss/oxide-darwin-x64': 4.2.1 + '@tailwindcss/oxide-freebsd-x64': 4.2.1 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.1 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.1 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.1 + '@tailwindcss/oxide-linux-x64-musl': 4.2.1 + '@tailwindcss/oxide-wasm32-wasi': 4.2.1 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.1 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.1 - '@tailwindcss/postcss@4.1.18': + '@tailwindcss/postcss@4.2.1': dependencies: '@alloc/quick-lru': 5.2.0 - '@tailwindcss/node': 4.1.18 - '@tailwindcss/oxide': 4.1.18 + '@tailwindcss/node': 4.2.1 + '@tailwindcss/oxide': 4.2.1 postcss: 8.5.6 - tailwindcss: 4.1.18 + tailwindcss: 4.2.1 - '@tailwindcss/vite@4.1.18(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2))': + '@tailwindcss/vite@4.2.1(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@tailwindcss/node': 4.1.18 - '@tailwindcss/oxide': 4.1.18 - tailwindcss: 4.1.18 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + '@tailwindcss/node': 4.2.1 + '@tailwindcss/oxide': 4.2.1 + tailwindcss: 4.2.1 + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) - '@tanstack/react-virtual@3.13.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-virtual@3.13.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/virtual-core': 3.13.13 + '@tanstack/virtual-core': 3.13.19 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tanstack/virtual-core@3.13.13': {} + '@tanstack/virtual-core@3.13.19': {} - '@temporalio/activity@1.14.0': + '@temporalio/activity@1.15.0': dependencies: - '@temporalio/client': 1.14.0 - '@temporalio/common': 1.14.0 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 abort-controller: 3.0.0 - '@temporalio/client@1.14.0': + '@temporalio/client@1.15.0': dependencies: '@grpc/grpc-js': 1.14.3 - '@temporalio/common': 1.14.0 - '@temporalio/proto': 1.14.0 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 abort-controller: 3.0.0 long: 5.3.2 uuid: 11.1.0 - '@temporalio/common@1.14.0': + '@temporalio/common@1.15.0': dependencies: - '@temporalio/proto': 1.14.0 + '@temporalio/proto': 1.15.0 long: 5.3.2 ms: 3.0.0-canary.1 nexus-rpc: 0.0.1 proto3-json-serializer: 2.0.2 - '@temporalio/core-bridge@1.14.0': + '@temporalio/core-bridge@1.15.0': dependencies: '@grpc/grpc-js': 1.14.3 - '@temporalio/common': 1.14.0 - arg: 5.0.2 - cargo-cp-artifact: 0.1.9 - which: 4.0.0 + '@temporalio/common': 1.15.0 - '@temporalio/nexus@1.14.0': + '@temporalio/nexus@1.15.0': dependencies: - '@temporalio/client': 1.14.0 - '@temporalio/common': 1.14.0 - '@temporalio/proto': 1.14.0 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 long: 5.3.2 nexus-rpc: 0.0.1 - '@temporalio/proto@1.14.0': + '@temporalio/proto@1.15.0': dependencies: long: 5.3.2 protobufjs: 7.5.4 - '@temporalio/worker@1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12)': + '@temporalio/worker@1.15.0(@swc/helpers@0.5.13)(esbuild@0.25.12)(tslib@2.8.1)': dependencies: '@grpc/grpc-js': 1.14.3 '@swc/core': 1.5.7(@swc/helpers@0.5.13) - '@temporalio/activity': 1.14.0 - '@temporalio/client': 1.14.0 - '@temporalio/common': 1.14.0 - '@temporalio/core-bridge': 1.14.0 - '@temporalio/nexus': 1.14.0 - '@temporalio/proto': 1.14.0 - '@temporalio/workflow': 1.14.0 + '@temporalio/activity': 1.15.0 + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/core-bridge': 1.15.0 + '@temporalio/nexus': 1.15.0 + '@temporalio/proto': 1.15.0 + '@temporalio/workflow': 1.15.0 abort-controller: 3.0.0 heap-js: 2.7.1 - memfs: 4.51.1 + memfs: 4.56.10(tslib@2.8.1) nexus-rpc: 0.0.1 proto3-json-serializer: 2.0.2 protobufjs: 7.5.4 rxjs: 7.8.2 source-map: 0.7.6 - source-map-loader: 4.0.2(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + source-map-loader: 4.0.2(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) supports-color: 8.1.1 - swc-loader: 0.2.6(@swc/core@1.5.7(@swc/helpers@0.5.13))(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + swc-loader: 0.2.7(@swc/core@1.5.7(@swc/helpers@0.5.13))(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) unionfs: 4.6.0 - webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) transitivePeerDependencies: - '@swc/helpers' - esbuild + - tslib - uglify-js - webpack-cli - '@temporalio/workflow@1.14.0': + '@temporalio/workflow@1.15.0': dependencies: - '@temporalio/common': 1.14.0 - '@temporalio/proto': 1.14.0 + '@temporalio/common': 1.15.0 + '@temporalio/proto': 1.15.0 nexus-rpc: 0.0.1 '@testing-library/dom@10.4.1': dependencies: - '@babel/code-frame': 7.27.1 - '@babel/runtime': 7.28.4 + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.28.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 @@ -25331,7 +25711,7 @@ snapshots: '@testing-library/react@15.0.6(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@testing-library/dom': 10.4.1 '@types/react-dom': 18.3.0 react: 18.3.1 @@ -25339,131 +25719,131 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - '@tiptap/core@3.14.0(@tiptap/pm@3.14.0)': + '@tiptap/core@3.20.0(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/pm': 3.14.0 + '@tiptap/pm': 3.20.0 - '@tiptap/extension-blockquote@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-blockquote@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-bold@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-bold@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-bubble-menu@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extension-bubble-menu@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@floating-ui/dom': 1.7.4 - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@floating-ui/dom': 1.7.5 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 optional: true - '@tiptap/extension-bullet-list@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-bullet-list@3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-list': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-code-block@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extension-code-block@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 - '@tiptap/extension-code@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-code@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-document@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-document@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-dropcursor@3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-dropcursor@3.20.0(@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extensions': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-floating-menu@3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extension-floating-menu@3.20.0(@floating-ui/dom@1.7.5)(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@floating-ui/dom': 1.7.4 - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@floating-ui/dom': 1.7.5 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 optional: true - '@tiptap/extension-gapcursor@3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-gapcursor@3.20.0(@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extensions': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-hard-break@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-hard-break@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-heading@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-heading@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-history@3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-history@3.20.0(@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extensions': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-horizontal-rule@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extension-horizontal-rule@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 - '@tiptap/extension-italic@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-italic@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-link@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extension-link@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 linkifyjs: 4.3.2 - '@tiptap/extension-list-item@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-list-item@3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-list': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-list-keymap@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-list-keymap@3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-list': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 - '@tiptap/extension-mention@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@tiptap/suggestion@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-mention@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)(@tiptap/suggestion@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 - '@tiptap/suggestion': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 + '@tiptap/suggestion': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-ordered-list@3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0))': + '@tiptap/extension-ordered-list@3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-list': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) - '@tiptap/extension-paragraph@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-paragraph@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-strike@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-strike@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-text@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-text@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extension-underline@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))': + '@tiptap/extension-underline@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) - '@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 - '@tiptap/pm@3.14.0': + '@tiptap/pm@3.20.0': dependencies: - prosemirror-changeset: 2.3.1 + prosemirror-changeset: 2.4.0 prosemirror-collab: 1.3.1 prosemirror-commands: 1.7.1 prosemirror-dropcursor: 1.8.2 @@ -25471,21 +25851,21 @@ snapshots: prosemirror-history: 1.5.0 prosemirror-inputrules: 1.5.1 prosemirror-keymap: 1.2.3 - prosemirror-markdown: 1.13.2 - prosemirror-menu: 1.2.5 + prosemirror-markdown: 1.13.4 + prosemirror-menu: 1.3.0 prosemirror-model: 1.25.4 prosemirror-schema-basic: 1.2.4 prosemirror-schema-list: 1.5.1 prosemirror-state: 1.4.4 - prosemirror-tables: 1.8.4 - prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4) - prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.4 + prosemirror-tables: 1.8.5 + prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6) + prosemirror-transform: 1.11.0 + prosemirror-view: 1.41.6 - '@tiptap/react@3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tiptap/react@3.20.0(@floating-ui/dom@1.7.5)(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)(@types/react-dom@18.3.0)(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 '@types/react': 18.3.1 '@types/react-dom': 18.3.0 '@types/use-sync-external-store': 0.0.6 @@ -25494,56 +25874,55 @@ snapshots: react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: - '@tiptap/extension-bubble-menu': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-floating-menu': 3.14.0(@floating-ui/dom@1.7.4)(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) + '@tiptap/extension-bubble-menu': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) + '@tiptap/extension-floating-menu': 3.20.0(@floating-ui/dom@1.7.5)(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) transitivePeerDependencies: - '@floating-ui/dom' - '@tiptap/starter-kit@3.14.0': + '@tiptap/starter-kit@3.20.0': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/extension-blockquote': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-bold': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-bullet-list': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) - '@tiptap/extension-code': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-code-block': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-document': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-dropcursor': 3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) - '@tiptap/extension-gapcursor': 3.14.0(@tiptap/extensions@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) - '@tiptap/extension-hard-break': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-heading': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-horizontal-rule': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-italic': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-link': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-list': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/extension-list-item': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) - '@tiptap/extension-list-keymap': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) - '@tiptap/extension-ordered-list': 3.14.0(@tiptap/extension-list@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)) - '@tiptap/extension-paragraph': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-strike': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-text': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extension-underline': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0)) - '@tiptap/extensions': 3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/extension-blockquote': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-bold': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-bullet-list': 3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) + '@tiptap/extension-code': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-code-block': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) + '@tiptap/extension-document': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-dropcursor': 3.20.0(@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) + '@tiptap/extension-gapcursor': 3.20.0(@tiptap/extensions@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) + '@tiptap/extension-hard-break': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-heading': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-horizontal-rule': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) + '@tiptap/extension-italic': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-link': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) + '@tiptap/extension-list': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) + '@tiptap/extension-list-item': 3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) + '@tiptap/extension-list-keymap': 3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) + '@tiptap/extension-ordered-list': 3.20.0(@tiptap/extension-list@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)) + '@tiptap/extension-paragraph': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-strike': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-text': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extension-underline': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0)) + '@tiptap/extensions': 3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 - '@tiptap/suggestion@3.14.0(@tiptap/core@3.14.0(@tiptap/pm@3.14.0))(@tiptap/pm@3.14.0)': + '@tiptap/suggestion@3.20.0(@tiptap/core@3.20.0(@tiptap/pm@3.20.0))(@tiptap/pm@3.20.0)': dependencies: - '@tiptap/core': 3.14.0(@tiptap/pm@3.14.0) - '@tiptap/pm': 3.14.0 + '@tiptap/core': 3.20.0(@tiptap/pm@3.20.0) + '@tiptap/pm': 3.20.0 '@tokenizer/inflate@0.2.7': dependencies: debug: 4.4.3(supports-color@5.5.0) fflate: 0.8.2 - token-types: 6.1.1 + token-types: 6.1.2 transitivePeerDependencies: - supports-color - '@tokenizer/inflate@0.3.1': + '@tokenizer/inflate@0.4.1': dependencies: debug: 4.4.3(supports-color@5.5.0) - fflate: 0.8.2 - token-types: 6.1.1 + token-types: 6.1.2 transitivePeerDependencies: - supports-color @@ -25553,17 +25932,17 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@toruslabs/base-controllers@5.11.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(utf-8-validate@5.0.10)': + '@toruslabs/base-controllers@5.11.0(@babel/runtime@7.28.6)(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@ethereumjs/util': 9.1.0 '@toruslabs/broadcast-channel': 10.0.2(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.4) - '@toruslabs/openlogin-jrpc': 8.3.0(@babel/runtime@7.28.4) - '@toruslabs/openlogin-utils': 8.2.1(@babel/runtime@7.28.4) + '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.6) + '@toruslabs/openlogin-jrpc': 8.3.0(@babel/runtime@7.28.6) + '@toruslabs/openlogin-utils': 8.2.1(@babel/runtime@7.28.6) async-mutex: 0.5.0 bignumber.js: 9.3.1 - bowser: 2.13.1 + bowser: 2.14.1 jwt-decode: 4.0.0 loglevel: 1.9.2 transitivePeerDependencies: @@ -25574,12 +25953,12 @@ snapshots: '@toruslabs/broadcast-channel@10.0.2(bufferutil@4.1.0)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@toruslabs/eccrypto': 4.0.0 - '@toruslabs/metadata-helpers': 5.1.0(@babel/runtime@7.28.4) + '@toruslabs/metadata-helpers': 5.1.0(@babel/runtime@7.28.6) loglevel: 1.9.2 oblivious-set: 1.4.0 - socket.io-client: 4.8.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + socket.io-client: 4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) unload: 2.4.1 transitivePeerDependencies: - '@sentry/types' @@ -25587,60 +25966,60 @@ snapshots: - supports-color - utf-8-validate - '@toruslabs/constants@13.4.0(@babel/runtime@7.28.4)': + '@toruslabs/constants@13.4.0(@babel/runtime@7.28.6)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@toruslabs/eccrypto@4.0.0': dependencies: elliptic: 6.6.1 - '@toruslabs/http-helpers@6.1.1(@babel/runtime@7.28.4)': + '@toruslabs/http-helpers@6.1.1(@babel/runtime@7.28.6)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 lodash.merge: 4.6.2 loglevel: 1.9.2 - '@toruslabs/metadata-helpers@5.1.0(@babel/runtime@7.28.4)': + '@toruslabs/metadata-helpers@5.1.0(@babel/runtime@7.28.6)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@toruslabs/eccrypto': 4.0.0 - '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.4) + '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.6) elliptic: 6.6.1 ethereum-cryptography: 2.2.1 json-stable-stringify: 1.3.0 transitivePeerDependencies: - '@sentry/types' - '@toruslabs/openlogin-jrpc@8.3.0(@babel/runtime@7.28.4)': + '@toruslabs/openlogin-jrpc@8.3.0(@babel/runtime@7.28.6)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 end-of-stream: 1.4.5 events: 3.3.0 fast-safe-stringify: 2.1.1 once: 1.4.0 - pump: 3.0.3 + pump: 3.0.4 readable-stream: 4.7.0 - '@toruslabs/openlogin-utils@8.2.1(@babel/runtime@7.28.4)': + '@toruslabs/openlogin-utils@8.2.1(@babel/runtime@7.28.6)': dependencies: - '@babel/runtime': 7.28.4 - '@toruslabs/constants': 13.4.0(@babel/runtime@7.28.4) + '@babel/runtime': 7.28.6 + '@toruslabs/constants': 13.4.0(@babel/runtime@7.28.6) base64url: 3.0.1 color: 4.2.3 - '@toruslabs/solana-embed@2.1.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': + '@toruslabs/solana-embed@2.1.0(@babel/runtime@7.28.6)(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@toruslabs/base-controllers': 5.11.0(@babel/runtime@7.28.4)(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.4) - '@toruslabs/openlogin-jrpc': 8.3.0(@babel/runtime@7.28.4) + '@toruslabs/base-controllers': 5.11.0(@babel/runtime@7.28.6)(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@toruslabs/http-helpers': 6.1.1(@babel/runtime@7.28.6) + '@toruslabs/openlogin-jrpc': 8.3.0(@babel/runtime@7.28.6) eth-rpc-errors: 4.0.3 fast-deep-equal: 3.1.3 - lodash-es: 4.17.22 + lodash-es: 4.17.23 loglevel: 1.9.2 - pump: 3.0.3 + pump: 3.0.4 transitivePeerDependencies: - '@sentry/types' - bufferutil @@ -25672,24 +26051,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.5 + '@babel/types': 7.29.0 '@types/bcrypt@5.0.2': dependencies: @@ -25711,11 +26090,11 @@ snapshots: '@types/cache-manager@5.0.0': dependencies: - cache-manager: 7.2.7 + cache-manager: 7.2.8 '@types/cacheable-request@6.0.3': dependencies: - '@types/http-cache-semantics': 4.0.4 + '@types/http-cache-semantics': 4.2.0 '@types/keyv': 3.1.4 '@types/node': 18.16.9 '@types/responselike': 1.0.3 @@ -25754,6 +26133,12 @@ snapshots: '@types/diff-match-patch@1.0.36': {} + '@types/dom-mediacapture-transform@0.1.11': + dependencies: + '@types/dom-webcodecs': 0.1.13 + + '@types/dom-webcodecs@0.1.13': {} + '@types/eslint-scope@3.7.7': dependencies: '@types/eslint': 9.6.1 @@ -25770,14 +26155,14 @@ snapshots: '@types/estree@1.0.8': {} - '@types/express-serve-static-core@4.19.7': + '@types/express-serve-static-core@4.19.8': dependencies: '@types/node': 18.16.9 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 - '@types/express-serve-static-core@5.1.0': + '@types/express-serve-static-core@5.1.1': dependencies: '@types/node': 18.16.9 '@types/qs': 6.14.0 @@ -25787,14 +26172,14 @@ snapshots: '@types/express@4.17.25': dependencies: '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 4.19.7 + '@types/express-serve-static-core': 4.19.8 '@types/qs': 6.14.0 '@types/serve-static': 1.15.10 '@types/express@5.0.6': dependencies: '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.1.0 + '@types/express-serve-static-core': 5.1.1 '@types/serve-static': 2.2.0 '@types/facebook-nodejs-business-sdk@20.0.3': {} @@ -25819,7 +26204,7 @@ snapshots: dependencies: '@types/unist': 3.0.3 - '@types/http-cache-semantics@4.0.4': {} + '@types/http-cache-semantics@4.2.0': {} '@types/http-errors@2.0.5': {} @@ -25855,7 +26240,7 @@ snapshots: '@types/ms': 2.1.0 '@types/node': 18.16.9 - '@types/katex@0.16.7': {} + '@types/katex@0.16.8': {} '@types/keyv@3.1.4': dependencies: @@ -25863,7 +26248,7 @@ snapshots: '@types/linkify-it@5.0.0': {} - '@types/lodash@4.17.21': {} + '@types/lodash@4.17.24': {} '@types/luxon@3.4.2': {} @@ -25904,6 +26289,10 @@ snapshots: dependencies: '@types/node': 18.16.9 + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 18.16.9 + '@types/mysql@2.15.27': dependencies: '@types/node': 18.16.9 @@ -25913,7 +26302,7 @@ snapshots: '@types/node': 18.16.9 form-data: 4.0.5 - '@types/node-telegram-bot-api@0.64.13': + '@types/node-telegram-bot-api@0.64.14': dependencies: '@types/node': 18.16.9 '@types/request': 2.48.13 @@ -25926,12 +26315,13 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/nodemailer@6.4.21': + '@types/node@20.19.35': + dependencies: + undici-types: 6.21.0 + + '@types/nodemailer@6.4.23': dependencies: - '@aws-sdk/client-ses': 3.956.0 '@types/node': 18.16.9 - transitivePeerDependencies: - - aws-crt '@types/oracledb@6.5.2': dependencies: @@ -25945,19 +26335,23 @@ snapshots: dependencies: '@types/pg': 8.15.5 + '@types/pg-pool@2.0.7': + dependencies: + '@types/pg': 8.15.6 + '@types/pg@8.15.5': dependencies: '@types/node': 18.16.9 - pg-protocol: 1.10.3 + pg-protocol: 1.12.0 pg-types: 2.2.0 '@types/pg@8.15.6': dependencies: '@types/node': 18.16.9 - pg-protocol: 1.10.3 + pg-protocol: 1.12.0 pg-types: 2.2.0 - '@types/prismjs@1.26.5': {} + '@types/prismjs@1.26.6': {} '@types/prop-types@15.7.15': {} @@ -26071,7 +26465,9 @@ snapshots: '@types/validator@13.15.10': {} - '@types/webextension-polyfill@0.12.4': {} + '@types/webextension-polyfill@0.12.5': {} + + '@types/wrap-ansi@3.0.0': {} '@types/ws@7.4.7': dependencies: @@ -26148,8 +26544,8 @@ snapshots: debug: 4.4.3(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.7.3 + minimatch: 9.0.9 + semver: 7.7.4 ts-api-utils: 1.4.3(typescript@5.5.4) optionalDependencies: typescript: 5.5.4 @@ -26158,7 +26554,7 @@ snapshots: '@typescript-eslint/utils@7.18.0(eslint@8.57.0)(typescript@5.5.4)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.0) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.5.4) @@ -26174,14 +26570,14 @@ snapshots: '@ucast/core@1.10.2': {} - '@ucast/js@3.0.4': + '@ucast/js@3.1.0': dependencies: '@ucast/core': 1.10.2 - '@ucast/mongo2js@1.4.0': + '@ucast/mongo2js@1.4.1': dependencies: '@ucast/core': 1.10.2 - '@ucast/js': 3.0.4 + '@ucast/js': 3.1.0 '@ucast/mongo': 2.4.3 '@ucast/mongo@2.4.3': @@ -26193,12 +26589,12 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@uiw/copy-to-clipboard@1.0.19': {} + '@uiw/copy-to-clipboard@1.0.20': {} '@uiw/react-markdown-preview@5.1.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 - '@uiw/copy-to-clipboard': 1.0.19 + '@babel/runtime': 7.28.6 + '@uiw/copy-to-clipboard': 1.0.20 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-markdown: 9.0.3(@types/react@18.3.1)(react@18.3.1) @@ -26211,19 +26607,19 @@ snapshots: rehype-slug: 6.0.0 remark-gfm: 4.0.1 remark-github-blockquote-alert: 1.3.1 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 transitivePeerDependencies: - '@types/react' - supports-color '@uiw/react-md-editor@4.0.11(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 '@uiw/react-markdown-preview': 5.1.5(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) rehype: 13.0.2 - rehype-prism-plus: 2.0.1 + rehype-prism-plus: 2.0.2 transitivePeerDependencies: - '@types/react' - supports-color @@ -26293,7 +26689,7 @@ snapshots: dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/aws-s3@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26307,7 +26703,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/companion-client@4.5.2(@uppy/core@4.5.3)': dependencies: @@ -26326,7 +26722,7 @@ snapshots: '@uppy/webcam': 4.3.2(@uppy/core@4.5.3) clsx: 2.1.1 dequal: 2.0.3 - preact: 10.28.0 + preact: 10.28.4 pretty-bytes: 6.1.1 '@uppy/compressor@2.3.2(@uppy/core@4.5.3)': @@ -26335,7 +26731,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 compressorjs: 1.2.1 - preact: 10.28.0 + preact: 10.28.4 promise-queue: 2.2.5 '@uppy/core@4.5.3': @@ -26343,11 +26739,11 @@ snapshots: '@transloadit/prettier-bytes': 0.3.5 '@uppy/store-default': 4.3.2 '@uppy/utils': 6.2.2 - lodash: 4.17.21 + lodash: 4.17.23 mime-match: 1.0.2 namespace-emitter: 2.0.1 nanoid: 5.1.6 - preact: 10.28.0 + preact: 10.28.4 '@uppy/dashboard@4.4.3(@uppy/core@4.5.3)': dependencies: @@ -26359,16 +26755,16 @@ snapshots: '@uppy/thumbnail-generator': 4.2.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 classnames: 2.5.1 - lodash: 4.17.21 + lodash: 4.17.23 nanoid: 5.1.6 - preact: 10.28.0 + preact: 10.28.4 shallow-equal: 3.1.0 '@uppy/drag-drop@4.2.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/dropbox@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26376,7 +26772,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/facebook@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26384,13 +26780,13 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/file-input@4.2.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/google-drive@4.4.2(@uppy/core@4.5.3)': dependencies: @@ -26398,20 +26794,20 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/image-editor@3.4.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 cropperjs: 1.6.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/informer@4.3.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/instagram@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26419,7 +26815,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/onedrive@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26427,13 +26823,13 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/progress-bar@4.3.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/provider-views@4.5.3(@uppy/core@4.5.3)': dependencies: @@ -26442,14 +26838,14 @@ snapshots: classnames: 2.5.1 nanoid: 5.1.6 p-queue: 8.1.1 - preact: 10.28.0 + preact: 10.28.4 '@uppy/react@4.5.2(@uppy/core@4.5.3)(@uppy/dashboard@4.4.3(@uppy/core@4.5.3))(@uppy/drag-drop@4.2.2(@uppy/core@4.5.3))(@uppy/file-input@4.2.2(@uppy/core@4.5.3))(@uppy/progress-bar@4.3.2(@uppy/core@4.5.3))(@uppy/screen-capture@4.4.2(@uppy/core@4.5.3))(@uppy/status-bar@4.2.3(@uppy/core@4.5.3))(@uppy/webcam@4.3.2(@uppy/core@4.5.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@uppy/components': 0.3.2 '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) use-sync-external-store: 1.6.0(react@18.3.1) @@ -26480,7 +26876,7 @@ snapshots: dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/status-bar@4.2.3(@uppy/core@4.5.3)': dependencies: @@ -26488,7 +26884,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 classnames: 2.5.1 - preact: 10.28.0 + preact: 10.28.4 '@uppy/store-default@4.3.2': {} @@ -26520,7 +26916,7 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 '@uppy/url@4.3.2(@uppy/core@4.5.3)': dependencies: @@ -26528,19 +26924,19 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 nanoid: 5.1.6 - preact: 10.28.0 + preact: 10.28.4 '@uppy/utils@6.2.2': dependencies: - lodash: 4.17.21 - preact: 10.28.0 + lodash: 4.17.23 + preact: 10.28.4 '@uppy/webcam@4.3.2(@uppy/core@4.5.3)': dependencies: '@uppy/core': 4.5.3 '@uppy/utils': 6.2.2 is-mobile: 4.0.0 - preact: 10.28.0 + preact: 10.28.4 '@uppy/xhr-upload@4.4.2(@uppy/core@4.5.3)': dependencies: @@ -26554,30 +26950,30 @@ snapshots: '@uppy/core': 4.5.3 '@uppy/provider-views': 4.5.3(@uppy/core@4.5.3) '@uppy/utils': 6.2.2 - preact: 10.28.0 + preact: 10.28.4 - '@upstash/redis@1.35.8': + '@upstash/redis@1.36.3': dependencies: uncrypto: 0.1.3 - '@urql/core@5.2.0(graphql@16.12.0)': + '@urql/core@5.2.0(graphql@16.13.0)': dependencies: - '@0no-co/graphql.web': 1.2.0(graphql@16.12.0) + '@0no-co/graphql.web': 1.2.0(graphql@16.13.0) wonka: 6.3.5 transitivePeerDependencies: - graphql - '@vercel/oidc@3.0.5': {} + '@vercel/oidc@3.2.0': {} - '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2))': + '@vitejs/plugin-react@4.7.0(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))': dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -26596,7 +26992,7 @@ snapshots: std-env: 3.10.0 strip-literal: 2.1.1 test-exclude: 6.0.0 - vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color @@ -26607,13 +27003,13 @@ snapshots: chai: 5.3.3 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2))': + '@vitest/mocker@3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 3.1.4 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) '@vitest/pretty-format@3.1.4': dependencies: @@ -26647,7 +27043,7 @@ snapshots: pathe: 1.1.2 picocolors: 1.1.1 sirv: 2.0.4 - vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vitest: 3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) '@vitest/utils@1.6.0': dependencies: @@ -26689,21 +27085,21 @@ snapshots: dependencies: '@wallet-standard/base': 1.1.0 - '@walletconnect/core@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 events: 3.3.0 lodash.isequal: 4.5.0 @@ -26733,21 +27129,21 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -26828,13 +27224,13 @@ snapshots: - bufferutil - utf-8-validate - '@walletconnect/keyvaluestorage@1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2)': + '@walletconnect/keyvaluestorage@1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0)': dependencies: '@walletconnect/safe-json': 1.0.2 idb-keyval: 6.2.2 - unstorage: 1.17.3(@upstash/redis@1.35.8)(idb-keyval@6.2.2)(ioredis@5.8.2) + unstorage: 1.17.4(@upstash/redis@1.36.3)(idb-keyval@6.2.2)(ioredis@5.10.0) optionalDependencies: - '@react-native-async-storage/async-storage': 1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)) + '@react-native-async-storage/async-storage': 1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -26876,16 +27272,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -26912,16 +27308,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -26948,13 +27344,13 @@ snapshots: - utf-8-validate - zod - '@walletconnect/solana-adapter@0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/solana-adapter@0.0.8(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@solana/wallet-adapter-base@0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: - '@reown/appkit': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.7.2(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@types/react@18.3.1)(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(react@18.3.1)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) '@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)) '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -26988,12 +27384,12 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/types@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2)': + '@walletconnect/types@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -27017,12 +27413,12 @@ snapshots: - ioredis - uploadthing - '@walletconnect/types@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2)': + '@walletconnect/types@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -27046,18 +27442,18 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/utils': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 lodash: 4.17.21 transitivePeerDependencies: @@ -27086,18 +27482,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) - '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) + '@walletconnect/utils': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -27126,18 +27522,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/types': 2.19.0(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 detect-browser: 5.3.0 @@ -27170,18 +27566,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(bufferutil@4.1.0)(ioredis@5.8.2)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(bufferutil@4.1.0)(ioredis@5.10.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/keyvaluestorage': 1.1.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.35.8)(ioredis@5.8.2) + '@walletconnect/types': 2.19.1(@react-native-async-storage/async-storage@1.24.0(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10)))(@upstash/redis@1.36.3)(ioredis@5.10.0) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 bs58: 6.0.0 @@ -27315,10 +27711,10 @@ snapshots: '@whatwg-node/fetch@0.10.13': dependencies: - '@whatwg-node/node-fetch': 0.8.4 + '@whatwg-node/node-fetch': 0.8.5 urlpattern-polyfill: 10.1.0 - '@whatwg-node/node-fetch@0.8.4': + '@whatwg-node/node-fetch@0.8.5': dependencies: '@fastify/busboy': 3.2.0 '@whatwg-node/disposablestack': 0.0.6 @@ -27329,7 +27725,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@whatwg-node/server@0.10.17': + '@whatwg-node/server@0.10.18': dependencies: '@envelop/instrumentation': 1.0.0 '@whatwg-node/disposablestack': 0.0.6 @@ -27339,7 +27735,7 @@ snapshots: '@wyw-in-js/processor-utils@0.5.5': dependencies: - '@babel/generator': 7.28.5 + '@babel/generator': 7.29.1 '@wyw-in-js/shared': 0.5.5 transitivePeerDependencies: - supports-color @@ -27348,22 +27744,22 @@ snapshots: dependencies: debug: 4.4.3(supports-color@5.5.0) find-up: 5.0.0 - minimatch: 9.0.5 + minimatch: 9.0.9 transitivePeerDependencies: - supports-color '@wyw-in-js/transform@0.5.5(typescript@5.5.4)': dependencies: - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 - '@babel/helper-module-imports': 7.27.1 - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-module-imports': 7.28.6 + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 '@wyw-in-js/processor-utils': 0.5.5 '@wyw-in-js/shared': 0.5.5 - babel-merge: 3.0.0(@babel/core@7.28.5) + babel-merge: 3.0.0(@babel/core@7.29.0) cosmiconfig: 8.3.6(typescript@5.5.4) happy-dom: 15.11.7 source-map: 0.7.6 @@ -27422,30 +27818,30 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.15.0 - acorn-walk: 8.3.4 + acorn: 8.16.0 + acorn-walk: 8.3.5 - acorn-import-assertions@1.9.0(acorn@8.15.0): + acorn-import-assertions@1.9.0(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn-import-attributes@1.9.5(acorn@8.15.0): + acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn-import-phases@1.0.4(acorn@8.15.0): + acorn-import-phases@1.0.4(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn-jsx@5.3.2(acorn@8.15.0): + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn-walk@8.3.4: + acorn-walk@8.3.5: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 - acorn@8.15.0: {} + acorn@8.16.0: {} agent-base@6.0.2: dependencies: @@ -27488,24 +27884,24 @@ snapshots: optionalDependencies: ajv: 8.12.0 - ajv-formats@2.1.1(ajv@8.17.1): + ajv-formats@2.1.1(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-formats@3.0.1(ajv@8.17.1): + ajv-formats@3.0.1(ajv@8.18.0): optionalDependencies: - ajv: 8.17.1 + ajv: 8.18.0 - ajv-keywords@3.5.2(ajv@6.12.6): + ajv-keywords@3.5.2(ajv@6.14.0): dependencies: - ajv: 6.12.6 + ajv: 6.14.0 - ajv-keywords@5.1.0(ajv@8.17.1): + ajv-keywords@5.1.0(ajv@8.18.0): dependencies: - ajv: 8.17.1 + ajv: 8.18.0 fast-deep-equal: 3.1.3 - ajv@6.12.6: + ajv@6.14.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -27519,7 +27915,7 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 - ajv@8.17.1: + ajv@8.18.0: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.1.0 @@ -27676,7 +28072,7 @@ snapshots: asn1.js@4.10.1: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 inherits: 2.0.4 minimalistic-assert: 1.0.1 @@ -27718,10 +28114,10 @@ snapshots: attr-accept@2.2.5: {} - autoprefixer@10.4.23(postcss@8.4.38): + autoprefixer@10.4.27(postcss@8.4.38): dependencies: browserslist: 4.28.1 - caniuse-lite: 1.0.30001761 + caniuse-lite: 1.0.30001775 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.4.38 @@ -27737,9 +28133,17 @@ snapshots: aws4@1.13.2: {} - axe-core@4.11.0: {} + axe-core@4.11.1: {} - axios@1.13.2(debug@4.4.3): + axios@1.13.5: + dependencies: + follow-redirects: 1.15.11(debug@4.4.3) + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axios@1.13.6(debug@4.4.3): dependencies: follow-redirects: 1.15.11(debug@4.4.3) form-data: 4.0.5 @@ -27751,28 +28155,28 @@ snapshots: axobject-query@4.1.0: {} - babel-jest@29.7.0(@babel/core@7.28.5): + babel-jest@29.7.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.28.5) + babel-preset-jest: 29.6.3(@babel/core@7.29.0) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - babel-merge@3.0.0(@babel/core@7.28.5): + babel-merge@3.0.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 deepmerge: 2.2.1 object.omit: 3.0.0 babel-plugin-istanbul@6.1.1: dependencies: - '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-plugin-utils': 7.28.6 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 5.2.1 @@ -27782,38 +28186,38 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: - '@babel/template': 7.27.2 - '@babel/types': 7.28.5 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.28.0 babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 cosmiconfig: 7.1.0 resolve: 1.22.11 - babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): + babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.29.0): dependencies: - '@babel/compat-data': 7.28.5 - '@babel/core': 7.28.5 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + '@babel/compat-data': 7.29.0 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.29.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): + babel-plugin-polyfill-corejs3@0.14.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) - core-js-compat: 3.47.0 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.29.0) + core-js-compat: 3.48.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.5): + babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color @@ -27821,35 +28225,37 @@ snapshots: dependencies: hermes-parser: 0.32.0 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.5): + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.5) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.5) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.5) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.5) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.5) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.5) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.5) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.5) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.5) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.5) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.5) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.0) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.0) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.0) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.0) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.0) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.0) - babel-preset-jest@29.6.3(@babel/core@7.28.5): + babel-preset-jest@29.6.3(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) bail@2.0.2: {} balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base-x@3.0.11: dependencies: safe-buffer: 5.2.1 @@ -27864,9 +28270,9 @@ snapshots: base64url@3.0.1: {} - baseline-browser-mapping@2.9.11: {} + baseline-browser-mapping@2.10.0: {} - basic-ftp@5.0.5: {} + basic-ftp@5.2.0: {} bcp-47-match@2.0.3: {} @@ -27898,7 +28304,7 @@ snapshots: bin-version-check@5.1.0: dependencies: bin-version: 6.0.0 - semver: 7.7.3 + semver: 7.7.4 semver-truncate: 3.0.0 bin-version@6.0.0: @@ -27923,26 +28329,9 @@ snapshots: blueimp-canvas-to-blob@3.29.0: {} - bn.js@4.12.2: {} + bn.js@4.12.3: {} - bn.js@5.2.2: {} - - body-parser@1.20.3: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.13.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color + bn.js@5.2.3: {} body-parser@1.20.4: dependencies: @@ -27954,22 +28343,22 @@ snapshots: http-errors: 2.0.1 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.14.0 + qs: 6.14.2 raw-body: 2.5.3 type-is: 1.6.18 unpipe: 1.0.0 transitivePeerDependencies: - supports-color - body-parser@2.2.1: + body-parser@2.2.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3(supports-color@5.5.0) http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 on-finished: 2.4.1 - qs: 6.14.0 + qs: 6.15.0 raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: @@ -27979,13 +28368,13 @@ snapshots: borsh@0.7.0: dependencies: - bn.js: 5.2.2 + bn.js: 5.2.3 bs58: 4.0.1 text-encoding-utf-8: 1.0.2 bottleneck@2.19.5: {} - bowser@2.13.1: {} + bowser@2.14.1: {} brace-expansion@1.1.12: dependencies: @@ -27996,6 +28385,10 @@ snapshots: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -28030,13 +28423,13 @@ snapshots: browserify-rsa@4.1.1: dependencies: - bn.js: 5.2.2 + bn.js: 5.2.3 randombytes: 2.1.0 safe-buffer: 5.2.1 browserify-sign@4.2.5: dependencies: - bn.js: 5.2.2 + bn.js: 5.2.3 browserify-rsa: 4.1.1 create-hash: 1.2.0 create-hmac: 1.1.7 @@ -28048,9 +28441,9 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001761 - electron-to-chromium: 1.5.267 + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001775 + electron-to-chromium: 1.5.302 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -28102,15 +28495,15 @@ snapshots: builtins@5.1.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 bundle-name@4.1.0: dependencies: run-applescript: 7.1.0 - bundle-require@5.1.0(esbuild@0.27.2): + bundle-require@5.1.0(esbuild@0.27.3): dependencies: - esbuild: 0.27.2 + esbuild: 0.27.3 load-tsconfig: 0.2.5 busboy@1.6.0: @@ -28121,10 +28514,10 @@ snapshots: cac@6.7.14: {} - cache-manager@7.2.7: + cache-manager@7.2.8: dependencies: - '@cacheable/utils': 2.3.2 - keyv: 5.5.5 + '@cacheable/utils': 2.4.0 + keyv: 5.6.0 cacheable-lookup@5.0.4: {} @@ -28168,12 +28561,12 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001761: {} + caniuse-lite@1.0.30001775: {} canvas@2.11.2: dependencies: '@mapbox/node-pre-gyp': 1.0.11 - nan: 2.24.0 + nan: 2.25.0 simple-get: 3.1.1 transitivePeerDependencies: - encoding @@ -28185,8 +28578,6 @@ snapshots: tslib: 2.8.1 upper-case-first: 2.0.2 - cargo-cp-artifact@0.1.9: {} - caseless@0.12.0: {} cbor-sync@1.0.4: {} @@ -28196,7 +28587,7 @@ snapshots: chai@5.3.3: dependencies: assertion-error: 2.0.1 - check-error: 2.1.1 + check-error: 2.1.3 deep-eql: 5.0.2 loupe: 3.2.1 pathval: 2.0.1 @@ -28241,15 +28632,13 @@ snapshots: chardet@0.7.0: {} - chardet@2.1.1: {} - charenc@0.0.2: {} chart.js@4.5.1: dependencies: '@kurkle/color': 0.3.4 - check-error@2.1.1: {} + check-error@2.1.3: {} cheerio-select@2.1.0: dependencies: @@ -28260,18 +28649,18 @@ snapshots: domhandler: 5.0.3 domutils: 3.2.2 - cheerio@1.1.2: + cheerio@1.2.0: dependencies: cheerio-select: 2.1.0 dom-serializer: 2.0.0 domhandler: 5.0.3 domutils: 3.2.2 encoding-sniffer: 0.2.1 - htmlparser2: 10.0.0 + htmlparser2: 10.1.0 parse5: 7.3.0 parse5-htmlparser2-tree-adapter: 7.1.0 parse5-parser-stream: 7.1.2 - undici: 7.16.0 + undici: 7.22.0 whatwg-mimetype: 4.0.0 chokidar@3.5.3: @@ -28302,6 +28691,10 @@ snapshots: dependencies: readdirp: 4.1.2 + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + chownr@2.0.0: {} chrome-launcher@0.15.2: @@ -28338,22 +28731,24 @@ snapshots: cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.0: {} + class-transformer@0.5.1: {} - class-validator-jsonschema@5.1.0(class-transformer@0.5.1)(class-validator@0.14.3): + class-validator-jsonschema@5.1.0(class-transformer@0.5.1)(class-validator@0.14.4): dependencies: class-transformer: 0.5.1 - class-validator: 0.14.3 + class-validator: 0.14.4 lodash.groupby: 4.6.0 lodash.merge: 4.6.2 openapi3-ts: 3.2.0 reflect-metadata: 0.2.2 tslib: 2.8.1 - class-validator@0.14.3: + class-validator@0.14.4: dependencies: '@types/validator': 13.15.10 - libphonenumber-js: 1.12.33 + libphonenumber-js: 1.12.38 validator: 13.15.26 class-variance-authority@0.6.1: @@ -28380,6 +28775,8 @@ snapshots: cli-width@3.0.0: {} + cli-width@4.1.0: {} + client-only@0.0.1: {} cliui@6.0.0: @@ -28463,7 +28860,7 @@ snapshots: commander@14.0.1: {} - commander@14.0.2: {} + commander@14.0.3: {} commander@2.20.3: {} @@ -28537,7 +28934,7 @@ snapshots: confbox@0.1.8: {} - confbox@0.2.2: {} + confbox@0.2.4: {} config-chain@1.1.13: dependencies: @@ -28602,23 +28999,21 @@ snapshots: cookie-signature@1.2.2: {} - cookie@0.7.1: {} - cookie@0.7.2: {} copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 - core-js-compat@3.47.0: + core-js-compat@3.48.0: dependencies: browserslist: 4.28.1 - core-js-pure@3.47.0: {} + core-js-pure@3.48.0: {} core-js@2.6.12: {} - core-js@3.47.0: {} + core-js@3.48.0: {} core-util-is@1.0.2: {} @@ -28629,6 +29024,11 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -28652,7 +29052,7 @@ snapshots: create-ecdh@4.0.4: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 elliptic: 6.6.1 create-hash@1.2.0: @@ -28867,10 +29267,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.7: - dependencies: - ms: 2.1.3 - debug@4.4.3(supports-color@5.5.0): dependencies: ms: 2.1.3 @@ -28881,7 +29277,7 @@ snapshots: decimal.js@10.6.0: {} - decode-named-character-reference@1.2.0: + decode-named-character-reference@1.3.0: dependencies: character-entities: 2.0.2 @@ -28929,7 +29325,7 @@ snapshots: side-channel: 1.1.0 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.19 + which-typed-array: 1.1.20 deep-is@0.1.4: {} @@ -28939,7 +29335,7 @@ snapshots: default-browser-id@5.0.1: {} - default-browser@5.4.0: + default-browser@5.5.0: dependencies: bundle-name: 4.1.0 default-browser-id: 5.0.1 @@ -28999,9 +29395,6 @@ snapshots: detect-browser@5.3.0: {} - detect-libc@1.0.3: - optional: true - detect-libc@2.1.2: {} detect-newline@3.1.0: {} @@ -29022,13 +29415,13 @@ snapshots: diff-sequences@29.6.3: {} - diff@4.0.2: {} + diff@4.0.4: {} - diff@5.2.0: {} + diff@5.2.2: {} diffie-hellman@5.0.3: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 miller-rabin: 4.0.1 randombytes: 2.1.0 @@ -29062,7 +29455,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 csstype: 3.2.3 dom-serializer@2.0.0: @@ -29081,6 +29474,10 @@ snapshots: dependencies: domelementtype: 2.3.0 + dompurify@3.3.1: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -29142,20 +29539,20 @@ snapshots: dependencies: safe-buffer: 5.2.1 - editorconfig@1.0.4: + editorconfig@1.0.7: dependencies: '@one-ini/wasm': 0.1.1 commander: 10.0.1 - minimatch: 9.0.1 - semver: 7.7.3 + minimatch: 9.0.9 + semver: 7.7.4 ee-first@1.1.1: {} - electron-to-chromium@1.5.267: {} + electron-to-chromium@1.5.302: {} elliptic@6.6.1: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 brorand: 1.1.0 hash.js: 1.1.7 hmac-drbg: 1.0.1 @@ -29165,7 +29562,7 @@ snapshots: emittery@0.13.1: {} - emoji-picker-react@4.16.1(react@18.3.1): + emoji-picker-react@4.18.0(react@18.3.1): dependencies: flairup: 1.0.0 react: 18.3.1 @@ -29193,12 +29590,12 @@ snapshots: dependencies: once: 1.4.0 - engine.io-client@6.6.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + engine.io-client@6.6.4(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 + debug: 4.4.3(supports-color@5.5.0) engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -29207,7 +29604,7 @@ snapshots: engine.io-parser@5.2.3: {} - enhanced-resolve@5.18.4: + enhanced-resolve@5.20.0: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 @@ -29218,6 +29615,8 @@ snapshots: entities@6.0.1: {} + entities@7.0.1: {} + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -29281,7 +29680,7 @@ snapshots: typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.19 + which-typed-array: 1.1.20 es-define-property@1.0.1: {} @@ -29353,10 +29752,10 @@ snapshots: dependencies: es6-promise: 4.2.8 - esbuild-register@3.6.0(esbuild@0.27.2): + esbuild-register@3.6.0(esbuild@0.27.3): dependencies: debug: 4.4.3(supports-color@5.5.0) - esbuild: 0.27.2 + esbuild: 0.27.3 transitivePeerDependencies: - supports-color @@ -29389,34 +29788,34 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 - esbuild@0.27.2: + esbuild@0.27.3: optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 escalade@3.2.0: {} @@ -29441,7 +29840,7 @@ snapshots: eslint-config-next@15.2.1(eslint@8.57.0)(typescript@5.5.4): dependencies: '@next/eslint-plugin-next': 15.2.1 - '@rushstack/eslint-patch': 1.15.0 + '@rushstack/eslint-patch': 1.16.1 '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/parser': 7.18.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 @@ -29475,7 +29874,7 @@ snapshots: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3(supports-color@5.5.0) eslint: 8.57.0 - get-tsconfig: 4.13.0 + get-tsconfig: 4.13.6 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.15 @@ -29509,7 +29908,7 @@ snapshots: has: 1.0.4 is-core-module: 2.16.1 is-glob: 4.0.3 - minimatch: 3.1.2 + minimatch: 3.1.5 object.values: 1.2.1 resolve: 1.22.11 semver: 6.3.1 @@ -29536,7 +29935,7 @@ snapshots: hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 - minimatch: 3.1.2 + minimatch: 3.1.5 object.fromentries: 2.0.8 object.groupby: 1.0.3 object.values: 1.2.1 @@ -29556,7 +29955,7 @@ snapshots: array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.11.0 + axe-core: 4.11.1 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -29564,19 +29963,19 @@ snapshots: hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 - minimatch: 3.1.2 + minimatch: 3.1.5 object.fromentries: 2.0.8 safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 eslint-plugin-jsx-a11y@6.7.1(eslint@8.57.0): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 aria-query: 5.3.2 array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.7 - axe-core: 4.11.0 + axe-core: 4.11.1 axobject-query: 3.2.4 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -29584,7 +29983,7 @@ snapshots: has: 1.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.5 - minimatch: 3.1.2 + minimatch: 3.1.5 object.entries: 1.1.9 object.fromentries: 2.0.8 semver: 6.3.1 @@ -29606,13 +30005,13 @@ snapshots: eslint: 8.57.0 estraverse: 5.3.0 jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 + minimatch: 3.1.5 object.entries: 1.1.9 object.fromentries: 2.0.8 object.hasown: 1.1.4 object.values: 1.2.1 prop-types: 15.8.1 - resolve: 2.0.0-next.5 + resolve: 2.0.0-next.6 semver: 6.3.1 string.prototype.matchall: 4.0.12 @@ -29628,12 +30027,12 @@ snapshots: estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 + minimatch: 3.1.5 object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 prop-types: 15.8.1 - resolve: 2.0.0-next.5 + resolve: 2.0.0-next.6 semver: 6.3.1 string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 @@ -29652,7 +30051,7 @@ snapshots: eslint@8.57.0: dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@8.57.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.0) '@eslint-community/regexpp': 4.12.2 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.0 @@ -29660,7 +30059,7 @@ snapshots: '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.3.0 - ajv: 6.12.6 + ajv: 6.14.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3(supports-color@5.5.0) @@ -29669,7 +30068,7 @@ snapshots: eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 - esquery: 1.6.0 + esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 6.0.1 @@ -29685,7 +30084,7 @@ snapshots: json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 - minimatch: 3.1.2 + minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 strip-ansi: 6.0.1 @@ -29695,13 +30094,13 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.15.0 - acorn-jsx: 5.3.2(acorn@8.15.0) + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) eslint-visitor-keys: 3.4.3 esprima@4.0.1: {} - esquery@1.6.0: + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -29748,6 +30147,8 @@ snapshots: eventemitter3@5.0.1: {} + eventemitter3@5.0.4: {} + events@3.3.0: {} eventsource-parser@3.0.6: {} @@ -29834,45 +30235,10 @@ snapshots: exponential-backoff@3.1.3: {} - express-rate-limit@7.5.1(express@5.2.1): + express-rate-limit@8.2.1(express@5.2.1): dependencies: express: 5.2.1 - - express@4.21.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.3 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.1 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.1 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.12 - proxy-addr: 2.0.7 - qs: 6.13.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.0 - serve-static: 1.16.2 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color + ip-address: 10.0.1 express@4.22.1: dependencies: @@ -29897,7 +30263,7 @@ snapshots: parseurl: 1.3.3 path-to-regexp: 0.1.12 proxy-addr: 2.0.7 - qs: 6.14.0 + qs: 6.14.2 range-parser: 1.2.1 safe-buffer: 5.2.1 send: 0.19.2 @@ -29913,7 +30279,7 @@ snapshots: express@5.2.1: dependencies: accepts: 2.0.0 - body-parser: 2.2.1 + body-parser: 2.2.2 content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 @@ -29932,7 +30298,7 @@ snapshots: once: 1.4.0 parseurl: 1.3.3 proxy-addr: 2.0.7 - qs: 6.14.0 + qs: 6.15.0 range-parser: 1.2.1 router: 2.2.0 send: 1.2.1 @@ -29968,7 +30334,7 @@ snapshots: facebook-nodejs-business-sdk@21.0.5: dependencies: - axios: 1.13.2(debug@4.4.3) + axios: 1.13.6(debug@4.4.3) currency-codes: 1.5.1 iso-3166-1: 2.1.1 js-sha256: 0.9.0 @@ -30020,17 +30386,17 @@ snapshots: fast-uri@3.1.0: {} - fast-xml-parser@4.5.3: + fast-xml-parser@4.5.4: dependencies: strnum: 1.1.2 - fast-xml-parser@5.2.5: + fast-xml-parser@5.3.6: dependencies: - strnum: 2.1.2 + strnum: 2.2.0 fastestsmallesttextencoderdecoder@1.0.22: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -30094,16 +30460,16 @@ snapshots: dependencies: '@tokenizer/inflate': 0.2.7 strtok3: 10.3.4 - token-types: 6.1.1 + token-types: 6.1.2 uint8array-extras: 1.5.0 transitivePeerDependencies: - supports-color - file-type@21.1.0: + file-type@21.3.0: dependencies: - '@tokenizer/inflate': 0.3.1 + '@tokenizer/inflate': 0.4.1 strtok3: 10.3.4 - token-types: 6.1.1 + token-types: 6.1.2 uint8array-extras: 1.5.0 transitivePeerDependencies: - supports-color @@ -30136,18 +30502,6 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@1.3.1: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - finalhandler@1.3.2: dependencies: debug: 2.6.9 @@ -30199,7 +30553,7 @@ snapshots: dependencies: magic-string: 0.30.21 mlly: 1.8.0 - rollup: 4.54.0 + rollup: 4.59.0 flairup@1.0.0: {} @@ -30244,17 +30598,17 @@ snapshots: fork-ts-checker-webpack-plugin@8.0.0(typescript@5.1.3)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.0 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 7.1.0 deepmerge: 4.3.1 fs-extra: 10.1.0 memfs: 3.5.3 - minimatch: 3.1.2 + minimatch: 3.1.5 node-abort-controller: 3.1.1 schema-utils: 3.3.0 - semver: 7.7.3 + semver: 7.7.4 tapable: 2.3.0 typescript: 5.1.3 webpack: 5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) @@ -30330,12 +30684,6 @@ snapshots: jsonfile: 6.2.0 universalify: 2.0.1 - fs-extra@11.3.2: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - fs-extra@11.3.3: dependencies: graceful-fs: 4.2.11 @@ -30459,7 +30807,7 @@ snapshots: get-stream@5.2.0: dependencies: - pump: 3.0.3 + pump: 3.0.4 get-stream@6.0.1: {} @@ -30474,13 +30822,13 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.13.0: + get-tsconfig@4.13.6: dependencies: resolve-pkg-maps: 1.0.0 get-uri@6.0.5: dependencies: - basic-ftp: 5.0.5 + basic-ftp: 5.2.0 data-uri-to-buffer: 6.0.2 debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: @@ -30514,30 +30862,30 @@ snapshots: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 + minimatch: 9.0.9 + minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@13.0.0: + glob@13.0.6: dependencies: - minimatch: 10.1.1 - minipass: 7.1.2 - path-scurry: 2.0.1 + minimatch: 10.2.4 + minipass: 7.1.3 + path-scurry: 2.0.2 glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.2 + minimatch: 3.1.5 once: 1.4.0 path-is-absolute: 1.0.1 glob@9.3.5: dependencies: fs.realpath: 1.0.0 - minimatch: 8.0.4 + minimatch: 8.0.7 minipass: 4.2.8 path-scurry: 1.11.1 @@ -30599,7 +30947,7 @@ snapshots: extend: 3.0.2 gaxios: 6.7.1 google-auth-library: 9.15.1 - qs: 6.14.0 + qs: 6.15.0 url-template: 2.0.8 uuid: 9.0.1 transitivePeerDependencies: @@ -30632,45 +30980,45 @@ snapshots: graceful-fs@4.2.11: {} - gradient-parser@1.1.1: {} + gradient-parser@1.2.0: {} graphemer@1.4.0: {} - graphql-query-complexity@0.12.0(graphql@16.12.0): + graphql-query-complexity@0.12.0(graphql@16.13.0): dependencies: - graphql: 16.12.0 + graphql: 16.13.0 lodash.get: 4.4.2 - graphql-request@6.1.0(graphql@16.12.0): + graphql-request@6.1.0(graphql@16.13.0): dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.12.0) + '@graphql-typed-document-node/core': 3.2.0(graphql@16.13.0) cross-fetch: 3.2.0 - graphql: 16.12.0 + graphql: 16.13.0 transitivePeerDependencies: - encoding - graphql-scalars@1.25.0(graphql@16.12.0): + graphql-scalars@1.25.0(graphql@16.13.0): dependencies: - graphql: 16.12.0 + graphql: 16.13.0 tslib: 2.8.1 - graphql-yoga@5.18.0(graphql@16.12.0): + graphql-yoga@5.18.0(graphql@16.13.0): dependencies: - '@envelop/core': 5.4.0 + '@envelop/core': 5.5.1 '@envelop/instrumentation': 1.0.0 - '@graphql-tools/executor': 1.5.0(graphql@16.12.0) - '@graphql-tools/schema': 10.0.30(graphql@16.12.0) - '@graphql-tools/utils': 10.11.0(graphql@16.12.0) + '@graphql-tools/executor': 1.5.1(graphql@16.13.0) + '@graphql-tools/schema': 10.0.31(graphql@16.13.0) + '@graphql-tools/utils': 10.11.0(graphql@16.13.0) '@graphql-yoga/logger': 2.0.1 '@graphql-yoga/subscription': 5.0.5 '@whatwg-node/fetch': 0.10.13 '@whatwg-node/promise-helpers': 1.3.2 - '@whatwg-node/server': 0.10.17 - graphql: 16.12.0 + '@whatwg-node/server': 0.10.18 + graphql: 16.13.0 lru-cache: 10.4.3 tslib: 2.8.1 - graphql@16.12.0: {} + graphql@16.13.0: {} groq-sdk@0.5.0: dependencies: @@ -30702,7 +31050,7 @@ snapshots: - encoding - supports-color - h3@1.15.4: + h3@1.15.5: dependencies: cookie-es: 1.2.2 crossws: 0.3.5 @@ -30711,7 +31059,7 @@ snapshots: iron-webcrypto: 1.2.1 node-mock-http: 1.0.4 radix3: 1.1.2 - ufo: 1.6.1 + ufo: 1.6.3 uncrypto: 0.1.3 handlebars@4.7.8: @@ -30733,7 +31081,7 @@ snapshots: har-validator@5.1.5: dependencies: - ajv: 6.12.6 + ajv: 6.14.0 har-schema: 2.0.0 has-bigints@1.1.0: {} @@ -30779,9 +31127,9 @@ snapshots: inherits: 2.0.4 minimalistic-assert: 1.0.1 - hashery@1.3.0: + hashery@1.5.0: dependencies: - hookified: 1.14.0 + hookified: 1.15.1 hasown@2.0.2: dependencies: @@ -30840,7 +31188,7 @@ snapshots: mdast-util-to-hast: 13.2.1 parse5: 7.3.0 unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 vfile: 6.0.3 web-namespaces: 2.0.1 zwitch: 2.0.4 @@ -30860,7 +31208,7 @@ snapshots: nth-check: 2.1.1 property-information: 7.1.0 space-separated-tokens: 2.0.2 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 zwitch: 2.0.4 hast-util-to-html@9.0.5: @@ -30950,14 +31298,20 @@ snapshots: help-me@5.0.0: {} - hermes-compiler@0.14.0: {} + hermes-compiler@250829098.0.9: {} hermes-estree@0.32.0: {} + hermes-estree@0.33.3: {} + hermes-parser@0.32.0: dependencies: hermes-estree: 0.32.0 + hermes-parser@0.33.3: + dependencies: + hermes-estree: 0.33.3 + highlight.js@10.7.3: {} highlightjs-vue@1.0.0: {} @@ -30974,21 +31328,21 @@ snapshots: dependencies: react-is: 16.13.1 - hono-openapi@0.4.8(hono@4.11.1)(openapi-types@12.1.3)(zod@3.25.76): + hono-openapi@0.4.8(hono@4.12.3)(openapi-types@12.1.3)(zod@3.25.76): dependencies: json-schema-walker: 2.0.0 openapi-types: 12.1.3 optionalDependencies: - hono: 4.11.1 + hono: 4.12.3 zod: 3.25.76 - hono@4.11.1: {} + hono@4.12.3: {} - hookified@1.14.0: {} + hookified@1.15.1: {} hot-reload-extension-vite@1.1.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) transitivePeerDependencies: - bufferutil - utf-8-validate @@ -31017,12 +31371,12 @@ snapshots: html-void-elements@3.0.0: {} - htmlparser2@10.0.0: + htmlparser2@10.1.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - entities: 6.0.1 + entities: 7.0.1 htmlparser2@8.0.2: dependencies: @@ -31033,14 +31387,6 @@ snapshots: http-cache-semantics@4.2.0: {} - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -31111,26 +31457,26 @@ snapshots: dependencies: diacritics: 1.3.0 - i18next-browser-languagedetector@8.2.0: + i18next-browser-languagedetector@8.2.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 i18next-resources-to-backend@1.2.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 - i18next@25.7.3(typescript@5.5.4): + i18next@25.8.13(typescript@5.5.4): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 optionalDependencies: typescript: 5.5.4 - ibm-cloud-sdk-core@5.4.5: + ibm-cloud-sdk-core@5.4.8: dependencies: '@types/debug': 4.1.12 '@types/node': 18.19.130 '@types/tough-cookie': 4.0.5 - axios: 1.13.2(debug@4.4.3) + axios: 1.13.6(debug@4.4.3) camelcase: 6.3.0 debug: 4.4.3(supports-color@5.5.0) dotenv: 16.6.1 @@ -31140,7 +31486,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.3 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.13.2) + retry-axios: 2.6.0(axios@1.13.6) tough-cookie: 4.1.4 transitivePeerDependencies: - supports-color @@ -31153,7 +31499,7 @@ snapshots: dependencies: safer-buffer: 2.1.2 - iconv-lite@0.7.1: + iconv-lite@0.7.2: dependencies: safer-buffer: 2.1.2 @@ -31184,16 +31530,16 @@ snapshots: import-in-the-middle@1.15.0: dependencies: - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) cjs-module-lexer: 1.4.3 module-details-from-path: 1.0.4 - import-in-the-middle@2.0.1: + import-in-the-middle@2.0.6: dependencies: - acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) - cjs-module-lexer: 1.4.3 + acorn: 8.16.0 + acorn-import-attributes: 1.9.5(acorn@8.16.0) + cjs-module-lexer: 2.2.0 module-details-from-path: 1.0.4 import-local@3.2.0: @@ -31218,7 +31564,7 @@ snapshots: inline-style-parser@0.2.7: {} - inlineresources@1.1.0: + inlineresources@1.2.0: dependencies: css-font-face-src: 1.0.0 url: 0.11.4 @@ -31231,7 +31577,7 @@ snapshots: cli-width: 3.0.0 external-editor: 3.1.0 figures: 3.2.0 - lodash: 4.17.21 + lodash: 4.17.23 mute-stream: 0.0.8 ora: 5.4.1 run-async: 2.4.1 @@ -31249,7 +31595,7 @@ snapshots: cli-width: 3.0.0 external-editor: 3.1.0 figures: 3.2.0 - lodash: 4.17.21 + lodash: 4.17.23 mute-stream: 0.0.8 ora: 5.4.1 run-async: 2.4.1 @@ -31259,26 +31605,6 @@ snapshots: through: 2.3.8 wrap-ansi: 7.0.0 - inquirer@8.2.7(@types/node@18.16.9): - dependencies: - '@inquirer/external-editor': 1.0.3(@types/node@18.16.9) - ansi-escapes: 4.3.2 - chalk: 4.1.2 - cli-cursor: 3.1.0 - cli-width: 3.0.0 - figures: 3.2.0 - lodash: 4.17.21 - mute-stream: 0.0.8 - ora: 5.4.1 - run-async: 2.4.1 - rxjs: 7.8.2 - string-width: 4.2.3 - strip-ansi: 6.0.1 - through: 2.3.8 - wrap-ansi: 6.2.0 - transitivePeerDependencies: - - '@types/node' - internal-slot@1.1.0: dependencies: es-errors: 1.3.0 @@ -31296,9 +31622,9 @@ snapshots: dependencies: loose-envify: 1.4.0 - ioredis@5.8.2: + ioredis@5.10.0: dependencies: - '@ioredis/commands': 1.4.0 + '@ioredis/commands': 1.5.1 cluster-key-slot: 1.1.2 debug: 4.4.3(supports-color@5.5.0) denque: 2.1.0 @@ -31310,6 +31636,8 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.0.1: {} + ip-address@10.1.0: {} ip-regex@4.3.0: {} @@ -31376,7 +31704,7 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 is-callable@1.2.7: {} @@ -31454,7 +31782,7 @@ snapshots: is-negative-zero@2.0.3: {} - is-network-error@1.3.0: {} + is-network-error@1.3.1: {} is-number-object@1.1.1: dependencies: @@ -31518,7 +31846,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.19 + which-typed-array: 1.1.20 is-typedarray@1.0.0: {} @@ -31543,7 +31871,7 @@ snapshots: dependencies: is-docker: 2.2.1 - is-wsl@3.1.0: + is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 @@ -31559,8 +31887,6 @@ snapshots: isexe@2.0.0: {} - isexe@3.1.1: {} - iso-3166-1@2.1.1: {} iso-datestring-validator@2.2.2: {} @@ -31585,8 +31911,8 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -31595,11 +31921,11 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.28.5 - '@babel/parser': 7.28.5 + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color @@ -31725,10 +32051,10 @@ snapshots: jest-config@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.5) + babel-jest: 29.7.0(@babel/core@7.29.0) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -31838,7 +32164,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.0 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -31941,15 +32267,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) - '@babel/types': 7.28.5 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -31960,7 +32286,7 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.7.3 + semver: 7.7.4 transitivePeerDependencies: - supports-color @@ -32037,7 +32363,7 @@ snapshots: js-beautify@1.15.4: dependencies: config-chain: 1.1.13 - editorconfig: 1.0.4 + editorconfig: 1.0.7 glob: 10.5.0 js-cookie: 3.0.5 nopt: 7.2.1 @@ -32078,7 +32404,7 @@ snapshots: jsdom@20.0.3(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10): dependencies: abab: 2.0.6 - acorn: 8.15.0 + acorn: 8.16.0 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -32101,7 +32427,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 11.0.0 - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) xml-name-validator: 4.0.0 optionalDependencies: canvas: 2.11.2 @@ -32133,7 +32459,7 @@ snapshots: whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 whatwg-url: 12.0.1 - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) xml-name-validator: 4.0.0 optionalDependencies: canvas: 2.11.2 @@ -32220,7 +32546,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.3 + semver: 7.7.4 jsprim@1.4.2: dependencies: @@ -32258,7 +32584,7 @@ snapshots: jwt-decode@4.0.0: {} - katex@0.16.27: + katex@0.16.33: dependencies: commander: 8.3.0 @@ -32266,7 +32592,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - keyv@5.5.5: + keyv@5.6.0: dependencies: '@keyv/serialize': 1.1.1 @@ -32276,26 +32602,26 @@ snapshots: kleur@4.1.5: {} - konva@10.0.12: {} + konva@10.2.0: {} - langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + langchain@0.3.37(@langchain/aws@0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(axios@1.13.6)(cheerio@1.2.0)(handlebars@4.7.8)(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) js-tiktoken: 1.0.21 js-yaml: 4.1.1 jsonpointer: 5.0.1 - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 yaml: 2.8.2 zod: 3.25.76 optionalDependencies: - '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) - axios: 1.13.2(debug@4.4.3) - cheerio: 1.1.2 + '@langchain/aws': 0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + axios: 1.13.6(debug@4.4.3) + cheerio: 1.2.0 handlebars: 4.7.8 transitivePeerDependencies: - '@opentelemetry/api' @@ -32304,24 +32630,24 @@ snapshots: - openai - ws - langchain@0.3.36(@langchain/aws@0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(axios@1.13.2)(cheerio@1.1.2)(handlebars@4.7.8)(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)): + langchain@0.3.37(@langchain/aws@0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))))(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(axios@1.13.6)(cheerio@1.2.0)(handlebars@4.7.8)(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)): dependencies: - '@langchain/core': 0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/openai': 0.5.18(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/openai': 0.5.18(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)) + '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) js-tiktoken: 1.0.21 js-yaml: 4.1.1 jsonpointer: 5.0.1 - langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 yaml: 2.8.2 zod: 3.25.76 optionalDependencies: - '@langchain/aws': 0.1.15(@langchain/core@0.3.79(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) - axios: 1.13.2(debug@4.4.3) - cheerio: 1.1.2 + '@langchain/aws': 0.1.15(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))) + axios: 1.13.6(debug@4.4.3) + cheerio: 1.2.0 handlebars: 4.7.8 transitivePeerDependencies: - '@opentelemetry/api' @@ -32330,33 +32656,33 @@ snapshots: - openai - ws - langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)): + langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 console-table-printer: 2.15.0 p-queue: 6.6.2 - semver: 7.7.3 + semver: 7.7.4 uuid: 10.0.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-proto': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + openai: 4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) - langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.2.0(@opentelemetry/api@1.9.0))(openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)): + langsmith@0.3.87(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 console-table-printer: 2.15.0 p-queue: 6.6.2 - semver: 7.7.3 + semver: 7.7.4 uuid: 10.0.0 optionalDependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/exporter-trace-otlp-proto': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - openai: 6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + openai: 6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76) language-subtag-registry@0.3.23: {} @@ -32377,7 +32703,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.12.33: {} + libphonenumber-js@1.12.38: {} lighthouse-logger@1.4.2: dependencies: @@ -32386,54 +32712,54 @@ snapshots: transitivePeerDependencies: - supports-color - lightningcss-android-arm64@1.30.2: + lightningcss-android-arm64@1.31.1: optional: true - lightningcss-darwin-arm64@1.30.2: + lightningcss-darwin-arm64@1.31.1: optional: true - lightningcss-darwin-x64@1.30.2: + lightningcss-darwin-x64@1.31.1: optional: true - lightningcss-freebsd-x64@1.30.2: + lightningcss-freebsd-x64@1.31.1: optional: true - lightningcss-linux-arm-gnueabihf@1.30.2: + lightningcss-linux-arm-gnueabihf@1.31.1: optional: true - lightningcss-linux-arm64-gnu@1.30.2: + lightningcss-linux-arm64-gnu@1.31.1: optional: true - lightningcss-linux-arm64-musl@1.30.2: + lightningcss-linux-arm64-musl@1.31.1: optional: true - lightningcss-linux-x64-gnu@1.30.2: + lightningcss-linux-x64-gnu@1.31.1: optional: true - lightningcss-linux-x64-musl@1.30.2: + lightningcss-linux-x64-musl@1.31.1: optional: true - lightningcss-win32-arm64-msvc@1.30.2: + lightningcss-win32-arm64-msvc@1.31.1: optional: true - lightningcss-win32-x64-msvc@1.30.2: + lightningcss-win32-x64-msvc@1.31.1: optional: true - lightningcss@1.30.2: + lightningcss@1.31.1: dependencies: detect-libc: 2.1.2 optionalDependencies: - lightningcss-android-arm64: 1.30.2 - lightningcss-darwin-arm64: 1.30.2 - lightningcss-darwin-x64: 1.30.2 - lightningcss-freebsd-x64: 1.30.2 - lightningcss-linux-arm-gnueabihf: 1.30.2 - lightningcss-linux-arm64-gnu: 1.30.2 - lightningcss-linux-arm64-musl: 1.30.2 - lightningcss-linux-x64-gnu: 1.30.2 - lightningcss-linux-x64-musl: 1.30.2 - lightningcss-win32-arm64-msvc: 1.30.2 - lightningcss-win32-x64-msvc: 1.30.2 + lightningcss-android-arm64: 1.31.1 + lightningcss-darwin-arm64: 1.31.1 + lightningcss-darwin-x64: 1.31.1 + lightningcss-freebsd-x64: 1.31.1 + lightningcss-linux-arm-gnueabihf: 1.31.1 + lightningcss-linux-arm64-gnu: 1.31.1 + lightningcss-linux-arm64-musl: 1.31.1 + lightningcss-linux-x64-gnu: 1.31.1 + lightningcss-linux-x64-musl: 1.31.1 + lightningcss-win32-arm64-msvc: 1.31.1 + lightningcss-win32-x64-msvc: 1.31.1 lilconfig@3.1.3: {} @@ -32450,21 +32776,21 @@ snapshots: linkifyjs@4.3.2: {} - lit-element@4.2.1: + lit-element@4.2.2: dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 - '@lit/reactive-element': 2.1.1 - lit-html: 3.3.1 + '@lit-labs/ssr-dom-shim': 1.5.1 + '@lit/reactive-element': 2.1.2 + lit-html: 3.3.2 - lit-html@3.3.1: + lit-html@3.3.2: dependencies: '@types/trusted-types': 2.0.7 lit@3.1.0: dependencies: - '@lit/reactive-element': 2.1.1 - lit-element: 4.2.1 - lit-html: 3.3.1 + '@lit/reactive-element': 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.2 load-esm@1.0.3: {} @@ -32492,7 +32818,7 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash-es@4.17.22: {} + lodash-es@4.17.23: {} lodash._baseiteratee@4.7.0: dependencies: @@ -32558,6 +32884,8 @@ snapshots: lodash@4.17.21: {} + lodash@4.17.23: {} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 @@ -32592,7 +32920,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.4: {} + lru-cache@11.2.6: {} lru-cache@4.1.5: dependencies: @@ -32633,8 +32961,8 @@ snapshots: magicast@0.3.5: dependencies: - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 source-map-js: 1.2.1 make-dir@3.1.0: @@ -32643,7 +32971,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 make-error@1.3.6: {} @@ -32653,7 +32981,7 @@ snapshots: map-or-similar@1.5.0: {} - markdown-it@14.1.0: + markdown-it@14.1.1: dependencies: argparse: 2.0.1 entities: 4.5.0 @@ -32666,23 +32994,23 @@ snapshots: marky@1.3.0: {} - mastra@0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(hono@4.11.1)(typescript@5.5.4)(zod@3.25.76): + mastra@0.13.4(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@opentelemetry/api@1.9.0)(@types/json-schema@7.0.15)(typescript@5.5.4)(zod@3.25.76): dependencies: '@clack/prompts': 0.11.0 '@expo/devcert': 1.2.1 '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/deployer': 0.19.1(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(typescript@5.5.4)(zod@3.25.76) '@mastra/loggers': 0.10.19(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)) - '@mastra/mcp': 0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(hono@4.11.1)(zod@3.25.76) - '@opentelemetry/auto-instrumentations-node': 0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)) - '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@mastra/mcp': 0.13.5(@cfworker/json-schema@4.1.1)(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(@types/json-schema@7.0.15)(zod@3.25.76) + '@opentelemetry/auto-instrumentations-node': 0.62.2(@opentelemetry/api@1.9.0)(@opentelemetry/core@2.5.1(@opentelemetry/api@1.9.0)) + '@opentelemetry/core': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-grpc': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/exporter-trace-otlp-http': 0.203.0(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/resources': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-node': 0.203.0(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 2.2.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.38.0 + '@opentelemetry/sdk-trace-base': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.40.0 '@webcontainer/env': 1.1.1 commander: 12.1.0 dotenv: 16.6.1 @@ -32692,20 +33020,19 @@ snapshots: open: 10.2.0 picocolors: 1.1.1 posthog-node: 4.18.0 - prettier: 3.7.4 + prettier: 3.8.1 shell-quote: 1.8.3 strip-json-comments: 5.0.3 tcp-port-used: 1.0.2 yocto-spinner: 0.2.3 zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-json-schema: 3.25.1(zod@3.25.76) transitivePeerDependencies: - '@cfworker/json-schema' - '@opentelemetry/api' - '@types/json-schema' - debug - encoding - - hono - supports-color - typescript @@ -32746,7 +33073,7 @@ snapshots: dependencies: '@types/mdast': 3.0.15 '@types/unist': 2.0.11 - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 mdast-util-to-string: 3.2.0 micromark: 3.2.0 micromark-util-decode-numeric-character-reference: 1.1.0 @@ -32759,11 +33086,11 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-from-markdown@2.0.2: + mdast-util-from-markdown@2.0.3: dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 micromark: 4.0.2 @@ -32788,7 +33115,7 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: @@ -32797,7 +33124,7 @@ snapshots: mdast-util-gfm-strikethrough@2.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -32807,7 +33134,7 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -32816,14 +33143,14 @@ snapshots: dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color mdast-util-gfm@3.1.0: dependencies: - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-gfm-autolink-literal: 2.0.1 mdast-util-gfm-footnote: 2.1.0 mdast-util-gfm-strikethrough: 2.0.0 @@ -32839,7 +33166,7 @@ snapshots: '@types/mdast': 4.0.4 devlop: 1.1.0 longest-streak: 3.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 unist-util-remove-position: 5.0.0 transitivePeerDependencies: @@ -32851,7 +33178,7 @@ snapshots: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -32864,7 +33191,7 @@ snapshots: '@types/unist': 3.0.3 ccount: 2.0.1 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 parse-entities: 4.0.2 stringify-entities: 4.0.4 @@ -32879,7 +33206,7 @@ snapshots: '@types/hast': 3.0.4 '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -32909,7 +33236,7 @@ snapshots: micromark-util-sanitize-uri: 2.0.1 trim-lines: 3.0.1 unist-util-position: 5.0.0 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 vfile: 6.0.3 mdast-util-to-markdown@2.1.2: @@ -32921,7 +33248,7 @@ snapshots: mdast-util-to-string: 4.0.0 micromark-util-classify-character: 2.0.1 micromark-util-decode-string: 2.0.1 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 zwitch: 2.0.4 mdast-util-to-string@3.2.0: @@ -32942,12 +33269,25 @@ snapshots: media-typer@1.1.0: {} + mediabunny@1.35.1: + dependencies: + '@types/dom-mediacapture-transform': 0.1.11 + '@types/dom-webcodecs': 0.1.13 + memfs@3.5.3: dependencies: fs-monkey: 1.1.0 - memfs@4.51.1: + memfs@4.56.10(tslib@2.8.1): dependencies: + '@jsonjoy.com/fs-core': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-fsa': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-builtins': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-to-fsa': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-node-utils': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-print': 4.56.10(tslib@2.8.1) + '@jsonjoy.com/fs-snapshot': 4.56.10(tslib@2.8.1) '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) glob-to-regex.js: 1.2.0(tslib@2.8.1) @@ -32978,50 +33318,50 @@ snapshots: methods@1.1.2: {} - metro-babel-transformer@0.83.3: + metro-babel-transformer@0.83.4: dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 flow-enums-runtime: 0.0.6 - hermes-parser: 0.32.0 + hermes-parser: 0.33.3 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-cache-key@0.83.3: + metro-cache-key@0.83.4: dependencies: flow-enums-runtime: 0.0.6 - metro-cache@0.83.3: + metro-cache@0.83.4: dependencies: exponential-backoff: 3.1.3 flow-enums-runtime: 0.0.6 https-proxy-agent: 7.0.6 - metro-core: 0.83.3 + metro-core: 0.83.4 transitivePeerDependencies: - supports-color - metro-config@0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + metro-config@0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: connect: 3.7.0 flow-enums-runtime: 0.0.6 jest-validate: 29.7.0 - metro: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - metro-cache: 0.83.3 - metro-core: 0.83.3 - metro-runtime: 0.83.3 + metro: 0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + metro-cache: 0.83.4 + metro-core: 0.83.4 + metro-runtime: 0.83.4 yaml: 2.8.2 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro-core@0.83.3: + metro-core@0.83.4: dependencies: flow-enums-runtime: 0.0.6 lodash.throttle: 4.1.1 - metro-resolver: 0.83.3 + metro-resolver: 0.83.4 - metro-file-map@0.83.3: + metro-file-map@0.83.4: dependencies: debug: 4.4.3(supports-color@5.5.0) fb-watchman: 2.0.2 @@ -33035,87 +33375,86 @@ snapshots: transitivePeerDependencies: - supports-color - metro-minify-terser@0.83.3: + metro-minify-terser@0.83.4: dependencies: flow-enums-runtime: 0.0.6 - terser: 5.44.1 + terser: 5.46.0 - metro-resolver@0.83.3: + metro-resolver@0.83.4: dependencies: flow-enums-runtime: 0.0.6 - metro-runtime@0.83.3: + metro-runtime@0.83.4: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 flow-enums-runtime: 0.0.6 - metro-source-map@0.83.3: + metro-source-map@0.83.4: dependencies: - '@babel/traverse': 7.28.5 - '@babel/traverse--for-generate-function-map': '@babel/traverse@7.28.5' - '@babel/types': 7.28.5 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-symbolicate: 0.83.3 + metro-symbolicate: 0.83.4 nullthrows: 1.1.1 - ob1: 0.83.3 + ob1: 0.83.4 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-symbolicate@0.83.3: + metro-symbolicate@0.83.4: dependencies: flow-enums-runtime: 0.0.6 invariant: 2.2.4 - metro-source-map: 0.83.3 + metro-source-map: 0.83.4 nullthrows: 1.1.1 source-map: 0.5.7 vlq: 1.0.1 transitivePeerDependencies: - supports-color - metro-transform-plugins@0.83.3: + metro-transform-plugins@0.83.4: dependencies: - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 flow-enums-runtime: 0.0.6 nullthrows: 1.1.1 transitivePeerDependencies: - supports-color - metro-transform-worker@0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + metro-transform-worker@0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 - '@babel/parser': 7.28.5 - '@babel/types': 7.28.5 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 flow-enums-runtime: 0.0.6 - metro: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-minify-terser: 0.83.3 - metro-source-map: 0.83.3 - metro-transform-plugins: 0.83.3 + metro: 0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + metro-babel-transformer: 0.83.4 + metro-cache: 0.83.4 + metro-cache-key: 0.83.4 + metro-minify-terser: 0.83.4 + metro-source-map: 0.83.4 + metro-transform-plugins: 0.83.4 nullthrows: 1.1.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - metro@0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): + metro@0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: - '@babel/code-frame': 7.27.1 - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - accepts: 1.3.8 + '@babel/code-frame': 7.29.0 + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + accepts: 2.0.0 chalk: 4.1.2 ci-info: 2.0.0 connect: 3.7.0 @@ -33123,25 +33462,25 @@ snapshots: error-stack-parser: 2.1.4 flow-enums-runtime: 0.0.6 graceful-fs: 4.2.11 - hermes-parser: 0.32.0 + hermes-parser: 0.33.3 image-size: 1.2.1 invariant: 2.2.4 jest-worker: 29.7.0 jsc-safe-url: 0.2.4 lodash.throttle: 4.1.1 - metro-babel-transformer: 0.83.3 - metro-cache: 0.83.3 - metro-cache-key: 0.83.3 - metro-config: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - metro-core: 0.83.3 - metro-file-map: 0.83.3 - metro-resolver: 0.83.3 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 - metro-symbolicate: 0.83.3 - metro-transform-plugins: 0.83.3 - metro-transform-worker: 0.83.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - mime-types: 2.1.35 + metro-babel-transformer: 0.83.4 + metro-cache: 0.83.4 + metro-cache-key: 0.83.4 + metro-config: 0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + metro-core: 0.83.4 + metro-file-map: 0.83.4 + metro-resolver: 0.83.4 + metro-runtime: 0.83.4 + metro-source-map: 0.83.4 + metro-symbolicate: 0.83.4 + metro-transform-plugins: 0.83.4 + metro-transform-worker: 0.83.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + mime-types: 3.0.2 nullthrows: 1.1.1 serialize-error: 2.1.0 source-map: 0.5.7 @@ -33155,7 +33494,7 @@ snapshots: micromark-core-commonmark@1.1.0: dependencies: - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 micromark-factory-destination: 1.1.0 micromark-factory-label: 1.1.0 micromark-factory-space: 1.1.0 @@ -33174,7 +33513,7 @@ snapshots: micromark-core-commonmark@2.0.3: dependencies: - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-factory-destination: 2.0.1 micromark-factory-label: 2.0.1 @@ -33251,9 +33590,9 @@ snapshots: micromark-extension-math@3.1.0: dependencies: - '@types/katex': 0.16.7 + '@types/katex': 0.16.8 devlop: 1.1.0 - katex: 0.16.27 + katex: 0.16.33 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 @@ -33373,14 +33712,14 @@ snapshots: micromark-util-decode-string@1.1.0: dependencies: - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 micromark-util-character: 1.2.0 micromark-util-decode-numeric-character-reference: 1.1.0 micromark-util-symbol: 1.1.0 micromark-util-decode-string@2.0.1: dependencies: - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 micromark-util-character: 2.1.1 micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-symbol: 2.0.1 @@ -33447,7 +33786,7 @@ snapshots: dependencies: '@types/debug': 4.1.12 debug: 4.4.3(supports-color@5.5.0) - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 micromark-core-commonmark: 1.1.0 micromark-factory-space: 1.1.0 micromark-util-character: 1.2.0 @@ -33469,7 +33808,7 @@ snapshots: dependencies: '@types/debug': 4.1.12 debug: 4.4.3(supports-color@5.5.0) - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-factory-space: 2.0.1 @@ -33494,7 +33833,7 @@ snapshots: miller-rabin@4.0.1: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 brorand: 1.1.0 mime-db@1.52.0: {} @@ -33529,23 +33868,19 @@ snapshots: minimalistic-crypto-utils@1.0.1: {} - minimatch@10.1.1: + minimatch@10.2.4: dependencies: - '@isaacs/brace-expansion': 5.0.0 + brace-expansion: 5.0.4 - minimatch@3.1.2: + minimatch@3.1.5: dependencies: brace-expansion: 1.1.12 - minimatch@8.0.4: + minimatch@8.0.7: dependencies: brace-expansion: 2.0.2 - minimatch@9.0.1: - dependencies: - brace-expansion: 2.0.2 - - minimatch@9.0.5: + minimatch@9.0.9: dependencies: brace-expansion: 2.0.2 @@ -33559,7 +33894,7 @@ snapshots: minipass@5.0.0: {} - minipass@7.1.2: {} + minipass@7.1.3: {} minizlib@2.1.2: dependencies: @@ -33576,19 +33911,19 @@ snapshots: mlly@1.8.0: dependencies: - acorn: 8.15.0 + acorn: 8.16.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.1 + ufo: 1.6.3 - mobx-react-lite@4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1): + mobx-react-lite@4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1): dependencies: mobx: 6.15.0 react: 18.3.1 use-sync-external-store: 1.6.0(react@18.3.1) optionalDependencies: react-dom: 18.3.1(react@18.3.1) - react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) mobx-state-tree@7.0.2(mobx@6.15.0)(typescript@5.5.4): dependencies: @@ -33656,6 +33991,8 @@ snapshots: mute-stream@0.0.8: {} + mute-stream@1.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -33664,7 +34001,7 @@ snapshots: namespace-emitter@2.0.1: {} - nan@2.24.0: {} + nan@2.25.0: {} nanoid@3.3.11: {} @@ -33684,50 +34021,50 @@ snapshots: neo-async@2.6.2: {} - nestjs-command@3.1.5(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(yargs@17.7.2): + nestjs-command@3.1.5(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(yargs@17.7.2): dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) lodash.compact: 3.0.1 lodash.flattendeep: 4.4.0 yargs: 17.7.2 - nestjs-real-ip@3.0.1(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2)): + nestjs-real-ip@3.0.1(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2)): dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@supercharge/request-ip': 1.2.0 - nestjs-temporal-core@3.2.0(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.20)(@temporalio/client@1.14.0)(@temporalio/common@1.14.0)(@temporalio/worker@1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12))(@temporalio/workflow@1.14.0)(reflect-metadata@0.1.14)(rxjs@7.8.2): + nestjs-temporal-core@3.2.3(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.22)(@temporalio/client@1.15.0)(@temporalio/common@1.15.0)(@temporalio/worker@1.15.0(@swc/helpers@0.5.13)(esbuild@0.25.12)(tslib@2.8.1))(@temporalio/workflow@1.15.0)(reflect-metadata@0.1.14)(rxjs@7.8.2): dependencies: - '@nestjs/common': 10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@nestjs/core': 10.4.20(@nestjs/common@10.4.20(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.20)(@nestjs/platform-express@10.4.20)(reflect-metadata@0.1.14)(rxjs@7.8.2) - '@temporalio/client': 1.14.0 - '@temporalio/common': 1.14.0 - '@temporalio/worker': 1.14.0(@swc/helpers@0.5.13)(esbuild@0.25.12) - '@temporalio/workflow': 1.14.0 + '@nestjs/common': 10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@nestjs/core': 10.4.22(@nestjs/common@10.4.22(class-transformer@0.5.1)(class-validator@0.14.4)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/microservices@10.4.22)(@nestjs/platform-express@10.4.22)(reflect-metadata@0.1.14)(rxjs@7.8.2) + '@temporalio/client': 1.15.0 + '@temporalio/common': 1.15.0 + '@temporalio/worker': 1.15.0(@swc/helpers@0.5.13)(esbuild@0.25.12)(tslib@2.8.1) + '@temporalio/workflow': 1.15.0 ms: 2.1.3 reflect-metadata: 0.1.14 rxjs: 7.8.2 netmask@2.0.2: {} - next-plausible@3.12.5(next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-plausible@3.12.5(next@14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1) + next: 14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.35(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.57.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.1): + next@14.2.35(@babel/core@7.29.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.58.2)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.97.3): dependencies: '@next/env': 14.2.35 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001761 + caniuse-lite: 1.0.30001775 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.28.5)(babel-plugin-macros@3.1.0)(react@18.3.1) + styled-jsx: 5.1.1(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 14.2.33 '@next/swc-darwin-x64': 14.2.33 @@ -33739,8 +34076,8 @@ snapshots: '@next/swc-win32-ia32-msvc': 14.2.33 '@next/swc-win32-x64-msvc': 14.2.33 '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.57.0 - sass: 1.97.1 + '@playwright/test': 1.58.2 + sass: 1.97.3 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -33767,9 +34104,9 @@ snapshots: lower-case: 2.0.2 tslib: 2.8.1 - node-abi@3.85.0: + node-abi@3.87.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 node-abort-controller@3.1.1: {} @@ -33782,7 +34119,14 @@ snapshots: node-emoji@1.11.0: dependencies: - lodash: 4.17.21 + lodash: 4.17.23 + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 node-fetch-native@1.6.7: {} @@ -33808,8 +34152,8 @@ snapshots: node-telegram-bot-api@0.66.0(request@2.88.2): dependencies: - '@cypress/request': 3.0.9 - '@cypress/request-promise': 5.0.0(@cypress/request@3.0.9)(request@2.88.2) + '@cypress/request': 3.0.10 + '@cypress/request-promise': 5.0.0(@cypress/request@3.0.10)(request@2.88.2) array.prototype.findindex: 2.2.4 bl: 1.2.3 debug: 3.2.7 @@ -33821,16 +34165,16 @@ snapshots: - request - supports-color - nodemailer@7.0.11: {} + nodemailer@7.0.13: {} - nodemon@3.1.11: + nodemon@3.1.14: dependencies: chokidar: 3.6.0 debug: 4.4.3(supports-color@5.5.0) ignore-by-default: 1.0.1 - minimatch: 3.1.2 + minimatch: 10.2.4 pstree.remy: 1.1.8 - semver: 7.7.3 + semver: 7.7.4 simple-update-notifier: 2.0.0 supports-color: 5.5.0 touch: 3.1.1 @@ -33850,14 +34194,14 @@ snapshots: normalize.css@8.0.1: {} - nostr-tools@2.19.4(typescript@5.5.4): + nostr-tools@2.23.3(typescript@5.5.4): dependencies: - '@noble/ciphers': 0.5.3 - '@noble/curves': 1.2.0 - '@noble/hashes': 1.3.1 - '@scure/base': 1.1.1 - '@scure/bip32': 1.3.1 - '@scure/bip39': 1.2.1 + '@noble/ciphers': 2.1.1 + '@noble/curves': 2.0.1 + '@noble/hashes': 2.0.1 + '@scure/base': 2.0.0 + '@scure/bip32': 2.0.1 + '@scure/bip39': 2.0.1 nostr-wasm: 0.1.0 optionalDependencies: typescript: 5.5.4 @@ -33896,7 +34240,7 @@ snapshots: oauth-sign@0.9.0: {} - ob1@0.83.3: + ob1@0.83.4: dependencies: flow-enums-runtime: 0.0.6 @@ -33965,7 +34309,7 @@ snapshots: dependencies: destr: 2.0.5 node-fetch-native: 1.6.7 - ufo: 1.6.1 + ufo: 1.6.3 on-exit-leak-free@0.2.0: {} @@ -33991,7 +34335,7 @@ snapshots: open@10.2.0: dependencies: - default-browser: 5.4.0 + default-browser: 5.5.0 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 wsl-utils: 0.1.0 @@ -34001,7 +34345,7 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 - openai@4.104.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@4.104.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): dependencies: '@types/node': 18.16.9 '@types/node-fetch': 2.6.13 @@ -34011,19 +34355,19 @@ snapshots: formdata-node: 4.4.1 node-fetch: 2.7.0 optionalDependencies: - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 transitivePeerDependencies: - encoding - openai@5.23.2(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@5.23.2(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): optionalDependencies: - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 - openai@6.15.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76): optionalDependencies: - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10) zod: 3.25.76 openapi-types@12.1.3: {} @@ -34072,7 +34416,7 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.11.1(typescript@5.5.4)(zod@3.22.4): + ox@0.12.4(typescript@5.5.4)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -34087,7 +34431,7 @@ snapshots: transitivePeerDependencies: - zod - ox@0.11.1(typescript@5.5.4)(zod@3.25.76): + ox@0.12.4(typescript@5.5.4)(zod@3.25.76): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -34151,7 +34495,7 @@ snapshots: p-queue@8.1.1: dependencies: - eventemitter3: 5.0.1 + eventemitter3: 5.0.4 p-timeout: 6.1.4 p-retry@4.6.2: @@ -34162,7 +34506,7 @@ snapshots: p-retry@6.2.1: dependencies: '@types/retry': 0.12.2 - is-network-error: 1.3.0 + is-network-error: 1.3.1 retry: 0.13.1 p-timeout@3.2.0: @@ -34228,14 +34572,14 @@ snapshots: '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 - decode-named-character-reference: 1.2.0 + decode-named-character-reference: 1.3.0 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.27.1 + '@babel/code-frame': 7.29.0 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -34293,12 +34637,12 @@ snapshots: path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 - minipass: 7.1.2 + minipass: 7.1.3 - path-scurry@2.0.1: + path-scurry@2.0.2: dependencies: - lru-cache: 11.2.4 - minipass: 7.1.2 + lru-cache: 11.2.6 + minipass: 7.1.3 path-to-regexp@0.1.12: {} @@ -34339,12 +34683,12 @@ snapshots: performance-now@2.1.0: {} - pg-cloudflare@1.2.7: + pg-cloudflare@1.3.0: optional: true - pg-connection-string@2.9.1: {} + pg-connection-string@2.11.0: {} - pg-cursor@2.15.3(pg@8.16.3): + pg-cursor@2.18.0(pg@8.16.3): dependencies: pg: 8.16.3 @@ -34352,10 +34696,14 @@ snapshots: pg-minify@1.8.0: {} - pg-pool@3.10.1(pg@8.16.3): + pg-pool@3.12.0(pg@8.16.3): dependencies: pg: 8.16.3 + pg-pool@3.12.0(pg@8.19.0): + dependencies: + pg: 8.19.0 + pg-promise@11.15.0(pg-query-stream@4.10.3(pg@8.16.3)): dependencies: assert-options: 0.8.3 @@ -34366,12 +34714,12 @@ snapshots: transitivePeerDependencies: - pg-native - pg-protocol@1.10.3: {} + pg-protocol@1.12.0: {} pg-query-stream@4.10.3(pg@8.16.3): dependencies: pg: 8.16.3 - pg-cursor: 2.15.3(pg@8.16.3) + pg-cursor: 2.18.0(pg@8.16.3) pg-types@2.2.0: dependencies: @@ -34383,13 +34731,23 @@ snapshots: pg@8.16.3: dependencies: - pg-connection-string: 2.9.1 - pg-pool: 3.10.1(pg@8.16.3) - pg-protocol: 1.10.3 + pg-connection-string: 2.11.0 + pg-pool: 3.12.0(pg@8.16.3) + pg-protocol: 1.12.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.2.7 + pg-cloudflare: 1.3.0 + + pg@8.19.0: + dependencies: + pg-connection-string: 2.11.0 + pg-pool: 3.12.0(pg@8.19.0) + pg-protocol: 1.12.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.3.0 pgpass@1.0.5: dependencies: @@ -34429,10 +34787,10 @@ snapshots: minimist: 1.2.8 on-exit-leak-free: 2.1.2 pino-abstract-transport: 2.0.0 - pump: 3.0.3 + pump: 3.0.4 readable-stream: 4.7.0 secure-json-parse: 2.7.0 - sonic-boom: 4.2.0 + sonic-boom: 4.2.1 strip-json-comments: 3.1.1 pino-pretty@13.1.3: @@ -34446,14 +34804,14 @@ snapshots: minimist: 1.2.8 on-exit-leak-free: 2.1.2 pino-abstract-transport: 3.0.0 - pump: 3.0.3 + pump: 3.0.4 secure-json-parse: 4.1.0 - sonic-boom: 4.2.0 + sonic-boom: 4.2.1 strip-json-comments: 5.0.3 pino-std-serializers@4.0.0: {} - pino-std-serializers@7.0.0: {} + pino-std-serializers@7.1.0: {} pino@7.11.0: dependencies: @@ -34475,12 +34833,12 @@ snapshots: atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 pino-abstract-transport: 2.0.0 - pino-std-serializers: 7.0.0 + pino-std-serializers: 7.1.0 process-warning: 5.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.0 + sonic-boom: 4.2.1 thread-stream: 3.1.0 pirates@4.0.7: {} @@ -34503,15 +34861,15 @@ snapshots: pkg-types@2.3.0: dependencies: - confbox: 0.2.2 + confbox: 0.2.4 exsolve: 1.0.8 pathe: 2.0.3 - playwright-core@1.57.0: {} + playwright-core@1.58.2: {} - playwright@1.57.0: + playwright@1.58.2: dependencies: - playwright-core: 1.57.0 + playwright-core: 1.58.2 optionalDependencies: fsevents: 2.3.2 @@ -34521,7 +34879,7 @@ snapshots: pngjs@5.0.0: {} - polotno@2.34.1(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): + polotno@2.36.10(@types/react@18.3.1)(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): dependencies: '@blueprintjs/core': 5.19.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@blueprintjs/icons': 5.23.0(@types/react@18.3.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -34531,25 +34889,26 @@ snapshots: fast-json-patch: 3.1.1 functions-have-names: 1.2.3 gifuct-js: 2.1.2 - gradient-parser: 1.1.1 - konva: 10.0.12 + gradient-parser: 1.2.0 + konva: 10.2.0 + mediabunny: 1.35.1 mensch: 0.3.4 mobx: 6.15.0 - mobx-react-lite: 4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) + mobx-react-lite: 4.1.1(mobx@6.15.0)(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) mobx-state-tree: 7.0.2(mobx@6.15.0)(typescript@5.5.4) nanoid: 3.3.11 quill: 1.3.7 - rasterizehtml: 1.4.0 + rasterizehtml: 1.4.1 react: 18.3.1 react-color: 2.19.3(react@18.3.1) react-dom: 18.3.1(react@18.3.1) - react-konva: 18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-konva-utils: 2.0.0(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) - react-sortablejs: 6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6) + react-konva: 18.2.14(@types/react@18.3.1)(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-konva-utils: 2.0.0(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + react-sortablejs: 6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.7) react-window: 1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - sortablejs: 1.15.6 + sortablejs: 1.15.7 svg-round-corners: 0.4.3 - swr: 2.3.8(react@18.3.1) + swr: 2.4.1(react@18.3.1) use-image: 1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@types/react' @@ -34627,29 +34986,37 @@ snapshots: dependencies: xtend: 4.0.2 - postgres@3.4.7: {} + postgres@3.4.8: {} - posthog-js@1.309.1: + posthog-js@1.356.1: dependencies: - '@posthog/core': 1.8.1 - core-js: 3.47.0 + '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.208.0 + '@opentelemetry/exporter-logs-otlp-http': 0.208.0(@opentelemetry/api@1.9.0) + '@opentelemetry/resources': 2.5.1(@opentelemetry/api@1.9.0) + '@opentelemetry/sdk-logs': 0.208.0(@opentelemetry/api@1.9.0) + '@posthog/core': 1.23.1 + '@posthog/types': 1.356.1 + core-js: 3.48.0 + dompurify: 3.3.1 fflate: 0.4.8 - preact: 10.28.0 - web-vitals: 4.2.4 + preact: 10.28.4 + query-selector-shadow-dom: 1.0.1 + web-vitals: 5.1.0 posthog-node@4.18.0: dependencies: - axios: 1.13.2(debug@4.4.3) + axios: 1.13.6(debug@4.4.3) transitivePeerDependencies: - debug - preact@10.28.0: {} + preact@10.28.4: {} prelude-ls@1.2.1: {} prettier@2.8.8: {} - prettier@3.7.4: {} + prettier@3.8.1: {} pretty-bytes@6.1.1: {} @@ -34731,9 +35098,9 @@ snapshots: property-information@7.1.0: {} - prosemirror-changeset@2.3.1: + prosemirror-changeset@2.4.0: dependencies: - prosemirror-transform: 1.10.5 + prosemirror-transform: 1.11.0 prosemirror-collab@1.3.1: dependencies: @@ -34743,45 +35110,45 @@ snapshots: dependencies: prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 + prosemirror-transform: 1.11.0 prosemirror-dropcursor@1.8.2: dependencies: prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.4 + prosemirror-transform: 1.11.0 + prosemirror-view: 1.41.6 prosemirror-gapcursor@1.4.0: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-view: 1.41.4 + prosemirror-view: 1.41.6 prosemirror-history@1.5.0: dependencies: prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.4 + prosemirror-transform: 1.11.0 + prosemirror-view: 1.41.6 rope-sequence: 1.3.4 prosemirror-inputrules@1.5.1: dependencies: prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 + prosemirror-transform: 1.11.0 prosemirror-keymap@1.2.3: dependencies: prosemirror-state: 1.4.4 w3c-keyname: 2.2.8 - prosemirror-markdown@1.13.2: + prosemirror-markdown@1.13.4: dependencies: '@types/markdown-it': 14.1.2 - markdown-it: 14.1.0 + markdown-it: 14.1.1 prosemirror-model: 1.25.4 - prosemirror-menu@1.2.5: + prosemirror-menu@1.3.0: dependencies: crelt: 1.0.6 prosemirror-commands: 1.7.1 @@ -34800,39 +35167,39 @@ snapshots: dependencies: prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 + prosemirror-transform: 1.11.0 prosemirror-state@1.4.4: dependencies: prosemirror-model: 1.25.4 - prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.4 + prosemirror-transform: 1.11.0 + prosemirror-view: 1.41.6 - prosemirror-tables@1.8.4: + prosemirror-tables@1.8.5: dependencies: prosemirror-keymap: 1.2.3 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 - prosemirror-view: 1.41.4 + prosemirror-transform: 1.11.0 + prosemirror-view: 1.41.6 - prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.4): + prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.6): dependencies: '@remirror/core-constants': 3.0.0 escape-string-regexp: 4.0.0 prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-view: 1.41.4 + prosemirror-view: 1.41.6 - prosemirror-transform@1.10.5: + prosemirror-transform@1.11.0: dependencies: prosemirror-model: 1.25.4 - prosemirror-view@1.41.4: + prosemirror-view@1.41.6: dependencies: prosemirror-model: 1.25.4 prosemirror-state: 1.4.4 - prosemirror-transform: 1.10.5 + prosemirror-transform: 1.11.0 proto-list@1.2.4: {} @@ -34887,7 +35254,7 @@ snapshots: public-encrypt@4.0.3: dependencies: - bn.js: 4.12.2 + bn.js: 4.12.3 browserify-rsa: 4.1.1 create-hash: 1.2.0 parse-asn1: 5.1.9 @@ -34899,7 +35266,7 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 - pump@3.0.3: + pump@3.0.4: dependencies: end-of-stream: 1.4.5 once: 1.4.0 @@ -34934,18 +35301,20 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 - qs@6.13.0: + qs@6.14.2: dependencies: side-channel: 1.1.0 - qs@6.14.0: + qs@6.15.0: dependencies: side-channel: 1.1.0 - qs@6.5.3: {} + qs@6.5.5: {} quansync@0.2.11: {} + query-selector-shadow-dom@1.0.1: {} + query-string@7.1.3: dependencies: decode-uri-component: 0.2.2 @@ -34997,20 +35366,13 @@ snapshots: range-parser@1.2.1: {} - rasterizehtml@1.4.0: + rasterizehtml@1.4.1: dependencies: - inlineresources: 1.1.0 + inlineresources: 1.2.0 sane-domparser-error: 0.2.0 url: 0.11.4 xmlserializer: 0.6.1 - raw-body@2.5.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - raw-body@2.5.3: dependencies: bytes: 3.1.2 @@ -35022,14 +35384,14 @@ snapshots: dependencies: bytes: 3.1.2 http-errors: 2.0.1 - iconv-lite: 0.7.1 + iconv-lite: 0.7.2 unpipe: 1.0.0 react-color@2.19.3(react@18.3.1): dependencies: '@icons/material': 0.2.4(react@18.3.1) - lodash: 4.17.21 - lodash-es: 4.17.22 + lodash: 4.17.23 + lodash-es: 4.17.23 material-colors: 1.2.6 prop-types: 15.8.1 react: 18.3.1 @@ -35075,7 +35437,7 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-dropzone@14.3.8(react@18.3.1): + react-dropzone@14.4.1(react@18.3.1): dependencies: attr-accept: 2.2.5 file-selector: 2.1.2 @@ -35084,29 +35446,29 @@ snapshots: react-error-boundary@4.1.2(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 react-fast-compare@3.2.2: {} - react-hook-form@7.69.0(react@18.3.1): + react-hook-form@7.71.2(react@18.3.1): dependencies: react: 18.3.1 - react-hotkeys-hook@5.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-hotkeys-hook@5.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-i18next@15.7.4(i18next@25.7.3(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): + react-i18next@15.7.4(i18next@25.8.13(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 html-parse-stringify: 3.0.1 - i18next: 25.7.3(typescript@5.5.4) + i18next: 25.8.13(typescript@5.5.4) react: 18.3.1 optionalDependencies: react-dom: 18.3.1(react@18.3.1) - react-native: 0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) + react-native: 0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10) typescript: 5.5.4 react-is@16.13.1: {} @@ -35115,21 +35477,21 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.3: {} + react-is@19.2.4: {} - react-konva-utils@2.0.0(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + react-konva-utils@2.0.0(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react-konva@18.2.14(@types/react@18.3.1)(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: - konva: 10.0.12 + konva: 10.2.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-konva: 18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-konva: 18.2.14(@types/react@18.3.1)(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) use-image: 1.1.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react-konva@18.2.14(@types/react@18.3.1)(konva@10.0.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-konva@18.2.14(@types/react@18.3.1)(konva@10.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@types/react-reconciler': 0.28.9(@types/react@18.3.1) its-fine: 1.2.5(@types/react@18.3.1)(react@18.3.1) - konva: 10.0.12 + konva: 10.2.0 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-reconciler: 0.29.2(react@18.3.1) @@ -35157,7 +35519,7 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 vfile: 6.0.3 transitivePeerDependencies: - supports-color @@ -35196,7 +35558,7 @@ snapshots: remark-parse: 11.0.0 remark-rehype: 11.1.2 unified: 11.0.5 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 vfile: 6.0.3 transitivePeerDependencies: - supports-color @@ -35210,31 +35572,30 @@ snapshots: react-lifecycles-compat: 3.0.4 warning: 4.0.3 - react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10): + react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10): dependencies: '@jest/create-cache-key-function': 29.7.0 - '@react-native/assets-registry': 0.83.1 - '@react-native/codegen': 0.83.1(@babel/core@7.28.5) - '@react-native/community-cli-plugin': 0.83.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) - '@react-native/gradle-plugin': 0.83.1 - '@react-native/js-polyfills': 0.83.1 - '@react-native/normalize-colors': 0.83.1 - '@react-native/virtualized-lists': 0.83.1(@types/react@18.3.1)(react-native@0.83.1(@babel/core@7.28.5)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) + '@react-native/assets-registry': 0.84.1 + '@react-native/codegen': 0.84.1(@babel/core@7.29.0) + '@react-native/community-cli-plugin': 0.84.1(bufferutil@4.1.0)(utf-8-validate@5.0.10) + '@react-native/gradle-plugin': 0.84.1 + '@react-native/js-polyfills': 0.84.1 + '@react-native/normalize-colors': 0.84.1 + '@react-native/virtualized-lists': 0.84.1(@types/react@18.3.1)(react-native@0.84.1(@babel/core@7.29.0)(@types/react@18.3.1)(bufferutil@4.1.0)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1) abort-controller: 3.0.0 anser: 1.4.10 ansi-regex: 5.0.1 - babel-jest: 29.7.0(@babel/core@7.28.5) + babel-jest: 29.7.0(@babel/core@7.29.0) babel-plugin-syntax-hermes-parser: 0.32.0 base64-js: 1.5.1 commander: 12.1.0 flow-enums-runtime: 0.0.6 - glob: 7.2.3 - hermes-compiler: 0.14.0 + hermes-compiler: 250829098.0.9 invariant: 2.2.4 jest-environment-node: 29.7.0 memoize-one: 5.2.1 - metro-runtime: 0.83.3 - metro-source-map: 0.83.3 + metro-runtime: 0.83.4 + metro-source-map: 0.83.4 nullthrows: 1.1.1 pretty-format: 29.7.0 promise: 8.3.0 @@ -35243,8 +35604,9 @@ snapshots: react-refresh: 0.14.2 regenerator-runtime: 0.13.11 scheduler: 0.27.0 - semver: 7.7.3 + semver: 7.7.4 stacktrace-parser: 0.1.11 + tinyglobby: 0.2.15 whatwg-fetch: 3.6.20 ws: 7.5.10(bufferutil@4.1.0)(utf-8-validate@5.0.10) yargs: 17.7.2 @@ -35322,13 +35684,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - react-sortablejs@6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6): + react-sortablejs@6.1.4(@types/sortablejs@1.15.9)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.7): dependencies: '@types/sortablejs': 1.15.9 classnames: 2.3.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - sortablejs: 1.15.6 + sortablejs: 1.15.7 tiny-invariant: 1.2.0 react-style-singleton@2.2.3(@types/react@18.3.1)(react@18.3.1): @@ -35341,7 +35703,7 @@ snapshots: react-syntax-highlighter@15.6.6(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 @@ -35355,7 +35717,7 @@ snapshots: react-textarea-autosize@8.3.4(@types/react@18.3.1)(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 react: 18.3.1 use-composed-ref: 1.4.0(@types/react@18.3.1)(react@18.3.1) use-latest: 1.3.0(@types/react@18.3.1)(react@18.3.1) @@ -35364,14 +35726,14 @@ snapshots: react-tooltip@5.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@floating-ui/dom': 1.7.4 + '@floating-ui/dom': 1.7.5 classnames: 2.5.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -35395,7 +35757,7 @@ snapshots: react-window@1.8.11(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 memoize-one: 5.2.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -35406,7 +35768,7 @@ snapshots: reactcss@1.2.3(react@18.3.1): dependencies: - lodash: 4.17.21 + lodash: 4.17.23 react: 18.3.1 read-cache@1.0.0: @@ -35447,6 +35809,8 @@ snapshots: readdirp@4.1.2: {} + readdirp@5.0.0: {} + real-require@0.1.0: {} real-require@0.2.0: {} @@ -35470,17 +35834,19 @@ snapshots: '@redis/search': 1.2.0(@redis/client@1.6.1) '@redis/time-series': 1.1.0(@redis/client@1.6.1) - redis@5.10.0: + redis@5.11.0: dependencies: - '@redis/bloom': 5.10.0(@redis/client@5.10.0) - '@redis/client': 5.10.0 - '@redis/json': 5.10.0(@redis/client@5.10.0) - '@redis/search': 5.10.0(@redis/client@5.10.0) - '@redis/time-series': 5.10.0(@redis/client@5.10.0) + '@redis/bloom': 5.11.0(@redis/client@5.11.0) + '@redis/client': 5.11.0 + '@redis/json': 5.11.0(@redis/client@5.11.0) + '@redis/search': 5.11.0(@redis/client@5.11.0) + '@redis/time-series': 5.11.0(@redis/client@5.11.0) + transitivePeerDependencies: + - '@node-rs/xxhash' redux@4.2.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 reflect-metadata@0.1.14: {} @@ -35506,10 +35872,17 @@ snapshots: refractor@4.9.0: dependencies: '@types/hast': 2.3.10 - '@types/prismjs': 1.26.5 + '@types/prismjs': 1.26.6 hastscript: 7.2.0 parse-entities: 4.0.2 + refractor@5.0.0: + dependencies: + '@types/hast': 3.0.4 + '@types/prismjs': 1.26.6 + hastscript: 9.0.1 + parse-entities: 4.0.2 + regenerate-unicode-properties@10.2.2: dependencies: regenerate: 1.4.2 @@ -35554,13 +35927,13 @@ snapshots: hast-util-heading-rank: 3.0.0 hast-util-is-element: 3.0.0 unified: 11.0.5 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 rehype-ignore@2.0.3: dependencies: hast-util-select: 6.0.4 unified: 11.0.5 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 rehype-parse@9.0.1: dependencies: @@ -35575,16 +35948,16 @@ snapshots: refractor: 4.9.0 rehype-parse: 9.0.1 unist-util-filter: 5.0.1 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 - rehype-prism-plus@2.0.1: + rehype-prism-plus@2.0.2: dependencies: hast-util-to-string: 3.0.1 parse-numeric-range: 1.3.0 - refractor: 4.9.0 + refractor: 5.0.0 rehype-parse: 9.0.1 unist-util-filter: 5.0.1 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 rehype-raw@7.0.0: dependencies: @@ -35596,7 +35969,7 @@ snapshots: dependencies: hast-util-select: 6.0.4 unified: 11.0.5 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 rehype-slug@6.0.0: dependencies: @@ -35604,7 +35977,7 @@ snapshots: github-slugger: 2.0.0 hast-util-heading-rank: 3.0.0 hast-util-to-string: 3.0.1 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 rehype-stringify@10.0.1: dependencies: @@ -35632,7 +36005,7 @@ snapshots: remark-github-blockquote-alert@1.3.1: dependencies: - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 remark-math@6.0.0: dependencies: @@ -35654,7 +36027,7 @@ snapshots: remark-parse@11.0.0: dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.3 micromark-util-types: 2.0.2 unified: 11.0.5 transitivePeerDependencies: @@ -35687,7 +36060,7 @@ snapshots: request-promise-core@1.1.3(request@2.88.2): dependencies: - lodash: 4.17.21 + lodash: 4.17.23 request: 2.88.2 request@2.88.2: @@ -35707,7 +36080,7 @@ snapshots: mime-types: 2.1.35 oauth-sign: 0.9.0 performance-now: 2.1.0 - qs: 6.5.3 + qs: 6.5.5 safe-buffer: 5.2.1 tough-cookie: 2.5.0 tunnel-agent: 0.6.0 @@ -35763,15 +36136,12 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.8: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - resolve@2.0.0-next.5: + resolve@2.0.0-next.6: dependencies: + es-errors: 1.3.0 is-core-module: 2.16.1 + node-exports-info: 1.6.0 + object-keys: 1.1.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -35786,9 +36156,9 @@ snapshots: restructure@2.0.1: {} - retry-axios@2.6.0(axios@1.13.2): + retry-axios@2.6.0(axios@1.13.6): dependencies: - axios: 1.13.2(debug@4.4.3) + axios: 1.13.6(debug@4.4.3) retry@0.10.1: {} @@ -35816,7 +36186,7 @@ snapshots: debug: 4.4.3(supports-color@5.5.0) es-module-lexer: 1.7.0 esbuild: 0.25.12 - get-tsconfig: 4.13.0 + get-tsconfig: 4.13.6 rollup: 4.50.2 unplugin-utils: 0.2.5 transitivePeerDependencies: @@ -35857,32 +36227,35 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.50.2 fsevents: 2.3.3 - rollup@4.54.0: + rollup@4.59.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.54.0 - '@rollup/rollup-android-arm64': 4.54.0 - '@rollup/rollup-darwin-arm64': 4.54.0 - '@rollup/rollup-darwin-x64': 4.54.0 - '@rollup/rollup-freebsd-arm64': 4.54.0 - '@rollup/rollup-freebsd-x64': 4.54.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 - '@rollup/rollup-linux-arm-musleabihf': 4.54.0 - '@rollup/rollup-linux-arm64-gnu': 4.54.0 - '@rollup/rollup-linux-arm64-musl': 4.54.0 - '@rollup/rollup-linux-loong64-gnu': 4.54.0 - '@rollup/rollup-linux-ppc64-gnu': 4.54.0 - '@rollup/rollup-linux-riscv64-gnu': 4.54.0 - '@rollup/rollup-linux-riscv64-musl': 4.54.0 - '@rollup/rollup-linux-s390x-gnu': 4.54.0 - '@rollup/rollup-linux-x64-gnu': 4.54.0 - '@rollup/rollup-linux-x64-musl': 4.54.0 - '@rollup/rollup-openharmony-arm64': 4.54.0 - '@rollup/rollup-win32-arm64-msvc': 4.54.0 - '@rollup/rollup-win32-ia32-msvc': 4.54.0 - '@rollup/rollup-win32-x64-gnu': 4.54.0 - '@rollup/rollup-win32-x64-msvc': 4.54.0 + '@rollup/rollup-android-arm-eabi': 4.59.0 + '@rollup/rollup-android-arm64': 4.59.0 + '@rollup/rollup-darwin-arm64': 4.59.0 + '@rollup/rollup-darwin-x64': 4.59.0 + '@rollup/rollup-freebsd-arm64': 4.59.0 + '@rollup/rollup-freebsd-x64': 4.59.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.59.0 + '@rollup/rollup-linux-arm-musleabihf': 4.59.0 + '@rollup/rollup-linux-arm64-gnu': 4.59.0 + '@rollup/rollup-linux-arm64-musl': 4.59.0 + '@rollup/rollup-linux-loong64-gnu': 4.59.0 + '@rollup/rollup-linux-loong64-musl': 4.59.0 + '@rollup/rollup-linux-ppc64-gnu': 4.59.0 + '@rollup/rollup-linux-ppc64-musl': 4.59.0 + '@rollup/rollup-linux-riscv64-gnu': 4.59.0 + '@rollup/rollup-linux-riscv64-musl': 4.59.0 + '@rollup/rollup-linux-s390x-gnu': 4.59.0 + '@rollup/rollup-linux-x64-gnu': 4.59.0 + '@rollup/rollup-linux-x64-musl': 4.59.0 + '@rollup/rollup-openbsd-x64': 4.59.0 + '@rollup/rollup-openharmony-arm64': 4.59.0 + '@rollup/rollup-win32-arm64-msvc': 4.59.0 + '@rollup/rollup-win32-ia32-msvc': 4.59.0 + '@rollup/rollup-win32-x64-gnu': 4.59.0 + '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 rope-sequence@1.3.4: {} @@ -35897,18 +36270,18 @@ snapshots: transitivePeerDependencies: - supports-color - rpc-websockets@9.3.2: + rpc-websockets@9.3.5: dependencies: '@swc/helpers': 0.5.13 - '@types/uuid': 8.3.4 + '@types/uuid': 10.0.0 '@types/ws': 8.18.1 buffer: 6.0.3 - eventemitter3: 5.0.1 - uuid: 8.3.2 - ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) + eventemitter3: 5.0.4 + uuid: 11.1.0 + ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) optionalDependencies: bufferutil: 4.1.0 - utf-8-validate: 5.0.10 + utf-8-validate: 6.0.6 rrweb-cssom@0.6.0: {} @@ -35925,6 +36298,8 @@ snapshots: run-async@2.4.1: {} + run-async@3.0.0: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -35984,15 +36359,15 @@ snapshots: sane-domparser-error@0.2.0: {} - sass@1.97.1: + sass@1.97.3: dependencies: chokidar: 4.0.3 immutable: 5.1.4 source-map-js: 1.2.1 optionalDependencies: - '@parcel/watcher': 2.5.1 + '@parcel/watcher': 2.5.6 - sax@1.4.3: {} + sax@1.4.4: {} saxes@6.0.0: dependencies: @@ -36007,15 +36382,15 @@ snapshots: schema-utils@3.3.0: dependencies: '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) + ajv: 6.14.0 + ajv-keywords: 3.5.2(ajv@6.14.0) schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 - ajv: 8.17.1 - ajv-formats: 2.1.1(ajv@8.17.1) - ajv-keywords: 5.1.0(ajv@8.17.1) + ajv: 8.18.0 + ajv-formats: 2.1.1(ajv@8.18.0) + ajv-keywords: 5.1.0(ajv@8.18.0) scroll-into-view-if-needed@2.2.31: dependencies: @@ -36035,29 +36410,13 @@ snapshots: semver-truncate@3.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 semver@6.3.1: {} semver@7.7.3: {} - send@0.19.0: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color + semver@7.7.4: {} send@0.19.2: dependencies: @@ -36105,15 +36464,6 @@ snapshots: dependencies: randombytes: 2.1.0 - serve-static@1.16.2: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.0 - transitivePeerDependencies: - - supports-color - serve-static@1.16.3: dependencies: encodeurl: 2.0.0 @@ -36177,7 +36527,7 @@ snapshots: dependencies: color: 4.2.3 detect-libc: 2.1.2 - semver: 7.7.3 + semver: 7.7.4 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 @@ -36271,7 +36621,7 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.7.3 + semver: 7.7.4 simple-wcswidth@1.1.2: {} @@ -36294,11 +36644,11 @@ snapshots: dependencies: '@juggle/resize-observer': 3.4.0 '@types/is-hotkey': 0.1.10 - '@types/lodash': 4.17.21 + '@types/lodash': 4.17.24 direction: 1.0.4 is-hotkey: 0.1.8 is-plain-object: 5.0.0 - lodash: 4.17.21 + lodash: 4.17.23 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) scroll-into-view-if-needed: 2.2.31 @@ -36320,21 +36670,21 @@ snapshots: dot-case: 3.0.4 tslib: 2.8.1 - socket.io-client@4.8.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): + socket.io-client@4.8.3(bufferutil@4.1.0)(utf-8-validate@5.0.10): dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 - engine.io-client: 6.6.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) - socket.io-parser: 4.2.4 + debug: 4.4.3(supports-color@5.5.0) + engine.io-client: 6.6.4(bufferutil@4.1.0)(utf-8-validate@5.0.10) + socket.io-parser: 4.2.5 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-parser@4.2.4: + socket.io-parser@4.2.5: dependencies: '@socket.io/component-emitter': 3.1.2 - debug: 4.3.7 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -36355,7 +36705,7 @@ snapshots: dependencies: atomic-sleep: 1.0.0 - sonic-boom@4.2.0: + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -36367,15 +36717,15 @@ snapshots: dependencies: is-plain-obj: 1.1.0 - sortablejs@1.15.6: {} + sortablejs@1.15.7: {} source-map-js@1.2.1: {} - source-map-loader@4.0.2(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + source-map-loader@4.0.2(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) source-map-support@0.5.13: dependencies: @@ -36441,8 +36791,6 @@ snapshots: statuses@1.5.0: {} - statuses@2.0.1: {} - statuses@2.0.2: {} std-env@3.10.0: {} @@ -36459,7 +36807,7 @@ snapshots: storybook-source-link@4.0.1(@storybook/addons@7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@storybook/addons': 7.6.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/preview-api': 7.6.21 + '@storybook/preview-api': 7.6.23 '@types/react': 18.3.1 react-error-boundary: 4.1.2(react@18.3.1) optionalDependencies: @@ -36498,7 +36846,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 string.prototype.includes@2.0.1: dependencies: @@ -36567,7 +36915,7 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.2: + strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 @@ -36594,13 +36942,13 @@ snapshots: stripe@15.12.0: dependencies: '@types/node': 18.16.9 - qs: 6.14.0 + qs: 6.15.0 striptags@3.2.0: {} strnum@1.1.2: {} - strnum@2.1.2: {} + strnum@2.2.0: {} strtok3@10.3.4: dependencies: @@ -36628,12 +36976,12 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - styled-jsx@5.1.1(@babel/core@7.28.5)(babel-plugin-macros@3.1.0)(react@18.3.1): + styled-jsx@5.1.1(@babel/core@7.29.0)(babel-plugin-macros@3.1.0)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 babel-plugin-macros: 3.1.0 stylis-plugin-rtl@2.1.1(stylis@4.3.6): @@ -36696,15 +37044,15 @@ snapshots: swagger-ui-dist@5.17.14: {} - swc-loader@0.2.6(@swc/core@1.5.7(@swc/helpers@0.5.13))(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + swc-loader@0.2.7(@swc/core@1.5.7(@swc/helpers@0.5.13))(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.13) '@swc/counter': 0.1.3 - webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + webpack: 5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) sweetalert2@11.4.8: {} - swr@2.3.8(react@18.3.1): + swr@2.4.1(react@18.3.1): dependencies: dequal: 2.0.3 react: 18.3.1 @@ -36716,7 +37064,7 @@ snapshots: synchronous-promise@2.0.17: {} - tabbable@6.3.0: {} + tabbable@6.4.0: {} tailwind-merge@1.14.0: {} @@ -36753,7 +37101,7 @@ snapshots: transitivePeerDependencies: - ts-node - tailwindcss@4.1.18: {} + tailwindcss@4.2.1: {} tapable@2.3.0: {} @@ -36777,14 +37125,14 @@ snapshots: dependencies: memoizerific: 1.11.3 - terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): + terser-webpack-plugin@5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.44.1 - webpack: 5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) + terser: 5.46.0 + webpack: 5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) optionalDependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.13) esbuild: 0.25.12 @@ -36795,16 +37143,16 @@ snapshots: jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.44.1 + terser: 5.46.0 webpack: 5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12) optionalDependencies: '@swc/core': 1.5.7(@swc/helpers@0.5.13) esbuild: 0.25.12 - terser@5.44.1: + terser@5.46.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.15.0 + acorn: 8.16.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -36812,7 +37160,7 @@ snapshots: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 - minimatch: 3.1.2 + minimatch: 3.1.5 text-encoding-utf-8@1.0.2: {} @@ -36917,9 +37265,9 @@ snapshots: '@tokenizer/token': 0.3.0 ieee754: 1.2.1 - token-types@6.1.1: + token-types@6.1.2: dependencies: - '@borewit/text-codec': 0.1.1 + '@borewit/text-codec': 0.2.1 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -36962,7 +37310,7 @@ snapshots: got: 11.8.6 into-stream: 6.0.0 is-stream: 2.0.1 - lodash: 4.17.21 + lodash: 4.17.23 p-map: 4.0.0 tus-js-client: 2.3.2 uuid: 8.3.2 @@ -37005,7 +37353,7 @@ snapshots: dependencies: tslib: 2.8.1 - ts-jest@29.4.6(@babel/core@7.28.5)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.28.5))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4): + ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.0))(esbuild@0.25.12)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.16.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@swc/core@1.5.7(@swc/helpers@0.5.13))(@types/node@18.16.9)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 @@ -37014,15 +37362,15 @@ snapshots: json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.7.3 + semver: 7.7.4 type-fest: 4.41.0 typescript: 5.5.4 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.28.5) + babel-jest: 29.7.0(@babel/core@7.29.0) esbuild: 0.25.12 jest-util: 29.7.0 @@ -37034,11 +37382,11 @@ snapshots: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 '@types/node': 18.16.9 - acorn: 8.15.0 - acorn-walk: 8.3.4 + acorn: 8.16.0 + acorn-walk: 8.3.5 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.2 + diff: 4.0.4 make-error: 1.3.6 typescript: 5.5.4 v8-compile-cache-lib: 3.0.1 @@ -37053,7 +37401,7 @@ snapshots: tsconfig-paths-webpack-plugin@4.0.1: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.20.0 tsconfig-paths: 4.2.0 tsconfig-paths@3.15.0: @@ -37077,18 +37425,18 @@ snapshots: tsup@8.5.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(jiti@2.6.1)(postcss@8.4.38)(typescript@5.5.4)(yaml@2.8.2): dependencies: - bundle-require: 5.1.0(esbuild@0.27.2) + bundle-require: 5.1.0(esbuild@0.27.3) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 debug: 4.4.3(supports-color@5.5.0) - esbuild: 0.27.2 + esbuild: 0.27.3 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.4.38)(yaml@2.8.2) resolve-from: 5.0.0 - rollup: 4.54.0 + rollup: 4.59.0 source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 @@ -37134,11 +37482,11 @@ snapshots: twemoji-parser@11.0.2: {} - twitter-api-v2@1.28.0: {} + twitter-api-v2@1.29.0: {} twitter-text@3.1.0: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 core-js: 2.6.12 punycode: 1.4.1 twemoji-parser: 11.0.2 @@ -37159,18 +37507,18 @@ snapshots: type-fest@4.41.0: {} - type-graphql@2.0.0-rc.1(class-validator@0.14.3)(graphql-scalars@1.25.0(graphql@16.12.0))(graphql@16.12.0): + type-graphql@2.0.0-rc.1(class-validator@0.14.4)(graphql-scalars@1.25.0(graphql@16.13.0))(graphql@16.13.0): dependencies: '@graphql-yoga/subscription': 5.0.5 '@types/node': 18.16.9 '@types/semver': 7.7.1 - graphql: 16.12.0 - graphql-query-complexity: 0.12.0(graphql@16.12.0) - graphql-scalars: 1.25.0(graphql@16.12.0) - semver: 7.7.3 + graphql: 16.13.0 + graphql-query-complexity: 0.12.0(graphql@16.13.0) + graphql-scalars: 1.25.0(graphql@16.13.0) + semver: 7.7.4 tslib: 2.8.1 optionalDependencies: - class-validator: 0.14.3 + class-validator: 0.14.4 type-is@1.6.18: dependencies: @@ -37228,7 +37576,7 @@ snapshots: uc.micro@2.1.0: {} - ufo@1.6.1: {} + ufo@1.6.3: {} uglify-js@3.19.3: optional: true @@ -37260,7 +37608,9 @@ snapshots: undici-types@5.26.5: {} - undici@7.16.0: {} + undici-types@6.21.0: {} + + undici@7.22.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -37278,7 +37628,7 @@ snapshots: unicode-property-aliases-ecmascript@2.2.0: {} - unicode-segmenter@0.14.4: {} + unicode-segmenter@0.14.5: {} unicode-trie@2.0.0: dependencies: @@ -37342,7 +37692,7 @@ snapshots: unist-util-remove-position@5.0.0: dependencies: '@types/unist': 3.0.3 - unist-util-visit: 5.0.0 + unist-util-visit: 5.1.0 unist-util-stringify-position@3.0.3: dependencies: @@ -37374,6 +37724,12 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + universalify@0.2.0: {} universalify@2.0.1: {} @@ -37387,13 +37743,6 @@ snapshots: pathe: 2.0.3 picomatch: 4.0.3 - unplugin@1.0.1: - dependencies: - acorn: 8.15.0 - chokidar: 3.6.0 - webpack-sources: 3.3.3 - webpack-virtual-modules: 0.5.0 - unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -37418,20 +37767,20 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - unstorage@1.17.3(@upstash/redis@1.35.8)(idb-keyval@6.2.2)(ioredis@5.8.2): + unstorage@1.17.4(@upstash/redis@1.36.3)(idb-keyval@6.2.2)(ioredis@5.10.0): dependencies: anymatch: 3.1.3 - chokidar: 4.0.3 + chokidar: 5.0.0 destr: 2.0.5 - h3: 1.15.4 - lru-cache: 10.4.3 + h3: 1.15.5 + lru-cache: 11.2.6 node-fetch-native: 1.6.7 ofetch: 1.5.1 - ufo: 1.6.1 + ufo: 1.6.3 optionalDependencies: - '@upstash/redis': 1.35.8 + '@upstash/redis': 1.36.3 idb-keyval: 6.2.2 - ioredis: 5.8.2 + ioredis: 5.10.0 untruncate-json@0.0.1: {} @@ -37463,13 +37812,13 @@ snapshots: url@0.11.4: dependencies: punycode: 1.4.1 - qs: 6.14.0 + qs: 6.15.0 urlpattern-polyfill@10.1.0: {} - urql@4.2.2(@urql/core@5.2.0(graphql@16.12.0))(react@18.3.1): + urql@4.2.2(@urql/core@5.2.0(graphql@16.13.0))(react@18.3.1): dependencies: - '@urql/core': 5.2.0(graphql@16.12.0) + '@urql/core': 5.2.0(graphql@16.13.0) react: 18.3.1 wonka: 6.3.5 @@ -37486,7 +37835,7 @@ snapshots: optionalDependencies: '@types/react': 18.3.1 - use-debounce@10.0.6(react@18.3.1): + use-debounce@10.1.0(react@18.3.1): dependencies: react: 18.3.1 @@ -37528,6 +37877,11 @@ snapshots: dependencies: node-gyp-build: 4.8.4 + utf-8-validate@6.0.6: + dependencies: + node-gyp-build: 4.8.4 + optional: true + util-deprecate@1.0.2: {} util@0.12.5: @@ -37536,7 +37890,7 @@ snapshots: is-arguments: 1.2.0 is-generator-function: 1.1.2 is-typed-array: 1.1.15 - which-typed-array: 1.1.19 + which-typed-array: 1.1.20 utils-merge@1.0.1: {} @@ -37560,7 +37914,7 @@ snapshots: uvu@0.5.6: dependencies: dequal: 2.0.3 - diff: 5.2.0 + diff: 5.2.2 kleur: 4.1.5 sade: 1.8.1 @@ -37635,7 +37989,7 @@ snapshots: - utf-8-validate - zod - viem@2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): + viem@2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -37643,7 +37997,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.5.4)(zod@3.22.4) isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.11.1(typescript@5.5.4)(zod@3.22.4) + ox: 0.12.4(typescript@5.5.4)(zod@3.22.4) ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.5.4 @@ -37652,7 +38006,7 @@ snapshots: - utf-8-validate - zod - viem@2.43.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.46.3(bufferutil@4.1.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.25.76): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 @@ -37660,7 +38014,7 @@ snapshots: '@scure/bip39': 1.6.0 abitype: 1.2.3(typescript@5.5.4)(zod@3.25.76) isows: 1.0.7(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)) - ox: 0.11.1(typescript@5.5.4)(zod@3.25.76) + ox: 0.12.4(typescript@5.5.4)(zod@3.25.76) ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10) optionalDependencies: typescript: 5.5.4 @@ -37669,13 +38023,13 @@ snapshots: - utf-8-validate - zod - vite-node@3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2): + vite-node@3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3(supports-color@5.5.0) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -37690,38 +38044,38 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)): + vite-tsconfig-paths@5.1.4(typescript@5.5.4)(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)): dependencies: debug: 4.4.3(supports-color@5.5.0) globrex: 0.1.2 tsconfck: 3.1.6(typescript@5.5.4) optionalDependencies: - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) transitivePeerDependencies: - supports-color - typescript - vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2): + vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.54.0 + rollup: 4.59.0 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 18.16.9 fsevents: 2.3.3 jiti: 2.6.1 - lightningcss: 1.30.2 - sass: 1.97.1 - terser: 5.44.1 + lightningcss: 1.31.1 + sass: 1.97.3 + terser: 5.46.0 yaml: 2.8.2 - vitest@3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2): + vitest@3.1.4(@types/debug@4.1.12)(@types/node@18.16.9)(@vitest/ui@1.6.0)(happy-dom@15.11.7)(jiti@2.6.1)(jsdom@22.1.0(bufferutil@4.1.0)(canvas@2.11.2)(utf-8-validate@5.0.10))(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2): dependencies: '@vitest/expect': 3.1.4 - '@vitest/mocker': 3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2)) + '@vitest/mocker': 3.1.4(vite@6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.1.4 '@vitest/snapshot': 3.1.4 @@ -37738,8 +38092,8 @@ snapshots: tinyglobby: 0.2.15 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) - vite-node: 3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.8.2) + vite: 6.4.1(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) + vite-node: 3.1.4(@types/node@18.16.9)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 @@ -37779,7 +38133,7 @@ snapshots: dependencies: loose-envify: 1.4.0 - watchpack@2.4.4: + watchpack@2.5.1: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -37788,11 +38142,12 @@ snapshots: dependencies: defaults: 1.0.4 - weaviate-client@3.10.0: + weaviate-client@3.11.0: dependencies: + '@datastructures-js/deque': 1.0.8 abort-controller-x: 0.5.0 - graphql: 16.12.0 - graphql-request: 6.1.0(graphql@16.12.0) + graphql: 16.13.0 + graphql-request: 6.1.0(graphql@16.13.0) long: 5.3.2 nice-grpc: 2.1.14 nice-grpc-client-middleware-retry: 3.1.13 @@ -37807,7 +38162,7 @@ snapshots: web-streams-polyfill@4.0.0-beta.3: {} - web-vitals@4.2.4: {} + web-vitals@5.1.0: {} webextension-polyfill@0.12.0: {} @@ -37817,11 +38172,9 @@ snapshots: webpack-node-externals@3.0.0: {} - webpack-sources@3.3.3: {} + webpack-sources@3.3.4: {} - webpack-virtual-modules@0.5.0: {} - - webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12): + webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -37829,11 +38182,11 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-phases: 1.0.4(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-phases: 1.0.4(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.20.0 es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -37845,9 +38198,9 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.104.1(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) - watchpack: 2.4.4 - webpack-sources: 3.3.3 + terser-webpack-plugin: 5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.105.3(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) + watchpack: 2.5.1 + webpack-sources: 3.3.4 transitivePeerDependencies: - '@swc/core' - esbuild @@ -37860,11 +38213,11 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.15.0 - acorn-import-assertions: 1.9.0(acorn@8.15.0) + acorn: 8.16.0 + acorn-import-assertions: 1.9.0(acorn@8.16.0) browserslist: 4.28.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.4 + enhanced-resolve: 5.20.0 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -37877,8 +38230,8 @@ snapshots: schema-utils: 3.3.0 tapable: 2.3.0 terser-webpack-plugin: 5.3.16(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)(webpack@5.87.0(@swc/core@1.5.7(@swc/helpers@0.5.13))(esbuild@0.25.12)) - watchpack: 2.4.4 - webpack-sources: 3.3.3 + watchpack: 2.5.1 + webpack-sources: 3.3.4 transitivePeerDependencies: - '@swc/core' - esbuild @@ -37940,7 +38293,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.19 + which-typed-array: 1.1.20 which-collection@1.0.2: dependencies: @@ -37951,7 +38304,7 @@ snapshots: which-module@2.0.1: {} - which-typed-array@1.1.19: + which-typed-array@1.1.20: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 @@ -37969,10 +38322,6 @@ snapshots: dependencies: isexe: 2.0.0 - which@4.0.0: - dependencies: - isexe: 3.1.1 - why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -38010,7 +38359,7 @@ snapshots: dependencies: ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.2 + strip-ansi: 7.2.0 wrappy@1.0.2: {} @@ -38024,11 +38373,6 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 - ws@8.17.1(bufferutil@4.1.0)(utf-8-validate@5.0.10): - optionalDependencies: - bufferutil: 4.1.0 - utf-8-validate: 5.0.10 - ws@8.18.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.1.0 @@ -38039,15 +38383,25 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 5.0.10 + ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 5.0.10 + + ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@6.0.6): + optionalDependencies: + bufferutil: 4.1.0 + utf-8-validate: 6.0.6 + wsl-utils@0.1.0: dependencies: - is-wsl: 3.1.0 + is-wsl: 3.1.1 xml-name-validator@4.0.0: {} xml2js@0.5.0: dependencies: - sax: 1.4.3 + sax: 1.4.4 xmlbuilder: 11.0.1 xml@1.0.1: {} @@ -38060,7 +38414,7 @@ snapshots: xmlserializer@0.6.1: {} - xstate@5.25.0: {} + xstate@5.28.0: {} xtend@4.0.2: {} @@ -38134,9 +38488,9 @@ snapshots: zod-from-json-schema@0.5.2: dependencies: - zod: 4.2.1 + zod: 4.3.6 - zod-to-json-schema@3.25.0(zod@3.25.76): + zod-to-json-schema@3.25.1(zod@3.25.76): dependencies: zod: 3.25.76 @@ -38144,9 +38498,9 @@ snapshots: zod@3.25.76: {} - zod@4.2.1: {} + zod@4.3.6: {} - zustand@5.0.9(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): + zustand@5.0.11(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)(use-sync-external-store@1.6.0(react@18.3.1)): optionalDependencies: '@types/react': 18.3.1 immer: 9.0.21 From 5751a62f444b41bfc263145f6d09ec8f1a407dcc Mon Sep 17 00:00:00 2001 From: Nevo David Date: Sun, 1 Mar 2026 17:13:39 +0700 Subject: [PATCH 290/340] feat: multiple sses --- libraries/nestjs-libraries/src/chat/start.mcp.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/nestjs-libraries/src/chat/start.mcp.ts b/libraries/nestjs-libraries/src/chat/start.mcp.ts index 7e8fab5c..a9018a50 100644 --- a/libraries/nestjs-libraries/src/chat/start.mcp.ts +++ b/libraries/nestjs-libraries/src/chat/start.mcp.ts @@ -13,12 +13,14 @@ export const startMcp = async (app: INestApplication) => { const agent = mastra.getAgent('postiz'); const tools = await agent.getTools(); - const server = new MCPServer({ + const serverConfig = { name: 'Postiz MCP', version: '1.0.0', tools, agents: { postiz: agent }, - }); + }; + + const server = new MCPServer(serverConfig); app.use('/mcp', async (req: Request, res: Response, next: () => void) => { // Skip if this is the /mcp/:id route @@ -140,7 +142,7 @@ export const startMcp = async (app: INestApplication) => { // @ts-ignore { requestId: req.params.id, auth: req.auth }, async () => { - await server.startSSE({ + await new MCPServer(serverConfig).startSSE({ url, ssePath: `/sse/${req.params.id}`, messagePath: `/message/${req.params.id}`, From daa1e0f91b3da84f7066b6297e25928a7663b99b Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 2 Mar 2026 14:11:34 +0700 Subject: [PATCH 291/340] feat: google my business pagination --- .../src/integrations/social/gmb.provider.ts | 144 +++++++++++------- 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts index 6c472da2..797c5441 100644 --- a/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/gmb.provider.ts @@ -174,18 +174,31 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { } async pages(accessToken: string) { - // Get all accounts first - const accountsResponse = await fetch( - 'https://mybusinessaccountmanagement.googleapis.com/v1/accounts', - { + // Get all accounts with pagination + const allAccounts: any[] = []; + let accountsPageToken: string | undefined; + + do { + const params = new URLSearchParams(); + if (accountsPageToken) { + params.set('pageToken', accountsPageToken); + } + const url = `https://mybusinessaccountmanagement.googleapis.com/v1/accounts${params.toString() ? `?${params}` : ''}`; + + const accountsResponse = await fetch(url, { headers: { Authorization: `Bearer ${accessToken}`, }, - } - ); - const accountsData = await accountsResponse.json(); + }); + const accountsData = await accountsResponse.json(); - if (!accountsData.accounts || accountsData.accounts.length === 0) { + if (accountsData.accounts) { + allAccounts.push(...accountsData.accounts); + } + accountsPageToken = accountsData.nextPageToken; + } while (accountsPageToken); + + if (allAccounts.length === 0) { return []; } @@ -198,65 +211,78 @@ export class GmbProvider extends SocialAbstract implements SocialProvider { locationName: string; }> = []; - for (const account of accountsData.accounts) { + for (const account of allAccounts) { const accountName = account.name; // format: accounts/{accountId} try { - const locationsResponse = await fetch( - `https://mybusinessbusinessinformation.googleapis.com/v1/${accountName}/locations?readMask=name,title,storefrontAddress,metadata`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, + // Get all locations with pagination + let locationsPageToken: string | undefined; + + do { + const params = new URLSearchParams({ + readMask: 'name,title,storefrontAddress,metadata', + }); + if (locationsPageToken) { + params.set('pageToken', locationsPageToken); } - ); - const locationsData = await locationsResponse.json(); - if (locationsData.locations) { - for (const location of locationsData.locations) { - // location.name is in format: locations/{locationId} - // We need the full path: accounts/{accountId}/locations/{locationId} - const locationId = location.name.replace('locations/', ''); - const fullResourceName = `${accountName}/locations/${locationId}`; - - // Get profile photo if available - let photoUrl = ''; - try { - const mediaResponse = await fetch( - `https://mybusinessbusinessinformation.googleapis.com/v1/${location.name}/media`, - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - } - ); - const mediaData = await mediaResponse.json(); - if (mediaData.mediaItems && mediaData.mediaItems.length > 0) { - const profilePhoto = mediaData.mediaItems.find( - (m: any) => - m.mediaFormat === 'PHOTO' && - m.locationAssociation?.category === 'PROFILE' - ); - if (profilePhoto?.googleUrl) { - photoUrl = profilePhoto.googleUrl; - } else if (mediaData.mediaItems[0]?.googleUrl) { - photoUrl = mediaData.mediaItems[0].googleUrl; - } - } - } catch { - // Ignore media fetch errors + const locationsResponse = await fetch( + `https://mybusinessbusinessinformation.googleapis.com/v1/${accountName}/locations?${params}`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, } + ); + const locationsData = await locationsResponse.json(); - allLocations.push({ - // id is the full resource path for the v4 API: accounts/{accountId}/locations/{locationId} - id: fullResourceName, - name: location.title || 'Unnamed Location', - picture: { data: { url: photoUrl } }, - accountName: accountName, - locationName: location.name, - }); + if (locationsData.locations) { + for (const location of locationsData.locations) { + // location.name is in format: locations/{locationId} + // We need the full path: accounts/{accountId}/locations/{locationId} + const locationId = location.name.replace('locations/', ''); + const fullResourceName = `${accountName}/locations/${locationId}`; + + // Get profile photo if available + let photoUrl = ''; + try { + const mediaResponse = await fetch( + `https://mybusinessbusinessinformation.googleapis.com/v1/${location.name}/media`, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + } + ); + const mediaData = await mediaResponse.json(); + if (mediaData.mediaItems && mediaData.mediaItems.length > 0) { + const profilePhoto = mediaData.mediaItems.find( + (m: any) => + m.mediaFormat === 'PHOTO' && + m.locationAssociation?.category === 'PROFILE' + ); + if (profilePhoto?.googleUrl) { + photoUrl = profilePhoto.googleUrl; + } else if (mediaData.mediaItems[0]?.googleUrl) { + photoUrl = mediaData.mediaItems[0].googleUrl; + } + } + } catch { + // Ignore media fetch errors + } + + allLocations.push({ + // id is the full resource path for the v4 API: accounts/{accountId}/locations/{locationId} + id: fullResourceName, + name: location.title || 'Unnamed Location', + picture: { data: { url: photoUrl } }, + accountName: accountName, + locationName: location.name, + }); + } } - } + locationsPageToken = locationsData.nextPageToken; + } while (locationsPageToken); } catch (error) { // Continue with other accounts if one fails console.error( From 16713f03b688b5e9fe8a55d031872ea7b6aec122 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 2 Mar 2026 14:20:43 +0700 Subject: [PATCH 292/340] feat: facebook and instagram connect --- .../integrations/social/facebook.provider.ts | 63 ++++++++++++++++-- .../integrations/social/instagram.provider.ts | 65 +++++++++++++++++-- 2 files changed, 115 insertions(+), 13 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts index ccad72e7..0050131e 100644 --- a/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/facebook.provider.ts @@ -262,13 +262,64 @@ export class FacebookProvider extends SocialAbstract implements SocialProvider { } async pages(accessToken: string) { - const { data } = await ( - await fetch( - `https://graph.facebook.com/v20.0/me/accounts?fields=id,username,name,picture.type(large)&access_token=${accessToken}` - ) - ).json(); + const seenIds = new Set(); + const allPages: any[] = []; - return data; + const fetchPaginated = async (startUrl: string) => { + let nextUrl: string | undefined = startUrl; + while (nextUrl) { + const response = await (await fetch(nextUrl)).json(); + if (response.data) { + for (const page of response.data) { + if (!seenIds.has(page.id)) { + seenIds.add(page.id); + allPages.push(page); + } + } + } + nextUrl = response.paging?.next; + } + }; + + // Fetch pages the user explicitly shared during the OAuth dialog + await fetchPaginated( + `https://graph.facebook.com/v20.0/me/accounts?fields=id,username,name,picture.type(large)&limit=100&access_token=${accessToken}` + ); + + // Also fetch pages via Business Manager API to discover pages + // not selected during the OAuth page selection step + try { + let bizUrl: string | undefined = + `https://graph.facebook.com/v20.0/me/businesses?access_token=${accessToken}`; + + while (bizUrl) { + const bizResponse = await (await fetch(bizUrl)).json(); + if (bizResponse.data) { + for (const business of bizResponse.data) { + try { + await fetchPaginated( + `https://graph.facebook.com/v20.0/${business.id}/owned_pages?fields=id,username,name,picture.type(large)&limit=100&access_token=${accessToken}` + ); + } catch { + // Continue with other businesses + } + + try { + await fetchPaginated( + `https://graph.facebook.com/v20.0/${business.id}/client_pages?fields=id,username,name,picture.type(large)&limit=100&access_token=${accessToken}` + ); + } catch { + // Continue with other businesses + } + } + } + bizUrl = bizResponse.paging?.next; + } + } catch { + // Business Manager API not available for all users + } + + return allPages; } async fetchPageInformation(accessToken: string, data: { page: string }) { diff --git a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts index 47fd7d91..435d23e9 100644 --- a/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/instagram.provider.ts @@ -412,21 +412,72 @@ export class InstagramProvider } async pages(accessToken: string) { - const { data } = await ( - await fetch( - `https://graph.facebook.com/v20.0/me/accounts?fields=id,instagram_business_account,username,name,picture.type(large)&access_token=${accessToken}&limit=500` - ) - ).json(); + const seenPageIds = new Set(); + const allFacebookPages: any[] = []; + + const fetchPaginated = async (startUrl: string) => { + let nextUrl: string | undefined = startUrl; + while (nextUrl) { + const response = await (await fetch(nextUrl)).json(); + if (response.data) { + for (const page of response.data) { + if (!seenPageIds.has(page.id)) { + seenPageIds.add(page.id); + allFacebookPages.push(page); + } + } + } + nextUrl = response.paging?.next; + } + }; + + // Fetch pages the user explicitly shared during the OAuth dialog + await fetchPaginated( + `https://graph.facebook.com/v20.0/me/accounts?fields=id,instagram_business_account,username,name,picture.type(large)&limit=100&access_token=${accessToken}` + ); + + // Also fetch pages via Business Manager API to discover pages + // not selected during the OAuth page selection step + try { + let bizUrl: string | undefined = + `https://graph.facebook.com/v20.0/me/businesses?access_token=${accessToken}`; + + while (bizUrl) { + const bizResponse = await (await fetch(bizUrl)).json(); + if (bizResponse.data) { + for (const business of bizResponse.data) { + try { + await fetchPaginated( + `https://graph.facebook.com/v20.0/${business.id}/owned_pages?fields=id,instagram_business_account,username,name,picture.type(large)&limit=100&access_token=${accessToken}` + ); + } catch { + // Continue with other businesses + } + + try { + await fetchPaginated( + `https://graph.facebook.com/v20.0/${business.id}/client_pages?fields=id,instagram_business_account,username,name,picture.type(large)&limit=100&access_token=${accessToken}` + ); + } catch { + // Continue with other businesses + } + } + } + bizUrl = bizResponse.paging?.next; + } + } catch { + // Business Manager API not available for all users + } const onlyConnectedAccounts = await Promise.all( - data + allFacebookPages .filter((f: any) => f.instagram_business_account) .map(async (p: any) => { return { pageId: p.id, ...(await ( await fetch( - `https://graph.facebook.com/v20.0/${p.instagram_business_account.id}?fields=name,profile_picture_url&access_token=${accessToken}&limit=500` + `https://graph.facebook.com/v20.0/${p.instagram_business_account.id}?fields=name,profile_picture_url&access_token=${accessToken}` ) ).json()), id: p.instagram_business_account.id, From 724e5bc497a48ff808332785853520e722a46fc0 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 2 Mar 2026 15:33:36 +0700 Subject: [PATCH 293/340] feat: don't post without a subscription --- apps/orchestrator/src/activities/post.activity.ts | 9 ++++++++- .../src/integrations/social/tiktok.provider.ts | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts index fcb9997e..bcf27f23 100644 --- a/apps/orchestrator/src/activities/post.activity.ts +++ b/apps/orchestrator/src/activities/post.activity.ts @@ -22,6 +22,7 @@ import { organizationId, postId as postIdSearchParam, } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute'; +import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service'; @Injectable() @Activity() @@ -33,7 +34,8 @@ export class PostActivity { private _integrationService: IntegrationService, private _refreshIntegrationService: RefreshIntegrationService, private _webhookService: WebhooksService, - private _temporalService: TemporalService + private _temporalService: TemporalService, + private _subscriptionService: SubscriptionService ) {} @ActivityMethod() @@ -83,6 +85,11 @@ export class PostActivity { @ActivityMethod() async getPostsList(orgId: string, postId: string) { + const subscription = await this._subscriptionService.getSubscription(orgId); + if (!subscription) { + return []; + } + const getPosts = await this._postService.getPostsRecursively( postId, true, diff --git a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts index b324a5a1..8930abba 100644 --- a/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/tiktok.provider.ts @@ -222,7 +222,7 @@ export class TiktokProvider extends SocialAbstract implements SocialProvider { if (body.indexOf('picture_size_check_failed') > -1) { return { type: 'bad-body' as const, - value: 'Picture / Video size is invalid, must be at least 720p', + value: 'Video must be at least 720p, Picture must no exceed 1080p', }; } From 98d9ddc83e3277c3a8b6f97e39f8ca5748d42b34 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 2 Mar 2026 17:12:54 +0700 Subject: [PATCH 294/340] feat: update stripe version --- .../src/services/stripe.service.ts | 123 +++++++++++------- package.json | 3 +- pnpm-lock.yaml | 51 ++++---- 3 files changed, 97 insertions(+), 80 deletions(-) diff --git a/libraries/nestjs-libraries/src/services/stripe.service.ts b/libraries/nestjs-libraries/src/services/stripe.service.ts index 455dae07..537eaa7a 100644 --- a/libraries/nestjs-libraries/src/services/stripe.service.ts +++ b/libraries/nestjs-libraries/src/services/stripe.service.ts @@ -5,16 +5,14 @@ import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/s import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { BillingSubscribeDto } from '@gitroom/nestjs-libraries/dtos/billing/billing.subscribe.dto'; -import { capitalize, groupBy } from 'lodash'; +import { groupBy } from 'lodash'; import { pricing } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/pricing'; import { AuthService } from '@gitroom/helpers/auth/auth.service'; import { TrackService } from '@gitroom/nestjs-libraries/track/track.service'; import { UsersService } from '@gitroom/nestjs-libraries/database/prisma/users/users.service'; import { TrackEnum } from '@gitroom/nestjs-libraries/user/track.enum'; -const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { - apiVersion: '2024-04-10', -}); +const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!); @Injectable() export class StripeService { @@ -102,17 +100,15 @@ export class StripeService { } async createSubscription(event: Stripe.CustomerSubscriptionCreatedEvent) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore const { uniqueId, billing, period, - }: { + } = event.data.object.metadata as { billing: 'STANDARD' | 'PRO'; period: 'MONTHLY' | 'YEARLY'; uniqueId: string; - } = event.data.object.metadata; + }; try { const check = await this.checkValidCard(event); @@ -134,17 +130,15 @@ export class StripeService { ); } async updateSubscription(event: Stripe.CustomerSubscriptionUpdatedEvent) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore const { uniqueId, billing, period, - }: { + } = event.data.object.metadata as { billing: 'STANDARD' | 'PRO'; period: 'MONTHLY' | 'YEARLY'; uniqueId: string; - } = event.data.object.metadata; + }; const check = await this.checkValidCard(event); if (!check) { @@ -199,8 +193,7 @@ export class StripeService { const productsList = groupBy( products.data.map((p) => ({ - // @ts-ignore - name: p.product?.name, + name: (p.product as Stripe.Product)?.name, recurring: p?.recurring?.interval!, price: p?.tiers?.[0]?.unit_amount! / 100, })), @@ -271,19 +264,21 @@ export class StripeService { }; try { - const price = await stripe.invoices.retrieveUpcoming({ + const price = await stripe.invoices.createPreview({ customer, subscription: currentUserSubscription?.data?.[0]?.id, - subscription_proration_behavior: 'create_prorations', - subscription_billing_cycle_anchor: 'now', - subscription_items: [ - { - id: currentUserSubscription?.data?.[0]?.items?.data?.[0]?.id, - price: findPrice?.id!, - quantity: 1, - }, - ], - subscription_proration_date: proration_date, + subscription_details: { + proration_behavior: 'create_prorations', + billing_cycle_anchor: 'now', + items: [ + { + id: currentUserSubscription?.data?.[0]?.items?.data?.[0]?.id, + price: findPrice?.id!, + quantity: 1, + }, + ], + proration_date: proration_date, + }, }); return { @@ -312,21 +307,49 @@ export class StripeService { await stripe.subscriptions.list({ customer, status: 'all', + expand: ['data.latest_invoice'], }) ).data.filter((f) => f.status !== 'canceled'), }; - const { cancel_at } = await stripe.subscriptions.update( - currentUserSubscription.data[0].id, - { - cancel_at_period_end: - !currentUserSubscription.data[0].cancel_at_period_end, - metadata: { - service: 'gitroom', - id, - }, - } - ); + const sub = currentUserSubscription.data[0]; + + // If the user is toggling back (un-cancelling), just remove the cancel + if (sub.cancel_at_period_end) { + const { cancel_at } = await stripe.subscriptions.update(sub.id, { + cancel_at_period_end: false, + metadata: { service: 'gitroom', id }, + }); + + return { + id, + cancel_at: cancel_at ? new Date(cancel_at * 1000) : undefined, + }; + } + + // Check if the latest invoice has a failed payment + const latestInvoice = sub.latest_invoice as Stripe.Invoice | null; + const hasFailedPayment = + sub.status === 'past_due' || + latestInvoice?.status === 'open' || + latestInvoice?.status === 'uncollectible'; + + if (hasFailedPayment) { + // Payment already failed — cancel immediately and delete subscription + await stripe.subscriptions.cancel(sub.id); + await this._subscriptionService.deleteSubscription(customer); + + return { + id, + cancel_at: new Date(), + }; + } + + // Payment succeeded — cancel at end of billing period + const { cancel_at } = await stripe.subscriptions.update(sub.id, { + cancel_at_period_end: true, + metadata: { service: 'gitroom', id }, + }); return { id, @@ -361,11 +384,16 @@ export class StripeService { const now = Math.floor(Date.now() / 1000); for (const promoCode of promotionCodes.data) { + const coupon = + typeof promoCode.promotion.coupon === 'string' + ? null + : promoCode.promotion.coupon; + // Check if it has autoapply metadata set to true (check both promo and coupon metadata) const autoApply = Object.assign( {}, promoCode.metadata, - promoCode.coupon.metadata + coupon?.metadata )?.autoapply; if (autoApply !== 'true') continue; @@ -373,8 +401,7 @@ export class StripeService { if (promoCode.expires_at && promoCode.expires_at < now) continue; // Check if the coupon has expired (redeem_by) - if (promoCode.coupon.redeem_by && promoCode.coupon.redeem_by < now) - continue; + if (coupon?.redeem_by && coupon.redeem_by < now) continue; // Check if max redemptions reached if ( @@ -403,15 +430,10 @@ export class StripeService { userId: string, allowTrial: boolean ) { - const stripeCustom = new Stripe(process.env.STRIPE_SECRET_KEY!, { - // @ts-ignore - apiVersion: '2025-03-31.basil', - }); - const user = await this._userService.getUserById(userId); try { - await stripeCustom.customers.update(customer, { + await stripe.customers.update(customer, { email: user.email, ...(body.dub ? { @@ -431,9 +453,7 @@ export class StripeService { } const isUtm = body.utm ? `&utm_source=${body.utm}` : ''; - // @ts-ignore - const { client_secret } = await stripeCustom.checkout.sessions.create({ - // @ts-ignore + const { client_secret } = await stripe.checkout.sessions.create({ ui_mode: 'custom', customer, return_url: @@ -805,8 +825,13 @@ export class StripeService { async paymentSucceeded(event: Stripe.InvoicePaymentSucceededEvent) { // get subscription from payment + const subscriptionId = + event.data.object.parent?.subscription_details?.subscription; + if (!subscriptionId) { + return { ok: true }; + } const subscription = await stripe.subscriptions.retrieve( - event.data.object.subscription as string + typeof subscriptionId === 'string' ? subscriptionId : subscriptionId.id ); const { userId, ud } = subscription.metadata; diff --git a/package.json b/package.json index 787c795c..4877bc52 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,6 @@ "@types/react-dropzone": "^4.2.2", "@types/remove-markdown": "^0.3.4", "@types/sha256": "^0.2.2", - "@types/stripe": "^8.0.417", "@types/striptags": "^0.0.5", "@types/yup": "^0.32.0", "@uidotdev/usehooks": "^2.4.1", @@ -225,7 +224,7 @@ "sharp": "^0.33.4", "simple-statistics": "^7.8.3", "slugify": "^1.6.6", - "stripe": "^15.5.0", + "stripe": "^20.4.0", "striptags": "^3.2.0", "subtitle": "4.2.2-alpha.0", "sweetalert2": "11.4.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e19f84f8..dd2638cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,7 +14,7 @@ importers: dependencies: '@ag-ui/mastra': specifier: 0.2.0 - version: 0.2.0(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(9413ba786a25fdda1f6d87c0f5568c1b))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) + version: 0.2.0(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@copilotkit/runtime@1.10.6(a81f1decc55e512f3b92296d45b735ac))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@ai-sdk/openai': specifier: ^2.0.52 version: 2.0.95(zod@3.25.76) @@ -41,7 +41,7 @@ importers: version: 1.10.6(@types/react@18.3.1)(graphql@16.13.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@copilotkit/runtime': specifier: 1.10.6 - version: 1.10.6(9413ba786a25fdda1f6d87c0f5568c1b) + version: 1.10.6(a81f1decc55e512f3b92296d45b735ac) '@dub/analytics': specifier: ^0.0.32 version: 0.0.32 @@ -258,9 +258,6 @@ importers: '@types/sha256': specifier: ^0.2.2 version: 0.2.2 - '@types/stripe': - specifier: ^8.0.417 - version: 8.0.417 '@types/striptags': specifier: ^0.0.5 version: 0.0.5 @@ -562,8 +559,8 @@ importers: specifier: ^1.6.6 version: 1.6.6 stripe: - specifier: ^15.5.0 - version: 15.12.0 + specifier: ^20.4.0 + version: 20.4.0(@types/node@18.16.9) striptags: specifier: ^3.2.0 version: 3.2.0 @@ -7831,10 +7828,6 @@ packages: '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} - '@types/stripe@8.0.417': - resolution: {integrity: sha512-PTuqskh9YKNENnOHGVJBm4sM0zE8B1jZw1JIskuGAPkMB+OH236QeN8scclhYGPA4nG6zTtPXgwpXdp+HPDTVw==} - deprecated: This is a stub types definition. stripe provides its own type definitions, so you do not need this installed. - '@types/striptags@0.0.5': resolution: {integrity: sha512-mBUyVLvQJcRlqbCsO6ma/YblmFGSo8Jo8T0+ZzBate9Y8gTtLHemeiebzAFSBfF97EsycYQnXHCDkNeduiKPyA==} @@ -15774,9 +15767,14 @@ packages: resolution: {integrity: sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - stripe@15.12.0: - resolution: {integrity: sha512-slTbYS1WhRJXVB8YXU8fgHizkUrM9KJyrw4Dd8pLEwzKHYyQTIE46EePC2MVbSDZdE24o1GdNtzmJV4PrPpmJA==} - engines: {node: '>=12.*'} + stripe@20.4.0: + resolution: {integrity: sha512-F/aN1IQ9vHmlyLNi3DkiIbyzQb6gyBG0uYFd/VrEVQSc9BLtlgknPUx0EvzZdBMRLFuRaPFIFd7Mxwtg7Pbwzw==} + engines: {node: '>=16'} + peerDependencies: + '@types/node': '>=16' + peerDependenciesMeta: + '@types/node': + optional: true striptags@3.2.0: resolution: {integrity: sha512-g45ZOGzHDMe2bdYMdIvdAfCQkCTDMGBazSw1ypMowwGIee7ZQ5dU0rBJ8Jqgl+jAKIv4dbeE1jscZq9wid1Tkw==} @@ -17320,10 +17318,10 @@ snapshots: '@ag-ui/core': 0.0.46 '@ag-ui/proto': 0.0.46 - '@ag-ui/langgraph@0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ag-ui/langgraph@0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@ag-ui/client': 0.0.46 - '@ag-ui/core': 0.0.37 + '@ag-ui/core': 0.0.46 '@langchain/core': 0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)) '@langchain/langgraph-sdk': 0.1.10(@langchain/core@0.3.80(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) partial-json: 0.1.7 @@ -17336,12 +17334,12 @@ snapshots: - react - react-dom - '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@copilotkit/runtime@1.10.6(9413ba786a25fdda1f6d87c0f5568c1b))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': + '@ag-ui/mastra@0.2.0(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@copilotkit/runtime@1.10.6(a81f1decc55e512f3b92296d45b735ac))(@mastra/core@0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76))(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76)': dependencies: '@ag-ui/client': 0.0.46 - '@ag-ui/core': 0.0.37 + '@ag-ui/core': 0.0.46 '@ai-sdk/ui-utils': 1.2.11(zod@3.25.76) - '@copilotkit/runtime': 1.10.6(9413ba786a25fdda1f6d87c0f5568c1b) + '@copilotkit/runtime': 1.10.6(a81f1decc55e512f3b92296d45b735ac) '@mastra/client-js': 0.15.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) '@mastra/core': 0.20.2(openapi-types@12.1.3)(react@18.3.1)(zod@3.25.76) rxjs: 7.8.1 @@ -19236,12 +19234,12 @@ snapshots: - encoding - graphql - '@copilotkit/runtime@1.10.6(9413ba786a25fdda1f6d87c0f5568c1b)': + '@copilotkit/runtime@1.10.6(a81f1decc55e512f3b92296d45b735ac)': dependencies: '@ag-ui/client': 0.0.46 - '@ag-ui/core': 0.0.37 + '@ag-ui/core': 0.0.46 '@ag-ui/encoder': 0.0.46 - '@ag-ui/langgraph': 0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.37)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@ag-ui/langgraph': 0.0.24(@ag-ui/client@0.0.46)(@ag-ui/core@0.0.46)(@opentelemetry/api@1.9.0)(@opentelemetry/exporter-trace-otlp-proto@0.203.0(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@2.5.1(@opentelemetry/api@1.9.0))(openai@6.25.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@3.25.76))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ag-ui/proto': 0.0.46 '@anthropic-ai/sdk': 0.57.0 '@copilotkit/shared': 1.10.6 @@ -26437,10 +26435,6 @@ snapshots: '@types/stack-utils@2.0.3': {} - '@types/stripe@8.0.417': - dependencies: - stripe: 15.12.0 - '@types/striptags@0.0.5': {} '@types/tedious@4.0.14': @@ -36939,10 +36933,9 @@ snapshots: strip-outer@2.0.0: {} - stripe@15.12.0: - dependencies: + stripe@20.4.0(@types/node@18.16.9): + optionalDependencies: '@types/node': 18.16.9 - qs: 6.15.0 striptags@3.2.0: {} From ff4ee6c5fe6f2e683f2fb442a8dd4c79c8773d12 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Mon, 2 Mar 2026 20:25:33 +0700 Subject: [PATCH 295/340] feat: maximum 500 --- .../src/integrations/social.abstract.ts | 6 +++--- .../src/integrations/social/threads.provider.ts | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libraries/nestjs-libraries/src/integrations/social.abstract.ts b/libraries/nestjs-libraries/src/integrations/social.abstract.ts index 10795896..ec13f7f6 100644 --- a/libraries/nestjs-libraries/src/integrations/social.abstract.ts +++ b/libraries/nestjs-libraries/src/integrations/social.abstract.ts @@ -121,9 +121,11 @@ export abstract class SocialAbstract { json = '{}'; } + const handleError = this.handleErrors(json || '{}'); + if ( request.status === 429 || - request.status === 500 || + (request.status === 500 && !handleError) || json.includes('rate_limit_exceeded') || json.includes('Rate limit') ) { @@ -137,8 +139,6 @@ export abstract class SocialAbstract { ); } - const handleError = this.handleErrors(json || '{}'); - if (handleError?.type === 'retry') { await timer(5000); return this.fetch( diff --git a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts index eca2307d..30cbee04 100644 --- a/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts +++ b/libraries/nestjs-libraries/src/integrations/social/threads.provider.ts @@ -39,10 +39,18 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { value: string; } | undefined { + console.log(body); if (body.includes('Error validating access token')) { return { type: 'refresh-token', value: 'Threads access token expired' }; } + if (body.includes('text must be at most 500 characters')) { + return { + type: 'bad-body', + value: 'Post text exceeds 500 characters limit', + }; + } + return undefined; } @@ -270,7 +278,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider { form.append('quote_post_id', quoteId); } - const { id: contentId } = await ( + const { id: contentId, ...all } = await ( await this.fetch(`https://graph.threads.net/v1.0/${userId}/threads`, { method: 'POST', body: form, From aa37ad0a113b4140147fba3a29f58982d71ab2fd Mon Sep 17 00:00:00 2001 From: Nevo David <100117126+nevo-david@users.noreply.github.com> Date: Mon, 2 Mar 2026 23:33:22 +0700 Subject: [PATCH 296/340] Add Virlo as a new sponsor in README Added Virlo as a new sponsor with logo and description. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index eb30259e..31704e12 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,9 @@ Integrate powerful social media scheduling capabilities into your SaaS.
Mu | Sponsor | Logo | Description | |---------|:-----------------------------------------------------------------------:|-----------------| | [Hostinger](https://www.hostinger.com/?ref=postiz) | Hostinger | Hostinger is on a mission to make online success possible for anyone – from developers to aspiring bloggers and business owners | +| [Virlo](https://dev.virlo.ai/?ref=postiz) | Virlo | Virlo is the #1 social media trend spotting and all-in-one GTM tool for teams leveraging short-form video | + + # Intro From 334dda7609bbb6a7a8cb2634ad354486ec60262e Mon Sep 17 00:00:00 2001 From: Nevo David Date: Tue, 3 Mar 2026 00:46:46 +0700 Subject: [PATCH 297/340] feat: oauth --- apps/backend/src/api/api.module.ts | 6 + .../api/routes/approved-apps.controller.ts | 24 + .../src/api/routes/oauth-app.controller.ts | 54 ++ .../src/api/routes/oauth.controller.ts | 119 +++++ .../services/auth/public.auth.middleware.ts | 55 +- .../src/app/(app)/oauth/authorize/layout.tsx | 18 + .../src/app/(app)/oauth/authorize/page.tsx | 208 ++++++++ .../approved-apps/approved-apps.component.tsx | 124 +++++ .../developer/developer.component.tsx | 488 ++++++++++++++++++ .../components/layout/settings.component.tsx | 10 +- .../public-api/public.component.tsx | 43 +- apps/frontend/src/middleware.ts | 6 +- .../src/database/prisma/database.module.ts | 4 + .../database/prisma/oauth/oauth.repository.ts | 225 ++++++++ .../database/prisma/oauth/oauth.service.ts | 162 ++++++ .../src/database/prisma/schema.prisma | 49 ++ .../src/dtos/oauth/authorize-oauth.dto.ts | 31 ++ .../src/dtos/oauth/create-oauth-app.dto.ts | 22 + .../src/dtos/oauth/token-exchange.dto.ts | 19 + .../src/dtos/oauth/update-oauth-app.dto.ts | 22 + 20 files changed, 1670 insertions(+), 19 deletions(-) create mode 100644 apps/backend/src/api/routes/approved-apps.controller.ts create mode 100644 apps/backend/src/api/routes/oauth-app.controller.ts create mode 100644 apps/backend/src/api/routes/oauth.controller.ts create mode 100644 apps/frontend/src/app/(app)/oauth/authorize/layout.tsx create mode 100644 apps/frontend/src/app/(app)/oauth/authorize/page.tsx create mode 100644 apps/frontend/src/components/approved-apps/approved-apps.component.tsx create mode 100644 apps/frontend/src/components/developer/developer.component.tsx create mode 100644 libraries/nestjs-libraries/src/database/prisma/oauth/oauth.repository.ts create mode 100644 libraries/nestjs-libraries/src/database/prisma/oauth/oauth.service.ts create mode 100644 libraries/nestjs-libraries/src/dtos/oauth/authorize-oauth.dto.ts create mode 100644 libraries/nestjs-libraries/src/dtos/oauth/create-oauth-app.dto.ts create mode 100644 libraries/nestjs-libraries/src/dtos/oauth/token-exchange.dto.ts create mode 100644 libraries/nestjs-libraries/src/dtos/oauth/update-oauth-app.dto.ts diff --git a/apps/backend/src/api/api.module.ts b/apps/backend/src/api/api.module.ts index 2abae59c..806fb662 100644 --- a/apps/backend/src/api/api.module.ts +++ b/apps/backend/src/api/api.module.ts @@ -33,6 +33,9 @@ import { ThirdPartyController } from '@gitroom/backend/api/routes/third-party.co import { MonitorController } from '@gitroom/backend/api/routes/monitor.controller'; import { NoAuthIntegrationsController } from '@gitroom/backend/api/routes/no.auth.integrations.controller'; import { EnterpriseController } from '@gitroom/backend/api/routes/enterprise.controller'; +import { OAuthAppController } from '@gitroom/backend/api/routes/oauth-app.controller'; +import { ApprovedAppsController } from '@gitroom/backend/api/routes/approved-apps.controller'; +import { OAuthController } from '@gitroom/backend/api/routes/oauth.controller'; const authenticatedController = [ UsersController, @@ -49,6 +52,8 @@ const authenticatedController = [ AutopostController, SetsController, ThirdPartyController, + OAuthAppController, + ApprovedAppsController, ]; @Module({ imports: [UploadModule], @@ -60,6 +65,7 @@ const authenticatedController = [ MonitorController, EnterpriseController, NoAuthIntegrationsController, + OAuthController, ...authenticatedController, ], providers: [ diff --git a/apps/backend/src/api/routes/approved-apps.controller.ts b/apps/backend/src/api/routes/approved-apps.controller.ts new file mode 100644 index 00000000..457d27e0 --- /dev/null +++ b/apps/backend/src/api/routes/approved-apps.controller.ts @@ -0,0 +1,24 @@ +import { Controller, Delete, Get, Param } from '@nestjs/common'; +import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; +import { User } from '@prisma/client'; +import { ApiTags } from '@nestjs/swagger'; +import { OAuthService } from '@gitroom/nestjs-libraries/database/prisma/oauth/oauth.service'; + +@ApiTags('Approved Apps') +@Controller('/user/approved-apps') +export class ApprovedAppsController { + constructor(private _oauthService: OAuthService) {} + + @Get('/') + async list(@GetUserFromRequest() user: User) { + return this._oauthService.getApprovedApps(user.id); + } + + @Delete('/:id') + async revoke( + @GetUserFromRequest() user: User, + @Param('id') id: string + ) { + return this._oauthService.revokeApp(user.id, id); + } +} diff --git a/apps/backend/src/api/routes/oauth-app.controller.ts b/apps/backend/src/api/routes/oauth-app.controller.ts new file mode 100644 index 00000000..93cccaf8 --- /dev/null +++ b/apps/backend/src/api/routes/oauth-app.controller.ts @@ -0,0 +1,54 @@ +import { Body, Controller, Delete, Get, Post, Put } from '@nestjs/common'; +import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; +import { Organization } from '@prisma/client'; +import { ApiTags } from '@nestjs/swagger'; +import { OAuthService } from '@gitroom/nestjs-libraries/database/prisma/oauth/oauth.service'; +import { CheckPolicies } from '@gitroom/backend/services/auth/permissions/permissions.ability'; +import { + AuthorizationActions, + Sections, +} from '@gitroom/backend/services/auth/permissions/permission.exception.class'; +import { CreateOAuthAppDto } from '@gitroom/nestjs-libraries/dtos/oauth/create-oauth-app.dto'; +import { UpdateOAuthAppDto } from '@gitroom/nestjs-libraries/dtos/oauth/update-oauth-app.dto'; + +@ApiTags('OAuth App') +@Controller('/user/oauth-app') +export class OAuthAppController { + constructor(private _oauthService: OAuthService) {} + + @Get('/') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async getApp(@GetOrgFromRequest() org: Organization) { + return this._oauthService.getApp(org.id); + } + + @Post('/') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async createApp( + @GetOrgFromRequest() org: Organization, + @Body() body: CreateOAuthAppDto + ) { + return this._oauthService.createApp(org.id, body); + } + + @Put('/') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async updateApp( + @GetOrgFromRequest() org: Organization, + @Body() body: UpdateOAuthAppDto + ) { + return this._oauthService.updateApp(org.id, body); + } + + @Delete('/') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async deleteApp(@GetOrgFromRequest() org: Organization) { + return this._oauthService.deleteApp(org.id); + } + + @Post('/rotate-secret') + @CheckPolicies([AuthorizationActions.Create, Sections.ADMIN]) + async rotateSecret(@GetOrgFromRequest() org: Organization) { + return this._oauthService.rotateSecret(org.id); + } +} diff --git a/apps/backend/src/api/routes/oauth.controller.ts b/apps/backend/src/api/routes/oauth.controller.ts new file mode 100644 index 00000000..c10bf6c0 --- /dev/null +++ b/apps/backend/src/api/routes/oauth.controller.ts @@ -0,0 +1,119 @@ +import { + Body, + Controller, + Get, + HttpException, + HttpStatus, + Post, + Query, + Req, + Res, +} from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { Request, Response } from 'express'; +import { OAuthService } from '@gitroom/nestjs-libraries/database/prisma/oauth/oauth.service'; +import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; +import { AuthService } from '@gitroom/helpers/auth/auth.service'; +import { User } from '@prisma/client'; +import { AuthorizeOAuthQueryDto, ApproveOAuthDto } from '@gitroom/nestjs-libraries/dtos/oauth/authorize-oauth.dto'; +import { TokenExchangeDto } from '@gitroom/nestjs-libraries/dtos/oauth/token-exchange.dto'; + +@ApiTags('OAuth') +@Controller('/oauth') +export class OAuthController { + constructor( + private _oauthService: OAuthService, + private _organizationService: OrganizationService + ) {} + + @Get('/authorize') + async authorize(@Query() query: AuthorizeOAuthQueryDto) { + const app = await this._oauthService.validateAuthorizationRequest( + query.client_id + ); + + return { + app: { + name: app.name, + description: app.description, + picture: app.picture, + clientId: app.clientId, + redirectUrl: app.redirectUrl, + }, + state: query.state, + }; + } + + @Post('/authorize') + async approveOrDeny( + @Body() body: ApproveOAuthDto, + @Req() req: Request, + @Res({ passthrough: true }) res: Response + ) { + const auth = (req.headers as any).auth || req.cookies.auth; + if (!auth) { + throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED); + } + + let user: User; + try { + user = AuthService.verifyJWT(auth) as User; + if (!user) { + throw new Error(); + } + } catch { + throw new HttpException('Unauthorized', HttpStatus.UNAUTHORIZED); + } + + const app = await this._oauthService.validateAuthorizationRequest( + body.client_id + ); + + if (body.action === 'deny') { + const redirectUrl = new URL(app.redirectUrl); + redirectUrl.searchParams.set('error', 'access_denied'); + if (body.state) { + redirectUrl.searchParams.set('state', body.state); + } + return { redirect: redirectUrl.toString() }; + } + + const orgs = await this._organizationService.getOrgsByUserId(user.id); + const org = orgs.find((o: any) => !o.users?.[0]?.disabled); + if (!org) { + throw new HttpException( + 'No active organization found', + HttpStatus.BAD_REQUEST + ); + } + + const code = await this._oauthService.createAuthorizationCode( + app.id, + user.id, + org.id + ); + + const redirectUrl = new URL(app.redirectUrl); + redirectUrl.searchParams.set('code', code); + if (body.state) { + redirectUrl.searchParams.set('state', body.state); + } + return { redirect: redirectUrl.toString() }; + } + + @Post('/token') + async token(@Body() body: TokenExchangeDto) { + if (body.grant_type !== 'authorization_code') { + throw new HttpException( + { error: 'unsupported_grant_type' }, + HttpStatus.BAD_REQUEST + ); + } + + return this._oauthService.exchangeCodeForToken( + body.code, + body.client_id, + body.client_secret + ); + } +} diff --git a/apps/backend/src/services/auth/public.auth.middleware.ts b/apps/backend/src/services/auth/public.auth.middleware.ts index 75648547..184cd98e 100644 --- a/apps/backend/src/services/auth/public.auth.middleware.ts +++ b/apps/backend/src/services/auth/public.auth.middleware.ts @@ -1,11 +1,15 @@ import { HttpStatus, Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { OrganizationService } from '@gitroom/nestjs-libraries/database/prisma/organizations/organization.service'; +import { OAuthService } from '@gitroom/nestjs-libraries/database/prisma/oauth/oauth.service'; import { HttpForbiddenException } from '@gitroom/nestjs-libraries/services/exception.filter'; @Injectable() export class PublicAuthMiddleware implements NestMiddleware { - constructor(private _organizationService: OrganizationService) {} + constructor( + private _organizationService: OrganizationService, + private _oauthService: OAuthService + ) {} async use(req: Request, res: Response, next: NextFunction) { const auth = (req.headers.authorization || req.headers.Authorization) as string; @@ -14,21 +18,44 @@ export class PublicAuthMiddleware implements NestMiddleware { return; } try { - const org = await this._organizationService.getOrgByApiKey(auth); - if (!org) { - res.status(HttpStatus.UNAUTHORIZED).json({ msg: 'Invalid API key' }); - return; - } + if (auth.startsWith('pos_')) { + const authorization = await this._oauthService.getOrgByOAuthToken(auth); + if (!authorization) { + res + .status(HttpStatus.UNAUTHORIZED) + .json({ msg: 'Invalid OAuth token' }); + return; + } - if (!!process.env.STRIPE_SECRET_KEY && !org.subscription) { - res - .status(HttpStatus.UNAUTHORIZED) - .json({ msg: 'No subscription found' }); - return; - } + const org = authorization.organization; + if (!!process.env.STRIPE_SECRET_KEY && !org.subscription) { + res + .status(HttpStatus.UNAUTHORIZED) + .json({ msg: 'No subscription found' }); + return; + } - // @ts-ignore - req.org = { ...org, users: [{ users: { role: 'SUPERADMIN' } }] }; + // @ts-ignore + req.org = { ...org, users: [{ users: { role: 'SUPERADMIN' } }] }; + } else { + const org = await this._organizationService.getOrgByApiKey(auth); + if (!org) { + res + .status(HttpStatus.UNAUTHORIZED) + .json({ msg: 'Invalid API key' }); + return; + } + + if (!!process.env.STRIPE_SECRET_KEY && !org.subscription) { + res + .status(HttpStatus.UNAUTHORIZED) + .json({ msg: 'No subscription found' }); + return; + } + + // @ts-ignore + req.org = { ...org, users: [{ users: { role: 'SUPERADMIN' } }] }; + } } catch (err) { throw new HttpForbiddenException(); } diff --git a/apps/frontend/src/app/(app)/oauth/authorize/layout.tsx b/apps/frontend/src/app/(app)/oauth/authorize/layout.tsx new file mode 100644 index 00000000..fd32dcea --- /dev/null +++ b/apps/frontend/src/app/(app)/oauth/authorize/layout.tsx @@ -0,0 +1,18 @@ +import { Metadata } from 'next'; +import { ReactNode } from 'react'; + +export const metadata: Metadata = { + title: 'Authorize Application', +}; + +export default async function OAuthLayout({ + children, +}: { + children: ReactNode; +}) { + return ( +
+ {children} +
+ ); +} diff --git a/apps/frontend/src/app/(app)/oauth/authorize/page.tsx b/apps/frontend/src/app/(app)/oauth/authorize/page.tsx new file mode 100644 index 00000000..14d203d8 --- /dev/null +++ b/apps/frontend/src/app/(app)/oauth/authorize/page.tsx @@ -0,0 +1,208 @@ +'use client'; + +import { useCallback, useEffect, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import { Logo } from '@gitroom/frontend/components/new-layout/logo'; + +export default function OAuthAuthorizePage() { + const searchParams = useSearchParams(); + const fetch = useFetch(); + const [appInfo, setAppInfo] = useState(null); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(true); + const [submitting, setSubmitting] = useState(false); + + const clientId = searchParams.get('client_id'); + const responseType = searchParams.get('response_type'); + const state = searchParams.get('state'); + + useEffect(() => { + if (!clientId || !responseType) { + setError('Missing required parameters (client_id, response_type)'); + setLoading(false); + return; + } + if (responseType !== 'code') { + setError('Only response_type=code is supported'); + setLoading(false); + return; + } + + const params = new URLSearchParams({ + client_id: clientId, + response_type: responseType, + ...(state ? { state } : {}), + }); + + fetch(`/oauth/authorize?${params}`) + .then((r) => r.json()) + .then((data) => { + if (data.statusCode && data.statusCode >= 400) { + setError(data.message || 'Invalid OAuth request'); + } else { + setAppInfo(data); + } + setLoading(false); + }) + .catch(() => { + setError('Failed to validate OAuth request'); + setLoading(false); + }); + }, [clientId, responseType, state]); + + const handleAction = useCallback( + async (action: 'approve' | 'deny') => { + setSubmitting(true); + try { + const result = await ( + await fetch('/oauth/authorize', { + method: 'POST', + body: JSON.stringify({ + client_id: clientId, + state, + action, + }), + }) + ).json(); + + if (result.redirect) { + window.location.href = result.redirect; + } + } catch { + setError('Failed to process authorization'); + setSubmitting(false); + } + }, + [clientId, state] + ); + + if (loading) { + return ( +
+
+
+
+
+
+
+ +
+
+ Please wait... +
+
+
+
+
+
+ ); + } + + if (error) { + return ( +
+
+
+
+
+
+
+ +
+
+ + + +
+
+ Authorization Error +
+
+ {error} +
+
+
+ ); + } + + if (!appInfo) { + return null; + } + + return ( +
+
+
+
+
+ +
+
+ +
+ +
+
+ {appInfo.app.picture?.path ? ( + {appInfo.app.name} + ) : ( +
+ {appInfo.app.name?.[0]?.toUpperCase() || '?'} +
+ )} +

+ {appInfo.app.name} +

+ {appInfo.app.description && ( +
+ {appInfo.app.description} +
+ )} +
+ +
+
+ This application is requesting access to your Postiz account. It + will be able to: +
+
    +
  • Access your integrations and channels
  • +
  • Create and schedule posts on your behalf
  • +
  • Read your post analytics
  • +
+
+ +
+ + +
+
+
+
+ ); +} diff --git a/apps/frontend/src/components/approved-apps/approved-apps.component.tsx b/apps/frontend/src/components/approved-apps/approved-apps.component.tsx new file mode 100644 index 00000000..5d4dffaf --- /dev/null +++ b/apps/frontend/src/components/approved-apps/approved-apps.component.tsx @@ -0,0 +1,124 @@ +'use client'; + +import { FC, Fragment, useCallback } from 'react'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import useSWR from 'swr'; +import { Button } from '@gitroom/react/form/button'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +const useApprovedApps = () => { + const fetch = useFetch(); + const load = useCallback(async () => { + return (await fetch('/user/approved-apps')).json(); + }, []); + return useSWR('approved-apps', load, { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + }); +}; + +export const ApprovedAppsComponent: FC = () => { + const fetch = useFetch(); + const toaster = useToaster(); + const t = useT(); + const { data: apps, mutate } = useApprovedApps(); + + const revokeApp = useCallback( + (app: any) => async () => { + if ( + await deleteDialog( + t( + 'are_you_sure_revoke_access', + `Are you sure you want to revoke access for ${app.oauthApp?.name}?`, + { name: app.oauthApp?.name } + ) + ) + ) { + try { + await fetch(`/user/approved-apps/${app.id}`, { + method: 'DELETE', + }); + toaster.show( + t('access_revoked', 'Access revoked successfully'), + 'success' + ); + mutate(); + } catch { + toaster.show(t('failed_to_revoke', 'Failed to revoke access'), 'warning'); + } + } + }, + [] + ); + + if (apps === undefined) { + return null; + } + + return ( +
+
+

+ {t('approved_apps', 'Approved Apps')} +

+
+ {t( + 'apps_you_have_authorized', + 'Applications you have authorized to access your Postiz account.' + )} +
+
+ +
+ {!apps?.length ? ( +
+ {t('no_approved_apps', 'No approved apps yet.')} +
+ ) : ( +
+ {apps.map((app: any) => ( +
+
+ {app.oauthApp?.picture?.path ? ( + {app.oauthApp.name} + ) : ( +
+ {app.oauthApp?.name?.[0]?.toUpperCase() || '?'} +
+ )} +
+
+ {app.oauthApp?.name} +
+ {app.oauthApp?.description && ( +
+ {app.oauthApp.description} +
+ )} +
+ {t('authorized_on', 'Authorized on')}{' '} + {new Date(app.createdAt).toLocaleDateString()} +
+
+
+ +
+ ))} +
+ )} +
+
+ ); +}; diff --git a/apps/frontend/src/components/developer/developer.component.tsx b/apps/frontend/src/components/developer/developer.component.tsx new file mode 100644 index 00000000..92cf6c65 --- /dev/null +++ b/apps/frontend/src/components/developer/developer.component.tsx @@ -0,0 +1,488 @@ +'use client'; + +import { FC, useCallback, useState } from 'react'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; +import useSWR from 'swr'; +import { Button } from '@gitroom/react/form/button'; +import { useToaster } from '@gitroom/react/toaster/toaster'; +import { useDecisionModal, useModals } from '@gitroom/frontend/components/layout/new-modal'; +import { MediaBox } from '@gitroom/frontend/components/media/media.component'; +import copy from 'copy-to-clipboard'; +import { useT } from '@gitroom/react/translation/get.transation.service.client'; + +const useOAuthApp = () => { + const fetch = useFetch(); + const load = useCallback(async () => { + const res = await fetch('/user/oauth-app'); + const text = await res.text(); + if (!text || text === 'null' || text === 'false') { + return null; + } + return JSON.parse(text); + }, []); + return useSWR('oauth-app', load, { + revalidateOnFocus: false, + revalidateOnReconnect: false, + revalidateIfStale: false, + }); +}; + +export const DeveloperComponent: FC = () => { + const fetch = useFetch(); + const toaster = useToaster(); + const decision = useDecisionModal(); + const modals = useModals(); + const t = useT(); + const { data: app, mutate } = useOAuthApp(); + const [plaintextSecret, setPlaintextSecret] = useState(null); + const [creating, setCreating] = useState(false); + const [editing, setEditing] = useState(false); + + const [name, setName] = useState(''); + const [description, setDescription] = useState(''); + const [redirectUrl, setRedirectUrl] = useState(''); + const [pictureId, setPictureId] = useState(undefined); + const [picturePath, setPicturePath] = useState(undefined); + + const startEditing = useCallback(() => { + if (!app) return; + setName(app.name || ''); + setDescription(app.description || ''); + setRedirectUrl(app.redirectUrl || ''); + setPictureId(app.pictureId || undefined); + setPicturePath(app.picture?.path || undefined); + setEditing(true); + }, [app]); + + const changeMedia = useCallback((selected: { id: string; path: string }[]) => { + const media = Array.isArray(selected) ? selected[0] : selected; + if (media) { + setPictureId(media.id); + setPicturePath(media.path); + } + }, []); + + const openMedia = useCallback(() => { + modals.openModal({ + title: t('media_library', 'Media Library'), + askClose: false, + closeOnEscape: true, + fullScreen: true, + size: 'calc(100% - 80px)', + height: 'calc(100% - 80px)', + children: (close: () => void) => ( + + ), + }); + }, [modals, t, changeMedia]); + + const createApp = useCallback(async () => { + if (!name || !redirectUrl) { + toaster.show('Name and Redirect URL are required', 'warning'); + return; + } + try { + const result = await ( + await fetch('/user/oauth-app', { + method: 'POST', + body: JSON.stringify({ + name, + description, + redirectUrl, + pictureId, + }), + }) + ).json(); + + if (result.clientSecret) { + setPlaintextSecret(result.clientSecret); + toaster.show( + 'App created! Copy your client secret now - it will only be shown once.', + 'success' + ); + } + setCreating(false); + mutate(); + } catch { + toaster.show('Failed to create app', 'warning'); + } + }, [name, description, redirectUrl, pictureId]); + + const updateApp = useCallback(async () => { + try { + await fetch('/user/oauth-app', { + method: 'PUT', + body: JSON.stringify({ + name, + description, + redirectUrl, + pictureId, + }), + }); + toaster.show('App updated', 'success'); + setEditing(false); + mutate(); + } catch { + toaster.show('Failed to update app', 'warning'); + } + }, [name, description, redirectUrl, pictureId]); + + const rotateSecret = useCallback(async () => { + const approved = await decision.open({ + title: 'Rotate Client Secret?', + description: + 'This will generate a new client secret and invalidate the current one. Any integrations using the old secret will stop working.', + approveLabel: 'Rotate', + cancelLabel: 'Cancel', + }); + if (!approved) return; + try { + const result = await ( + await fetch('/user/oauth-app/rotate-secret', { method: 'POST' }) + ).json(); + if (result.clientSecret) { + setPlaintextSecret(result.clientSecret); + toaster.show( + 'Secret rotated! Copy your new client secret now.', + 'success' + ); + mutate(); + } + } catch { + toaster.show('Failed to rotate secret', 'warning'); + } + }, [decision]); + + const deleteApp = useCallback(async () => { + const approved = await decision.open({ + title: 'Delete OAuth App?', + description: + 'This will delete the OAuth application and revoke all user authorizations. This action cannot be undone.', + approveLabel: 'Delete', + cancelLabel: 'Cancel', + }); + if (!approved) return; + try { + await fetch('/user/oauth-app', { method: 'DELETE' }); + toaster.show('OAuth app deleted', 'success'); + setPlaintextSecret(null); + mutate(); + } catch { + toaster.show('Failed to delete app', 'warning'); + } + }, [decision]); + + const copyToClipboard = useCallback( + (text: string, label: string) => { + copy(text); + toaster.show(`${label} copied to clipboard`, 'success'); + }, + [] + ); + + if (app === undefined) { + return null; + } + + if (!app && !creating) { + return ( +
+
+

{t('developer', 'Developer')}

+
+ {t( + 'create_an_oauth_application', + 'Create an OAuth application to allow third-party integrations with Postiz on behalf of your users.' + )} +
+ + {t( + 'read_the_oauth_documentation', + 'Read the OAuth documentation.' + )} + +
+
+ +
+
+
+ ); + } + + if (creating && !app) { + return ( +
+
+

+ {t('create_oauth_app', 'Create OAuth App')} +

+
+ {t( + 'fill_in_the_details_for_your_oauth_application', + 'Fill in the details for your OAuth application.' + )} +
+
+
+
+ + setName(e.target.value)} + placeholder="My Application" + maxLength={100} + /> +
+
+ +